123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817 |
- /*
- * $Id$
- *
- * POSTGRES module, portions of this code were templated using
- * the mysql module, thus it's similarity.
- *
- *
- * Copyright (C) 2003 August.Net Services, LLC
- *
- * This file is part of ser, a free SIP server.
- *
- * ser 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
- *
- * For a license to use the ser software under conditions
- * other than those described here, or to purchase support for this
- * software, please contact iptel.org by e-mail at the following addresses:
- * [email protected]
- *
- * ser 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ---
- *
- * History
- * -------
- * 2003-04-06 initial code written (Greg Fausak/Andy Fullford)
- *
- */
- #define MAXCOLUMNS 512
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include "../../dprint.h"
- #include "../../mem/mem.h"
- #include "db_utils.h"
- #include "defs.h"
- #include "dbase.h"
- #include "con_postgres.h"
- #include "aug_std.h"
- long getpid();
- static char sql_buf[SQL_BUF_LEN];
- static int submit_query(db_con_t* _h, const char* _s);
- static int connect_db(db_con_t* _h, const char* _db_url);
- static int disconnect_db(db_con_t* _h);
- static int free_query(db_con_t* _h);
- /*
- ** connect_db Connect to a database
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- ** char *_db_url the database to connect to
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- **
- ** Notes :
- ** If currently connected, a disconnect is done first
- ** if this process did the connection, otherwise the
- ** disconnect is not done before the new connect.
- ** This is important, as the process that owns the connection
- ** should clean up after itself.
- */
- static int connect_db(db_con_t* _h, const char* _db_url)
- {
- char* user, *password, *host, *port, *database;
- if(! _h)
- {
- PLOG("connect_db", "must pass db_con_t!");
- return(-1);
- }
- if(CON_CONNECTED(_h))
- {
- DLOG("connect_db", "disconnect first!");
- disconnect_db(_h);
- }
- /*
- ** CON_CONNECTED(_h) is now 0, set by disconnect_db()
- */
- /*
- ** Note :
- ** Make a scratch pad copy of given SQL URL.
- ** all memory allocated to this connection is rooted
- ** from this.
- ** This is an important concept.
- ** as long as you always allocate memory using the function:
- ** mem = aug_alloc(size, CON_SQLURL(_h)) or
- ** str = aug_strdup(string, CON_SQLURL(_h))
- ** where size is the amount of memory, then in the future
- ** when CON_SQLURL(_h) is freed (in the function disconnect_db())
- ** all other memory allocated in this manner is freed.
- ** this will keep memory leaks from happening.
- */
- CON_SQLURL(_h) = aug_strdup((char *) _db_url, (char *) _h);
- /*
- ** get the connection parameters parsed from the db_url string
- ** it looks like: sql://username:userpass@dbhost:dbport/dbname
- ** username/userpass : name and password for the database
- ** dbhost : the host name or ip address hosting the database
- ** dbport : the port to connect to database on
- ** dbname : the name of the database
- */
- if(parse_sql_url(CON_SQLURL(_h),
- &user,&password,&host,&port,&database) < 0)
- {
- char buf[256];
- sprintf(buf, "Error while parsing %s", _db_url);
- PLOG("connect_db", buf);
- aug_free(CON_SQLURL(_h));
- return -3;
- }
- /*
- ** finally, actually connect to the database
- */
- CON_CONNECTION(_h) =
- PQsetdbLogin(host,port,NULL,NULL,database,user, password);
- if(CON_CONNECTION(_h) == 0
- || PQstatus(CON_CONNECTION(_h)) != CONNECTION_OK)
- {
- PLOG("connect_db", PQerrorMessage(CON_CONNECTION(_h)));
- PQfinish(CON_CONNECTION(_h));
- aug_free(CON_SQLURL(_h));
- return -4;
- }
- CON_PID(_h) = getpid();
- /*
- ** all is well, database was connected, we can now submit_query's
- */
- CON_CONNECTED(_h) = 1;
- return 0;
- }
- /*
- ** disconnect_db Disconnect a database
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- **
- ** Notes :
- ** All memory associated with CON_SQLURL is freed.
- **
- */
- static int disconnect_db(db_con_t* _h)
- {
- if(! _h)
- {
- DLOG("disconnect_db", "null db_con_t, ignored!\n");
- return(0);
- }
- /*
- ** free lingering memory tree if it exists
- */
- if(CON_SQLURL(_h))
- {
- aug_free(CON_SQLURL(_h));
- CON_SQLURL(_h) = (char *) 0;
- }
- /*
- ** ignore if there is no current connection
- */
- if(CON_CONNECTED(_h) != 1)
- {
- DLOG("disconnect_db", "not connected, ignored!\n");
- return 0;
- }
- /*
- ** make sure we are trying to close a connection that was opened
- ** by our process ID
- */
- if(CON_PID(_h) == getpid())
- {
- PQfinish(CON_CONNECTION(_h));
- CON_CONNECTED(_h) = 0;
- }
- else
- {
- DLOG("disconnect_db",
- "attempt to release connection not owned, ignored!\n");
- }
- return 0;
- }
- /*
- ** db_init initialize database for future queries
- **
- ** Arguments :
- ** char *_sqlurl; sql database to open
- **
- ** Returns :
- ** db_con_t * NULL upon error
- ** db_con_t * if successful
- **
- ** Notes :
- ** db_init must be called prior to any database
- ** functions.
- */
- db_con_t *db_init(const char* _sqlurl)
- {
- db_con_t* res;
- DLOG("db_init", "entry");
- /*
- ** this is the root memory for this database connection.
- */
- res = aug_alloc(sizeof(db_con_t) + sizeof(struct con_postgres), NULL);
- memset(res, 0, sizeof(db_con_t) + sizeof(struct con_postgres));
- if (connect_db(res, _sqlurl) < 0)
- {
- PLOG("db_init", "Error while trying to open database, FATAL\n");
- aug_free(res);
- return((db_con_t *) 0);
- }
- return res;
- }
- /*
- ** db_close last function to call when db is no longer needed
- **
- ** Arguments :
- ** db_con_t * the connection to shut down, as supplied by db_init()
- **
- ** Returns :
- ** (void)
- **
- ** Notes :
- ** All memory and resources are freed.
- */
- void db_close(db_con_t* _h)
- {
- DLOG("db_close", "entry");
- if(! _h)
- {
- PLOG("db_close", "no handle passed, ignored");
- return;
- }
- disconnect_db(_h);
- aug_free(_h);
- }
- /*
- ** submit_query run a query
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- ** char *_s the text query to run
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- */
- static int submit_query(db_con_t* _h, const char* _s)
- {
- int rv;
- /*
- ** this bit of nonsense in case our connection get screwed up
- */
- switch(rv = PQstatus(CON_CONNECTION(_h)))
- {
- case CONNECTION_OK: break;
- case CONNECTION_BAD:
- PLOG("submit_query", "connection reset");
- PQreset(CON_CONNECTION(_h));
- break;
- }
- /*
- ** free any previous query that is laying about
- */
- if(CON_RESULT(_h))
- {
- free_query(_h);
- }
- /*
- ** exec the query
- */
- CON_RESULT(_h) = PQexec(CON_CONNECTION(_h), _s);
- rv = 0;
- if(PQresultStatus(CON_RESULT(_h)) == 0)
- {
- PLOG("submit_query", "initial failure, FATAL");
- /*
- ** terrible error??
- */
- rv = -3;
- }
- else
- {
- /*
- ** the query ran, get the status
- */
- switch(PQresultStatus(CON_RESULT(_h)))
- {
- case PGRES_EMPTY_QUERY: rv = -9; break;
- case PGRES_COMMAND_OK: rv = 0; break;
- case PGRES_TUPLES_OK: rv = 0; break;
- case PGRES_COPY_OUT: rv = -4; break;
- case PGRES_COPY_IN: rv = -5; break;
- case PGRES_BAD_RESPONSE: rv = -6; break;
- case PGRES_NONFATAL_ERROR: rv = -7; break;
- case PGRES_FATAL_ERROR: rv = -8; break;
- default: rv = -2; break;
- }
- }
- if(rv < 0)
- {
- /*
- ** log the error
- */
- char buf[256];
- sprintf(buf, "query '%s', result '%s'\n",
- _s, PQerrorMessage(CON_CONNECTION(_h)));
- PLOG("submit_query", buf);
- }
- return(rv);
- }
- /*
- ** free_query clear the db channel and clear any old query result status
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- */
- static int free_query(db_con_t* _h)
- {
- if(CON_RESULT(_h))
- {
- PQclear(CON_RESULT(_h));
- CON_RESULT(_h) = 0;
- }
- return 0;
- }
- /*
- ** db_free_query free the query and free the result memory
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- ** db_res_t * the result of a query
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- */
- int db_free_query(db_con_t* _h, db_res_t* _r)
- {
- free_query(_h);
- free_result(_r);
- return 0;
- }
- /*
- ** begin_transaction begin transaction
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- ** char * this is just in case an error message
- ** is printed, we will know which query
- ** was going to be run, giving us a code debug hint
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- **
- ** Notes :
- ** This function may be called with a messed up communication
- ** channel. Therefore, alot of this function is dealing with
- ** that. Wen the layering gets corrected later this stuff
- ** should continue to work correctly, it will just be
- ** way to defensive.
- */
- static int begin_transaction(db_con_t * _h, char *_s)
- {
- PGresult *mr;
- int rv;
- /*
- ** Note:
- ** The upper layers of code may attempt a transaction
- ** before opening or having a valid connection to the
- ** database. We try to sense this, and open the database
- ** if we have the sqlurl in the _h structure. Otherwise,
- ** all we can do is return an error.
- */
- if(_h)
- {
- if(CON_CONNECTED(_h))
- {
- mr = PQexec(CON_CONNECTION(_h), "BEGIN");
- if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
- {
- /*
- ** We get here if the connection to the
- ** db is corrupt, which can happen a few
- ** different ways, but all of them are
- ** related to the parent process forking,
- ** or being forked.
- */
- PLOG("begin_transaction","corrupt connection");
- CON_CONNECTED(_h) = 0;
- }
- else
- {
- /*
- ** this is the normal way out.
- ** the transaction ran fine.
- */
- PQclear(mr);
- return(0);
- }
- }
- else
- {
- DLOG("begin_transaction", "called before db_init");
- }
- /*
- ** if we get here we have a corrupt db connection,
- ** but we probably have a valid db_con_t structure.
- ** attempt to open the db.
- */
- if((rv = connect_db(_h, CON_SQLURL(_h))) != 0)
- {
- /*
- ** our attempt to fix the connection failed
- */
- char buf[256];
- sprintf(buf, "no connection, FATAL %d!", rv);
- PLOG("begin_transaction",buf);
- return(rv);
- }
- }
- else
- {
- PLOG("begin_transaction","must call db_init first!");
- return(-1);
- }
- /*
- ** we get here if the database connection was corrupt,
- ** i didn't want to use recursion ...
- */
- mr = PQexec(CON_CONNECTION(_h), "BEGIN");
- if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
- {
- char buf[256];
- sprintf("FATAL %s, '%s'!\n",
- PQerrorMessage(CON_CONNECTION(_h)), _s);
- PLOG("begin_transaction", buf);
- return(-1);
- }
- DLOG("begin_transaction", "db channel reset successful");
- PQclear(mr);
- return(0);
- }
- /*
- ** commit_transaction any begin_transaction must be terminated with this
- **
- ** Arguments :
- ** db_con_t * as previously supplied by db_init()
- **
- ** Returns :
- ** 0 upon success
- ** negative number upon failure
- */
- static int commit_transaction(db_con_t * _h)
- {
- PGresult *mr;
- mr = PQexec(CON_CONNECTION(_h), "COMMIT");
- if(!mr || PQresultStatus(mr) != PGRES_COMMAND_OK)
- {
- PLOG("commit_transaction", "error");
- return -1;
- }
- PQclear(mr);
- return(0);
- }
- /*
- * Print list of columns separated by comma
- */
- static int print_columns(char* _b, int _l, db_key_t* _c, int _n)
- {
- int i;
- int res = 0;
- for(i = 0; i < _n; i++) {
- if (i == (_n - 1)) {
- res += snprintf(_b + res, _l - res, "%s ", _c[i]);
- } else {
- res += snprintf(_b + res, _l - res, "%s,", _c[i]);
- }
- }
- return res;
- }
- /*
- * Print list of values separated by comma
- */
- static int print_values(char* _b, int _l, db_val_t* _v, int _n)
- {
- int i, res = 0, l;
- for(i = 0; i < _n; i++) {
- l = _l - res;
- /* LOG(L_ERR, "%d sizes l = _l - res %d = %d - %d\n", i, l,_l,res);
- */
- if (val2str(_v + i, _b + res, &l) < 0) {
- LOG(L_ERR,
- "print_values(): Error converting value to string\n");
- return 0;
- }
- res += l;
- if (i != (_n - 1)) {
- *(_b + res) = ',';
- res++;
- }
- }
- return res;
- }
- /*
- * Print where clause of SQL statement
- */
- static int print_where(char* _b, int _l, db_key_t* _k,
- db_op_t* _o, db_val_t* _v, int _n)
- {
- int i;
- int res = 0;
- int l;
- for(i = 0; i < _n; i++) {
- if (_o) {
- res += snprintf(_b + res, _l - res, "%s%s",
- _k[i], _o[i]);
- } else {
- res += snprintf(_b + res, _l - res, "%s=", _k[i]);
- }
- l = _l - res;
- val2str(&(_v[i]), _b + res, &l);
- res += l;
- if (i != (_n - 1)) {
- res += snprintf(_b + res, _l - res, " AND ");
- }
- }
- return res;
- }
- /*
- * Print set clause of update SQL statement
- */
- static int print_set(char* _b, int _l, db_key_t* _k,
- db_val_t* _v, int _n)
- {
- int i;
- int res = 0;
- int l;
- for(i = 0; i < _n; i++) {
- res += snprintf(_b + res, _l - res, "%s=", _k[i]);
- l = _l - res;
- val2str(&(_v[i]), _b + res, &l);
- res += l;
- if (i != (_n - 1)) {
- if ((_l - res) >= 1) {
- *(_b + res++) = ',';
- }
- }
- }
- return res;
- }
- /*
- * Query table for specified rows
- * _h: structure representing database connection
- * _k: key names
- * _op: operators
- * _v: values of the keys that must match
- * _c: column names to return
- * _n: nmber of key=values pairs to compare
- * _nc: number of columns to return
- * _o: order by the specified column
- */
- int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op,
- db_val_t* _v, db_key_t* _c, int _n, int _nc,
- db_key_t _o, db_res_t** _r)
- {
- int off, rv;
- if (!_c) {
- off = snprintf(sql_buf, SQL_BUF_LEN,
- "select * from %s ", CON_TABLE(_h));
- } else {
- off = snprintf(sql_buf, SQL_BUF_LEN, "select ");
- off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _c, _nc);
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
- "from %s ", CON_TABLE(_h));
- }
- if (_n) {
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off, "where ");
- off += print_where(sql_buf + off, SQL_BUF_LEN - off,
- _k, _op, _v, _n);
- }
- if (_o) {
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off,
- "order by %s", _o);
- }
- if(begin_transaction(_h, sql_buf)) return(-1);
- if (submit_query(_h, sql_buf) < 0) {
- LOG(L_ERR, "db_query(): Error while submitting query\n");
- return -2;
- }
- rv = get_result(_h, _r);
- free_query(_h);
- commit_transaction(_h);
- return(rv);
- }
- /*
- * Execute a raw SQL query
- */
- int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r)
- {
- int rv;
- if(begin_transaction(_h, sql_buf)) return(-1);
- if (submit_query(_h, _s) < 0) {
- LOG(L_ERR, "db_raw_query(): Error while submitting query\n");
- return -2;
- }
- rv = get_result(_h, _r);
- free_query(_h);
- commit_transaction(_h);
- return(rv);
- }
- /*
- * Retrieve result set
- */
- int get_result(db_con_t* _h, db_res_t** _r)
- {
- *_r = new_result_pg(CON_SQLURL(_h));
- if (!CON_RESULT(_h)) {
- LOG(L_ERR, "get_result(): error");
- free_result(*_r);
- *_r = 0;
- return -3;
- }
- if (convert_result(_h, *_r) < 0) {
- LOG(L_ERR, "get_result(): Error while converting result\n");
- free_result(*_r);
- *_r = 0;
- return -4;
- }
- return 0;
- }
- /*
- * Insert a row into specified table
- * _h: structure representing database connection
- * _k: key names
- * _v: values of the keys
- * _n: number of key=value pairs
- */
- int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n)
- {
- int off;
- off = snprintf(sql_buf, SQL_BUF_LEN, "insert into %s (", CON_TABLE(_h));
- off += print_columns(sql_buf + off, SQL_BUF_LEN - off, _k, _n);
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off, ") values (");
- off += print_values(sql_buf + off, SQL_BUF_LEN - off, _v, _n);
- *(sql_buf + off++) = ')';
- *(sql_buf + off) = '\0';
- if(begin_transaction(_h, sql_buf)) return(-1);
- if (submit_query(_h, sql_buf) < 0) {
- LOG(L_ERR, "db_insert(): Error while inserting\n");
- return -2;
- }
- free_query(_h);
- commit_transaction(_h);
- return(0);
- }
- /*
- * Delete a row from the specified table
- * _h: structure representing database connection
- * _k: key names
- * _o: operators
- * _v: values of the keys that must match
- * _n: number of key=value pairs
- */
- int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n)
- {
- int off;
- off = snprintf(sql_buf, SQL_BUF_LEN, "delete from %s", CON_TABLE(_h));
- if (_n) {
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
- off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
- _o, _v, _n);
- }
- if(begin_transaction(_h, sql_buf)) return(-1);
- if (submit_query(_h, sql_buf) < 0) {
- LOG(L_ERR, "db_delete(): Error while deleting\n");
- return -2;
- }
- free_query(_h);
- commit_transaction(_h);
- return(0);
- }
- /*
- * Update some rows in the specified table
- * _h: structure representing database connection
- * _k: key names
- * _o: operators
- * _v: values of the keys that must match
- * _uk: updated columns
- * _uv: updated values of the columns
- * _n: number of key=value pairs
- * _un: number of columns to update
- */
- int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
- db_key_t* _uk, db_val_t* _uv, int _n, int _un)
- {
- int off;
- off = snprintf(sql_buf, SQL_BUF_LEN, "update %s set ", CON_TABLE(_h));
- off += print_set(sql_buf + off, SQL_BUF_LEN - off, _uk, _uv, _un);
- if (_n) {
- off += snprintf(sql_buf + off, SQL_BUF_LEN - off, " where ");
- off += print_where(sql_buf + off, SQL_BUF_LEN - off, _k,
- _o, _v, _n);
- *(sql_buf + off) = '\0';
- }
- if(begin_transaction(_h, sql_buf)) return(-1);
- if (submit_query(_h, sql_buf) < 0) {
- LOG(L_ERR, "db_update(): Error while updating\n");
- return -2;
- }
- free_query(_h);
- commit_transaction(_h);
- return(0);
- }
|