/**
* db.c
*
* Copyright (C) 2003 - 2007 Bojan Smojver, Rexursive
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
#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 rv;
rxv_spin_db_t *db=data;
if((rv=apr_dbd_close(db->driver,db->handle))!=APR_SUCCESS)
return rv;
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);
db->atime=ctx->atime; /* set access time */
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++;
}
db->atime=ctx->atime; /* set access time */
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);
}