diff options
Diffstat (limited to 'src/modules/dbpgsql.c')
-rw-r--r-- | src/modules/dbpgsql.c | 382 |
1 files changed, 382 insertions, 0 deletions
diff --git a/src/modules/dbpgsql.c b/src/modules/dbpgsql.c new file mode 100644 index 00000000..91c187ee --- /dev/null +++ b/src/modules/dbpgsql.c @@ -0,0 +1,382 @@ +/* + Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/** + * dbpgsql.c + * PostgreSQL driver file + * Handles connection and queries to PostgreSQL backend + */ + +#include "libpq-fe.h" /* PostgreSQL header */ + +#include "dbmail.h" +#define THIS_MODULE "sql" + +#define BYTEAOID 17 + +const char * db_get_sql(sql_fragment_t frag) +{ + switch(frag) { + case SQL_TO_CHAR: + return "TO_CHAR(%s, 'YYYY-MM-DD HH24:MI:SS' )"; + break; + case SQL_TO_DATE: + return "TO_DATE(%s,'YYYY-MM-DD')"; + break; + case SQL_TO_DATETIME: + return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')"; + break; + case SQL_TO_UNIXEPOCH: + return "ROUND(DATE_PART('epoch',%s))"; + break; + case SQL_CURRENT_TIMESTAMP: + return "CURRENT_TIMESTAMP"; + break; + case SQL_EXPIRE: + return "NOW() - INTERVAL '%d DAY'"; + break; + case SQL_IGNORE: + case SQL_BINARY: + return ""; + break; + case SQL_REGEXP: + return "~"; + break; + case SQL_SENSITIVE_LIKE: + return "LIKE"; + break; + case SQL_INSENSITIVE_LIKE: + return "ILIKE"; + break; + case SQL_ENCODE_ESCAPE: + return "ENCODE(%s::bytea,'escape')"; + break; + case SQL_STRCASE: + return "LOWER(%s)"; + break; + case SQL_PARTIAL: + return "SUBSTRING(%s,0,255)"; + break; + + } + return NULL; +} + +static PGconn *conn = NULL; +static PGresult *res = NULL; +static void _create_binary_table(void); +static void _free_binary_table(void); +static void _set_binary_table(unsigned row, unsigned field); +static char*** bintbl = NULL; +extern db_param_t _db_params; + +int db_connect() +{ + GString *cs = g_string_new(""); + + /* Warn if both the host= and sqlsocket= parameters are defined. + * Prefer just the socket if given. */ + if (strlen(_db_params.sock) && strlen(_db_params.host) + && strncmp(_db_params.host, "localhost", FIELDSIZE != 0)) { + TRACE(TRACE_WARNING, "PostgreSQL socket and a hostname other " + "than localhost have both been defined. The socket " + "will be used and the hostname will be ignored."); + g_string_append_printf(cs, "host='%s'", _db_params.sock); + } else if (strlen(_db_params.sock)) { + g_string_append_printf(cs, "host='%s'", _db_params.sock); + } else { + g_string_append_printf(cs, "host='%s'", _db_params.host); + } + + /* Add the username and password. */ + g_string_append_printf(cs, + " user='%s' password='%s' dbname='%s'", + _db_params.user, _db_params.pass, _db_params.db); + + /* Finally the port, if given. */ + if (_db_params.port != 0) + g_string_append_printf(cs, " port='%d'", _db_params.port); + + conn = PQconnectdb(cs->str); + + g_string_free(cs, TRUE); + + if (PQstatus(conn) == CONNECTION_BAD) { + TRACE(TRACE_ERROR, "PQconnectdb failed: %s", PQerrorMessage(conn)); + return -1; + } + + return 0; +} + +int db_check_connection() { + if (!conn) { + /* There seem to be some circumstances which cause + * db_check_connection to be called before db_connect. */ + TRACE(TRACE_ERROR, "connection with database invalid, retrying"); + return db_connect(); + } + + if (PQstatus(conn) == CONNECTION_BAD) { + PQreset(conn); + if (PQstatus(conn) == CONNECTION_BAD) { + TRACE(TRACE_ERROR, "connection with database gone bad"); + return -1; + } + } + return 0; +} + +int db_disconnect() +{ + if (res) + db_free_result(); + PQfinish(conn); + conn = NULL; + + return 0; +} + +unsigned db_num_rows() +{ + int num_rows; + if (!res) + return 0; + + num_rows = PQntuples(res); + if (num_rows < 0) + return 0; + else + return (unsigned) num_rows; +} + +unsigned db_num_fields() +{ + int num_fields; + + if (!res) + return 0; + num_fields = PQnfields(res); + if (num_fields < 0) + return 0; + else + return (unsigned) num_fields; +} + +void db_free_result() +{ + _free_binary_table(); + if (res != NULL) + PQclear(res); + res = NULL; +} + +static void _create_binary_table(void){ + unsigned rows, fields, i; + rows = db_num_rows(); fields = db_num_fields(); + + if(!bintbl){ + bintbl = (char***)g_malloc(sizeof(char**) * rows); + memset(bintbl, 0, sizeof(char**) * rows); + for(i = 0; i < rows; i++){ + *(bintbl + i) = (char**)g_malloc(sizeof(char*) * fields); + memset(*(bintbl + i), 0, sizeof(char*) * fields); + } + } +} + +static void _free_binary_table(void){ + unsigned rows, fields, i, j; + rows = db_num_rows(); fields = db_num_fields(); + + if(bintbl){ + for(i = 0; i < rows; i++){ + for(j = 0; j < fields; j++) + if(bintbl[i][j]) + g_free(bintbl[i][j]); + g_free(bintbl[i]); + } + g_free(bintbl); + bintbl = NULL; + } + +} +static void _set_binary_table(unsigned row, unsigned field){ + unsigned char* tmp; + size_t result_size; + if(!bintbl[row][field]){ + tmp = PQunescapeBytea((const unsigned char *)PQgetvalue(res, row, field), &result_size); + bintbl[row][field] = (char*)g_malloc(result_size + 1); + memcpy(bintbl[row][field], tmp, result_size); + PQfreemem(tmp); tmp = NULL; + bintbl[row][field][result_size] = '\0'; + } +} + +const char *db_get_result(unsigned row, unsigned field) +{ + if (!res) { + TRACE(TRACE_WARNING, "result set is NULL"); + return NULL; + } + + if ((row > db_num_rows()) || (field > db_num_fields())) { + TRACE(TRACE_WARNING, "row = %u or field = %u out of range", row, field); + return NULL; + } + if(PQftype(res, field) == BYTEAOID){ + _create_binary_table(); + _set_binary_table(row, field); + return bintbl[row][field]; + } + return PQgetvalue(res, row, field); +} + +u64_t db_insert_result(const char *sequence_identifier) +{ + char query[DEF_QUERYSIZE]; + memset(query,0,DEF_QUERYSIZE); + + u64_t insert_result; + + /* postgres uses the currval call on a sequence to determine + * the result value of an insert query */ + snprintf(query, DEF_QUERYSIZE, + "SELECT currval('%s%s_seq')",_db_params.pfx, sequence_identifier); + + db_query(query); + if (db_num_rows() == 0) { + db_free_result(); + return 0; + } + insert_result = strtoull(db_get_result(0, 0), NULL, 10); + db_free_result(); + return insert_result; +} + +int db_query(const char *q) +{ + int PQresultStatusVar; + + db_free_result(); + + g_return_val_if_fail(q != NULL,DM_EQUERY); + + if (db_check_connection()) + return DM_EQUERY; + + TRACE(TRACE_DEBUG, "[%s]", q); + + if (! (res = PQexec(conn, q))) + return DM_EQUERY; + + PQresultStatusVar = PQresultStatus(res); + + if (PQresultStatusVar != PGRES_COMMAND_OK && PQresultStatusVar != PGRES_TUPLES_OK) { + TRACE(TRACE_ERROR, "query failed [%s] : [%s]\n", q, PQresultErrorMessage(res)); + db_free_result(); + return DM_EQUERY; + } + + return 0; +} + +unsigned long db_escape_string(char *to, + const char *from, unsigned long length) +{ + assert(from); + return PQescapeString(to, from, length); +} + +char * db_escape_binary(const char *from, unsigned long length) +{ + size_t to_length; + unsigned char *esc_to; + char *to; + + esc_to = PQescapeBytea((const unsigned char *)from, length, &to_length); + to = g_strndup((char *)esc_to, to_length); + PQfreemem(esc_to); + return to; +} + +int db_savepoint(const char *id) +{ + return db_savepoint_transaction(id); +} +int db_savepoint_rollback(const char *id) +{ + return db_rollback_savepoint_transaction(id); +} + +int db_do_cleanup(const char **tables, int num_tables) +{ + int result = 0; + int i; + char the_query[DEF_QUERYSIZE]; + + for (i = 0; i < num_tables; i++) { + snprintf(the_query, DEF_QUERYSIZE, "VACUUM %s%s", _db_params.pfx,tables[i]); + if (db_query(the_query) == -1) { + TRACE(TRACE_ERROR, "error vacuuming table [%s%s]", + _db_params.pfx,tables[i]); + result = -1; + } + } + return result; +} + +u64_t db_get_length(unsigned row, unsigned field) +{ + if (!res) { + TRACE(TRACE_WARNING, "result set is NULL"); + return -1; + } + + if ((row >= db_num_rows()) || (field >= db_num_fields())) { + TRACE(TRACE_ERROR, "row = %u or field = %u out of range", row, field); + return -1; + } + if(PQftype(res, field) == BYTEAOID){ + _create_binary_table(); + _set_binary_table(row, field); + return strlen(bintbl[row][field]); + } + return PQgetlength(res, row, field); +} + +u64_t db_get_affected_rows() + +{ + char *s; + if (! res) return 0; + if ((s = PQcmdTuples(res)) != NULL) + return strtoull(s, NULL, 10); + return 0; +} + +void *db_get_result_set() +{ + return (void *) res; +} + +void db_set_result_set(void *the_result_set) +{ + res = (PGresult *) the_result_set; +} |