Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

dbpool_oracle.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * dbpool_oracle.c - implement Oracle operations for generic database connection pool
00059  *
00060  * Note: Oracle 8i and 9i support tested. This implementation will not
00061  * work with Oracle 7 version. Please do not use Oracle 9i-rc2 OCI
00062  * libraries on Linux, due to strange memory problems. If you do not
00063  * believe me, just check it youself with valgrind ;)
00064  *
00065  * Alexander Malysh <a.malysh@centrium.de>
00066  * Robert Gałach <robert.galach@my.tenbit.pl>
00067  *      2004 Added support for binding variables.
00068  */
00069 
00070 #ifdef HAVE_ORACLE
00071 
00072 #include <oci.h>
00073 
00074 /* forward decl. */
00075 static int oracle_select(void *theconn, const Octstr *sql, List *binds, List **res);
00076 
00077 struct ora_conn {
00078     /* environment handle */
00079     OCIEnv *envp;
00080     /* context handle */
00081     OCISvcCtx *svchp;
00082     /* error handle */
00083     OCIError *errhp;
00084 };
00085 
00086 /* This function prints the error */
00087 static void oracle_checkerr(OCIError *errhp, sword status)
00088 {
00089     text errbuf[512];
00090     sb4 errcode = 0;
00091 
00092     switch (status) {
00093         case OCI_SUCCESS:
00094             break;
00095         case OCI_SUCCESS_WITH_INFO:
00096             error(0, "Error - OCI_SUCCESS_WITH_INFO");
00097             break;
00098         case OCI_NEED_DATA:
00099             error(0, "Error - OCI_NEED_DATA");
00100             break;
00101         case OCI_NO_DATA:
00102             error(0, "Error - OCI_NODATA");
00103             break;
00104         case OCI_ERROR:
00105             if (errhp == NULL) break;
00106             (void) OCIErrorGet((dvoid *)errhp, (ub4) 1, (text *) NULL, &errcode,
00107                                errbuf, (ub4) sizeof(errbuf), OCI_HTYPE_ERROR);
00108             error(0, "Error - %.*s", 512, errbuf);
00109             break;
00110         case OCI_INVALID_HANDLE:
00111             error(0, "Error - OCI_INVALID_HANDLE");
00112             break;
00113         case OCI_STILL_EXECUTING:
00114             error(0, "Error - OCI_STILL_EXECUTE");
00115             break;
00116         case OCI_CONTINUE:
00117             error(0, "Error - OCI_CONTINUE");
00118             break;
00119         default:
00120             break;
00121     }
00122 }
00123 
00124 
00125 /*
00126  * Malloc callback function to get tracking of OCI allocs.
00127  */
00128 static void *oracle_malloc(void *ctx, size_t size)
00129 {
00130     void *ret = gw_malloc(size);
00131     debug("dbpool.oracle",0,"oracle_malloc called size=%d @%08lx", size, 
00132           (long) ret);
00133     return ret;
00134 }
00135 
00136 
00137 /*
00138  * Free callback function to get tracking of OCI allocs.
00139  */
00140 static void oracle_free(void *ctx, void *ptr)
00141 {
00142     debug("dbpool.oracle",0,"oracle_free called @%08lx", (long) ptr);
00143     gw_free(ptr);
00144 }
00145 
00146 
00147 /*
00148  * Realloc callback function to get tracking of OCI allocs.
00149  */
00150 static void *oracle_realloc(void *ctx, void *ptr, size_t size)
00151 {
00152     void *ret = gw_realloc(ptr, size);
00153     debug("dbpool.oracle",0,"oracle_realloc called size=%d", size);
00154     return ret;
00155 }
00156 
00157 static void* oracle_open_conn(const DBConf *db_conf)
00158 {
00159     OracleConf *cfg = db_conf->oracle;
00160     sword errorcode = 0;
00161     text version[512];
00162     struct ora_conn *conn = gw_malloc(sizeof(struct ora_conn));
00163 
00164     gw_assert(conn != NULL);
00165     memset(conn, 0, sizeof(struct ora_conn));
00166 
00167     debug("dbpool.oracle",0,"oracle_open_conn called");
00168 
00169     /* init OCI environment */
00170     errorcode = OCIEnvCreate(&conn->envp,
00171                              OCI_THREADED|OCI_ENV_NO_MUTEX,
00172                              NULL,
00173                              oracle_malloc,
00174                              oracle_realloc,
00175                              oracle_free,
00176                              0,0);
00177     if (errorcode != OCI_SUCCESS) {
00178          oracle_checkerr(NULL, errorcode);
00179          error(0, "Got error while OCIEnvCreate %d", errorcode);
00180          gw_free(conn);
00181          return NULL;
00182     }
00183 
00184     debug("dbpool.oracle",0,"oci environment created");
00185 
00186     /* allocate error handle */
00187     errorcode = OCIHandleAlloc(conn->envp, (dvoid**) &conn->errhp, 
00188                                OCI_HTYPE_ERROR, 0, 0);
00189     if (errorcode != OCI_SUCCESS) {
00190         oracle_checkerr(NULL, errorcode);
00191         OCIHandleFree(conn->envp, OCI_HTYPE_ENV);
00192         gw_free(conn);
00193         return NULL;
00194     }
00195 
00196     debug("dbpool.oracle",0,"oci error handle allocated");
00197 
00198     /* open oracle user session */
00199     errorcode = OCILogon(conn->envp, conn->errhp, &conn->svchp,
00200                          octstr_get_cstr(cfg->username), octstr_len(cfg->username),
00201                          octstr_get_cstr(cfg->password), octstr_len(cfg->password),
00202                          octstr_get_cstr(cfg->tnsname), octstr_len(cfg->tnsname));
00203 
00204     if (errorcode != OCI_SUCCESS) {
00205         oracle_checkerr(conn->errhp, errorcode);
00206         OCIHandleFree(conn->errhp, OCI_HTYPE_ERROR);
00207         OCIHandleFree(conn->envp, OCI_HTYPE_ENV);
00208         gw_free(conn);
00209         return NULL;
00210     }
00211 
00212     debug("dbpool.oracle",0,"connected to database");
00213 
00214     errorcode = OCIServerVersion(conn->svchp, conn->errhp, version, 
00215                                  sizeof(version), OCI_HTYPE_SVCCTX);
00216     if (errorcode != OCI_SUCCESS) {
00217         oracle_checkerr(conn->errhp, errorcode);
00218     } else {
00219         info(0, "Connected to: %s", version);
00220     }
00221 
00222     return conn;
00223 }
00224 
00225 
00226 static void oracle_close_conn(void *theconn)
00227 {
00228     struct ora_conn *conn = (struct ora_conn*) theconn;
00229 
00230     gw_assert(conn != NULL);
00231 
00232     if (conn->svchp != NULL)
00233         oracle_checkerr(conn->errhp, OCILogoff(conn->svchp, conn->errhp));
00234 
00235     OCIHandleFree(conn->errhp, OCI_HTYPE_ERROR);
00236     OCIHandleFree(conn->envp, OCI_HTYPE_ENV);
00237     /* OCITerminate(OCI_DEFAULT); */
00238 
00239     gw_free(conn);
00240 }
00241 
00242 
00243 static int oracle_check_conn(void *conn)
00244 {
00245     Octstr *sql;
00246     List *res;
00247     int ret;
00248 
00249     /* TODO Check for appropriate OCI function */
00250     sql = octstr_create("SELECT 1 FROM DUAL");
00251 
00252     ret = oracle_select(conn, sql, NULL, &res);
00253     if (ret != -1 && gwlist_len(res) > 0) {
00254         List *row = gwlist_extract_first(res);
00255         gwlist_destroy(row, octstr_destroy_item);
00256     }
00257     if (ret != -1)
00258         gwlist_destroy(res, NULL);
00259 
00260     octstr_destroy(sql);
00261 
00262     return ret;
00263 }
00264 
00265 
00266 static void oracle_conf_destroy(DBConf *theconf)
00267 {
00268     OracleConf *conf = theconf->oracle;
00269 
00270     octstr_destroy(conf->username);
00271     octstr_destroy(conf->password);
00272     octstr_destroy(conf->tnsname);
00273 
00274     gw_free(conf);
00275     gw_free(theconf);
00276 }
00277 
00278 
00279 static int oracle_select(void *theconn, const Octstr *sql, List *binds, List **res)
00280 {
00281     List *row;
00282     OCIStmt *stmt;
00283     OCIParam *dparam;
00284     sword status;
00285     ub4 columns;
00286     ub4 i;
00287     struct data_s {
00288         text *data;
00289         ub2 size;
00290         sb2 ind;
00291         ub2 type;
00292     };
00293     struct data_s *data;
00294     struct ora_conn *conn = (struct ora_conn*) theconn;
00295     int binds_len = (binds ? gwlist_len(binds) : 0);
00296 
00297     *res = NULL;
00298 
00299     /* allocate statement handle */
00300     status = OCIHandleAlloc(conn->envp, (dvoid**)&stmt, OCI_HTYPE_STMT, 0,0);
00301     if (OCI_SUCCESS != status) {
00302         oracle_checkerr(conn->errhp, status);
00303         return -1;
00304     }
00305     /* prepare statement */
00306     status = OCIStmtPrepare(stmt, conn->errhp, octstr_get_cstr(sql), 
00307                             octstr_len(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
00308     if (OCI_SUCCESS != status) {
00309         oracle_checkerr(conn->errhp, status);
00310         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00311         return -1;
00312     }
00313 
00314     /* bind variables */
00315     for (i = 0; i < binds_len; i++) {
00316         OCIBind *bndhp = NULL;
00317         Octstr *bind = gwlist_get(binds, i);
00318         status = OCIBindByPos(stmt, &bndhp, 
00319                               conn->errhp, (i+1), (dvoid *) octstr_get_cstr(bind),
00320                               (sword) octstr_len(bind)+1, SQLT_STR, (dvoid *) 0, (ub2 *)0,
00321                               (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DEFAULT);
00322         if (OCI_SUCCESS != status) {
00323             oracle_checkerr(conn->errhp, status);
00324             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00325             return -1;
00326         }
00327     }
00328     /* execute our statement */
00329     status = OCIStmtExecute(conn->svchp, stmt, conn->errhp, 0, 0, NULL, NULL, 
00330                             OCI_DEFAULT);
00331     if (OCI_SUCCESS != status && OCI_NO_DATA != status) {
00332         oracle_checkerr(conn->errhp, status);
00333         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00334         return -1;
00335     }
00336     /* receive column count */
00337     status = OCIAttrGet(stmt, OCI_HTYPE_STMT, &columns, 0, OCI_ATTR_PARAM_COUNT, 
00338                         conn->errhp);
00339     if (status != OCI_SUCCESS) {
00340         oracle_checkerr(conn->errhp, status);
00341         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00342         return -1;
00343     }
00344 
00345     debug("dbpool.oracle",0,"SQL has %d columns", columns);
00346 
00347     /* allocate array of pointers */
00348     debug("dbpool.oracle",0,"alloc size=%d",sizeof(text*)*columns);
00349     data = gw_malloc(sizeof(struct data_s)*columns);
00350 
00351     debug("dbpool.oracle",0,"retrieve data_size");
00352     /* retrieve data size for every column and allocate it */
00353     for (i=0 ; i < columns; i++) {
00354         OCIDefine *defh;
00355 
00356         status = OCIParamGet(stmt, OCI_HTYPE_STMT, conn->errhp, 
00357                              (dvoid**) &dparam, i+1);
00358         if (status != OCI_SUCCESS) {
00359             oracle_checkerr(conn->errhp, status);
00360             columns = i;
00361             for (i = 0; i < columns; i++)
00362                 gw_free(data[i].data);
00363             gw_free(data);
00364             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00365             return -1;
00366         }
00367 
00368         status = OCIAttrGet(dparam, OCI_DTYPE_PARAM, (dvoid*) &data[i].size, 
00369                             0, OCI_ATTR_DATA_SIZE, conn->errhp);
00370         if (status != OCI_SUCCESS) {
00371             oracle_checkerr(conn->errhp, status);
00372             columns = i;
00373             for (i = 0; i < columns; i++)
00374                 gw_free(data[i].data);
00375             gw_free(data);
00376             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00377             return -1;
00378         }
00379 
00380         status = OCIAttrGet(dparam, OCI_DTYPE_PARAM, (dvoid*) &data[i].type, 
00381                             0, OCI_ATTR_DATA_TYPE, conn->errhp);
00382         if (status != OCI_SUCCESS) {
00383             oracle_checkerr(conn->errhp, status);
00384             columns = i;
00385             for (i = 0; i < columns; i++)
00386                 gw_free(data[i].data);
00387             gw_free(data);
00388             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00389             return -1;
00390         }
00391 
00392         /* convert all data types to C-Strings except DATE */
00393         if (data[i].type != SQLT_DAT) {
00394             data[i].size++; /* terminating zero */
00395             data[i].type = SQLT_STR;
00396         }
00397 
00398         debug("dbpool.oracle",0,"alloc size=%d", data[i].size);
00399         data[i].data = gw_malloc(data[i].size);
00400 
00401         /* bind allocated values to statement handle */
00402         status = OCIDefineByPos(stmt, &defh, conn->errhp, i+1, data[i].data, 
00403                                 data[i].size, data[i].type, &data[i].ind, 
00404                                 0, 0, OCI_DEFAULT);
00405         if (status != OCI_SUCCESS) {
00406             oracle_checkerr(conn->errhp, status);
00407             columns = i;
00408             for (i = 0; i <= columns; i++)
00409                 gw_free(data[i].data);
00410             gw_free(data);
00411             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00412             return -1;
00413         }
00414     }
00415 
00416     *res = gwlist_create();
00417     /* fetch data */
00418     while ((status = OCIStmtFetch(stmt, conn->errhp, 1, 
00419                                   OCI_FETCH_NEXT, OCI_DEFAULT)) == OCI_SUCCESS ||
00420             status == OCI_SUCCESS_WITH_INFO) {
00421 
00422         row = gwlist_create();
00423         for (i = 0; i < columns; i++) {
00424             if (data[i].data == NULL || data[i].ind == -1) {
00425                 gwlist_insert(row, i, octstr_create(""));
00426             } else {
00427                 gwlist_insert(row, i, octstr_create_from_data(data[i].data, data[i].size));
00428             }
00429             /* debug("dbpool.oracle",0,"inserted value = '%s'", 
00430                      octstr_get_cstr(gwlist_get(row,i))); */
00431         }
00432         gwlist_append(*res, row);
00433     }
00434 
00435     /* ignore OCI_NO_DATA error */
00436     if (status != OCI_NO_DATA) {
00437         List *row;
00438         oracle_checkerr(conn->errhp, status);
00439         for (i = 0; i < columns; i++)
00440             gw_free(data[i].data);
00441         gw_free(data);
00442         while ((row = gwlist_extract_first(*res)) != NULL)
00443             gwlist_destroy(row, octstr_destroy_item);
00444         gwlist_destroy(*res, NULL);
00445         *res = NULL;
00446         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00447         return -1;
00448     }
00449 
00450     for (i = 0; i < columns; i++)
00451         gw_free(data[i].data);
00452 
00453     gw_free(data);
00454     OCIHandleFree(stmt, OCI_HTYPE_STMT);
00455 
00456     return 0;
00457 }
00458 
00459 
00460 static int oracle_update(void *theconn, const Octstr *sql, List *binds)
00461 {
00462     OCIStmt *stmt;
00463     sword status;
00464     ub4 rows = 0, i;
00465     struct ora_conn *conn = (struct ora_conn*) theconn;
00466     int binds_len = (binds ? gwlist_len(binds) : 0);
00467     
00468     /* allocate statement handle */
00469     status = OCIHandleAlloc(conn->envp, (dvoid**)&stmt, OCI_HTYPE_STMT, 0,0);
00470     if (OCI_SUCCESS != status) {
00471         oracle_checkerr(conn->errhp, status);
00472         return -1;
00473     }
00474     debug("dbpool.oracle",0,"OCIStmt allocated");
00475     /* prepare statement */
00476     status = OCIStmtPrepare(stmt, conn->errhp, octstr_get_cstr(sql), 
00477                             octstr_len(sql), OCI_NTV_SYNTAX, OCI_DEFAULT);
00478     if (OCI_SUCCESS != status) {
00479         oracle_checkerr(conn->errhp, status);
00480         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00481         return -1;
00482     }
00483     debug("dbpool.oracle",0,"OCIStmtPrepare done");
00484    
00485     /* bind variables */
00486     for (i = 0; i < binds_len; i++) {
00487         Octstr *bind = gwlist_get(binds, i);
00488         OCIBind *bndhp = NULL;
00489         status = OCIBindByPos(stmt, &bndhp, 
00490                               conn->errhp, (i+1), (dvoid *) octstr_get_cstr(bind),
00491                               (sword) octstr_len(bind)+1, SQLT_STR, (dvoid *) 0, (ub2 *)0,
00492                               (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DEFAULT);
00493         if (OCI_SUCCESS != status) {
00494             oracle_checkerr(conn->errhp, status);
00495             OCIHandleFree(stmt, OCI_HTYPE_STMT);
00496             return -1;
00497         }
00498     }
00499     
00500     /* execute our statement */
00501     status = OCIStmtExecute(conn->svchp, stmt, conn->errhp, 1, 0, NULL, NULL, 
00502                             /*OCI_DEFAULT*/ OCI_COMMIT_ON_SUCCESS);
00503     if (OCI_SUCCESS != status && OCI_NO_DATA != status) {
00504         oracle_checkerr(conn->errhp, status);
00505         OCIHandleFree(stmt, OCI_HTYPE_STMT);
00506         return -1;
00507     }
00508     debug("dbpool.oracle",0,"OCIStmtExecute done");
00509     /* retrieve #rows processed so far */
00510     status = OCIAttrGet(stmt, OCI_HTYPE_STMT, &rows, 0, OCI_ATTR_ROW_COUNT, 
00511                         conn->errhp);
00512     if (status != OCI_SUCCESS) {
00513         oracle_checkerr(conn->errhp, status);
00514         /* we doesn't return error here, because sql is executed and commited already */
00515     }
00516     debug("dbpool.oracle",0,"rows processed = %d", rows);
00517 
00518     OCIHandleFree(stmt, OCI_HTYPE_STMT);
00519     
00520     return (int) rows;
00521 }
00522 
00523 static struct db_ops oracle_ops = {
00524     .open = oracle_open_conn,
00525     .close = oracle_close_conn,
00526     .check = oracle_check_conn,
00527     .conf_destroy = oracle_conf_destroy,
00528     .select = oracle_select,
00529     .update = oracle_update
00530 };
00531 
00532 #endif
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.