Dynamic database backends. Just for code sharing for now, but mysql should work.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 29 Oct 2009 15:07:20 +0000 (15:07 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 29 Oct 2009 15:07:20 +0000 (15:07 +0000)
Changes:
- reshufle build process + small various fixes
- extended DB module API
- internal object-oriented API
- buffered commands remains in main module
- update the mysql backend module + some small other changes

org.glite.lbjp-common.db/Makefile
org.glite.lbjp-common.db/interface/db-int.h [new file with mode: 0644]
org.glite.lbjp-common.db/interface/db.h
org.glite.lbjp-common.db/project/get_soname.sh
org.glite.lbjp-common.db/src/db-mysql.c
org.glite.lbjp-common.db/src/db.c [new file with mode: 0644]

index 265a056..6e96135 100644 (file)
@@ -6,13 +6,10 @@ distdir=.
 globalprefix=glite
 lbutilsprefix=lbu
 package=glite-lb-utils-db
-version=0.2.0
 PREFIX=/opt/glite
-flavour=gcc32thr
 
-glite_location=/opt/glite
 mysql-devel_prefix=/opt/mysql
-mysql_version=4.1.11
+postgresql_prefix=/opt
 cppunit_prefix=/opt/cppunit
 thrflavour=gcc32pthr
 nothrflavour=gcc32
@@ -32,23 +29,32 @@ ifeq (${host_cpu},x86_64)
        archlib:=lib64
 endif   
 
-MYSQL_SONAME:=$(shell ../project/get_soname.sh ${mysql-devel_prefix}/${archlib} ${mysql_prefix}/${archlib} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib)
+MYSQL_SONAME:=$(shell ../project/get_soname.sh mysqlclient ${mysql-devel_prefix}/${archlib} ${mysql_prefix}/${archlib} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib)
+PSQL_SONAME:=$(shell ../project/get_soname.sh pq ${postgresql_prefix}/${archlib} ${postgresql_prefix}/lib)
+
+MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql
+PSQL_CPPFLAGS=-I`pg_config --includedir`
 
-MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql -DMYSQL_SONAME=\"${MYSQL_SONAME}\"
-MYSQL_LIBS=-lz
 
 CFLAGS:= \
        ${DEBUG} \
-       -DVERSION=\"${version}\" \
+       -DVERSION=\"${module.version}\" \
        -I${stagedir}/include -I${top_srcdir}/src -I. \
        -I${top_srcdir}/interface \
        ${COVERAGE_FLAGS} \
-       ${MYSQL_CPPFLAGS} \
        -D_GNU_SOURCE
 
 ifdef LBS_DB_PROFILE
        CFLAGS:=${CFLAGS} -DLBS_DB_PROFILE
 endif
+ifneq (${mysql-devel_prefix},no)
+       OBJS:=${OBJS} db-mysql.o
+       CFLAGS:=${CFLAGS} -DMYSQL_SONAME=\"${MYSQL_SONAME}\"
+endif
+ifneq (${postgresql_prefix},no)
+       OBJS:=${OBJS} db-pg.o
+       CFLAGS:=${CFLAGS} -DPSQL_SONAME=\"${PSQL_SONAME}\"
+endif
 
 TEST_LIBS:=-L${cppunit_prefix}/lib -lcppunit
 TEST_INC:=-I${cppunit_prefix}/include
@@ -59,9 +65,9 @@ COMPILE:=libtool --mode=compile ${CC} ${CFLAGS}
 LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/lib ${LDFLAGS} 
 INSTALL:=libtool --mode=install install
 
-EXT_LIBS:=${MYSQL_LIBS} -lglite_lbu_trio -lpthread -ldl
-OBJS:=db.o
-TESTOBJS:=dbtest.o
+EXT_LIBS:=-lglite_lbu_trio -lpthread -ldl
+TESTOBJS:=${OBJS} dbtest.o
+OBJS:=${OBJS} db.o
 HDRS:=db.h
 LOBJS:=${OBJS:.o=.lo}
 LTESTOBJS:=${TESTOBJS:.o=.lo}
@@ -69,18 +75,26 @@ LTESTOBJS:=${TESTOBJS:.o=.lo}
 default all: compile doc
 
 check_soname:
-       if [ "${MYSQL_SONAME}" = notfound ]; then \
-               echo "MySQL shared library not found!"; \
-               false; \
+       if [ "${mysql-devel_prefix}" != no ]; then \
+               if [ "${MYSQL_SONAME}" = notfound ]; then \
+                       echo "MySQL shared library not found!"; \
+                       false; \
+               fi \
+       fi
+       if [ "${postgresql_prefix}" != no ]; then \
+               if [ "${PSQL_SONAME}" = notfound ]; then \
+                       echo "PostgreSQL shared library not found!"; \
+                       false; \
+               fi \
        fi
 
 db.lo: check_soname
 
 libglite_lbu_db.la: ${LOBJS}
-       ${LINK} -o $@ $< ${EXT_LIBS}
+       ${LINK} -o $@ $+ ${EXT_LIBS}
 
 libglite_lbu_dbtest.la: ${LTESTOBJS}
-       ${LINK} -o $@ $< ${EXT_LIBS}
+       ${LINK} -o $@ $+ ${EXT_LIBS}
 
 dbtest.lo dbtest.o: db.c db.h
        ${COMPILE} -DGLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH=10 -c $< -o $@
@@ -107,7 +121,7 @@ doc:
 
 olddoc:
        cp ${top_srcdir}/doc/*.dox .
-       echo "PROJECT_NUMBER = ${version}" >> C.dox
+       echo "PROJECT_NUMBER = ${module.version}" >> C.dox
        doxygen C.dox
 
 stage: compile
@@ -116,22 +130,22 @@ stage: compile
 dist: distsrc distbin
 
 distsrc:
-       mkdir -p ${top_srcdir}/${package}-${version}
-       cd ${top_srcdir} && GLOBIGNORE="${package}-${version}" && cp -Rf * ${package}-${version}
-       cd ${top_srcdir} && tar -czf ${distdir}/${package}-${version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${version}
-       rm -rf ${top_srcdir}/${package}-${version}
+       mkdir -p ${top_srcdir}/${package}-${module.version}
+       cd ${top_srcdir} && GLOBIGNORE="${package}-${module.version}" && cp -Rf * ${package}-${module.version}
+       cd ${top_srcdir} && tar -czf ${distdir}/${package}-${module.version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${module.version}
+       rm -rf ${top_srcdir}/${package}-${module.version}
 
 distbin:
        $(MAKE) install PREFIX=`pwd`/tmpbuilddir${stagedir}
-       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz *; cd $$save_dir
+       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${module.version}_bin.tar.gz *; cd $$save_dir
        rm -rf tmpbuilddir
         
 install: all
        -mkdir -p ${PREFIX}/lib
-       -mkdir -p ${PREFIX}/share/doc/${package}-${version}
+       -mkdir -p ${PREFIX}/share/doc/${package}-${module.version}
        -mkdir -p ${PREFIX}/include/${globalprefix}/${lbutilsprefix}
-       ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${version}
-       -cp -r C ${PREFIX}/share/doc/${package}-${version}
+       ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${module.version}
+       -cp -r C ${PREFIX}/share/doc/${package}-${module.version}
        ${INSTALL} -m 755 "libglite_lbu_db.la" "${PREFIX}/lib/libglite_lbu_db.la"; \
        ${INSTALL} -m 644 ${top_srcdir}/interface/${HDRS} ${PREFIX}/include/${globalprefix}/${lbutilsprefix}
 
@@ -140,10 +154,18 @@ clean:
        rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/
        rm -rvf db_expire db_test
 
+db-mysql.o db-mysql.lo: db-mysql.c
+       ${COMPILE} ${MYSQL_CPPFLAGS} -c $<
+
+db-pg.o db-pg.lo: db-pg.c
+       ${COMPILE} ${PSQL_CPPFLAGS} -c $<
+
 %.o %.lo: %.c
        ${COMPILE} -c $<
 
-db.lo: db.c db.h
-db_test.lo: libglite_lbu_dbtest.la db.h db_test.c
+db.lo: db.c db.h db-int.h
+db_test.lo: libglite_lbu_dbtest.la db.h db-int.h db_test.c
+db-mysql.lo: db-mysql.c db-int.h db.h
+db-pg.lo: db-pg.c db-int.h db.h
 
 .PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage
diff --git a/org.glite.lbjp-common.db/interface/db-int.h b/org.glite.lbjp-common.db/interface/db-int.h
new file mode 100644 (file)
index 0000000..4df0a9a
--- /dev/null
@@ -0,0 +1,59 @@
+
+#define dprintf(CTX, FMT...) if ((CTX)->caps & GLITE_LBU_DB_CAP_ERRORS) fprintf(stderr, ##FMT)
+
+struct glite_lbu_DBContext_s {
+       int backend;
+       struct {
+               int code;
+               char *desc;
+       } err;
+       int caps;
+};
+typedef struct glite_lbu_DBContext_s glite_lbu_DBContext_t;
+
+struct glite_lbu_Statement_s {
+       glite_lbu_DBContext ctx;
+};
+typedef struct glite_lbu_Statement_s glite_lbu_Statement_t;
+
+
+
+typedef struct {
+       int backend;
+
+       int (*initContext)(glite_lbu_DBContext *ctx);
+       void (*freeContext)(glite_lbu_DBContext ctx);
+       int (*connect)(glite_lbu_DBContext ctx, const char *cs);
+       void (*close)(glite_lbu_DBContext ctx);
+       int (*queryCaps)(glite_lbu_DBContext ctx);
+       void (*setCaps)(glite_lbu_DBContext ctx, int caps);
+
+       int (*transaction)(glite_lbu_DBContext ctx);
+       int (*commit)(glite_lbu_DBContext ctx);
+       int (*rollback)(glite_lbu_DBContext ctx);
+
+       int (*fetchRow)(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results);
+       void (*freeStmt)(glite_lbu_Statement *stmt);
+
+       int (*queryIndices)(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names);
+       int (*execSQL)(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt);
+       int (*queryColumns)(glite_lbu_Statement stmt_gen, char **cols);
+
+       int (*prepareStmt)(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmt);
+       int (*execPreparedStmt_v)(glite_lbu_Statement stmt, int n, va_list ap);
+       long int (*lastid)(glite_lbu_Statement stmt);
+
+       void (*timeToDB)(time_t, char **str);
+       void (*timestampToDB)(double t, char **str);
+       time_t (*DBToTime)(const char *str);
+       double (*DBToTimestamp)(const char *str);
+} glite_lbu_DBBackend_t;
+
+int glite_lbu_DBSetError(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...);
+
+void glite_lbu_TimeToStrGeneric(time_t t, char **str, const char *amp);
+void glite_lbu_TimestampToStrGeneric(double t, char **str, const char *amp);
+void glite_lbu_TimeToStr(time_t t, char **str);
+void glite_lbu_TimestampToStr(double t, char **str);
+time_t glite_lbu_StrToTime(const char *str);
+double glite_lbu_StrToTimestamp(const char *str);
index bd7d7e0..6c36a4d 100644 (file)
@@ -1,9 +1,6 @@
 #ifndef GLITE_LBU_DB_H
 #define GLITE_LBU_DB_H
 
-#ident "$Header$"
-
-
 #include <time.h>
 #include <stdarg.h>
 
@@ -20,12 +17,13 @@ extern "C" {
  * Database modul module API (LB & JP Utils).
  *
  * There are two ways to access DB here:
- * - simple:
  *
+ * - simple:
  * SQL commands as single string. All values are incorporated in the SQL command strings. Proper escaping is required.
- * - enhanced:
  *
+ * - enhanced:
  * Prepared SQL commands with separated parameters, functions PrepareStmt() and ExecPreparedStmt(). All values are delivered in separated buffers. Its faster for multiple using and more secure.
+ *
  * @{
  */
 
@@ -105,6 +103,16 @@ typedef enum {
 
 
 /**
+ * Supported DB backends.
+ */
+typedef enum {
+       GLITE_LBU_DB_BACKEND_MYSQL = 0,
+       GLITE_LBU_DB_BACKEND_PSQL,
+       GLITE_LBU_DB_BACKEND_LAST
+} glite_lbu_DBBackendNo;
+
+
+/**
  * Get error state from DB context.
  *
  * \param[in]  ctx   context to work with
@@ -115,11 +123,21 @@ int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc);
 
 
 /**
+ * Clear the error from DB context.
+ *
+ * \param[in] ctx  context to work with
+ */
+int glite_lbu_DBClearError(glite_lbu_DBContext ctx);
+
+
+/**
  * Initialize the database context.
  *
  * \param[out] ctx   result context
+ * \param[in]  backend  required database backend
+ * \return     error code
  */
-int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx);
+int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx, int backend);
 
 
 /**
@@ -257,7 +275,7 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k
  * \param[in]   t    the converted time
  * \param[out]  str  result allocated string
  */
-void glite_lbu_TimeToDB(time_t t, char **str);
+void glite_lbu_TimeToDB(glite_lbu_DBContext ctx, time_t t, char **str);
 
 
 /** 
@@ -268,7 +286,7 @@ void glite_lbu_TimeToDB(time_t t, char **str);
  * \param[in]   t    the converted time
  * \param[out]  str  result allocated string
  */
-void glite_lbu_TimestampToDB(double t, char **str);
+void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, double t, char **str);
 
 
 /**
@@ -276,10 +294,21 @@ void glite_lbu_TimestampToDB(double t, char **str);
  *
  * String is expected in database for (ISO format).
  *
+ * \param[in] ctx  context to work with
  * \param[in] str  the converted string
  * \return         result time
  */
-time_t glite_lbu_DBToTime(const char *str);
+time_t glite_lbu_DBToTime(glite_lbu_DBContext ctx, const char *str);
+
+
+/**
+ * Convert database-specific time string to time (double).
+ *
+ * \param[in] ctx  context to work with
+ * \param[in] str  the converted string
+ * \return         result time
+ * */
+double glite_lbu_DBToTimestamp(glite_lbu_DBContext ctx, const char *str);
 
 
 /**
index d5f41b3..66c6aab 100755 (executable)
@@ -1,9 +1,11 @@
 #! /bin/sh
 
 lib=""
+filename="lib$1.so"
+shift
 for prefix in $@; do
        for dir in "$prefix" "$prefix/mysql"; do
-               l=`find $dir -maxdepth 1 -name libmysqlclient.so* | head -n 1`
+               l=`find $dir -maxdepth 1 -name "${filename}"* 2>/dev/null | head -n 1`
                if [ -f "$l" ]; then
                        lib=$l
                        break
@@ -15,7 +17,7 @@ for prefix in $@; do
 done
 
 if [ x"" != x"$lib" ]; then
-       readelf -d $lib | grep SONAME | sed 's/.*\(libmysqlclient.so.[0-9]\{1,\}\).*/\1/'
+       readelf -d $lib | grep SONAME | sed "s/.*\(${filename}.[0-9]\{1,\}\).*/\1/"
 else
        echo notfound
 fi
index 1c91526..971616f 100644 (file)
@@ -1,5 +1,3 @@
-#ident "$Header$"
-
 #include <sys/types.h>
 #ifdef LBS_DB_PROFILE
 #include <sys/time.h>
@@ -23,6 +21,7 @@
 
 #include "glite/lbu/trio.h"
 #include "db.h"
+#include "db-int.h"
 
 
 #define GLITE_LBU_MYSQL_INDEX_VERSION 40001
 #define GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH 256
 #endif
 
-#define CLR_ERR(CTX) lbu_clrerr((CTX))
-#define ERR(CTX, CODE, ARGS...) lbu_err((CTX), (CODE), __FUNCTION__, __LINE__, ##ARGS)
-#define STATUS(CTX) ((CTX)->err.code)
+#define CLR_ERR(CTX) glite_lbu_DBClearError((glite_lbu_DBContext)(CTX))
+#define ERR(CTX, CODE, ARGS...) glite_lbu_DBSetError((glite_lbu_DBContext)((CTX)), (CODE), __FUNCTION__, __LINE__, ##ARGS)
+#define STATUS(CTX) glite_lbu_DBError((glite_lbu_DBContext)((CTX)), NULL, NULL)
 #define MY_ERR(CTX) myerr((CTX), __FUNCTION__, __LINE__)
 #define MY_ERRSTMT(STMT) myerrstmt((STMT), __FUNCTION__, __LINE__)
 #define MY_ISOKSTMT(STMT, RETRY) myisokstmt((STMT), __FUNCTION__, __LINE__, (RETRY))
 
-#define USE_TRANS(CTX) ((CTX->caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0)
-#define LOAD(SYM, SYM2) if ((*(void **)(&db_handle.SYM) = dlsym(db_handle.lib, SYM2)) == NULL) { \
-       err = ERR(*ctx, ENOENT, "can't load symbol %s from mysql library (%s)", SYM2, dlerror()); \
+#define USE_TRANS(CTX) ((CTX->generic.caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0)
+#define LOAD(SYM, SYM2) if ((*(void **)(&mysql_module.SYM) = dlsym(mysql_module.lib, SYM2)) == NULL) { \
+       err = ERR(ctx, ENOENT, "can't load symbol %s from mysql library (%s)", SYM2, dlerror()); \
        break; \
 }
 
-#define dprintf(CTX, FMT...) if (CTX->caps & GLITE_LBU_DB_CAP_ERRORS) fprintf(stderr, ##FMT)
 
 
-struct glite_lbu_DBContext_s {
-       MYSQL *mysql;
-       const char *cs;
-       int caps;
-       struct {
-               int code;
-               char *desc;
-       } err;
+struct glite_lbu_DBContextMysql_s {
+       struct glite_lbu_DBContext_s generic;
        int in_transaction;     /* this flag is set whenever we are in DB transaction */
+       MYSQL *mysql;
 };
+typedef struct glite_lbu_DBContextMysql_s *glite_lbu_DBContextMysql;
 
 
-struct glite_lbu_Statement_s {
-       glite_lbu_DBContext  ctx;
+struct glite_lbu_StatementMysql_s {
+       glite_lbu_Statement_t generic;
 
        /* for simple commands */
        MYSQL_RES           *result;
@@ -71,20 +65,7 @@ struct glite_lbu_Statement_s {
        unsigned long        nrfields;
        char                *sql;
 };
-
-
-struct glite_lbu_bufInsert_s {
-       glite_lbu_DBContext ctx;
-       char    *table_name;
-       char    *columns;       /* names of columns to be inserted into 
-                                * (values separated with commas) */
-       char    **rows;         /* each row hold string of one row to be inserted
-                                * (values separated with commas) */
-       long    rec_num,        /* actual number of rows in structure */
-               rec_size;       /* approx. size of a real insert string */
-       long    size_limit,     /* size and # of records limit which trigger */
-               record_limit;   /* real insert; zero means unlimitted */
-};
+typedef struct glite_lbu_StatementMysql_s *glite_lbu_StatementMysql;
 
 
 /*
@@ -145,56 +126,93 @@ typedef struct {
        my_ulonglong (*mysql_stmt_affected_rows)(MYSQL_STMT *stmt);
        MYSQL_RES *(*mysql_stmt_result_metadata)(MYSQL_STMT *stmt);
        int (*mysql_stmt_fetch_column)(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset);
-} mysql_lib_handle_t;
-
+} mysql_module_t;
+
+
+/* backend module declaration */
+int glite_lbu_InitDBContextMysql(glite_lbu_DBContext *ctx_gen);
+void glite_lbu_FreeDBContextMysql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBConnectMysql(glite_lbu_DBContext ctx_gen, const char *cs);
+void glite_lbu_DBCloseMysql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBQueryCapsMysql(glite_lbu_DBContext ctx_gen);
+void glite_lbu_DBSetCapsMysql(glite_lbu_DBContext commmon_ctx, int caps);
+int glite_lbu_TransactionMysql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_CommitMysql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_RollbackMysql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_FetchRowMysql(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results);
+void glite_lbu_FreeStmtMysql(glite_lbu_Statement *stmt);
+int glite_lbu_QueryIndicesMysql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names);
+int glite_lbu_ExecSQLMysql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt);
+int glite_lbu_QueryColumnsMysql(glite_lbu_Statement stmt_gen, char **cols);
+int glite_lbu_PrepareStmtMysql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen);
+int glite_lbu_ExecPreparedStmtMysql_v(glite_lbu_Statement stmt_gen, int n, va_list ap);
+long int glite_lbu_LastidMysql(glite_lbu_Statement stmt_gen);
+
+glite_lbu_DBBackend_t mysql_backend = {
+       backend: GLITE_LBU_DB_BACKEND_MYSQL,
+
+       initContext: glite_lbu_InitDBContextMysql,
+       freeContext: glite_lbu_FreeDBContextMysql,
+       connect: glite_lbu_DBConnectMysql,
+       close: glite_lbu_DBCloseMysql,
+       queryCaps: glite_lbu_DBQueryCapsMysql,
+       setCaps: glite_lbu_DBSetCapsMysql,
+       transaction: glite_lbu_TransactionMysql,
+       commit: glite_lbu_CommitMysql,
+       rollback: glite_lbu_RollbackMysql,
+       fetchRow: glite_lbu_FetchRowMysql,
+       freeStmt: glite_lbu_FreeStmtMysql,
+       queryIndices: glite_lbu_QueryIndicesMysql,
+       execSQL: glite_lbu_ExecSQLMysql,
+       queryColumns: glite_lbu_QueryColumnsMysql,
+
+       timeToDB: glite_lbu_TimeToStr,
+       timestampToDB: glite_lbu_TimestampToStr,
+       DBToTime: glite_lbu_StrToTime,
+       DBToTimestamp: glite_lbu_StrToTimestamp,
+
+       prepareStmt: glite_lbu_PrepareStmtMysql,
+       execPreparedStmt_v: glite_lbu_ExecPreparedStmtMysql_v,
+       lastid: glite_lbu_LastidMysql,
+};
 
-static mysql_lib_handle_t db_handle = {
+static mysql_module_t mysql_module = {
        lib: NULL,
-       lock: PTHREAD_MUTEX_INITIALIZER
+       lock: PTHREAD_MUTEX_INITIALIZER,
 };
 
 
-static int lbu_clrerr(glite_lbu_DBContext ctx);
-static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...);
-static int myerr(glite_lbu_DBContext ctx, const char *source, int line);
-static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line);
-static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry);
-static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql);
+static int myerr(glite_lbu_DBContextMysql ctx, const char *source, int line);
+static int myerrstmt(glite_lbu_StatementMysql stmt, const char *source, int line);
+static int myisokstmt(glite_lbu_StatementMysql stmt, const char *source, int line, int *retry);
+static int db_connect(glite_lbu_DBContextMysql ctx, const char *cs, MYSQL **mysql);
 static void db_close(MYSQL *mysql);
-static int transaction_test(glite_lbu_DBContext ctx);
-static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned long *lengths, char **results);
-static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results);
+static int transaction_test(glite_lbu_DBContext ctx, int *caps);
+static int FetchRowSimple(glite_lbu_DBContextMysql ctx, MYSQL_RES *result, unsigned long *lengths, char **results);
+static int FetchRowPrepared(glite_lbu_DBContextMysql ctx, glite_lbu_StatementMysql stmt, unsigned int n, unsigned long *lengths, char **results);
 static void set_time(MYSQL_TIME *mtime, const double time);
 static void glite_lbu_DBCleanup(void);
-static void glite_lbu_FreeStmt_int(glite_lbu_Statement stmt);
+static void glite_lbu_FreeStmt_int(glite_lbu_StatementMysql stmt);
 
 
 /* ---- common ---- */
 
 
-int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc) {
-       if (text) *text = strdup(strerror(ctx->err.code));
-       if (desc) {
-               if (ctx->err.desc) *desc = strdup(ctx->err.desc);
-               else *desc = NULL;
-       }
-
-       return ctx->err.code;
-}
-
 
-int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx) {
+int glite_lbu_InitDBContextMysql(glite_lbu_DBContext *ctx_gen) {
+       glite_lbu_DBContextMysql ctx;
        int err = 0;
-       unsigned int ver_u;
+       int ver_u;
 
-       *ctx = calloc(1, sizeof **ctx);
-       if (!*ctx) return ENOMEM;
+       ctx = calloc(1, sizeof *ctx);
+       if (!ctx) return ENOMEM;
+       *ctx_gen = (glite_lbu_DBContext)ctx;
 
        /* dynamic load the mysql library */
-       pthread_mutex_lock(&db_handle.lock);
-       if (!db_handle.lib) {
-               db_handle.lib = dlopen(MYSQL_SONAME, RTLD_LAZY | RTLD_LOCAL);
-               if (!db_handle.lib) return ERR(*ctx, ENOENT, "dlopen(): " MYSQL_SONAME ": %s", dlerror());
+       pthread_mutex_lock(&mysql_module.lock);
+       if (!mysql_module.lib) {
+               mysql_module.lib = dlopen(MYSQL_SONAME, RTLD_LAZY | RTLD_LOCAL);
+               if (!mysql_module.lib) return ERR(ctx, ENOENT, "dlopen(): " MYSQL_SONAME ": %s", dlerror());
                do {
                        LOAD(mysql_init, "mysql_init");
                        LOAD(mysql_get_client_version, "mysql_get_client_version");
@@ -228,63 +246,68 @@ int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx) {
                        LOAD(mysql_stmt_fetch_column, "mysql_stmt_fetch_column");
 
                        // check the runtime version
-                       ver_u = db_handle.mysql_get_client_version();
+                       ver_u = mysql_module.mysql_get_client_version();
                        if (ver_u != MYSQL_VERSION_ID) {
-                               fprintf(stderr,"Warning: MySQL library version mismatch (compiled '%lu', runtime '%lu')", MYSQL_VERSION_ID, ver_u);
-                               syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%lu', runtime '%lu')", MYSQL_VERSION_ID, ver_u);
+                               fprintf(stderr,"Warning: MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u);
+                               syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u);
                        }
 
-                       pthread_mutex_unlock(&db_handle.lock);
+                       pthread_mutex_unlock(&mysql_module.lock);
                        atexit(glite_lbu_DBCleanup);
                } while(0);
 
                if (err) {
-                       dlclose(db_handle.lib);
-                       db_handle.lib = NULL;
-                       pthread_mutex_unlock(&db_handle.lock);
+                       dlclose(mysql_module.lib);
+                       mysql_module.lib = NULL;
+                       pthread_mutex_unlock(&mysql_module.lock);
                        return err;
                }
-       } else pthread_mutex_unlock(&db_handle.lock);
+       } else pthread_mutex_unlock(&mysql_module.lock);
 
        return 0;
 }
 
 
-void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) {
+void glite_lbu_FreeDBContextMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        if (ctx) {
                assert(ctx->mysql == NULL);
-               free(ctx->err.desc);
                free(ctx);
        }
 }
 
 
-int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+int glite_lbu_DBConnectMysql(glite_lbu_DBContext ctx_gen, const char *cs) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        if (db_connect(ctx, cs, &ctx->mysql) != 0 ||
-           glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0 ||
-           glite_lbu_ExecSQL(ctx, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", NULL) < 0)
+           glite_lbu_ExecSQL(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0 ||
+           glite_lbu_ExecSQL(ctx_gen, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", NULL) < 0)
                return STATUS(ctx);
        else
                return 0;
 }
 
 
-void glite_lbu_DBClose(glite_lbu_DBContext ctx) {
+void glite_lbu_DBCloseMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        db_close(ctx->mysql);
        ctx->mysql = NULL;
        CLR_ERR(ctx);
 }
 
 
-int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
+int glite_lbu_DBQueryCapsMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
        MYSQL   *m = ctx->mysql;
-       int     major,minor,sub,version,caps,origcaps;
+       int     major,minor,sub,version,caps;
        const char *ver_s;
 
-       origcaps = ctx->caps;
        caps = 0;
 
-       ver_s = db_handle.mysql_get_server_info(m);
+       ver_s = mysql_module.mysql_get_server_info(m);
        if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub))
                return ERR(ctx, EINVAL, "problem retreiving MySQL version");
        version = 10000*major + 100*minor + sub;
@@ -293,25 +316,25 @@ int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
        if (version >= GLITE_LBU_MYSQL_PREPARED_VERSION) caps |= GLITE_LBU_DB_CAP_PREPARED;
 
        CLR_ERR(ctx);
-       transaction_test(ctx);
-       if ((ctx->caps & GLITE_LBU_DB_CAP_TRANSACTIONS)) caps |= GLITE_LBU_DB_CAP_TRANSACTIONS;
+       transaction_test(ctx_gen, &caps);
 
-       ctx->caps = origcaps;
        if (STATUS(ctx) == 0) return caps;
        else return -1;
 }
 
 
-void glite_lbu_DBSetCaps(glite_lbu_DBContext ctx, int caps) {
-       ctx->caps = caps;
+void glite_lbu_DBSetCapsMysql(glite_lbu_DBContext ctx_gen, int caps) {
+       ctx_gen->caps = caps;
 }
 
 
-int glite_lbu_Transaction(glite_lbu_DBContext ctx) {
+int glite_lbu_TransactionMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        CLR_ERR(ctx);
        if (USE_TRANS(ctx)) {
-               if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=0", NULL) < 0) goto err;
-               if (glite_lbu_ExecSQL(ctx, "BEGIN", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "SET AUTOCOMMIT=0", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "BEGIN", NULL) < 0) goto err;
                ctx->in_transaction = 1;
        }
 err:
@@ -319,11 +342,13 @@ err:
 }
 
 
-int glite_lbu_Commit(glite_lbu_DBContext ctx) {
+int glite_lbu_CommitMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        CLR_ERR(ctx);
        if (USE_TRANS(ctx)) {
-               if (glite_lbu_ExecSQL(ctx, "COMMIT", NULL) < 0) goto err;
-               if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "COMMIT", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
                ctx->in_transaction = 0;
        }
 err:
@@ -331,11 +356,13 @@ err:
 }
 
 
-int glite_lbu_Rollback(glite_lbu_DBContext ctx) {
+int glite_lbu_RollbackMysql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+
        CLR_ERR(ctx);
        if (USE_TRANS(ctx)) { 
-               if (glite_lbu_ExecSQL(ctx, "ROLLBACK", NULL) < 0) goto err;
-               if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "ROLLBACK", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQL(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
                ctx->in_transaction = 0;
        }
 err:
@@ -343,31 +370,37 @@ err:
 }
 
 
-int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+int glite_lbu_FetchRowMysql(glite_lbu_Statement stmt_gen, unsigned int n, unsigned long *lengths, char **results) {
+       glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen;
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)stmt->generic.ctx;
+
        memset(results, 0, n * sizeof(*results));
-       if (stmt->result) return FetchRowSimple(stmt->ctx, stmt->result, lengths, results);
-       else return FetchRowPrepared(stmt->ctx, stmt, n, lengths, results);
+       if (stmt->result) return FetchRowSimple(ctx, stmt->result, lengths, results);
+       else return FetchRowPrepared(ctx, stmt, n, lengths, results);
 }
 
 
-static void glite_lbu_FreeStmt_int(glite_lbu_Statement stmt) {
+static void glite_lbu_FreeStmt_int(glite_lbu_StatementMysql stmt) {
        if (stmt) {
-               if (stmt->result) db_handle.mysql_free_result(stmt->result);
-               if (stmt->stmt) db_handle.mysql_stmt_close(stmt->stmt);
+               if (stmt->result) mysql_module.mysql_free_result(stmt->result);
+               if (stmt->stmt) mysql_module.mysql_stmt_close(stmt->stmt);
                free(stmt->sql);
        }
 }
 
 
-void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
+void glite_lbu_FreeStmtMysql(glite_lbu_Statement *stmt_gen) {
+       glite_lbu_StatementMysql *stmt = (glite_lbu_StatementMysql*)stmt_gen;
+
        glite_lbu_FreeStmt_int(*stmt);
        free(*stmt);
        *stmt = NULL;
 }
 
 
-int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names) {
-       glite_lbu_Statement       stmt = NULL;
+int glite_lbu_QueryIndicesMysql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+       glite_lbu_Statement stmt;
 
        size_t  i,j,ret;
 
@@ -386,18 +419,18 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k
        Key_name = Seq_in_index = Column_name = Sub_part = -1;
 
        asprintf(&sql, "show index from %s", table);
-       if (glite_lbu_ExecSQL(ctx,sql,&stmt)<0) {
+       if (glite_lbu_ExecSQLMysql(ctx_gen,sql,&stmt)<0) {
                free(sql);
                return STATUS(ctx);
        }
        free(sql);
 
-       while ((ret = glite_lbu_FetchRow(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) {
+       while ((ret = glite_lbu_FetchRowMysql(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) {
                assert(ret <= (int)(sizeof showcol/sizeof showcol[0]));
 
                if (!col_names) {
                        col_names = malloc(ret * sizeof col_names[0]);
-                       glite_lbu_QueryColumns(stmt,col_names);
+                       glite_lbu_QueryColumnsMysql(stmt,col_names);
                        for (i=0; i<ret; i++) 
                                if (!strcasecmp(col_names[i],"Key_name")) Key_name = i;
                                else if (!strcasecmp(col_names[i],"Seq_in_index")) Seq_in_index = i;
@@ -435,7 +468,7 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k
                for (i = 0; i<ret; i++) free(showcol[i]);
        }
 
-       glite_lbu_FreeStmt(&stmt);
+       glite_lbu_FreeStmtMysql(&stmt);
        free(cols);
        free(col_names);
 
@@ -464,7 +497,9 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k
 
 /* ---- simple ---- */
 
-int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt) {
+int glite_lbu_ExecSQLMysql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+       glite_lbu_StatementMysql stmt;
        int     merr;
        int     retry_nr = 0;
        int     do_reconnect = 0;
@@ -480,7 +515,8 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem
 
        CLR_ERR(ctx);
 
-       if (stmt) *stmt = NULL;
+       if (stmt_gen) *stmt_gen = NULL;
+       stmt = NULL;
 
 #ifdef LBS_DB_PROFILE
        gettimeofday(&start,NULL);
@@ -488,26 +524,26 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem
 
        while (retry_nr == 0 || do_reconnect) {
                do_reconnect = 0;
-               if (db_handle.mysql_query(ctx->mysql, cmd)) {
+               if (mysql_module.mysql_query(ctx->mysql, cmd)) {
                        /* error occured */
-                       switch (merr = db_handle.mysql_errno(ctx->mysql)) {
+                       switch (merr = mysql_module.mysql_errno(ctx->mysql)) {
                                case 0:
                                        break;
                                case ER_DUP_ENTRY: 
-                                       ERR(ctx, EEXIST, db_handle.mysql_error(ctx->mysql));
+                                       ERR(ctx, EEXIST, mysql_module.mysql_error(ctx->mysql));
                                        return -1;
                                        break;
                                case CR_SERVER_LOST:
                                case CR_SERVER_GONE_ERROR:
                                        if (ctx->in_transaction) {
-                                               ERR(ctx, ERESTART, db_handle.mysql_error(ctx->mysql));
+                                               ERR(ctx, ERESTART, mysql_module.mysql_error(ctx->mysql));
                                                return -1;
                                        }
                                        else if (retry_nr <= 0) 
                                                do_reconnect = 1;
                                        break;
                                case ER_LOCK_DEADLOCK:
-                                       ERR(ctx, EDEADLOCK, db_handle.mysql_error(ctx->mysql));
+                                       ERR(ctx, EDEADLOCK, mysql_module.mysql_error(ctx->mysql));
                                        return -1;
                                        break;  
                                default:
@@ -519,24 +555,25 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem
                retry_nr++;
        }
 
-       if (stmt) {
-               *stmt = calloc(1, sizeof(**stmt));
-               if (!*stmt) {
+       if (stmt_gen) {
+               stmt = calloc(1, sizeof(*stmt));
+               if (!stmt) {
                        ERR(ctx, ENOMEM, NULL);
                        return -1;
                }
-               (**stmt).ctx = ctx;
-               (**stmt).result = db_handle.mysql_store_result(ctx->mysql);
-               if (!(**stmt).result) {
-                       if (db_handle.mysql_errno(ctx->mysql)) {
+               stmt->generic.ctx = ctx_gen;
+               stmt->result = mysql_module.mysql_store_result(ctx->mysql);
+               if (!stmt->result) {
+                       if (mysql_module.mysql_errno(ctx->mysql)) {
                                MY_ERR(ctx);
-                               *stmt = NULL;
+                               free(stmt);
                                return -1;
                        }
                }
+               *stmt_gen = (glite_lbu_Statement)stmt;
        } else {
-               MYSQL_RES       *r = db_handle.mysql_store_result(ctx->mysql);
-               db_handle.mysql_free_result(r);
+               MYSQL_RES       *r = mysql_module.mysql_store_result(ctx->mysql);
+               mysql_module.mysql_free_result(r);
        }
 #ifdef LBS_DB_PROFILE
        pid = getpid();
@@ -551,102 +588,76 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem
        fprintf(stderr,"[%d] %s\n[%d] %3ld.%06ld (sum: %3ld.%06ld)\n",pid,cmd,pid,end.tv_sec,end.tv_usec,sum.tv_sec,sum.tv_usec);
 #endif
 
-       return db_handle.mysql_affected_rows(ctx->mysql);
+       return mysql_module.mysql_affected_rows(ctx->mysql);
 }
 
 
-int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols)
+int glite_lbu_QueryColumnsMysql(glite_lbu_Statement stmt_gen, char **cols)
 {
+       glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen;
        int     i = 0;
        MYSQL_FIELD     *f;
 
-       CLR_ERR(stmt->ctx);
-       if (!stmt->result) return ERR(stmt->ctx, EINVAL, "QueryColumns implemented only for simple API");
-       while ((f = db_handle.mysql_fetch_field(stmt->result))) cols[i++] = f->name;
+       CLR_ERR(stmt->generic.ctx);
+       if (!stmt->result) return ERR(stmt->generic.ctx, ENOTSUP, "QueryColumns implemented only for simple API");
+       while ((f = mysql_module.mysql_fetch_field(stmt->result))) cols[i++] = f->name;
        return i == 0;
 }
 
 
-void glite_lbu_TimeToDB(time_t t, char **str) {
-       struct tm       *tm = gmtime(&t);
-
-       asprintf(str,"'%4d-%02d-%02d %02d:%02d:%02d'",tm->tm_year+1900,tm->tm_mon+1,
-               tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
-}
-
-
-void glite_lbu_TimestampToDB(double t, char **str) {
-       time_t tsec = t;
-       struct tm *tm = gmtime(&tsec);
-
-       t = t - tsec + tm->tm_sec;
-       asprintf(str,"'%4d-%02d-%02d %02d:%02d:%02.09f'",tm->tm_year+1900,tm->tm_mon+1,
-                tm->tm_mday,tm->tm_hour,tm->tm_min,t);
-}
-
-
-time_t glite_lbu_DBToTime(const char *str) {
-       struct tm       tm;
-
-       memset(&tm,0,sizeof(tm));
-       setenv("TZ","UTC",1); tzset();
-       sscanf(str,"%4d-%02d-%02d %02d:%02d:%02d",
-               &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
-               &tm.tm_hour,&tm.tm_min,&tm.tm_sec);
-       tm.tm_year -= 1900;
-       tm.tm_mon--;
-
-       return mktime(&tm);
-}
-
 /* ---- prepared --- */
 
-int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmt) {
+int glite_lbu_PrepareStmtMysql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen) {
+       glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+       glite_lbu_StatementMysql stmt;
        int ret, retry;
        MYSQL_RES *meta;
 
        // init
-       *stmt = calloc(1, sizeof(**stmt));
-       (*stmt)->ctx = ctx;
+       stmt = calloc(1, sizeof(*stmt));
+       stmt->generic.ctx = ctx_gen;
+       *stmt_gen = NULL;
 
        // create the SQL command
-       if (((*stmt)->stmt = db_handle.mysql_stmt_init(ctx->mysql)) == NULL)
-               return MY_ERRSTMT(*stmt);
+       if ((stmt->stmt = mysql_module.mysql_stmt_init(ctx->mysql)) == NULL)
+               return STATUS(ctx);
 
        // prepare the SQL command
        retry = 1;
        do {
-               db_handle.mysql_stmt_prepare((*stmt)->stmt, sql, strlen(sql));
-               ret = MY_ISOKSTMT(*stmt, &retry);
+               mysql_module.mysql_stmt_prepare(stmt->stmt, sql, strlen(sql));
+               ret = MY_ISOKSTMT(stmt, &retry);
        } while (ret == 0);
        if (ret == -1) goto failed;
 
        // number of fields (0 for no results)
-       if ((meta = db_handle.mysql_stmt_result_metadata((*stmt)->stmt)) != NULL) {
-               (*stmt)->nrfields = db_handle.mysql_num_fields(meta);
-               db_handle.mysql_free_result(meta);
+       if ((meta = mysql_module.mysql_stmt_result_metadata(stmt->stmt)) != NULL) {
+               stmt->nrfields = mysql_module.mysql_num_fields(meta);
+               mysql_module.mysql_free_result(meta);
        } else
-               (*stmt)->nrfields = 0;
+               stmt->nrfields = 0;
 
        // remember the command
-       (*stmt)->sql = strdup(sql);
+       stmt->sql = strdup(sql);
 
+       *stmt_gen = (glite_lbu_Statement)stmt;
        return CLR_ERR(ctx);
 
 failed:
-       glite_lbu_FreeStmt(stmt);
+       glite_lbu_FreeStmt_int(stmt);
+       free(stmt);
        return STATUS(ctx);
 }
 
 
-int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
+int glite_lbu_ExecPreparedStmtMysql_v(glite_lbu_Statement stmt_gen, int n, va_list ap) {
+       glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen;
        int i, prepare_retry;
        glite_lbu_DBType type;
        char *pchar;
        int *pint;
        long int *plint;
        MYSQL_TIME *ptime;
-       glite_lbu_DBContext ctx;
        int ret, retry;
        MYSQL_BIND *binds = NULL;
        void **data = NULL;
@@ -723,7 +734,7 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
        do {
                // bind parameters
                if (n) {
-                       if (db_handle.mysql_stmt_bind_param(stmt->stmt, binds) != 0) {
+                       if (mysql_module.mysql_stmt_bind_param(stmt->stmt, binds) != 0) {
                                MY_ERRSTMT(stmt);
                                ret = -1;
                                goto statement_failed;
@@ -731,19 +742,18 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
                }
 
                // run
-               ctx = stmt->ctx;
                retry = 1;
                do {
-                       db_handle.mysql_stmt_execute(stmt->stmt);
+                       mysql_module.mysql_stmt_execute(stmt->stmt);
                        ret = MY_ISOKSTMT(stmt, &retry);
                } while (ret == 0);
        statement_failed:
                if (ret == -1) {
-                       if (db_handle.mysql_stmt_errno(stmt->stmt) == ER_UNKNOWN_STMT_HANDLER) {
+                       if (mysql_module.mysql_stmt_errno(stmt->stmt) == ER_UNKNOWN_STMT_HANDLER) {
                                // expired the prepared command ==> restore it
-                               if (glite_lbu_PrepareStmt(stmt->ctx, stmt->sql, &newstmt) == -1) goto failed;
+                               if (glite_lbu_PrepareStmt(stmt->generic.ctx, stmt->sql, &newstmt) == -1) goto failed;
                                glite_lbu_FreeStmt_int(stmt);
-                               memcpy(stmt, newstmt, sizeof(struct glite_lbu_Statement_s));
+                               memcpy(stmt, newstmt, sizeof(struct glite_lbu_StatementMysql_s));
                                prepare_retry--;
                                ret = 0;
                        } else goto failed;
@@ -753,7 +763,7 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
        // result
        retry = 1;
        do {
-               db_handle.mysql_stmt_store_result(stmt->stmt);
+               mysql_module.mysql_stmt_store_result(stmt->stmt);
                ret = MY_ISOKSTMT(stmt, &retry);
        } while (ret == 0);
        if (ret == -1) goto failed;
@@ -765,8 +775,8 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
                free(binds);
                free(lens);
        }
-       CLR_ERR(ctx);
-       return db_handle.mysql_stmt_affected_rows(stmt->stmt);
+       CLR_ERR(stmt->generic.ctx);
+       return mysql_module.mysql_stmt_affected_rows(stmt->stmt);
 
 failed:
        for (i = 0; i < n; i++) free(data[i]);
@@ -777,167 +787,30 @@ failed:
 }
 
 
-int glite_lbu_ExecPreparedStmt(glite_lbu_Statement stmt, int n, ...) {
-       va_list ap;
-       int retval;
-
-       va_start(ap, n);
-       retval = glite_lbu_ExecPreparedStmt_v(stmt, n, ap);
-       va_end(ap);
-
-       return retval;
-}
-
-
-int glite_lbu_bufferedInsertInit(glite_lbu_DBContext ctx, glite_lbu_bufInsert *bi, const char *table_name, long size_limit, long record_limit, const char *columns)
-{
-       *bi = calloc(1, sizeof(*bi));
-       (*bi)->ctx = ctx;
-       (*bi)->table_name = strdup(table_name);
-       (*bi)->columns = strdup(columns);
-       (*bi)->rec_num = 0;
-       (*bi)->rec_size = 0;
-       (*bi)->rows = calloc(record_limit, sizeof(*((*bi)->rows)) );
-       (*bi)->size_limit = size_limit;
-       (*bi)->record_limit = record_limit;
-
-        return CLR_ERR(ctx);
-}
-
-
-static int flush_bufferd_insert(glite_lbu_bufInsert bi)
-{
-       char *stmt, *vals, *temp;
-       long i;
-
-
-       if (!bi->rec_num)
-               return STATUS(bi->ctx);
-
-       asprintf(&vals,"(%s)", bi->rows[0]);
-       for (i=1; i < bi->rec_num; i++) {
-               // XXX:  use string add (preallocated memory)
-               asprintf(&temp,"%s,(%s)", vals, bi->rows[i]);
-               free(vals); vals = temp; temp = NULL;
-               free(bi->rows[i]);
-               bi->rows[i] = NULL;
-       }
-       
-       trio_asprintf(&stmt, "insert into %|Ss(%|Ss) values %s;",
-               bi->table_name, bi->columns, vals);
-
-       if (glite_lbu_ExecSQL(bi->ctx,stmt,NULL) < 0) {
-                if (STATUS(bi->ctx) == EEXIST)
-                        CLR_ERR(bi->ctx);
-        }
-
-       /* reset bi counters */
-       bi->rec_size = 0;
-       bi->rec_num = 0;
-       
-       free(vals);
-       free(stmt);
-
-       return STATUS(bi->ctx);
-}
-
-
-int glite_lbu_bufferedInsert(glite_lbu_bufInsert bi, const char *row)
-{
-       bi->rows[bi->rec_num++] = strdup(row);
-       bi->rec_size += strlen(row);
-
-       if ((bi->size_limit && bi->rec_size >= bi->size_limit) ||
-               (bi->record_limit && bi->rec_num >= bi->record_limit))
-       {
-               if (flush_bufferd_insert(bi))
-                       return STATUS(bi->ctx);
-       }
-
-       return CLR_ERR(bi->ctx);
-}
-
-
-static void free_buffered_insert(glite_lbu_bufInsert bi) {
-       long i;
-
-       free(bi->table_name);
-       free(bi->columns);
-       for (i=0; i < bi->rec_num; i++) {
-               free(bi->rows[i]);
-       }
-       free(bi->rows);
-}
-
-
-int glite_lbu_bufferedInsertClose(glite_lbu_bufInsert bi)
-{
-       if (flush_bufferd_insert(bi))
-               return STATUS(bi->ctx);
-       free_buffered_insert(bi);
-
-       return CLR_ERR(bi->ctx);
-}
-
-
-long int glite_lbu_Lastid(glite_lbu_Statement stmt) {
+long int glite_lbu_LastidMysql(glite_lbu_Statement stmt_gen) {
+       glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen;
        my_ulonglong i;
 
-       CLR_ERR(stmt->ctx);
-       i = db_handle.mysql_stmt_insert_id(stmt->stmt);
+       CLR_ERR(stmt_gen->ctx);
+       i = mysql_module.mysql_stmt_insert_id(stmt->stmt);
        assert(i < ((unsigned long int)-1) >> 1);
        return (long int)i;
 }
 
 
 /*
- * helping compatibility function: clear error from the context
- */
-static int lbu_clrerr(glite_lbu_DBContext ctx) {
-       ctx->err.code = 0;
-       if (ctx->err.desc) {
-               free(ctx->err.desc);
-               ctx->err.desc = NULL;
-       }
-       return 0;
-}
-
-
-/*
- * helping compatibility function: sets error on the context
- */
-static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...) {
-       va_list ap;
-
-       if (code) {
-               ctx->err.code = code;
-               free(ctx->err.desc);
-               if (desc) {
-                       va_start(ap, desc);
-                       vasprintf(&ctx->err.desc, desc, ap);
-                       va_end(ap);
-               } else
-                       ctx->err.desc = NULL;
-               dprintf(ctx, "[db %d] %s:%d %s\n", getpid(), func, line, desc ? ctx->err.desc : "");
-               return code;
-       } else
-               return ctx->err.code;
-}
-
-
-/*
  * helping function: find oud mysql error and sets on the context
  */
-static int myerr(glite_lbu_DBContext ctx, const char *source, int line) {
-       return lbu_err(ctx, EIO, source, line, db_handle.mysql_error(ctx->mysql));
+static int myerr(glite_lbu_DBContextMysql ctx, const char *source, int line) {
+       return glite_lbu_DBSetError(&ctx->generic, EIO, source, line, mysql_module.mysql_error(ctx->mysql));
 }
 
 
 /*
  * helping function: find oud mysql stmt error and sets on the context
  */
-static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line) { 
-       return lbu_err(stmt->ctx, EIO, source, line, db_handle.mysql_stmt_error(stmt->stmt));
+static int myerrstmt(glite_lbu_StatementMysql stmt, const char *source, int line) {    
+       return glite_lbu_DBSetError(stmt->generic.ctx, EIO, source, line, mysql_module.mysql_stmt_error(stmt->stmt));
 }
 
 
@@ -948,13 +821,13 @@ static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line) {
  * \return  0 retry
  * \return  1 OK
  */
-static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry) {
-       switch (db_handle.mysql_stmt_errno(stmt->stmt)) {
+static int myisokstmt(glite_lbu_StatementMysql stmt, const char *source, int line, int *retry) {
+       switch (mysql_module.mysql_stmt_errno(stmt->stmt)) {
                case 0:
                        return 1;
                        break;
                case ER_DUP_ENTRY:
-                       lbu_err(stmt->ctx, EEXIST, source, line, db_handle.mysql_stmt_error(stmt->stmt));
+                       glite_lbu_DBSetError(stmt->generic.ctx, EEXIST, source, line, mysql_module.mysql_stmt_error(stmt->stmt));
                        return -1;
                        break;
                case CR_SERVER_LOST:
@@ -978,7 +851,7 @@ static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, in
 /*
  * mysql connect
  */
-static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) {
+static int db_connect(glite_lbu_DBContextMysql ctx, const char *cs, MYSQL **mysql) {
        char    *buf = NULL;
        char    *host,*user,*pw,*db; 
        char    *slash,*at,*colon;
@@ -992,12 +865,12 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) {
 
        if (!cs) return ERR(ctx, EINVAL, "connect string not specified");
        
-       if (!(*mysql = db_handle.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL);
+       if (!(*mysql = mysql_module.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL);
 
-       db_handle.mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my");
+       mysql_module.mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my");
 #if MYSQL_VERSION_ID >= 50013
        /* XXX: may result in weird behaviour in the middle of transaction */
-       db_handle.mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect);
+       mysql_module.mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect);
 #endif
 
        host = user = pw = db = NULL;
@@ -1023,19 +896,15 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) {
        /* ljocha: CLIENT_FOUND_ROWS added to make authorization check
         * working in update_notif(). 
         * Hope it does not break anything else */ 
-       if (!db_handle.mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) {
-               char *desc;
-               free(buf);
+       if (!mysql_module.mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) {
                ret = MY_ERR(ctx);
-               desc = ctx->err.desc;
-               ctx->err.desc = NULL;
-               glite_lbu_DBClose(ctx);
-               ctx->err.desc = desc;
-               return ctx->err.code = ret;
+               db_close(*mysql);
+               *mysql = NULL;
+               free(buf);
+               return ret;
        }
        free(buf);
 
-       ctx->cs = cs;
        return CLR_ERR(ctx);
 }
 
@@ -1044,39 +913,39 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) {
  * mysql close
  */
 static void db_close(MYSQL *mysql) {
-       if (mysql) db_handle.mysql_close(mysql);
+       if (mysql) mysql_module.mysql_close(mysql);
 }
 
 
 /*
  * test transactions capability:
  */
-static int transaction_test(glite_lbu_DBContext ctx) {
+static int transaction_test(glite_lbu_DBContext ctx, int *caps) {
        glite_lbu_Statement stmt;
        char *table[1] = { NULL }, *res[2] = { NULL, NULL }, *cmd = NULL;
        int retval;
 
-       ctx->caps &= ~GLITE_LBU_DB_CAP_TRANSACTIONS;
+       (*caps) &= ~GLITE_LBU_DB_CAP_TRANSACTIONS;
 
-       if ((retval = glite_lbu_ExecSQL(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRow(stmt, 1, NULL, table) < 0) goto quit;
+       if ((retval = glite_lbu_ExecSQLMysql(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRowMysql(stmt, 1, NULL, table) < 0) goto quit;
        glite_lbu_FreeStmt(&stmt);
 
        trio_asprintf(&cmd, "SHOW CREATE TABLE %|Ss", table[0]);
-       if (glite_lbu_ExecSQL(ctx, cmd, &stmt) <= 0 || (retval = glite_lbu_FetchRow(stmt, 2, NULL, res)) < 0 ) goto quit;
+       if (glite_lbu_ExecSQLMysql(ctx, cmd, &stmt) <= 0 || (retval = glite_lbu_FetchRowMysql(stmt, 2, NULL, res)) < 0 ) goto quit;
        if (retval != 2 || strcmp(res[0], table[0])) {
                ERR(ctx, EIO, "unexpected show create result");
                goto quit;
        }
 
        if (strstr(res[1],"ENGINE=InnoDB"))
-               ctx->caps |= GLITE_LBU_DB_CAP_TRANSACTIONS;
+               (*caps) |= GLITE_LBU_DB_CAP_TRANSACTIONS;
 
 #ifdef LBS_DB_PROFILE
        fprintf(stderr, "[%d] use_transactions = %d\n", getpid(), USE_TRANS(ctx));
 #endif
 
 quit:
-       glite_lbu_FreeStmt(&stmt);
+       glite_lbu_FreeStmtMysql(&stmt);
        free(table[0]);
        free(res[0]);
        free(res[1]);
@@ -1088,22 +957,22 @@ quit:
 /*
  * simple version of the fetch
  */
-static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned long *lengths, char **results) {
+static int FetchRowSimple(glite_lbu_DBContextMysql ctx, MYSQL_RES *result, unsigned long *lengths, char **results) {
        MYSQL_ROW            row;
        unsigned int         nr, i;
        unsigned long       *len;
 
        CLR_ERR(ctx);
 
-       if (!(row = db_handle.mysql_fetch_row(result))) {
-               if (db_handle.mysql_errno((MYSQL *) ctx->mysql)) {
+       if (!(row = mysql_module.mysql_fetch_row(result))) {
+               if (mysql_module.mysql_errno((MYSQL *) ctx->mysql)) {
                        MY_ERR(ctx);
                        return -1;
                } else return 0;
        }
 
-       nr = db_handle.mysql_num_fields(result);
-       len = db_handle.mysql_fetch_lengths(result);
+       nr = mysql_module.mysql_num_fields(result);
+       len = mysql_module.mysql_fetch_lengths(result);
        for (i=0; i<nr; i++) {
                if (lengths) lengths[i] = len[i];
                if (len[i]) {
@@ -1121,7 +990,7 @@ static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned l
 /*
  * prepared version of the fetch
  */
-static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+static int FetchRowPrepared(glite_lbu_DBContextMysql ctx, glite_lbu_StatementMysql stmt, unsigned int n, unsigned long *lengths, char **results) {
        int ret, retry;
        unsigned int i;
        MYSQL_BIND *binds = NULL;
@@ -1144,12 +1013,12 @@ static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, u
                binds[i].length = &lengths[i];
                binds[i].buffer = results[i] = calloc(1, GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH);
        }
-       if (db_handle.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
+       if (mysql_module.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
 
        // fetch data, all can be truncated
        retry = 1;
        do {
-               switch(db_handle.mysql_stmt_fetch(stmt->stmt)) {
+               switch(mysql_module.mysql_stmt_fetch(stmt->stmt)) {
 #ifdef MYSQL_DATA_TRUNCATED
                        case MYSQL_DATA_TRUNCATED:
 #endif
@@ -1179,7 +1048,7 @@ static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, u
 
                        retry = 1;
                        do {
-                               switch (db_handle.mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) {
+                               switch (mysql_module.mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) {
                                        case 0: ret = 1; break;
                                        case 1: ret = MY_ISOKSTMT(stmt, &retry); break;
                                        case MYSQL_NO_DATA: ret = 0; goto quit; /* it's OK */
@@ -1228,12 +1097,12 @@ static void set_time(MYSQL_TIME *mtime, const double time) {
 
 
 static void glite_lbu_DBCleanup(void) {
-       pthread_mutex_lock(&db_handle.lock);
-       if (db_handle.lib) {
-               dlclose(db_handle.lib);
-               db_handle.lib = NULL;
+       pthread_mutex_lock(&mysql_module.lock);
+       if (mysql_module.lib) {
+               dlclose(mysql_module.lib);
+               mysql_module.lib = NULL;
        }
-       pthread_mutex_unlock(&db_handle.lock);
+       pthread_mutex_unlock(&mysql_module.lock);
 }
 
 
diff --git a/org.glite.lbjp-common.db/src/db.c b/org.glite.lbjp-common.db/src/db.c
new file mode 100644 (file)
index 0000000..c799165
--- /dev/null
@@ -0,0 +1,392 @@
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glite/lbu/trio.h>
+
+#include "db.h"
+#include "db-int.h"
+
+
+#define VALID(BACKEND) ((BACKEND) >= 0 && (BACKEND) < GLITE_LBU_DB_BACKEND_LAST)
+
+
+struct glite_lbu_bufInsert_s {
+       glite_lbu_DBContext ctx;
+       char    *table_name;
+       char    *columns;       /* names of columns to be inserted into 
+                                * (values separated with commas) */
+       char    **rows;         /* each row hold string of one row to be inserted
+                                * (values separated with commas) */
+       long    rec_num,        /* actual number of rows in structure */
+               rec_size;       /* approx. size of a real insert string */
+       long    size_limit,     /* size and # of records limit which trigger */
+               record_limit;   /* real insert; zero means unlimitted */
+};
+
+
+/* possible backends */
+#ifdef MYSQL_SONAME
+extern glite_lbu_DBBackend_t mysql_backend;
+#else
+#define mysql_backend no_backend
+#endif
+#ifdef PSQL_SONAME
+extern glite_lbu_DBBackend_t psql_backend;
+#else
+#define psql_backend no_backend
+#endif
+
+glite_lbu_DBBackend_t no_backend = {
+       backend: -1,
+
+       /* functions unspecified */
+};
+
+glite_lbu_DBBackend_t *backends[GLITE_LBU_DB_BACKEND_LAST] = {
+       &mysql_backend,
+       &psql_backend
+};
+
+
+/* --- internal functions ---- */
+
+int glite_lbu_DBClearError(glite_lbu_DBContext ctx) {
+       ctx->err.code = 0;
+       if (ctx->err.desc) {
+               free(ctx->err.desc);
+               ctx->err.desc = NULL;
+       }
+       return 0;
+}
+
+
+int glite_lbu_DBSetError(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...) {
+       va_list ap;
+
+       if (!code) return ctx->err.code;
+
+       ctx->err.code = code;
+       free(ctx->err.desc);
+       if (desc) {
+               va_start(ap, desc);
+               vasprintf(&ctx->err.desc, desc, ap);
+               va_end(ap);
+       } else
+               ctx->err.desc = NULL;
+       dprintf(ctx, "[db %d] %s:%d %s\n", getpid(), func, line, ctx->err.desc);
+       return code;
+}
+
+
+void glite_lbu_TimeToStrGeneric(time_t t, char **str, const char *amp) {
+       struct tm       *tm = gmtime(&t);
+
+       asprintf(str,"%s%4d-%02d-%02d %02d:%02d:%02d%s",amp,tm->tm_year+1900,tm->tm_mon+1,
+               tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,amp);
+}
+
+
+void glite_lbu_TimeToStr(time_t t, char **str) {
+       glite_lbu_TimeToStrGeneric(t, str, "'");
+}
+
+
+void glite_lbu_TimestampToStrGeneric(double t, char **str, const char *amp) {
+       time_t tsec = t;
+       struct tm *tm = gmtime(&tsec);
+
+       t = t - tsec + tm->tm_sec;
+       asprintf(str,"%s%4d-%02d-%02d %02d:%02d:%02.09f%s",amp,tm->tm_year+1900,tm->tm_mon+1,
+                tm->tm_mday,tm->tm_hour,tm->tm_min,t,amp);
+}
+
+
+void glite_lbu_TimestampToStr(double t, char **str) {
+       glite_lbu_TimestampToStrGeneric(t, str, "'");
+}
+
+
+time_t glite_lbu_StrToTime(const char *str) {
+       struct tm       tm;
+
+       memset(&tm,0,sizeof(tm));
+       setenv("TZ","UTC",1); tzset();
+       sscanf(str,"%4d-%02d-%02d %02d:%02d:%02d",
+               &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
+               &tm.tm_hour,&tm.tm_min,&tm.tm_sec);
+       tm.tm_year -= 1900;
+       tm.tm_mon--;
+
+       return mktime(&tm);
+}
+
+
+double glite_lbu_StrToTimestamp(const char *str) {
+       struct tm       tm;
+       double  sec;
+
+       memset(&tm,0,sizeof(tm));
+       setenv("TZ","UTC",1); tzset();
+       sscanf(str,"%4d-%02d-%02d %02d:%02d:%lf",
+               &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
+               &tm.tm_hour,&tm.tm_min,&sec);
+       tm.tm_year -= 1900;
+       tm.tm_mon--;
+       tm.tm_sec = sec;
+
+       return (sec - tm.tm_sec) + mktime(&tm);
+}
+
+
+/* ---- API ---- */
+
+
+int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc) {
+       if (text) *text = strdup(strerror(ctx->err.code));
+       if (desc) {
+               if (ctx->err.desc) *desc = strdup(ctx->err.desc);
+               else *desc = NULL;
+       }
+
+       return ctx->err.code;
+}
+
+
+int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx, int backend) {
+       if (!VALID(backend)) return EINVAL;
+       if (backends[backend]->backend != backend) return ENOTSUP;
+       return backends[backend]->initContext(ctx);
+}
+
+
+void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return;
+
+       free(ctx->err.desc);
+       ctx->err.desc = NULL;
+       backends[ctx->backend]->freeContext(ctx);
+}
+
+
+int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->connect(ctx, cs);
+}
+
+
+void glite_lbu_DBClose(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return;
+       backends[ctx->backend]->close(ctx);
+}
+
+
+int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->queryCaps(ctx);
+}
+
+
+void glite_lbu_DBSetCaps(glite_lbu_DBContext ctx, int caps) {
+       if (!VALID(ctx->backend)) return;
+       return backends[ctx->backend]->setCaps(ctx, caps);
+}
+
+
+int glite_lbu_Transaction(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->transaction(ctx);
+}
+
+
+int glite_lbu_Commit(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->commit(ctx);
+}
+
+
+int glite_lbu_Rollback(glite_lbu_DBContext ctx) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->rollback(ctx);
+}
+
+
+int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+       if (!VALID(stmt->ctx->backend)) return EINVAL;
+       return backends[stmt->ctx->backend]->fetchRow(stmt, n, lengths, results);
+}
+
+
+void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
+       if (!VALID((*stmt)->ctx->backend)) return;
+       return backends[(*stmt)->ctx->backend]->freeStmt(stmt);
+}
+
+
+int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->queryIndices(ctx, table, key_names, column_names);
+}
+
+
+int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->execSQL(ctx, cmd, stmt);
+}
+
+
+int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols) {
+       if (!VALID(stmt->ctx->backend)) return EINVAL;
+       return backends[stmt->ctx->backend]->queryColumns(stmt, cols);
+}
+
+
+int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmt) {
+       if (!VALID(ctx->backend)) return EINVAL;
+       return backends[ctx->backend]->prepareStmt(ctx, sql, stmt);
+}
+
+
+int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
+       if (!VALID(stmt->ctx->backend)) return EINVAL;
+       return backends[stmt->ctx->backend]->execPreparedStmt_v(stmt, n, ap);
+}
+
+
+int glite_lbu_ExecPreparedStmt(glite_lbu_Statement stmt, int n, ...) {
+       va_list ap;
+       int retval;
+
+       va_start(ap, n);
+       retval = glite_lbu_ExecPreparedStmt_v(stmt, n, ap);
+       va_end(ap);
+
+       return retval;
+}
+
+
+long int glite_lbu_Lastid(glite_lbu_Statement stmt) {
+       if (!VALID(stmt->ctx->backend)) return EINVAL;
+       return backends[stmt->ctx->backend]->lastid(stmt);
+}
+
+
+void glite_lbu_TimeToDB(glite_lbu_DBContext ctx, time_t t, char **str) {
+       if (!VALID(ctx->backend)) return;
+       return backends[ctx->backend]->timeToDB(t, str);
+}
+
+
+void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, double t, char **str) {
+       if (!VALID(ctx->backend)) return;
+       return backends[ctx->backend]->timestampToDB(t, str);
+}
+
+
+time_t glite_lbu_DBToTime(glite_lbu_DBContext ctx, const char *str) {
+       if (!VALID(ctx->backend)) return (time_t)-1;
+       return backends[ctx->backend]->DBToTime(str);
+}
+
+
+double glite_lbu_DBToTimestamp(glite_lbu_DBContext ctx, const char *str) {
+       if (!VALID(ctx->backend)) return -1;
+       return backends[ctx->backend]->DBToTimestamp(str);
+}
+
+
+#define STATUS(CTX) ((CTX)->err.code)
+#define CLR_ERR(CTX) glite_lbu_DBClearError((CTX))
+int glite_lbu_bufferedInsertInit(glite_lbu_DBContext ctx, glite_lbu_bufInsert *bi, const char *table_name, long size_limit, long record_limit, const char *columns)
+{
+       *bi = calloc(1, sizeof(*bi));
+       (*bi)->ctx = ctx;
+       (*bi)->table_name = strdup(table_name);
+       (*bi)->columns = strdup(columns);
+       (*bi)->rec_num = 0;
+       (*bi)->rec_size = 0;
+       (*bi)->rows = calloc(record_limit, sizeof(*((*bi)->rows)) );
+       (*bi)->size_limit = size_limit;
+       (*bi)->record_limit = record_limit;
+
+        return CLR_ERR(ctx);
+}
+
+
+static int flush_bufferd_insert(glite_lbu_bufInsert bi)
+{
+       char *stmt, *vals, *temp;
+       long i;
+
+
+       if (!bi->rec_num)
+               return STATUS(bi->ctx);
+
+       asprintf(&vals,"(%s)", bi->rows[0]);
+       for (i=1; i < bi->rec_num; i++) {
+               // XXX:  use string add (preallocated memory)
+               asprintf(&temp,"%s,(%s)", vals, bi->rows[i]);
+               free(vals); vals = temp; temp = NULL;
+               free(bi->rows[i]);
+               bi->rows[i] = NULL;
+       }
+       
+       trio_asprintf(&stmt, "insert into %|Ss(%|Ss) values %s;",
+               bi->table_name, bi->columns, vals);
+
+       if (glite_lbu_ExecSQL(bi->ctx,stmt,NULL) < 0) {
+                if (STATUS(bi->ctx) == EEXIST)
+                        CLR_ERR(bi->ctx);
+        }
+
+       /* reset bi counters */
+       bi->rec_size = 0;
+       bi->rec_num = 0;
+       
+       free(vals);
+       free(stmt);
+
+       return STATUS(bi->ctx);
+}
+
+
+int glite_lbu_bufferedInsert(glite_lbu_bufInsert bi, const char *row)
+{
+       bi->rows[bi->rec_num++] = strdup(row);
+       bi->rec_size += strlen(row);
+
+       if ((bi->size_limit && bi->rec_size >= bi->size_limit) ||
+               (bi->record_limit && bi->rec_num >= bi->record_limit))
+       {
+               if (flush_bufferd_insert(bi))
+                       return STATUS(bi->ctx);
+       }
+
+       return CLR_ERR(bi->ctx);
+}
+
+
+static void free_buffered_insert(glite_lbu_bufInsert bi) {
+       long i;
+
+       free(bi->table_name);
+       free(bi->columns);
+       for (i=0; i < bi->rec_num; i++) {
+               free(bi->rows[i]);
+       }
+       free(bi->rows);
+}
+
+
+int glite_lbu_bufferedInsertClose(glite_lbu_bufInsert bi)
+{
+       if (flush_bufferd_insert(bi))
+               return STATUS(bi->ctx);
+       free_buffered_insert(bi);
+
+       return CLR_ERR(bi->ctx);
+}