postgreSQL support

Manfred Haelters <[email protected]> Tue, 25 Jan 2005 15:30:50 +0100
Newsgroups gmane.comp.apache.mod-log-sql
Message-ID <[email protected]>
I want to get mod_log_sql working with postgreSQL, but it the support is 
not yet plugged in. This is why I tried to complete the 
mod_lod_sql_pgsql.c.  (see attachment)

The result works for me, I did not yet have the time to fully test it 
(small tests work).

The problems I've encountered are the following:
1/ PQescapeString does not place the ' around the strings. So I had to 
do this manually.
2/ I limited the queries to 250 characters (in the escaped string), 
since otherwise big request don't get logged but are put in the preserve 
file.
3/ I always had to return the escaped string even when the from_str was 
empty (in order to get the ' in place)
4/ I removed the check for the unexistant table, since I don't know how 
to do this.

I'm not a C developper, so if I did it wrong, I would appreciate to 
receive the remarks. Also If someone knows how to use the dbi interface 
(especially the config in apache), please let me know

Manfred

_______________________________________________
Download the latest version at http://www.outoforder.cc/projects/apache/mod_log_sql/

To unsubscribe send an e-mail to 
mod_log_sql-unsubscribe-7qY7E20V6GW73k+5HYS8LQqVMODqnSLI@public.gmane.org
mod_log_sql_pgsql.c (text/plain, 6.9 KB)
/* $Id: mod_log_sql_pgsql.c 120 2004-04-17 15:14:12Z [email protected] $ */

#define WITH_APACHE20 1

#if defined(WITH_APACHE20)
#	include "apache20.h"
#elif defined(WITH_APACHE13)
#	include "apache13.h"
#else
#	error Unsupported Apache version
#endif

/* Undefine these to prevent conflicts between Apache ap_config_auto.h and 
 * my config.h. Only really needed for Apache < 2.0.48, but it can't hurt.
 */
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION

#include "config.h"

#include "mod_log_sql.h"

#include "libpq-fe.h"

/* Connect to the PGSQL database */
static logsql_opendb_ret log_sql_pgsql_connect(server_rec *s, logsql_dbconnection *db)
{
	const char *host = apr_table_get(db->parms,"hostname");
	const char *user = apr_table_get(db->parms,"username");
	const char *passwd = apr_table_get(db->parms,"password");
	const char *database = apr_table_get(db->parms,"database");
	const char *s_tcpport = apr_table_get(db->parms,"port");
	
	db->handle = PQsetdbLogin(host, s_tcpport, NULL, NULL, database, user, passwd);

	if (PQstatus(db->handle) == CONNECTION_OK) {
		log_error(APLOG_MARK,APLOG_DEBUG,0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
				host, s_tcpport, database, user);
		return LOGSQL_OPENDB_SUCCESS;
	} else {
		log_error(APLOG_MARK,APLOG_DEBUG,0, s,"mod_log_sql: database connection error: %s",
				PQerrorMessage(db->handle));
		log_error(APLOG_MARK,APLOG_DEBUG, 0, s,"HOST: '%s' PORT: '%s' DB: '%s' USER: '%s'",
				host, s_tcpport, database, user);
		return LOGSQL_OPENDB_FAIL;
	}
}

/* Close the DB link */
static void log_sql_pgsql_close(logsql_dbconnection *db)
{
	PQfinish((PGconn*)(db->handle));
}

/* Routine to escape the 'dangerous' characters that would otherwise
 * corrupt the INSERT string: ', \, and "
 * Also PQescapeString does not place the ' around the string. So we have
 * to do this manually
 */
static const char *log_sql_pgsql_escape(const char *from_str, apr_pool_t *p, 
								logsql_dbconnection *db)
{
	char *temp;
	if (!from_str)
		return NULL;
	else {
	  	char *to_str;
		unsigned long length = strlen(from_str);
		unsigned long retval;

		/* Pre-allocate a new string that could hold twice the original, which would only
		 * happen if the whole original string was 'dangerous' characters. 
		 * And forsee the space for the 2 '
		 */
		temp = to_str = (char *) apr_palloc(p, length * 2 + 3);
		if (!to_str) {
			return from_str;
		}

		*temp = '\'';
		temp++;
		
		retval = PQescapeString(temp, from_str, length);
		
		/* avoid the string to be tolong for the sql database*/
		if (retval > 250) retval = 250;
		
		*(temp+retval) = '\'';
		*(temp+retval+1) = '\0';
		
		/* We must always return the to_str, because we always need the ' added */
//		if (retval)
		  return to_str;
//		else
//		  return from_str;
	}
}

/* Run a sql insert query and return a categorized error or success */
static logsql_query_ret log_sql_pgsql_query(request_rec *r,logsql_dbconnection *db,
								const char *query)
{
	PGresult *result;
	void (*handler) (int);
	unsigned int real_error = 0;
	/*const char *real_error_str = NULL;*/

	PGconn *conn = db->handle;

	if (PQstatus(conn) != CONNECTION_OK) {
		return LOGSQL_QUERY_NOLINK;
	}
	/* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
	/* Does postgresql do this also ??? */
	handler = signal(SIGPIPE, SIG_IGN);
	
	result = PQexec(conn, query);
	/* Run the query */
	if (PQresultStatus(result) == PGRES_COMMAND_OK) {
		signal(SIGPIPE, handler);
		PQclear(result);
		return LOGSQL_QUERY_SUCCESS;
	}
	/* Check to see if the error is "nonexistent table" */
	/*	removed ... don't know how ! (sorry)
	real_error = mysql_errno(dblink);

	if (real_error == ER_NO_SUCH_TABLE) {
		log_error(APLOG_MARK,APLOG_ERR,0, r->server,"table does not exist, preserving query");
		signal(SIGPIPE, handler);
		PQclear(result);
		return LOGSQL_QUERY_NOTABLE;
	}*/
	
	/* Restore SIGPIPE to its original handler function */
	signal(SIGPIPE, handler);
	PQclear(result);
	return LOGSQL_QUERY_FAIL;
}

/* Create table table_name of type table_type. */
static logsql_table_ret log_sql_pgsql_create(request_rec *r, logsql_dbconnection *db,
						logsql_tabletype table_type, const char *table_name)
{
	PGresult *result;
	const char *tabletype = apr_table_get(db->parms,"tabletype");
	void (*handler) (int);
	char *type_suffix = NULL;

	char *create_prefix = "create table if not exists `";
	char *create_suffix = NULL;
	char *create_sql;

	PGconn *conn = db->handle;

/*	if (!global_config.createtables) {
		return APR_SUCCESS;
	}*/

	switch (table_type) {
	case LOGSQL_TABLE_ACCESS:
		create_suffix = 
	"` (id char(19),\
       agent varchar(255),\
       bytes_sent int unsigned,\
       child_pid smallint unsigned,\
       cookie varchar(255),\
	   machine_id varchar(25),\
       request_file varchar(255),\
       referer varchar(255),\
       remote_host varchar(50),\
       remote_logname varchar(50),\
       remote_user varchar(50),\
       request_duration smallint unsigned,\
       request_line varchar(255),\
       request_method varchar(10),\
       request_protocol varchar(10),\
       request_time char(28),\
       request_uri varchar(255),\
	   request_args varchar(255),\
       server_port smallint unsigned,\
       ssl_cipher varchar(25),\
       ssl_keysize smallint unsigned,\
       ssl_maxkeysize smallint unsigned,\
       status smallint unsigned,\
       time_stamp int unsigned,\
       virtual_host varchar(255))";
		break;
	case LOGSQL_TABLE_COOKIES:
	case LOGSQL_TABLE_HEADERSIN:
	case LOGSQL_TABLE_HEADERSOUT:
	case LOGSQL_TABLE_NOTES:
		create_suffix = 
	"` (id char(19),\
	   item varchar(80),\
	   val varchar(80))";
		break;
	}
	
	if (tabletype) {
		type_suffix = apr_pstrcat(r->pool, " TYPE=", 
							tabletype, NULL);
	}
	/* Find memory long enough to hold the whole CREATE string + \0 */
	create_sql = apr_pstrcat(r->pool, create_prefix, table_name, create_suffix,
						type_suffix, NULL);

	log_error(APLOG_MARK,APLOG_DEBUG,0, r->server,"create string: %s", create_sql);

	if (PQstatus(conn) != CONNECTION_OK) {
		return LOGSQL_QUERY_NOLINK;
	}
	/* A failed mysql_query() may send a SIGPIPE, so we ignore that signal momentarily. */
	handler = signal(SIGPIPE, SIG_IGN);

	/* Run the create query */
	result = PQexec(conn, create_sql);
  	if (PQresultStatus(result) != PGRES_COMMAND_OK) {
		log_error(APLOG_MARK,APLOG_ERR,0, r->server,"failed to create table: %s",
			table_name);
		signal(SIGPIPE, handler);
		PQclear(result);
		return LOGSQL_TABLE_FAIL;
	}
	signal(SIGPIPE, handler);
	PQclear(result);
	return LOGSQL_TABLE_SUCCESS;
}

static char *supported_drivers[] = {"pgsql",NULL};
static logsql_dbdriver pgsql_driver = {
	"pgsql", NULL,
	log_sql_pgsql_connect,	/* open DB connection */
	log_sql_pgsql_close,	/* close DB connection */
	log_sql_pgsql_escape,	/* escape query */
	log_sql_pgsql_query,	/* insert query */
	log_sql_pgsql_create	/* create table */
};

LOGSQL_REGISTER(pgsql) {
	log_sql_register_driver(p,&pgsql_driver);
	LOGSQL_REGISTER_RETURN;
}