Backport new DB module and subsequent changes to branch.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 22 Dec 2009 14:24:36 +0000 (14:24 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Tue, 22 Dec 2009 14:24:36 +0000 (14:24 +0000)
15 files changed:
org.glite.lb.server/src/dump.c
org.glite.lb.server/src/index.c.T
org.glite.lb.server/src/notif_match.c
org.glite.lb.server/src/notification.c
org.glite.lb.server/src/openserver.c
org.glite.lb.server/src/query.c
org.glite.lb.server/src/store.c.T
org.glite.lb/configure
org.glite.lbjp-common.db/Makefile
org.glite.lbjp-common.db/examples/db_expire.c
org.glite.lbjp-common.db/examples/db_test.c
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 [moved from org.glite.lbjp-common.db/src/db.c with 52% similarity]
org.glite.lbjp-common.db/src/db-pg.c [new file with mode: 0644]

index 1b6771e..f8b37b3 100644 (file)
@@ -55,8 +55,8 @@ int edg_wll_DumpEventsServer(edg_wll_Context ctx,const edg_wll_DumpRequest *req,
                return edg_wll_Error(ctx,NULL,NULL);
        }
 
-       glite_lbu_TimeToDB(from, &from_s);
-       glite_lbu_TimeToDB(to, &to_s);
+       glite_lbu_TimeToStr(from, &from_s);
+       glite_lbu_TimeToStr(to, &to_s);
 
        trio_asprintf(&stmt,
                        "select event,dg_jobid,code,prog,host,u.cert_subj,time_stamp,usec,level,arrived "
@@ -175,7 +175,7 @@ static int handle_specials(edg_wll_Context ctx,time_t *t)
                                case ENOENT: *t = 0; 
                                             edg_wll_ResetError(ctx);
                                             break;
-                               case 0: *t = glite_lbu_DBToTime(time_s); 
+                               case 0: *t = glite_lbu_StrToTime(time_s); 
                                        assert(*t >= 0);
                                        break;
                                default: break;
@@ -190,7 +190,7 @@ static int handle_specials(edg_wll_Context ctx,time_t *t)
 static char *time_to_string(time_t t, char **ptr) {
        char *s;
 
-       glite_lbu_TimeToDB(t, &s);
+       glite_lbu_TimeToStr(t, &s);
        s[strlen(s) - 1] = '\0';
        *ptr = s;
 
index de8a715..3e471b5 100644 (file)
@@ -170,7 +170,7 @@ static char *to_sql_string(edg_wll_JobStat const *stat,int offset)
 static char *to_sql_timeval(edg_wll_JobStat const *stat,int offset)
 {
        char    *out;
-       glite_lbu_TimeToDB( ((struct timeval *) (((char *) stat) + offset))->tv_sec, &out );
+       glite_lbu_TimeToStr( ((struct timeval *) (((char *) stat) + offset))->tv_sec, &out );
        return out;
 }
 
@@ -378,17 +378,17 @@ edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context ctx,
                                break;
                        case EDG_WLL_QUERY_ATTR_TIME:
                                if (stat->stateEnterTimes)
-                                       glite_lbu_TimeToDB(stat->stateEnterTimes[job_index_cols[i].qrec.attr_id.state+1], &data);
+                                       glite_lbu_TimeToStr(stat->stateEnterTimes[job_index_cols[i].qrec.attr_id.state+1], &data);
                                else data = strdup("0");
                                break;
                        case EDG_WLL_QUERY_ATTR_RESUBMITTED:
                                asprintf(&data, "%d", stat->resubmitted);
                                break;
                        case EDG_WLL_QUERY_ATTR_STATEENTERTIME:
-                               glite_lbu_TimeToDB(stat->stateEnterTime.tv_sec, &data);
+                               glite_lbu_TimeToStr(stat->stateEnterTime.tv_sec, &data);
                                break;
                        case EDG_WLL_QUERY_ATTR_LASTUPDATETIME:
-                               glite_lbu_TimeToDB(stat->lastUpdateTime.tv_sec, &data);
+                               glite_lbu_TimeToStr(stat->lastUpdateTime.tv_sec, &data);
                                break;
                        case EDG_WLL_QUERY_ATTR_JDL_ATTR: // This is not the correct way to handle jdl searches.
                                 /* There's no way to index individual JDL attributes */
@@ -398,12 +398,12 @@ edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context ctx,
                                break;
 /*                     case EDG_WLL_QUERY_ATTR_STATEENTERTIME: /// XXX: Which way of handling this is correct?
                                if (stat->stateEnterTime)
-                                       glite_lbu_TimeToDB(stat->stateEnterTime, &data);
+                                       glite_lbu_TimeToStr(stat->stateEnterTime, &data);
                                else data = strdup("0");
                                break;
                        case EDG_WLL_QUERY_ATTR_LASTUPDATETIME:
                                if (stat->lastUpdateTime)
-                                       glite_lbu_TimeToDB(stat->lastUpdateTime, &data);
+                                       glite_lbu_TimeToStr(stat->lastUpdateTime, &data);
                                else data = strdup("0");
                                break;*/
 
index f351605..54264c3 100644 (file)
@@ -92,7 +92,7 @@ int edg_wll_NotifMatch(edg_wll_Context ctx, const edg_wll_JobStat *oldstat, cons
        if (edg_wll_ExecSQL(ctx,jobq,&jobs) < 0) goto err;
 
        while ((ret = edg_wll_FetchRow(ctx,jobs,sizeof(jobc)/sizeof(jobc[0]),NULL,jobc)) > 0) {
-               if (now > (expires = glite_lbu_DBToTime(jobc[2]))) {
+               if (now > (expires = glite_lbu_StrToTime(jobc[2]))) {
                        edg_wll_NotifExpired(ctx,jobc[0]);
                        if (debug) fprintf(stderr,"[%d] NOTIFY:%s expired at %s UTC\n",
                                        getpid(),jobc[0],asctime(gmtime(&expires)));
index 024b932..0ca77a8 100644 (file)
@@ -89,7 +89,7 @@ int edg_wll_NotifNewServer(
                *valid = time(NULL) + ctx->notifDuration;       
        adjust_validity(ctx,valid);
 
-       glite_lbu_TimeToDB(*valid, &time_s);
+       glite_lbu_TimeToStr(*valid, &time_s);
        if ( !time_s )
        {
                edg_wll_SetError(ctx, errno, NULL);
@@ -205,7 +205,7 @@ int edg_wll_NotifBindServer(
                        *valid = time(NULL) + ctx->notifDuration;       
                adjust_validity(ctx,valid);
 
-               glite_lbu_TimeToDB(*valid, &time_s);
+               glite_lbu_TimeToStr(*valid, &time_s);
                if ( !time_s )
                {
                        edg_wll_SetError(ctx, errno, "Formating validity time");
@@ -379,7 +379,7 @@ int edg_wll_NotifRefreshServer(
                        *valid = time(NULL) + ctx->notifDuration;       
                adjust_validity(ctx,valid);
 
-               glite_lbu_TimeToDB(*valid, &time_s);
+               glite_lbu_TimeToStr(*valid, &time_s);
                if ( !time_s )
                {
                        edg_wll_SetError(ctx, errno, "Formating validity time");
@@ -685,7 +685,7 @@ static int update_notif(
                int     expires;
                
                *v2 = 0;
-               expires = glite_lbu_DBToTime(v+1);
+               expires = glite_lbu_StrToTime(v+1);
 /*
                printf("edg_wll_NotifChangeIL(ctx, %s, %s, %d)\n",
                                nid_s? nid_s: "nid", host, port);
@@ -820,7 +820,7 @@ static int check_notif_age(edg_wll_Context ctx, const edg_wll_NotifId nid) {
        if ( !(nid_s = edg_wll_NotifIdGetUnique(nid)) )
                goto cleanup;
 
-       glite_lbu_TimeToDB(now, &time_s);
+       glite_lbu_TimeToStr(now, &time_s);
        if ( !time_s )
        {
                edg_wll_SetError(ctx, errno, NULL);
index 8580ff5..28ef18d 100644 (file)
@@ -15,7 +15,7 @@ edg_wll_ErrorCode edg_wll_Open(edg_wll_Context ctx, char *cs)
        char *cols[20];
        glite_lbu_Statement stmt;
 
-       if (!ctx->dbctx && glite_lbu_InitDBContext((glite_lbu_DBContext*) &ctx->dbctx) != 0) {
+       if (!ctx->dbctx && glite_lbu_InitDBContext((glite_lbu_DBContext*) &ctx->dbctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) {
                char *ed;
 
                glite_lbu_DBError(ctx->dbctx, NULL, &ed);
index 01f53e8..6a3ee43 100644 (file)
@@ -790,14 +790,14 @@ static char *ec_to_head_where(edg_wll_Context ctx,const edg_wll_QueryRec **ec)
                case EDG_WLL_QUERY_ATTR_TIME:
                case EDG_WLL_QUERY_ATTR_STATEENTERTIME:
                case EDG_WLL_QUERY_ATTR_LASTUPDATETIME:
-                       glite_lbu_TimeToDB(ec[m][n].value.t.tv_sec, &dbt);
+                       glite_lbu_TimeToStr(ec[m][n].value.t.tv_sec, &dbt);
                        if ( conds )
                        {
                                if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
                                {
                                        trio_asprintf(&aux, "%s", dbt);
                                        free(dbt);
-                                       glite_lbu_TimeToDB(ec[m][n].value2.t.tv_sec, &dbt);
+                                       glite_lbu_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt);
                                        trio_asprintf(&out, "%s OR (e.time_stamp >= %s AND e.time_stamp <= %s)", conds, aux, dbt);
                                        free(aux);
                                }
@@ -814,7 +814,7 @@ static char *ec_to_head_where(edg_wll_Context ctx,const edg_wll_QueryRec **ec)
                        {
                                trio_asprintf(&aux, "%s", dbt);
                                free(dbt);
-                               glite_lbu_TimeToDB(ec[m][n].value2.t.tv_sec, &dbt);
+                               glite_lbu_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt);
                                trio_asprintf(&conds, "(e.time_stamp >= %s AND e.time_stamp <= %s)", aux, dbt);
                                free(aux);
                        }
@@ -1078,14 +1078,14 @@ static char *jc_to_head_where(
 
                        *where_flags |= FL_SEL_STATUS;
 
-                       glite_lbu_TimeToDB(jc[m][n].value.t.tv_sec, &dbt);
+                       glite_lbu_TimeToStr(jc[m][n].value.t.tv_sec, &dbt);
                        if ( conds )
                        {
                                if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
                                {
                                        trio_asprintf(&aux, "%s", dbt);
                                        free(dbt);
-                                       glite_lbu_TimeToDB(jc[m][n].value2.t.tv_sec, &dbt);
+                                       glite_lbu_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt);
                                        trio_asprintf(&tmps, "%s OR (s.%s >= %s AND s.%s <= %s)", conds, cname, aux, cname, dbt);
                                        free(dbt);
                                        free(aux);
@@ -1100,7 +1100,7 @@ static char *jc_to_head_where(
                        {
                                trio_asprintf(&aux, "%s", dbt);
                                free(dbt);
-                               glite_lbu_TimeToDB(jc[m][n].value2.t.tv_sec, &dbt);
+                               glite_lbu_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt);
                                trio_asprintf(&conds, "(s.%s >= %s AND s.%s <= %s)", cname, aux, cname, dbt);
                                free(dbt);
                                free(aux);
@@ -1374,7 +1374,7 @@ int convert_event_head(edg_wll_Context ctx,char **f,edg_wll_Event *e)
        e->any.user = f[4];
        f[4] = NULL;
        
-       e->any.timestamp.tv_sec = glite_lbu_DBToTime(f[5]);
+       e->any.timestamp.tv_sec = glite_lbu_StrToTime(f[5]);
        free(f[5]); f[5] = NULL;
        
        e->any.timestamp.tv_usec = atoi(f[6]);
@@ -1383,7 +1383,7 @@ int convert_event_head(edg_wll_Context ctx,char **f,edg_wll_Event *e)
        e->any.level = atoi(f[7]); 
        free(f[7]); f[7] = NULL;
 
-       e->any.arrived.tv_sec = glite_lbu_DBToTime(f[8]); 
+       e->any.arrived.tv_sec = glite_lbu_StrToTime(f[8]); 
        e->any.arrived.tv_usec = 0;
        free(f[8]); f[8] = NULL;
 
index 365268f..a83152f 100644 (file)
@@ -61,13 +61,13 @@ int edg_wll_StoreEvent(edg_wll_Context ctx,edg_wll_Event *e,const char *ulm,int
 
        lowercase_usertag(e);
        jobid = edg_wlc_JobIdGetUnique(e->any.jobId);
-       glite_lbu_TimeToDB(e->any.timestamp.tv_sec, &stamp);
+       glite_lbu_TimeToStr(e->any.timestamp.tv_sec, &stamp);
        ssrc = edg_wll_SourceToString(e->any.source);
 
        if ( ctx->event_load )
-               glite_lbu_TimeToDB(e->any.arrived.tv_sec, &now_s);
+               glite_lbu_TimeToStr(e->any.arrived.tv_sec, &now_s);
        else
-               glite_lbu_TimeToDB(time(NULL), &now_s);
+               glite_lbu_TimeToStr(time(NULL), &now_s);
 
        edg_wll_ResetError(ctx);
        switch (check_auth(ctx,e)) {
index 93e3eea..6c3cf92 100755 (executable)
@@ -28,7 +28,7 @@ my $sec_tag = '';
 my $jobid_tag = '';
 my $libdir = 'lib';
 
-my @nodes = qw/client server logger utils doc ws-test db jpprimary jpindex jpclient/;
+my @nodes = qw/client server logger utils doc ws-test db jpprimary jpindex jpclient harvester/;
 my %enable_nodes;
 my %disable_nodes;
 
@@ -46,13 +46,18 @@ my %extern_prefix = (
        voms => '/opt/glite',
        gridsite => '/opt/glite',
        lcas => '/opt/glite',
+       trustmanager => '/opt/glite',
        ant => '/usr',
        jdk => '/usr',
        libtar => '/usr',
+       axis => '/usr',
+       log4c => '/usr',
+       postgresql => '/usr'
 );
 
 my %jar = (
-       'commons-codec' => '/usr/share/java/commons-codec-1.3.jar',
+       'commons-codec' => '/usr/share/java/commons-codec.jar',
+       'commons-lang' => '/usr/share/java/commons-lang.jar',
 );
 
 
@@ -67,10 +72,10 @@ my %deps_type;
 my %topbuild;
 
 my %lbmodules = (
-       'lb' => [ qw/client common doc logger server state-machine types utils ws-interface ws-test/], 
+       'lb' => [ qw/client common doc logger server state-machine types utils ws-interface ws-test harvester/], 
        'security' => [qw/gss gsoap-plugin/],
        'lbjp-common' => [qw/db maildir server-bones trio jp-interface/],
-       'jobid' => [qw/api-c api-cpp/],
+       'jobid' => [qw/api-c api-cpp api-java/],
        'jp' => [ qw/client doc index primary server-common ws-interface/ ],
        );
 
@@ -294,7 +299,7 @@ sub mode_checkout() {
 BEGIN{
 %need_externs_aux = (
        'lb.client' => [ qw/cppunit:B classads/ ],
-       'lb.client-java' => [ qw/ant:B jglobus jdk:B/ ],
+       'lb.client-java' => [ qw/ant:B jdk:B axis:B trustmanager/ ],
        'lb.common' => [ qw/expat cares:B cppunit:B classads/ ],
        'lb.doc' => [],
        'lb.logger' => [ qw/cppunit:B/ ],
@@ -304,13 +309,14 @@ BEGIN{
        'lb.ws-interface' => [],
        'lb.ws-test' => [ qw/gsoap:B/ ],
        'lb.types' => [ qw// ],
-       'lbjp-common.db' => [ qw/mysql:R mysql-devel:B/ ],
+       'lb.harvester' => [ qw/postgresql:R/ ],
+       'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ],
        'lbjp-common.maildir' => [ qw// ],
        'lbjp-common.server-bones' => [ qw// ],
        'lbjp-common.trio' => [ qw/cppunit:B/ ],
        'lbjp-common.jp-interface' => [ qw/cppunit:B/ ],
        'security.gss' =>  [ qw/globus_essentials:R globus:B cares cppunit:B/ ],
-       'security.gsoap-plugin' =>  [ qw/cppunit:B globus_essentials:R globus:B cares gsoap:B/ ],
+       'security.gsoap-plugin' =>  [ qw/cppunit:B globus_essentials:R globus:B cares:B gsoap:B/ ],
        'jobid.api-c' =>  [ qw/cppunit:B/ ],
        'jobid.api-cpp' =>  [ qw/cppunit:B/ ],
        'jobid.api-java' =>  [ qw/ant:B jdk:B/ ],
@@ -333,6 +339,7 @@ for my $ext (keys %need_externs_aux) {
 
 %need_jars = (
        'jobid.api-java' => [ qw/commons-codec/ ],
+       'lb.client-java' => [ qw/commons-lang/ ],
 );
 
 for my $jar (keys %need_jars) {
@@ -350,6 +357,7 @@ for my $jar (keys %need_jars) {
        / ],
        'lb.client-java' => [ qw/
                lb.types:B
+               lb.ws-interface:B
                jobid.api-java
        / ],
        'lb.common' => [ qw/
@@ -379,6 +387,10 @@ for my $jar (keys %need_jars) {
        'lb.ws-test' => [ qw/security.gsoap-plugin lb.ws-interface/ ],
        'lb.ws-interface' => [ qw/lb.types:B/ ],
        'lb.types' => [ qw// ],
+       'lb.harvester' => [ qw/
+               jobid.api-c lbjp-common.trio lbjp-common.db lb.common lb.client
+               security.gss
+       / ],
        'lbjp-common.db' => [ qw/lbjp-common.trio/ ],
        'lbjp-common.maildir' => [ qw// ],
        'lbjp-common.server-bones' => [ qw// ],
@@ -451,7 +463,7 @@ sub mkinc
        my %aux;
        undef %aux;
        my @m=qw/
-lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java
+lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java lb.harvester
 security.gss security.gsoap-plugin
 jobid.api-c jobid.api-cpp jobid.api-java
 lbjp-common.db lbjp-common.maildir lbjp-common.server-bones lbjp-common.trio lbjp-common.jp-interface
@@ -517,6 +529,7 @@ BEGIN{
                voms=>'org.glite.security.voms-api-cpp',
                gridsite=>'org.gridsite.shared',
                lcas=>'org.glite.security.lcas',
+               trustmanager=>'org.glite.security.trustmanager',
        );
        %etics_projects = (
                vdt=>[qw/globus globus_essentials/],
index 94eb1cf..dafb817 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=/usr
 cppunit_prefix=/opt/cppunit
 thrflavour=gcc32pthr
 nothrflavour=gcc32
@@ -26,23 +23,33 @@ VPATH=${top_srcdir}/interface:${top_srcdir}/src:${top_srcdir}/examples
 
 DEBUG:=-g -O0 -W -Wall
 
-MYSQL_SONAME:=$(shell ../project/get_soname.sh ${mysql-devel_prefix}/${libdir} ${mysql_prefix}/${libdir} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib)
+MYSQL_SONAME:=$(shell ../project/get_soname.sh mysqlclient ${mysql-devel_prefix}/${libdir} ${mysql_prefix}/${libdir} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib)
+PSQL_SONAME:=$(shell ../project/get_soname.sh pq ${postgresql_prefix}/${libdir} ${postgresql_prefix}/lib)
+
+MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql
+PSQL_CPPFLAGS:=-I${postgresql_prefix}/include
 
-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
+       -D_GNU_SOURCE \
+       -DHAVE_SYSLOG_H=1
 
 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
@@ -53,9 +60,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}
@@ -63,23 +70,32 @@ 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}
+libglite_lbu_db.la: check_soname ${LOBJS}
+       ${LINK} -o $@ $+ ${EXT_LIBS}
 
-libglite_lbu_dbtest.la: ${LTESTOBJS}
-       ${LINK} -o $@ $< ${EXT_LIBS}
+libglite_lbu_dbtest.la: check_soname ${LTESTOBJS}
+       ${LINK} -o $@ $+ ${EXT_LIBS}
 
 dbtest.lo dbtest.o: db.c db.h
        ${COMPILE} -DGLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH=10 -c $< -o $@
 
-db_test: db_test.lo libglite_lbu_dbtest.la
+db_test_mysql: db_test_mysql.lo libglite_lbu_dbtest.la
+       ${LINK} -o $@ $+ ${EXT_LIBS}
+
+db_test_psql: db_test_psql.lo libglite_lbu_dbtest.la
        ${LINK} -o $@ $+ ${EXT_LIBS}
 
 db_expire: db_expire.lo libglite_lbu_dbtest.la
@@ -95,13 +111,13 @@ test_coverage:
        cd coverage && $(MAKE) -f ../Makefile top_srcdir=../../ COVERAGE_FLAGS="-fprofile-arcs -ftest-coverage" check
        cd coverage && for i in `echo ${OBJS} | tr ' ' '\012' | sort -u`; do gcov $$i ; done
 
-examples: db_test db_expire
+examples: db_test_mysql db_test_psql db_expire
 
 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
@@ -110,34 +126,49 @@ 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}
 
 clean:
        rm -rvf *.o *.lo .libs lib* *.c *.h *.dox C/ CPP/
        rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/
-       rm -rvf db_expire db_test
+       rm -rvf db_expire db_test db_test_mysql db_test_psql
+
+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 $<
+
+db_test_mysql.o db_test_mysql.lo: db_test.c
+       ${COMPILE} -DMYSQL_BACKEND=1 -c $< -o $@
+
+db_test_psql.o db_test_psql.lo: db_test.c
+       ${COMPILE} -DPSQL_BACKEND=1 -c $< -o $@
 
 %.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_psql.lo: libglite_lbu_dbtest.la db.h db-int.h db_test.c
+db_test_mysql.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
+.PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage check_soname
index 2eb3ba6..3830e0f 100644 (file)
@@ -80,7 +80,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
 
        // init
        dprintf(("connecting to %s...\n", cs));
-       if (glite_lbu_InitDBContext(&ctx) != 0) {
+       if (glite_lbu_InitDBContext(&ctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) {
                print_error(ctx);
                goto failctx;
        }
index f87aff7..e1b639f 100644 (file)
@@ -6,6 +6,11 @@
  *   mysqladmin -u root -p create test
  *   mysql -u root -p -e 'GRANT ALL on test.* to testuser@localhost'
  *
+ * Or postgres:
+ *
+ *   createuser -U postgres testuser
+ *   createdb -U postgres --owner testuser test
+ *
  * Use CS environment variable when using different user/pwd@machine:dbname.
  */
 
 #include <stdlib.h>
 #include <string.h>
 
-#include "glite/lbu/db.h"
+#include <glite/lbu/trio.h>
+
+#include "db.h"
 
 #define CS "testuser/@localhost:test"
-#if defined(DB_BACKEND) && DB_BACKEND == postgresql
+
+#ifdef PSQL_BACKEND
 #define CREATE_CMD "CREATE TABLE \"data\" (\n\
     \"id\"    INTEGER NOT NULL,\n\
     \"user\"  VARCHAR(32) NOT NULL,\n\
     PRIMARY KEY (\"id\")\n\
 )"
 #define AMP "\""
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES ($1, $2, $3)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = $1"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_PSQL
+
 #else
+
 #define CREATE_CMD "CREATE TABLE data (\n\
     `id`    INT NOT NULL,\n\
     `user`  VARCHAR(32) NOT NULL,\n\
     INDEX(`user`)\n\
 ) engine=innodb"
 #define AMP "`"
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_MYSQL
 #endif
+
 #define DROP_CMD "DROP TABLE " AMP "data" AMP
 #define INSERT_TRIO_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (%d, %s, %s)"
 #define SELECT_TRIO_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = '%s'"
-#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
-#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
 
 #define dprintf(ARGS) { printf("%s: ", name); printf ARGS; }
 
@@ -77,7 +92,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
        int caps;
 
 #ifndef NO_PREPARED
-       char blob1[] = "Guess: blob or \000string?";
+       char blob1[] = "Guess: blob or _string?"; blob1[15] = 0;
        char blob2[] = {0, 1, 2, 3, 4, 5};
 #endif
 
@@ -92,7 +107,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
 
        // init
        dprintf(("connecting to %s...\n", cs));
-       if (glite_lbu_InitDBContext(&ctx) != 0) goto failctx;
+       if (glite_lbu_InitDBContext(&ctx, DB_TEST_BACKEND) != 0) goto failctx;
        if (glite_lbu_DBConnect(ctx, cs) != 0) goto failctx;
        if ((caps = glite_lbu_DBQueryCaps(ctx)) == -1) goto failcon;
 #ifndef NO_PREPARED
index bd7d7e0..46a17ae 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,28 @@ 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);
+
+
+/* Generic helper time convert functions. */
+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 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
similarity index 52%
rename from org.glite.lbjp-common.db/src/db.c
rename to org.glite.lbjp-common.db/src/db-mysql.c
index 1c91526..60468d7 100644 (file)
@@ -1,5 +1,3 @@
-#ident "$Header$"
-
 #include <sys/types.h>
 #ifdef LBS_DB_PROFILE
 #include <sys/time.h>
 #include <stdarg.h>
 #include <dlfcn.h>
 #include <pthread.h>
+#ifdef HAVE_SYSLOG_H
 #include <syslog.h>
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#ifndef STDCALL
+#define STDCALL __stdcall
+#endif
+#else
+#define STDCALL
+#endif
 
 #include <mysql.h>
 #include <mysqld_error.h>
@@ -23,6 +32,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 +76,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;
 
 
 /*
@@ -115,86 +107,123 @@ typedef struct {
        void *lib;
        pthread_mutex_t lock;
 
-       void *(*mysql_init)(void *);
-       unsigned long (*mysql_get_client_version)(void);
-       int (*mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg);
-       unsigned int (*mysql_errno)(MYSQL *mysql);
-       const char *(*mysql_error)(MYSQL *mysql);
-       unsigned int (*mysql_stmt_errno)(MYSQL_STMT *stmt);
-       const char *(*mysql_stmt_error)(MYSQL_STMT *stmt);
-       MYSQL *(*mysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag);
-       void (*mysql_close)(MYSQL *mysql);
-       int (*mysql_query)(MYSQL *mysql, const char *stmt_str);
-       MYSQL_RES *(*mysql_store_result)(MYSQL *mysql);
-       void (*mysql_free_result)(MYSQL_RES *result);
-       my_ulonglong (*mysql_affected_rows)(MYSQL *mysql);
-       my_bool (*mysql_stmt_close)(MYSQL_STMT *);
-       unsigned int (*mysql_num_fields)(MYSQL_RES *result);
-       unsigned long *(*mysql_fetch_lengths)(MYSQL_RES *result);
-       my_bool (*mysql_stmt_bind_result)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
-       int (*mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, unsigned long length);
-       int (*mysql_stmt_store_result)(MYSQL_STMT *stmt);
-       MYSQL_ROW (*mysql_fetch_row)(MYSQL_RES *result);
-       MYSQL_FIELD *(*mysql_fetch_field)(MYSQL_RES *result);
-       const char *(*mysql_get_server_info)(MYSQL *mysql);
-       MYSQL_STMT *(*mysql_stmt_init)(MYSQL *mysql);
-       my_bool (*mysql_stmt_bind_param)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
-       int (*mysql_stmt_execute)(MYSQL_STMT *stmt);
-       int (*mysql_stmt_fetch)(MYSQL_STMT *stmt);
-       my_ulonglong (*mysql_stmt_insert_id)(MYSQL_STMT *stmt);
-       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;
-
-
-static mysql_lib_handle_t db_handle = {
+       void * STDCALL(*mysql_init)(void *);
+       unsigned long STDCALL(*mysql_get_client_version)(void);
+       int STDCALL(*mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg);
+       unsigned int STDCALL(*mysql_errno)(MYSQL *mysql);
+       const char *STDCALL(*mysql_error)(MYSQL *mysql);
+       unsigned int STDCALL(*mysql_stmt_errno)(MYSQL_STMT *stmt);
+       const char *STDCALL(*mysql_stmt_error)(MYSQL_STMT *stmt);
+       MYSQL * STDCALL(*mysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag);
+       void STDCALL(*mysql_close)(MYSQL *mysql);
+       int STDCALL(*mysql_query)(MYSQL *mysql, const char *stmt_str);
+       MYSQL_RES *STDCALL(*mysql_store_result)(MYSQL *mysql);
+       void STDCALL(*mysql_free_result)(MYSQL_RES *result);
+       my_ulonglong STDCALL(*mysql_affected_rows)(MYSQL *mysql);
+       my_bool STDCALL(*mysql_stmt_close)(MYSQL_STMT *);
+       unsigned int STDCALL(*mysql_num_fields)(MYSQL_RES *result);
+       unsigned long *STDCALL(*mysql_fetch_lengths)(MYSQL_RES *result);
+       my_bool STDCALL(*mysql_stmt_bind_result)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
+       int STDCALL(*mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, unsigned long length);
+       int STDCALL(*mysql_stmt_store_result)(MYSQL_STMT *stmt);
+       MYSQL_ROW STDCALL(*mysql_fetch_row)(MYSQL_RES *result);
+       MYSQL_FIELD *STDCALL(*mysql_fetch_field)(MYSQL_RES *result);
+       const char *STDCALL(*mysql_get_server_info)(MYSQL *mysql);
+       MYSQL_STMT *STDCALL(*mysql_stmt_init)(MYSQL *mysql);
+       my_bool STDCALL(*mysql_stmt_bind_param)(MYSQL_STMT *stmt, MYSQL_BIND *bind);
+       int STDCALL(*mysql_stmt_execute)(MYSQL_STMT *stmt);
+       int STDCALL(*mysql_stmt_fetch)(MYSQL_STMT *stmt);
+       my_ulonglong STDCALL(*mysql_stmt_insert_id)(MYSQL_STMT *stmt);
+       my_ulonglong STDCALL(*mysql_stmt_affected_rows)(MYSQL_STMT *stmt);
+       MYSQL_RES *STDCALL(*mysql_stmt_result_metadata)(MYSQL_STMT *stmt);
+       int STDCALL(*mysql_stmt_fetch_column)(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset);
+} 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_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());
+       /* dynamic load the client library */
+       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 +257,70 @@ 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);
+#ifdef SYSLOG_H
+                               syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u);
+#endif
                        }
 
-                       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_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0 ||
+           glite_lbu_ExecSQLMysql(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 +329,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_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=0", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQLMysql(ctx_gen, "BEGIN", NULL) < 0) goto err;
                ctx->in_transaction = 1;
        }
 err:
@@ -319,11 +355,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_ExecSQLMysql(ctx_gen, "COMMIT", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
                ctx->in_transaction = 0;
        }
 err:
@@ -331,11 +369,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_ExecSQLMysql(ctx_gen, "ROLLBACK", NULL) < 0) goto err;
+               if (glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err;
                ctx->in_transaction = 0;
        }
 err:
@@ -343,31 +383,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 +432,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 +481,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 +510,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 +528,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 +537,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 +568,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 +601,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 +747,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 +755,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_PrepareStmtMysql(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 +776,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 +788,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 +800,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 +834,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 +864,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 +878,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 +909,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 +926,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;
-       glite_lbu_FreeStmt(&stmt);
+       if ((retval = glite_lbu_ExecSQLMysql(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRowMysql(stmt, 1, NULL, table) < 0) goto quit;
+       glite_lbu_FreeStmtMysql(&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 +970,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 +1003,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 +1026,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 +1061,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 +1110,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-pg.c b/org.glite.lbjp-common.db/src/db-pg.c
new file mode 100644 (file)
index 0000000..7d8d743
--- /dev/null
@@ -0,0 +1,603 @@
+/**
+ * Simple postgres module with org.glite.lbjp-common.db interface.
+ * 
+ * PostgreSQL limitations:
+ *  - prepared commands requires server >= 8.2
+ *  - binary data need to be handled manually (libpq limitation)
+ */
+
+#include <sys/types.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libpq-fe.h>
+
+#include "glite/lbu/trio.h"
+#include "db.h"
+#include "db-int.h"
+
+#ifdef WIN32
+#define STDCALL __stdcall
+#else
+#define STDCALL
+#endif
+
+#define DB_CONNECT_TIMEOUT "20"
+
+#ifdef LOG
+  #define lprintf(FMT...) fprintf(stdout, "[db-pg] %s: ", __FUNCTION__); fprintf(stdout, ##FMT);
+#else
+  #define lprintf(FMT...)
+#endif
+
+#define set_error(CTX, CODE, DESC...) glite_lbu_DBSetError((glite_lbu_DBContext)(CTX), (CODE), __FUNCTION__, __LINE__, ##DESC)
+
+#define LOAD(SYM, SYM2) if ((*(void **)(&psql_module.SYM) = dlsym(psql_module.lib, SYM2)) == NULL) { \
+       err = set_error(ctx, ENOENT, "can't load symbol '%s' from psql library (%s)", SYM2, dlerror()); \
+       break; \
+}
+
+
+struct glite_lbu_DBContextPsql_s {
+       struct glite_lbu_DBContext_s generic;
+       PGconn *conn;
+       int prepared_counts[4];
+};
+typedef struct glite_lbu_DBContextPsql_s *glite_lbu_DBContextPsql;
+
+struct glite_lbu_StatementPsql_s {
+       glite_lbu_Statement_t generic;
+       PGresult *res;
+       int row, nrows;
+       char *sql, *name;
+};
+typedef struct glite_lbu_StatementPsql_s *glite_lbu_StatementPsql;
+
+typedef struct {
+       void *lib;
+       pthread_mutex_t lock;
+
+       /* functions from 8.3.8 client library version (libpq-fe.h) */
+       PGconn *STDCALL(*PQconnectdb)(const char *conninfo);
+       ConnStatusType STDCALL(*PQstatus)(const PGconn *conn);
+       void STDCALL(*PQfinish)(PGconn *conn);
+       char *STDCALL(*PQerrorMessage)(const PGconn *conn);
+       int STDCALL(*PQnfields)(const PGresult *res);
+       char *STDCALL(*PQgetvalue)(const PGresult *res, int tup_num, int field_num);
+       int STDCALL(*PQgetlength)(const PGresult *res, int tup_num, int field_num);
+       void STDCALL(*PQclear)(PGresult *res);
+       PGresult *STDCALL(*PQexec)(PGconn *conn, const char *query);
+       ExecStatusType STDCALL(*PQresultStatus)(const PGresult *res);
+       char *STDCALL(*PQresultErrorMessage)(const PGresult *res);
+       char *STDCALL(*PQcmdTuples)(PGresult *res);
+       int STDCALL(*PQntuples)(const PGresult *res);
+       char *STDCALL(*PQfname)(const PGresult *res, int field_num);
+       size_t STDCALL(*PQescapeStringConn)(PGconn *conn,
+               char *to, const char *from, size_t length,
+               int *error);
+       void STDCALL(*PQfreemem)(void *ptr);
+} psql_module_t;
+
+
+static void glite_lbu_DBCleanup(void);
+
+/* backend module declaration */
+int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen);
+void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs);
+void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen);
+void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext commmon_ctx, int caps);
+int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results);
+void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt);
+//int glite_lbu_QueryIndicesPsql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names);
+int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt);
+int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols);
+int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen);
+int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap);
+//long int glite_lbu_LastidPsql(glite_lbu_Statement stmt_gen);
+
+glite_lbu_DBBackend_t psql_backend = {
+       backend: GLITE_LBU_DB_BACKEND_PSQL,
+
+       initContext: glite_lbu_InitDBContextPsql,
+       freeContext: glite_lbu_FreeDBContextPsql,
+       connect: glite_lbu_DBConnectPsql,
+       close: glite_lbu_DBClosePsql,
+       queryCaps: glite_lbu_DBQueryCapsPsql,
+       setCaps: glite_lbu_DBSetCapsPsql,
+       transaction: glite_lbu_TransactionPsql,
+       commit: glite_lbu_CommitPsql,
+       rollback: glite_lbu_RollbackPsql,
+       fetchRow: glite_lbu_FetchRowPsql,
+       freeStmt: glite_lbu_FreeStmtPsql,
+       queryIndices: NULL /*glite_lbu_QueryIndicesPsql*/,
+       execSQL: glite_lbu_ExecSQLPsql,
+       queryColumns: glite_lbu_QueryColumnsPsql,
+
+       timeToDB: glite_lbu_TimeToStr,
+       timestampToDB: glite_lbu_TimestampToStr,
+       DBToTime: glite_lbu_StrToTime,
+       DBToTimestamp: glite_lbu_StrToTimestamp,
+
+       prepareStmt: glite_lbu_PrepareStmtPsql,
+       execPreparedStmt_v: glite_lbu_ExecPreparedStmtPsql_v,
+       lastid: NULL/*glite_lbu_LastidPsql*/,
+};
+
+static psql_module_t psql_module = {
+       lib: NULL,
+       lock: PTHREAD_MUTEX_INITIALIZER,
+};
+
+
+/* nicer identifiers in PREPARE/EXECUTE commands */
+static const char *prepared_names[4] = {"select", "update", "insert", "other"};
+
+
+int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen) {
+       glite_lbu_DBContextPsql ctx;
+       int err = 0;
+
+       ctx = calloc(1, sizeof *ctx);
+       if (!ctx) return ENOMEM;
+       *ctx_gen = (glite_lbu_DBContext)ctx;
+
+       /* dynamic load of the client library */
+       pthread_mutex_lock(&psql_module.lock);
+       if (!psql_module.lib) {
+               psql_module.lib = dlopen(PSQL_SONAME, RTLD_LAZY | RTLD_LOCAL);
+               if (!psql_module.lib) return set_error(ctx, ENOENT, "dlopen(): " PSQL_SONAME ": %s", dlerror());
+               do {
+                       LOAD(PQconnectdb, "PQconnectdb");
+                       LOAD(PQstatus, "PQstatus");
+                       LOAD(PQfinish, "PQfinish");
+                       LOAD(PQerrorMessage, "PQerrorMessage");
+                       LOAD(PQnfields, "PQnfields");
+                       LOAD(PQgetvalue, "PQgetvalue");
+                       LOAD(PQgetlength, "PQgetlength");
+                       LOAD(PQclear, "PQclear");
+                       LOAD(PQexec, "PQexec");
+                       LOAD(PQresultStatus, "PQresultStatus");
+                       LOAD(PQresultErrorMessage, "PQresultErrorMessage");
+                       LOAD(PQcmdTuples, "PQcmdTuples");
+                       LOAD(PQntuples, "PQntuples");
+                       LOAD(PQfname, "PQfname");
+                       LOAD(PQescapeStringConn, "PQescapeStringConn");
+                       LOAD(PQfreemem, "PQfreemem");
+
+                       pthread_mutex_unlock(&psql_module.lock);
+                       atexit(glite_lbu_DBCleanup);
+               } while(0);
+
+               if (err) {
+                       dlclose(psql_module.lib);
+                       psql_module.lib = NULL;
+                       pthread_mutex_unlock(&psql_module.lock);
+                       return err;
+               }
+       } else pthread_mutex_unlock(&psql_module.lock);
+
+       return 0;
+}
+
+
+void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+
+       if (ctx) {
+               assert(ctx->conn == NULL);
+               free(ctx);
+       }
+}
+
+
+int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+       char *buf, *slash, *at, *colon, *host, *user, *pw, *db, *pgcsbuf, *pgcs;
+       char *err;
+
+       host = user = pw = db = NULL;
+       buf = strdup(cs);
+       slash = strchr(buf,'/');
+       at = strrchr(buf,'@');
+       colon = strrchr(buf,':');
+       if (!slash || !at || !colon) {
+               free(buf);
+               return set_error(ctx, EINVAL, "Invalid DB connect string");
+       }
+       *slash = *at = *colon = 0;
+       host = at+1;
+       user = buf;
+       pw = slash+1;
+       db = colon+1;
+
+       trio_asprintf(&pgcsbuf, "host='%|Ss' dbname='%|Ss' user='%|Ss' password='%|Ss' connect_timeout=" DB_CONNECT_TIMEOUT, host, db, user, pw);
+       /* simulate socket acces via localhost similar to MySQL */
+       if (strcmp(host, "localhost") == 0) pgcs = pgcsbuf + strlen("host='localhost' ");
+       else pgcs = pgcsbuf;
+       free(buf);
+
+       lprintf("connection string = %s\n", pgcs);
+       ctx->conn = psql_module.PQconnectdb(pgcs);
+       free(pgcsbuf);
+       if (!ctx->conn) return ENOMEM;
+
+       
+
+       if (psql_module.PQstatus(ctx->conn) != CONNECTION_OK) {
+               asprintf(&err, "Can't connect, %s", psql_module.PQerrorMessage(ctx->conn));
+               psql_module.PQfinish(ctx->conn);
+               ctx->conn = NULL;
+               set_error(ctx, EIO, err);
+               free(err);
+               return EIO;
+       }
+
+       return 0;
+}
+
+
+void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+
+       if (ctx->conn) {
+               psql_module.PQfinish(ctx->conn);
+               ctx->conn = NULL;
+       }
+}
+
+
+int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+       glite_lbu_Statement stmt;
+       int major, minor, sub, version;
+       int has_prepared = 0;
+       char *res = NULL;
+
+       if (glite_lbu_ExecSQLPsql(ctx_gen, "SHOW server_version", &stmt) == -1) return -1;
+       switch (glite_lbu_FetchRowPsql(stmt, 1, NULL, &res)) {
+       case 1:
+               break;
+       case -1:
+               has_prepared = -1;
+               goto quit;
+       default:
+               goto quit;
+       }
+       if (sscanf(res, "%d.%d.%d", &major, &minor, &sub) != 3) {
+               set_error(ctx, EIO, "can't parse PostgreSQL version string");
+               goto quit;
+       }
+       version = 10000*major + 100*minor + sub;
+       has_prepared = version >= 80200 ? GLITE_LBU_DB_CAP_PREPARED : 0;
+
+quit:
+       free(res);
+       glite_lbu_FreeStmtPsql(&stmt);
+       return has_prepared;
+}
+
+
+void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext ctx_gen, int caps) {
+       ctx_gen->caps = caps;
+}
+
+
+int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+       return 0;
+}
+
+
+int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+       return 0;
+}
+
+
+int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+       return 0;
+}
+
+
+int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt_gen, unsigned int maxn, unsigned long *lengths, char **results) {
+       glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+       unsigned int i, n;
+       char *s;
+
+       if (stmt->row >= stmt->nrows) return 0;
+
+       n = psql_module.PQnfields(stmt->res);
+       if (n <= 0) {
+               set_error(stmt->generic.ctx, EINVAL, "Result set w/o columns");
+               return -1;
+       }
+       if (n > maxn) {
+               set_error(stmt->generic.ctx, EINVAL, "Not enough room for the result");
+               return -1;
+       }
+       for (i = 0; i < n; i++) {
+               /* sanity check for internal error (NULL when invalid row) */
+               s = psql_module.PQgetvalue(stmt->res, stmt->row, i) ? : "";
+               results[i] = strdup(s);
+               if (lengths) lengths[i] = strlen(s);
+       }
+
+       stmt->row++;
+       return n;
+}
+
+
+void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt_gen) {
+       glite_lbu_DBContextPsql ctx;
+       glite_lbu_StatementPsql stmt;
+       char *sql;
+
+       if (!*stmt_gen) return;
+       stmt = (glite_lbu_StatementPsql)(*stmt_gen);
+       ctx = (glite_lbu_DBContextPsql)stmt->generic.ctx;
+       if (stmt->res) psql_module.PQclear(stmt->res);
+       if (stmt->name) {
+               asprintf(&sql, "DEALLOCATE %s", stmt->name);
+               stmt->res = psql_module.PQexec(ctx->conn, sql);
+               free(sql);
+               psql_module.PQclear(stmt->res);
+       }
+       free(stmt->name);
+       free(stmt->sql);
+       free(stmt);
+       *stmt_gen = NULL;
+}
+
+
+int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt_out) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+       glite_lbu_StatementPsql stmt = NULL;
+       int status, n;
+       char *nstr, *errmsg, *pos;
+       PGresult *res;
+
+       lprintf("command = %s\n", cmd);
+       if (stmt_out) *stmt_out = NULL;
+       if ((res = psql_module.PQexec(ctx->conn, cmd)) == NULL) {
+               ctx->generic.err.code = ENOMEM;
+               return -1;
+       }
+
+       status = psql_module.PQresultStatus(res);
+       if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+               errmsg = psql_module.PQresultErrorMessage(res);
+               if (errmsg) {
+                       errmsg = strdup(errmsg);
+                       if ((pos = strrchr(errmsg, '\n')) != NULL) pos[0] = '\0';
+               }
+               set_error(ctx, EIO, errmsg);
+               free(errmsg);
+               psql_module.PQclear(res);
+               return -1;
+       }
+
+       nstr = psql_module.PQcmdTuples(res);
+       if (nstr && nstr[0]) n = atoi(nstr);
+       else n = psql_module.PQntuples(res);
+       if (stmt_out) {
+               stmt = calloc(1, sizeof(*stmt));
+               stmt->generic.ctx = ctx_gen;
+               stmt->res = res;
+               stmt->nrows = n;
+               *stmt_out = (glite_lbu_Statement)stmt;
+       } else {
+               psql_module.PQclear(res);
+       }
+       return n;
+}
+
+
+int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols) {
+       glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+       int n, i;
+
+       n = psql_module.PQnfields(stmt->res);
+       for (i = 0; i < n; i++) {
+               cols[i] = psql_module.PQfname(stmt->res, i);
+       }
+       return -1;
+}
+
+
+int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_out) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+       int i, retval = -1;
+       const char *selectp, *updatep, *insertp, *minp;
+       char *sqlPrep = NULL, *s = NULL;
+       glite_lbu_StatementPsql stmt;
+       PGresult *res = NULL;
+
+       // init
+       stmt = calloc(1, sizeof(*stmt));
+       stmt->generic.ctx = ctx_gen;
+       stmt->sql = strdup(sql);
+
+       // name of the prepared command used as ID in postgres
+       i = -1;
+       minp = stmt->sql + strlen(stmt->sql);
+       selectp = strcasestr(stmt->sql, "SELECT");
+       updatep = strcasestr(stmt->sql, "UPDATE");
+       insertp = strcasestr(stmt->sql, "INSERT");
+       if (selectp && selectp < minp) { minp = selectp; i = 0; }
+       if (updatep && updatep < minp) { minp = updatep; i = 1; }
+       if (insertp && insertp < minp) { minp = insertp; i = 2; }
+       if (i == -1 || minp[0] == '\0') i = 3;
+       asprintf(&stmt->name, "%s%d", prepared_names[i], ++ctx->prepared_counts[i]);
+
+       asprintf(&sqlPrep, "PREPARE %s AS %s", stmt->name, stmt->sql);
+       lprintf("prepare = %s\n", sqlPrep);
+       res = psql_module.PQexec(ctx->conn, sqlPrep);
+       if (psql_module.PQresultStatus(res) != PGRES_COMMAND_OK) {
+               asprintf(&s, "error preparing command: %s", psql_module.PQerrorMessage(ctx->conn));
+               set_error(ctx, EIO, s);
+               free(s); s = NULL;
+               goto quit;
+       }
+
+       *stmt_out = (glite_lbu_Statement)stmt;
+       retval = 0;
+
+quit:
+       free(sqlPrep);
+       if (res) psql_module.PQclear(res);
+       if (!retval) return 0;
+
+       free(stmt->name);
+       free(stmt->sql);
+       free(stmt);
+       return retval;
+}
+
+
+int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap) {
+       glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)stmt_gen->ctx;
+       int i, retval = -1, status;
+       char **tmpdata = NULL;
+       char *sql = NULL, *s, *nstr;
+       size_t data_len = 0;
+
+       glite_lbu_DBType type;
+
+       if (!stmt || !stmt->sql || !stmt->name)
+               return set_error(ctx, EINVAL, "PrepareStmt() not called");
+
+       if (stmt->res) {
+               psql_module.PQclear(stmt->res);
+               stmt->res = NULL;
+       }
+
+       // gather parameters
+       if (n) {
+               tmpdata = calloc(n, sizeof(char *));
+       }
+       for (i = 0; i < n; i++) {
+               type = va_arg(ap, glite_lbu_DBType);
+
+               switch(type) {
+               case GLITE_LBU_DB_TYPE_TINYINT:
+                       asprintf(&tmpdata[i], "%d", va_arg(ap, int));
+                       break;
+
+               case GLITE_LBU_DB_TYPE_INT:
+                       asprintf(&tmpdata[i], "%ld", va_arg(ap, long int));
+                       break;
+
+               case GLITE_LBU_DB_TYPE_TINYBLOB:
+               case GLITE_LBU_DB_TYPE_TINYTEXT:
+               case GLITE_LBU_DB_TYPE_BLOB:
+               case GLITE_LBU_DB_TYPE_TEXT:
+               case GLITE_LBU_DB_TYPE_MEDIUMBLOB:
+               case GLITE_LBU_DB_TYPE_MEDIUMTEXT:
+               case GLITE_LBU_DB_TYPE_LONGBLOB:
+               case GLITE_LBU_DB_TYPE_LONGTEXT: {
+                       char *tmp, *s;
+                       unsigned long binary_len;
+
+                       s = va_arg(ap, char *);
+                       binary_len = va_arg(ap, unsigned long);
+                       lprintf("blob, len = %lu, ptr = %p\n", binary_len, s);
+                       if (s) {
+                               tmp = malloc(2*binary_len + 1);
+                               psql_module.PQescapeStringConn(ctx->conn, tmp, s, binary_len, NULL);
+                               asprintf(&tmpdata[i], "'%s'", tmp);
+                               lprintf("escaped: '%s'\n", tmpdata[i]);
+                               free(tmp);
+                       } else
+                               tmpdata[i] = strdup("NULL");
+                       break;
+               }
+
+
+               case GLITE_LBU_DB_TYPE_VARCHAR:
+               case GLITE_LBU_DB_TYPE_CHAR:
+                       s = va_arg(ap, char *);
+                       if (s) trio_asprintf(&tmpdata[i], "'%|Ss'", s);
+                       else tmpdata[i] = strdup("NULL");
+                       break;
+
+               case GLITE_LBU_DB_TYPE_DATE:
+               case GLITE_LBU_DB_TYPE_TIME:
+               case GLITE_LBU_DB_TYPE_DATETIME:
+                       glite_lbu_TimeToStr(va_arg(ap, time_t), &tmpdata[i]);
+                       break;
+
+               case GLITE_LBU_DB_TYPE_TIMESTAMP:
+                       glite_lbu_TimestampToStr(va_arg(ap, double), &tmpdata[i]);
+                       break;
+
+               case GLITE_LBU_DB_TYPE_NULL:
+                       tmpdata[i] = strdup("NULL");
+                       break;
+
+               case GLITE_LBU_DB_TYPE_BOOLEAN:
+                       tmpdata[i] = strdup(va_arg(ap, int) ? "true" : "false");
+                       break;
+
+               default:
+                       lprintf("unknown type %d\n", type);
+                       set_error(ctx, EINVAL, "unimplemented type");
+                       goto quit;
+               }
+
+               data_len += strlen(tmpdata[i]);
+       }
+
+       asprintf(&sql, "EXECUTE %s", stmt->name);
+       s = realloc(sql, strlen(sql) + (2 * n - 2) + strlen(" ()") + data_len + 1);
+       if (!s) goto quit;
+       sql = s;
+       for (i = 0; i < n; i++) {
+               strcat(sql, i ? ", " : " (" ); s += 2;
+               strcat(sql, tmpdata[i]);
+       }
+       if (n) strcat(sql, ")");
+
+       lprintf("exec prepared: n = %d, sql = '%s'\n", n, sql);
+       stmt->res = psql_module.PQexec(ctx->conn, sql);
+       status = psql_module.PQresultStatus(stmt->res);
+       if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+               asprintf(&s, "error executing prepared command '%s' parameters '%s': %s", stmt->sql, sql, psql_module.PQerrorMessage(ctx->conn));
+               set_error(ctx, EIO, s);
+               free(s); s = NULL;
+               goto quit;
+       }
+       nstr = psql_module.PQcmdTuples(stmt->res);
+       //lprintf("cmdtuples: '%s'\n", nstr);
+       if (nstr && nstr[0]) retval = atoi(nstr);
+       else retval = psql_module.PQntuples(stmt->res);
+       stmt->nrows = retval;
+       stmt->row = 0;
+       //lprintf("ntuples/retval: %d\n", retval);
+
+quit:
+       for (i = 0; i < n; i++) free(tmpdata[i]);
+       free(tmpdata);
+       free(sql);
+       return retval;
+}
+
+
+static void glite_lbu_DBCleanup(void) {
+       pthread_mutex_lock(&psql_module.lock);
+       if (psql_module.lib) {
+               dlclose(psql_module.lib);
+               psql_module.lib = NULL;
+       }
+       pthread_mutex_unlock(&psql_module.lock);
+}