/** * db.c * * Copyright (C) 2003 - 2006 Bojan Smojver, Rexursive * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licence as published by the Free * Software Foundation; either version 2 of the Licence, 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 Licence for * more details. * * You should have received a copy of the GNU General Public Licence along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as two special exceptions, Bojan Smojver, Rexursive, gives * permission to: * * 1. Link, both statically and dynamically, the code of this program with * Apache HTTP Server, Apache Portable Runtime and Apache Portable Runtime * Utility Library from Apache Software Foundation (or with modified versions * of the above, that use the same licence - Apache Software Licence 1.1, 2.0 * or any later version), and distribute linked combinations including the * program and the above software from Apache Software Foundation. You must * obey the GNU General Public Licence in all respects for all of the code * used other than Apache HTTP Server, Apache Portable Runtime and Apache * Portable Runtime Utility Library. * * 2. Dynamically link any shared library with this program at run-time, * through the interface of SpinApplication or LoadModule run-time * configuration directives of Apache HTTP Server, as provided by this program * or Apache HTTP Server itself, regardless of licensing terms of those shared * libraries. You must obey the GNU General Public Licence in all respects for * all of the code used other than that of those shared libraries. * * If you modify this file, you may extend these exceptions to your version of * the file, but you are not obligated to do so. If you do not wish to do so, * delete one or both of these exception statements from your version. * */ #include "private.h" /* various get/set functions */ apr_pool_t *rxv_spin_db_pool(rxv_spin_db_t *db){ return db?db->pool:NULL; } char *rxv_spin_db_cinfo(rxv_spin_db_t *db){ return db?db->cinfo:NULL; } const apr_dbd_driver_t *rxv_spin_db_driver(rxv_spin_db_t *db){ return db?db->driver:NULL; } apr_dbd_t *rxv_spin_db_handle(rxv_spin_db_t *db){ return db?db->handle:NULL; } apr_dbd_transaction_t *rxv_spin_db_txn(rxv_spin_db_txn_t *txn){ return txn?txn->txn:NULL; } /* connection functions */ static apr_status_t db_clean(void *data){ apr_status_t status; rxv_spin_db_t *db=data; if((status=apr_dbd_close(db->driver,db->handle))!=APR_SUCCESS) return status; if(db->cpool){ apr_hash_set(db->cpool->conns,db->cinfo,APR_HASH_KEY_STRING,NULL); APR_RING_REMOVE(db,link); } return APR_SUCCESS; } rxv_spin_db_t *rxv_spin_db_connect(rxv_spin_ctx_t *ctx,const char *conninfo){ rxv_spin_cpool_t *cpool; rxv_spin_db_t *db; char *p,*dname; apr_pool_t *lpool; if(!(ctx && conninfo)) return NULL; cpool=ctx->cpool; /* try to reuse connection from the pool */ if(cpool && (db=apr_hash_get(cpool->conns,conninfo,APR_HASH_KEY_STRING))){ APR_RING_REMOVE(db,link); APR_RING_INSERT_HEAD(&cpool->list,db,rxv_spin_db,link); return db; } /* we have to have driver:conn_string format */ if(!((p=strchr(conninfo,':')) && *(p+1))) return NULL; /* connection specific memory pool */ apr_pool_create(&lpool,cpool?cpool->pool:ctx->pool); dname=apr_pstrmemdup(lpool,conninfo,p-conninfo); /* connection not found, we'll set one up */ db=apr_pcalloc(lpool,sizeof(*db)); /* fetch database specific driver */ if(apr_dbd_get_driver(ctx->ppool,dname,&db->driver)!=APR_SUCCESS){ apr_pool_destroy(lpool); return NULL; } /* open up database connection */ if(apr_dbd_open(db->driver,lpool,p+1,&db->handle)!=APR_SUCCESS){ apr_pool_destroy(lpool); return NULL; } /* we have to copy into the connection pool now */ db->cinfo=apr_pstrdup(lpool,conninfo); db->pool=lpool; db->cleanup=db_clean; apr_pool_create(&db->spool,lpool); if(cpool){ db->cpool=cpool; APR_RING_INSERT_HEAD(&cpool->list,db,rxv_spin_db,link); apr_hash_set(cpool->conns,db->cinfo,APR_HASH_KEY_STRING,db); cpool->count++; } apr_pool_cleanup_register(lpool,db,db_clean,db_clean); return db; } /* * WARNING: BIG TIME CHEATING! */ /* the fake apr_dbd_get_name function */ #ifndef HAVE_APR_DBD_GET_NAME static const char *get_name(const apr_dbd_driver_t *driver,apr_pool_t *pool, apr_dbd_results_t *res,int col){ #if defined (HAVE_LIBPQ) && defined (HAVE_LIBPQ_FE_H) struct apr_dbd_pgsql_results_t{ int random; PGconn *handle; PGresult *res; size_t ntuples; size_t sz; size_t index; } *pgres; #endif #if defined (HAVE_LIBMYSQLCLIENT_R) && defined (HAVE_MYSQL_H) struct apr_dbd_mysql_results_t{ int random; MYSQL_RES *res; MYSQL_STMT *statement; MYSQL_BIND *bind; } *myres; #endif #if defined (HAVE_LIBSQLITE) && defined (HAVE_SQLITE_H) struct apr_dbd_sqlite2_results_t{ int random; sqlite *handle; char **res; size_t ntuples; size_t sz; size_t index; } *s2res; #endif #if defined (HAVE_LIBSQLITE3) && defined (HAVE_SQLITE3_H) typedef struct{ char *name; char *value; int size; int type; } apr_dbd_sqlite3_column_t; typedef struct{ apr_dbd_results_t *res; apr_dbd_sqlite3_column_t **columns; apr_dbd_row_t *next_row; int columnCount; int rownum; } apr_dbd_sqlite3_row_t; struct apr_dbd_sqlite3_results_t{ int random; sqlite3 *handle; sqlite3_stmt *stmt; apr_dbd_sqlite3_row_t *next_row; size_t sz; int tuples; char **col_names; } *s3res; #endif const char *dname=apr_dbd_name(driver); if(!strcmp(dname,"pgsql")){ #if defined (HAVE_LIBPQ) && defined (HAVE_LIBPQ_FE_H) pgres=(struct apr_dbd_pgsql_results_t *)res; return (pgres->res?PQfname(pgres->res,col):NULL); #endif } else if(!strcmp(dname,"mysql")){ #if defined (HAVE_LIBMYSQLCLIENT_R) && defined (HAVE_MYSQL_H) myres=(struct apr_dbd_mysql_results_t *)res; if((col<0) || (col>=mysql_num_fields(myres->res))) return NULL; return mysql_fetch_fields(myres->res)[col].name; #endif } else if(!strcmp(dname,"sqlite2")){ #if defined (HAVE_LIBSQLITE) && defined (HAVE_SQLITE_H) s2res=(struct apr_dbd_sqlite2_results_t *)res; if((col<0) || (col>=s2res->sz)) return NULL; return s2res->res[col]; #endif } else if(!strcmp(dname,"sqlite3")){ #if defined (HAVE_LIBSQLITE3) && defined (HAVE_SQLITE3_H) s3res=(struct apr_dbd_sqlite3_results_t *)res; if((col<0) || (col>=s3res->sz)) return NULL; return s3res->next_row->columns[col]->name; #endif } return apr_psprintf(pool,"column%d",col); } #endif /* result conversion functions */ rxv_spin_data_t *rxv_spin_db_data(apr_pool_t *pool,rxv_spin_db_t *db, apr_dbd_results_t *dbdres){ rxv_spin_data_t *result; apr_dbd_row_t *row=NULL; int i,j,cols,rows; rxv_spin_data_t **arrays,*array; const char *entry; if(!(pool && dbdres)) return NULL; cols=apr_dbd_num_cols(db->driver,dbdres); rows=apr_dbd_num_tuples(db->driver,dbdres); result=apr_pcalloc(pool,sizeof(*result)); /* return empty result if number of rows/columns isn't above zero */ if(cols<=0 || rows<=0) return result; arrays=apr_palloc(pool,sizeof(*arrays)*cols); result->type=RXV_SPIN_DATA_RWS; result->size=rows; result->cols=apr_hash_make(pool); for(j=0;jdriver,pool,dbdres,j); #else const char *cname=apr_dbd_get_name(db->driver,dbdres,j); #endif if(!cname) return NULL; array=apr_pcalloc(pool,sizeof(*array)*rows); apr_hash_set(result->cols,cname,APR_HASH_KEY_STRING,*(arrays+j)=array); } for(i=0;idriver,pool,dbdres,&row,-1)){ for(j=0;jdriver,row,j); single->type=RXV_SPIN_DATA_SGL; if(entry){ single->data=apr_pstrdup(pool,entry); single->size=strlen(entry); } } } else{ return NULL; } } return result; } /* query related functions */ rxv_spin_data_t *rxv_spin_db_select(apr_pool_t *pool,rxv_spin_db_t *db, const char *query){ apr_dbd_results_t *dbdres=NULL; rxv_spin_data_t *res; if(!(pool && db && query)) return NULL; apr_pool_clear(db->spool); if(apr_dbd_select(db->driver,db->spool,db->handle,&dbdres,query,1)) return NULL; res=rxv_spin_db_data(pool,db,dbdres); return res; } int rxv_spin_db_query(apr_pool_t *pool,rxv_spin_db_t *db,const char *query){ int nrows=-1; if(!(pool && db && query)) return -1; if(apr_dbd_query(db->driver,db->handle,&nrows,query)) return -1; return nrows; } /* point here if there is prepared statement support, but prepare failed */ static const apr_dbd_prepared_t *badstmt=(apr_dbd_prepared_t *)""; /* prepared statement related functions */ static apr_dbd_prepared_t *db_prepare(apr_pool_t *pool,rxv_spin_db_t *db, const char *query){ apr_dbd_prepared_t *stmt=NULL; apr_pool_t *lpool=NULL; char *q,*ascdig; if(!(pool && db && query)) return NULL; /* try to reuse statement from the connection */ if(db->stmts){ if((stmt=apr_hash_get(db->stmts,query,APR_HASH_KEY_STRING))) return stmt==badstmt?NULL:stmt; } else{ db->stmts=apr_hash_make(db->pool); } if(!(ascdig=rxv_spin_hash(pool,query))) return NULL; q=apr_pstrdup(db->pool,query); /* set up a new prepared statment */ apr_pool_create(&lpool,db->pool); if(apr_dbd_prepare(db->driver,lpool, db->handle,query,ascdig,&stmt)==APR_SUCCESS){ apr_hash_set(db->stmts,q,APR_HASH_KEY_STRING,stmt); } else{ apr_hash_set(db->stmts,q,APR_HASH_KEY_STRING,badstmt); apr_pool_destroy(lpool); } return stmt; } rxv_spin_data_t *rxv_spin_db_pselect(apr_pool_t *pool,rxv_spin_db_t *db, const char *query,...){ apr_dbd_prepared_t *stmt=NULL; apr_dbd_results_t *dbdres=NULL; rxv_spin_data_t *res; int nargs=0; const char **args,**p; va_list ap; /* find the number of arguments */ va_start(ap,query); for(;va_arg(ap,char*);nargs++) ; va_end(ap); /* copy arguments to an array */ args=apr_pcalloc(pool,sizeof(*args)*(nargs+1)); va_start(ap,query); for(p=args;(*p=va_arg(ap,char*));p++) ; va_end(ap); if(!(stmt=db_prepare(pool,db,query))) return NULL; apr_pool_clear(db->spool); if(apr_dbd_pselect(db->driver,db->spool,db->handle,&dbdres,stmt,1,nargs,args)) return NULL; res=rxv_spin_db_data(pool,db,dbdres); return res; } int rxv_spin_db_pquery(apr_pool_t *pool,rxv_spin_db_t *db, const char *query,...){ apr_dbd_prepared_t *stmt=NULL; int nrows=-1,nargs=0; const char **args,**p; va_list ap; /* find the number of arguments */ va_start(ap,query); for(;va_arg(ap,char*);nargs++) ; va_end(ap); /* copy arguments to an array */ args=apr_pcalloc(pool,sizeof(*args)*(nargs+1)); va_start(ap,query); for(p=args;(*p=va_arg(ap,char*));p++) ; va_end(ap); if(!(stmt=db_prepare(pool,db,query))) return -1; if(apr_dbd_pquery(db->driver,pool,db->handle,&nrows,stmt,nargs,args)) return -1; return nrows; } rxv_spin_db_txn_t *rxv_spin_db_start(apr_pool_t *pool,rxv_spin_db_t *db){ rxv_spin_db_txn_t *txn; if(!(pool && db)) return NULL; txn=apr_pcalloc(pool,sizeof(*txn)); if(!apr_dbd_transaction_start(db->driver,pool,db->handle,&txn->txn)){ txn->pool=pool; txn->db=db; return txn; } return NULL; } apr_status_t rxv_spin_db_end(rxv_spin_db_txn_t *txn){ if(!txn) return APR_EGENERAL; if(!apr_dbd_transaction_end(txn->db->driver,txn->pool,txn->txn)) return APR_SUCCESS; return APR_EGENERAL; } apr_status_t rxv_spin_db_status(apr_pool_t *pool,rxv_spin_db_t *db){ if(!(pool && db)) return APR_EGENERAL; return apr_dbd_check_conn(db->driver,pool,db->handle); }