db_unixodbc/dbase.c

Go to the documentation of this file.
00001 /*
00002  * $Id: dbase.c 5431 2009-01-07 14:46:10Z henningw $
00003  *
00004  * UNIXODBC module core functions
00005  *
00006  * Copyright (C) 2005-2006 Marco Lorrai
00007  * Copyright (C) 2007-2008 1&1 Internet AG
00008  *
00009  * This file is part of Kamailio, a free SIP server.
00010  *
00011  * Kamailio is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * Kamailio is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  *
00026  * History:
00027  * --------
00028  *  2005-12-01  initial commit (chgen)
00029  *  2006-04-03  fixed invalid handle to extract error (sgupta)
00030  *  2006-04-04  removed deprecated ODBC functions, closed cursors on error
00031  *              (sgupta)
00032  *  2006-05-05  Fixed reconnect code to actually work on connection loss 
00033  *              (sgupta)
00034  */
00035 
00036 
00037 #include "../../mem/mem.h"
00038 #include "../../dprint.h"
00039 #include "../../db/db_query.h"
00040 #include "val.h"
00041 #include "con.h"
00042 #include "row.h"
00043 #include "res.h"
00044 #include "list.h"
00045 #include "db_unixodbc.h"
00046 #include "dbase.h"
00047 
00048 
00049 /*
00050  * Reconnect if connection is broken
00051  */
00052 static int reconnect(const db_con_t* _h)
00053 {
00054    int ret = 0;
00055    SQLCHAR outstr[1024];
00056    SQLSMALLINT outstrlen;
00057    char conn_str[MAX_CONN_STR_LEN];
00058 
00059    LM_ERR("Attempting DB reconnect\n");
00060 
00061    /* Disconnect */
00062    SQLDisconnect (CON_CONNECTION(_h));
00063 
00064    /* Reconnect */
00065    if (!db_unixodbc_build_conn_str(CON_ID(_h), conn_str)) {
00066       LM_ERR("failed to build connection string\n");
00067       return ret;
00068    }
00069 
00070    ret = SQLDriverConnect(CON_CONNECTION(_h), (void *)1,
00071          (SQLCHAR*)conn_str, SQL_NTS, outstr, sizeof(outstr),
00072          &outstrlen, SQL_DRIVER_COMPLETE);
00073    if (!SQL_SUCCEEDED(ret)) {
00074       LM_ERR("failed to connect\n");
00075       db_unixodbc_extract_error("SQLDriverConnect", CON_CONNECTION(_h),
00076          SQL_HANDLE_DBC, NULL);
00077       return ret;
00078    }
00079 
00080    ret = SQLAllocHandle(SQL_HANDLE_STMT, CON_CONNECTION(_h),
00081          &CON_RESULT(_h));
00082    if (!SQL_SUCCEEDED(ret)) {
00083       LM_ERR("Statement allocation error %d\n", (int)(long)CON_CONNECTION(_h));
00084       db_unixodbc_extract_error("SQLAllocStmt", CON_CONNECTION(_h), SQL_HANDLE_DBC,NULL);
00085       return ret;
00086    }
00087 
00088    return ret;
00089 }
00090 
00091 /*
00092  * Send an SQL query to the server
00093  */
00094 static int db_unixodbc_submit_query(const db_con_t* _h, const str* _s)
00095 {
00096    int ret = 0;
00097    SQLCHAR sqlstate[7];
00098 
00099    if (!_h || !_s || !_s->s) {
00100       LM_ERR("invalid parameter value\n");
00101       return -1;
00102    }
00103 
00104    /* first do some cleanup if required */
00105    if(CON_RESULT(_h))
00106    {
00107       SQLCloseCursor(CON_RESULT(_h));
00108       SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h));
00109    }
00110 
00111    ret = SQLAllocHandle(SQL_HANDLE_STMT, CON_CONNECTION(_h), &CON_RESULT(_h));
00112    if (!SQL_SUCCEEDED(ret))
00113    {
00114       LM_ERR("statement allocation error %d\n",
00115             (int)(long)CON_CONNECTION(_h));
00116       db_unixodbc_extract_error("SQLAllocStmt", CON_CONNECTION(_h), SQL_HANDLE_DBC,
00117          (char*)sqlstate);
00118       
00119       /* Connection broken */
00120       if( !strncmp((char*)sqlstate,"08003",5) ||
00121       !strncmp((char*)sqlstate,"08S01",5) ) {
00122          ret = reconnect(_h);
00123          if( !SQL_SUCCEEDED(ret) ) return ret;
00124       } else {
00125          return ret;
00126       }
00127    }
00128 
00129    ret=SQLExecDirect(CON_RESULT(_h),  (SQLCHAR*)_s->s, _s->len);
00130    if (!SQL_SUCCEEDED(ret))
00131    {
00132       SQLCHAR sqlstate[7];
00133       LM_ERR("rv=%d. Query= %.*s\n", ret, _s->len, _s->s);
00134       db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h), SQL_HANDLE_STMT,
00135          (char*)sqlstate);
00136 
00137       /* Connection broken */
00138       if( !strncmp((char*)sqlstate,"08003",5) ||
00139           !strncmp((char*)sqlstate,"08S01",5) 
00140           )
00141       {
00142          ret = reconnect(_h);
00143          if( SQL_SUCCEEDED(ret) ) {
00144             /* Try again */
00145             ret=SQLExecDirect(CON_RESULT(_h),  (SQLCHAR*)_s->s, _s->len);
00146             if (!SQL_SUCCEEDED(ret)) {
00147                LM_ERR("rv=%d. Query= %.*s\n", ret, _s->len, _s->s);
00148                db_unixodbc_extract_error("SQLExecDirect", CON_RESULT(_h),
00149                   SQL_HANDLE_STMT, (char*)sqlstate);
00150                /* Close the cursor */
00151                SQLCloseCursor(CON_RESULT(_h));
00152                SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h));
00153             }
00154          }
00155 
00156       }
00157       else {
00158          /* Close the cursor */ 
00159          SQLCloseCursor(CON_RESULT(_h));
00160          SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h));
00161       }
00162    }
00163 
00164    return ret;
00165 }
00166 
00167 
00168 
00169 /*
00170  * Initialize database module
00171  * No function should be called before this
00172  */
00173 db_con_t* db_unixodbc_init(const str* _url)
00174 {
00175    return db_do_init(_url, (void*)db_unixodbc_new_connection);
00176 }
00177 
00178 /*
00179  * Shut down database module
00180  * No function should be called after this
00181  */
00182 void db_unixodbc_close(db_con_t* _h)
00183 {
00184    return db_do_close(_h, db_unixodbc_free_connection);
00185 }
00186 
00187 /*
00188  * Retrieve result set
00189  */
00190 static int db_unixodbc_store_result(const db_con_t* _h, db_res_t** _r)
00191 {
00192    if ((!_h) || (!_r))
00193    {
00194       LM_ERR("invalid parameter value\n");
00195       return -1;
00196    }
00197 
00198    *_r = db_new_result();
00199 
00200    if (*_r == 0)
00201    {
00202       LM_ERR("no memory left\n");
00203       return -2;
00204    }
00205 
00206    if (db_unixodbc_convert_result(_h, *_r) < 0)
00207    {
00208       LM_ERR("failed to convert result\n");
00209       LM_DBG("freeing result set at %p\n", _r);
00210       pkg_free(*_r);
00211       *_r = 0;
00212       return -4;
00213    }
00214    return 0;
00215 }
00216 
00217 /*
00218  * Release a result set from memory
00219  */
00220 int db_unixodbc_free_result(db_con_t* _h, db_res_t* _r)
00221 {
00222    if ((!_h) || (!_r))
00223    {
00224       LM_ERR("invalid parameter value\n");
00225       return -1;
00226    }
00227 
00228    if (db_free_result(_r) < 0)
00229    {
00230       LM_ERR("failed to free result structure\n");
00231       return -1;
00232    }
00233    SQLFreeHandle(SQL_HANDLE_STMT, CON_RESULT(_h));
00234    CON_RESULT(_h) = 0;
00235    return 0;
00236 }
00237 
00238 /*
00239  * Query table for specified rows
00240  * _h: structure representing database connection
00241  * _k: key names
00242  * _op: operators
00243  * _v: values of the keys that must match
00244  * _c: column names to return
00245  * _n: number of key=values pairs to compare
00246  * _nc: number of columns to return
00247  * _o: order by the specified column
00248  */
00249 int db_unixodbc_query(const db_con_t* _h, const db_key_t* _k, const db_op_t* _op,
00250       const db_val_t* _v, const db_key_t* _c, const int _n, const int _nc,
00251       const db_key_t _o, db_res_t** _r)
00252 {
00253    return db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r,
00254          db_unixodbc_val2str,  db_unixodbc_submit_query, db_unixodbc_store_result);
00255 }
00256 
00257 /*!
00258  * \brief Gets a partial result set, fetch rows from a result
00259  *
00260  * Gets a partial result set, fetch a number of rows from a databae result.
00261  * This function initialize the given result structure on the first run, and
00262  * fetches the nrows number of rows. On subsequenting runs, it uses the
00263  * existing result and fetches more rows, until it reaches the end of the
00264  * result set. Because of this the result needs to be null in the first
00265  * invocation of the function. If the number of wanted rows is zero, the
00266  * function returns anything with a result of zero.
00267  * \param _h structure representing the database connection
00268  * \param _r pointer to a structure representing the result
00269  * \param nrows number of fetched rows
00270  * \return return zero on success, negative value on failure
00271  */
00272 int db_unixodbc_fetch_result(const db_con_t* _h, db_res_t** _r, const int nrows)
00273 {
00274    int row_n = 0, i = 0, ret = 0, len;
00275    SQLSMALLINT columns;
00276    list* rows = NULL;
00277    list* rowstart = NULL;
00278    strn* temp_row = NULL;
00279 
00280    if ((!_h) || (!_r) || nrows < 0)
00281    {
00282       LM_ERR("invalid parameter value\n");
00283       return -1;
00284    }
00285 
00286    /* exit if the fetch count is zero */
00287    if (nrows == 0) {
00288       if (*_r)
00289          db_free_result(*_r);
00290       *_r = 0;
00291       return 0;
00292    }
00293 
00294    /* On the first fetch for a query, allocate structures and get columns */
00295    if(*_r == NULL) {
00296       /* Allocate a new result structure */
00297       *_r = db_new_result();
00298       LM_DBG("just allocated a new db result structure");
00299 
00300       if (*_r == NULL) {
00301          LM_ERR("no memory left\n");
00302          return -2;
00303       }
00304 
00305       /* Get columns names and count */
00306       if (db_unixodbc_get_columns(_h, *_r) < 0) {
00307          LM_ERR("getting column names failed\n");
00308          db_free_columns(*_r);
00309          return -2;
00310       }
00311 
00312    /* On subsequent fetch attempts, reuse already allocated structures */
00313    } else {
00314       LM_DBG("db result structure already exist, reusing\n");
00315       /* free old rows */
00316       if(RES_ROWS(*_r) != NULL)
00317          db_free_rows(*_r);
00318       RES_ROWS(*_r) = 0;
00319       RES_ROW_N(*_r) = 0;
00320    }
00321 
00322    SQLNumResultCols(CON_RESULT(_h), (SQLSMALLINT *)&columns);
00323 
00324    /* Allocate a temporary row */
00325    temp_row = (strn*)pkg_malloc( columns*sizeof(strn) );
00326    if(!temp_row) {
00327       LM_ERR("no private memory left\n");
00328       return -1;
00329    }
00330 
00331    /* Now fetch nrows at most */
00332    len = sizeof(db_row_t) * nrows;
00333    RES_ROWS(*_r) = (struct db_row*)pkg_malloc(len);
00334    if (!RES_ROWS(*_r)) {
00335       LM_ERR("no memory left\n");
00336       return -5;
00337    }
00338    LM_DBG("allocated %d bytes for RES_ROWS at %p\n", len, RES_ROWS(*_r));
00339 
00340    LM_DBG("Now fetching %i rows at most\n", nrows);
00341    while(SQL_SUCCEEDED(ret = SQLFetch(CON_RESULT(_h)))) {
00342       LM_DBG("fetching %d columns for row %d...\n",columns, row_n);
00343       for(i=0; i < columns; i++) {
00344          SQLLEN indicator;
00345          LM_DBG("fetching column %d\n",i);
00346 
00347          ret = SQLGetData(CON_RESULT(_h), i+1, SQL_C_CHAR,
00348                temp_row[i].s, STRN_LEN, &indicator);
00349 
00350          if (SQL_SUCCEEDED(ret)) {
00351             if (indicator == SQL_NULL_DATA)
00352                strcpy(temp_row[i].s, "NULL");
00353          } else {
00354             LM_ERR("SQLGetData failed\n");
00355          }
00356       }
00357 
00358       LM_DBG("got temp_row at %p\n", temp_row);
00359 
00360       if (db_unixodbc_list_insert(&rowstart, &rows, columns, temp_row) < 0) {
00361          LM_ERR("SQL result row insert failed\n");
00362          pkg_free(temp_row);
00363          temp_row= NULL;
00364          pkg_free(*_r);
00365          *_r = 0;
00366          return -5;
00367       }
00368 
00369       row_n++;
00370       if (row_n == nrows) {
00371          break;
00372       }
00373    }
00374    
00375    /* Free temporary row data */
00376    LM_DBG("freeing temp_row at %p\n", temp_row);
00377    pkg_free(temp_row);
00378    CON_ROW(_h) = NULL;
00379 
00380    RES_ROW_N(*_r) = row_n;
00381    if (!row_n) {
00382       LM_DBG("no more rows to process for db fetch");
00383       RES_ROWS(*_r) = 0;
00384       return 0;
00385    }
00386 
00387    /* Convert rows to internal format */
00388    memset(RES_ROWS(*_r), 0, len);
00389    i = 0;
00390    rows = rowstart;
00391    while(rows)
00392    {
00393       LM_DBG("converting row #%d\n", i);
00394       CON_ROW(_h) = rows->data;
00395       if (!CON_ROW(_h))
00396       {
00397          LM_ERR("string null\n");
00398          RES_ROW_N(*_r) = row_n;
00399          db_free_rows(*_r);
00400          return -3;
00401       }
00402       if (db_unixodbc_convert_row(_h, *_r, &(RES_ROWS(*_r)[i]), rows->lengths) < 0) {
00403          LM_ERR("converting fetched row #%d failed\n", i);
00404          RES_ROW_N(*_r) = i;
00405          db_free_rows(*_r);
00406          return -4;
00407       }
00408       i++;
00409       rows = rows->next;
00410    }
00411    db_unixodbc_list_destroy(rowstart);
00412 
00413    /* update the total number of rows processed */
00414    RES_LAST_ROW(*_r) += row_n;
00415    LM_DBG("fetch from db processed %d rows so far\n", RES_LAST_ROW(*_r));
00416 
00417    return 0;
00418 }
00419 
00420 
00421 /*
00422  * Execute a raw SQL query
00423  */
00424 int db_unixodbc_raw_query(const db_con_t* _h, const str* _s, db_res_t** _r)
00425 {
00426    return db_do_raw_query(_h, _s, _r, db_unixodbc_submit_query,
00427          db_unixodbc_store_result);
00428 }
00429 
00430 /*
00431  * Insert a row into specified table
00432  * _h: structure representing database connection
00433  * _k: key names
00434  * _v: values of the keys
00435  * _n: number of key=value pairs
00436  */
00437 int db_unixodbc_insert(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n)
00438 {
00439    return db_do_insert(_h, _k, _v, _n, db_unixodbc_val2str,
00440          db_unixodbc_submit_query);
00441 }
00442 
00443 /*
00444  * Delete a row from the specified table
00445  * _h: structure representing database connection
00446  * _k: key names
00447  * _o: operators
00448  * _v: values of the keys that must match
00449  * _n: number of key=value pairs
00450  */
00451 int db_unixodbc_delete(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
00452       const db_val_t* _v, const int _n)
00453 {
00454    return db_do_delete(_h, _k, _o, _v, _n, db_unixodbc_val2str,
00455          db_unixodbc_submit_query);
00456 }
00457 
00458 /*
00459  * Update some rows in the specified table
00460  * _h: structure representing database connection
00461  * _k: key names
00462  * _o: operators
00463  * _v: values of the keys that must match
00464  * _uk: updated columns
00465  * _uv: updated values of the columns
00466  * _n: number of key=value pairs
00467  * _un: number of columns to update
00468  */
00469 int db_unixodbc_update(const db_con_t* _h, const db_key_t* _k, const db_op_t* _o,
00470       const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, const int _n, const int _un)
00471 {
00472    return db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, db_unixodbc_val2str,
00473          db_unixodbc_submit_query);
00474 }
00475 
00476 /*
00477  * Just like insert, but replace the row if it exists
00478  */
00479 int db_unixodbc_replace(const db_con_t* _h, const db_key_t* _k, const db_val_t* _v, const int _n)
00480 {
00481    return db_do_replace(_h, _k, _v, _n, db_unixodbc_val2str,
00482          db_unixodbc_submit_query);
00483 }
00484 
00485 /*
00486  * Store name of table that will be used by
00487  * subsequent database functions
00488  */
00489 int db_unixodbc_use_table(db_con_t* _h, const str* _t)
00490 {
00491    return db_use_table(_h, _t);
00492 }

Generated on Tue May 22 14:00:25 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6