Revert DB module API changes for branch 2.0, second stage - DB module with original...
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 8 Jan 2010 11:56:29 +0000 (11:56 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Fri, 8 Jan 2010 11:56:29 +0000 (11:56 +0000)
14 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.server/test/test_query_events.cpp
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.c [new file with mode: 0644]

index f8b37b3..1b6771e 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_TimeToStr(from, &from_s);
-       glite_lbu_TimeToStr(to, &to_s);
+       glite_lbu_TimeToDB(from, &from_s);
+       glite_lbu_TimeToDB(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_StrToTime(time_s); 
+                               case 0: *t = glite_lbu_DBToTime(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_TimeToStr(t, &s);
+       glite_lbu_TimeToDB(t, &s);
        s[strlen(s) - 1] = '\0';
        *ptr = s;
 
index 3e471b5..de8a715 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_TimeToStr( ((struct timeval *) (((char *) stat) + offset))->tv_sec, &out );
+       glite_lbu_TimeToDB( ((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_TimeToStr(stat->stateEnterTimes[job_index_cols[i].qrec.attr_id.state+1], &data);
+                                       glite_lbu_TimeToDB(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_TimeToStr(stat->stateEnterTime.tv_sec, &data);
+                               glite_lbu_TimeToDB(stat->stateEnterTime.tv_sec, &data);
                                break;
                        case EDG_WLL_QUERY_ATTR_LASTUPDATETIME:
-                               glite_lbu_TimeToStr(stat->lastUpdateTime.tv_sec, &data);
+                               glite_lbu_TimeToDB(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_TimeToStr(stat->stateEnterTime, &data);
+                                       glite_lbu_TimeToDB(stat->stateEnterTime, &data);
                                else data = strdup("0");
                                break;
                        case EDG_WLL_QUERY_ATTR_LASTUPDATETIME:
                                if (stat->lastUpdateTime)
-                                       glite_lbu_TimeToStr(stat->lastUpdateTime, &data);
+                                       glite_lbu_TimeToDB(stat->lastUpdateTime, &data);
                                else data = strdup("0");
                                break;*/
 
index 54264c3..f351605 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_StrToTime(jobc[2]))) {
+               if (now > (expires = glite_lbu_DBToTime(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 0ca77a8..024b932 100644 (file)
@@ -89,7 +89,7 @@ int edg_wll_NotifNewServer(
                *valid = time(NULL) + ctx->notifDuration;       
        adjust_validity(ctx,valid);
 
-       glite_lbu_TimeToStr(*valid, &time_s);
+       glite_lbu_TimeToDB(*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_TimeToStr(*valid, &time_s);
+               glite_lbu_TimeToDB(*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_TimeToStr(*valid, &time_s);
+               glite_lbu_TimeToDB(*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_StrToTime(v+1);
+               expires = glite_lbu_DBToTime(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_TimeToStr(now, &time_s);
+       glite_lbu_TimeToDB(now, &time_s);
        if ( !time_s )
        {
                edg_wll_SetError(ctx, errno, NULL);
index 28ef18d..8580ff5 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, GLITE_LBU_DB_BACKEND_MYSQL) != 0) {
+       if (!ctx->dbctx && glite_lbu_InitDBContext((glite_lbu_DBContext*) &ctx->dbctx) != 0) {
                char *ed;
 
                glite_lbu_DBError(ctx->dbctx, NULL, &ed);
index 6a3ee43..01f53e8 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_TimeToStr(ec[m][n].value.t.tv_sec, &dbt);
+                       glite_lbu_TimeToDB(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_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt);
+                                       glite_lbu_TimeToDB(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_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt);
+                               glite_lbu_TimeToDB(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_TimeToStr(jc[m][n].value.t.tv_sec, &dbt);
+                       glite_lbu_TimeToDB(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_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt);
+                                       glite_lbu_TimeToDB(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_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt);
+                               glite_lbu_TimeToDB(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_StrToTime(f[5]);
+       e->any.timestamp.tv_sec = glite_lbu_DBToTime(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_StrToTime(f[8]); 
+       e->any.arrived.tv_sec = glite_lbu_DBToTime(f[8]); 
        e->any.arrived.tv_usec = 0;
        free(f[8]); f[8] = NULL;
 
index a83152f..365268f 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_TimeToStr(e->any.timestamp.tv_sec, &stamp);
+       glite_lbu_TimeToDB(e->any.timestamp.tv_sec, &stamp);
        ssrc = edg_wll_SourceToString(e->any.source);
 
        if ( ctx->event_load )
-               glite_lbu_TimeToStr(e->any.arrived.tv_sec, &now_s);
+               glite_lbu_TimeToDB(e->any.arrived.tv_sec, &now_s);
        else
-               glite_lbu_TimeToStr(time(NULL), &now_s);
+               glite_lbu_TimeToDB(time(NULL), &now_s);
 
        edg_wll_ResetError(ctx);
        switch (check_auth(ctx,e)) {
index ab2a9a3..5224281 100644 (file)
@@ -112,7 +112,7 @@ int QueryEventsTest::ExecStmt(const char *qry, glite_lbu_Statement *stmt_out)
 }
 
 extern "C" {
-int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx, int backend) { return 0; }
+int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx) { return 0; }
 void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) { }
 int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char*str) { return 0; }
 void glite_lbu_DBClose(glite_lbu_DBContext ctx) { }
@@ -145,8 +145,8 @@ void glite_lbu_FreeStmt(glite_lbu_Statement *) {}
 int debug;
 
 int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char**cols) { return 0; }
-void glite_lbu_TimeToStr(long t, char **s) { *s = NULL; }
-time_t glite_lbu_StrToTime(const char *c) { return (time_t)-1; }
+void glite_lbu_TimeToDB(long t, char **s) { *s = NULL; }
+time_t glite_lbu_DBToTime(const char *c) { return (time_t)-1; }
 
 int glite_lbu_Transaction(glite_lbu_DBContext ctx) { return 0; }
 int glite_lbu_Commit(glite_lbu_DBContext ctx) { return 0; }
index dafb817..94eb1cf 100644 (file)
@@ -6,10 +6,13 @@ 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
-postgresql_prefix=/usr
+mysql_version=4.1.11
 cppunit_prefix=/opt/cppunit
 thrflavour=gcc32pthr
 nothrflavour=gcc32
@@ -23,33 +26,23 @@ VPATH=${top_srcdir}/interface:${top_srcdir}/src:${top_srcdir}/examples
 
 DEBUG:=-g -O0 -W -Wall
 
-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_SONAME:=$(shell ../project/get_soname.sh ${mysql-devel_prefix}/${libdir} ${mysql_prefix}/${libdir} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib)
 
+MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql -DMYSQL_SONAME=\"${MYSQL_SONAME}\"
+MYSQL_LIBS=-lz
 
 CFLAGS:= \
        ${DEBUG} \
-       -DVERSION=\"${module.version}\" \
+       -DVERSION=\"${version}\" \
        -I${stagedir}/include -I${top_srcdir}/src -I. \
        -I${top_srcdir}/interface \
        ${COVERAGE_FLAGS} \
-       -D_GNU_SOURCE \
-       -DHAVE_SYSLOG_H=1
+       ${MYSQL_CPPFLAGS} \
+       -D_GNU_SOURCE
 
 ifdef LBS_DB_PROFILE
        CFLAGS:=${CFLAGS} -DLBS_DB_PROFILE
 endif
-ifneq (${mysql-devel_prefix},no)
-       OBJS:=${OBJS} db-mysql.o
-       CFLAGS:=${CFLAGS} -DMYSQL_SONAME=\"${MYSQL_SONAME}\"
-endif
-ifneq (${postgresql_prefix},no)
-       OBJS:=${OBJS} db-pg.o
-       CFLAGS:=${CFLAGS} -DPSQL_SONAME=\"${PSQL_SONAME}\"
-endif
 
 TEST_LIBS:=-L${cppunit_prefix}/lib -lcppunit
 TEST_INC:=-I${cppunit_prefix}/include
@@ -60,9 +53,9 @@ COMPILE:=libtool --mode=compile ${CC} ${CFLAGS}
 LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/lib ${LDFLAGS} 
 INSTALL:=libtool --mode=install install
 
-EXT_LIBS:=-lglite_lbu_trio -lpthread -ldl
-TESTOBJS:=${OBJS} dbtest.o
-OBJS:=${OBJS} db.o
+EXT_LIBS:=${MYSQL_LIBS} -lglite_lbu_trio -lpthread -ldl
+OBJS:=db.o
+TESTOBJS:=dbtest.o
 HDRS:=db.h
 LOBJS:=${OBJS:.o=.lo}
 LTESTOBJS:=${TESTOBJS:.o=.lo}
@@ -70,32 +63,23 @@ LTESTOBJS:=${TESTOBJS:.o=.lo}
 default all: compile doc
 
 check_soname:
-       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 \
+       if [ "${MYSQL_SONAME}" = notfound ]; then \
+               echo "MySQL shared library not found!"; \
+               false; \
        fi
 
-libglite_lbu_db.la: check_soname ${LOBJS}
-       ${LINK} -o $@ $+ ${EXT_LIBS}
+db.lo: check_soname
 
-libglite_lbu_dbtest.la: check_soname ${LTESTOBJS}
-       ${LINK} -o $@ $+ ${EXT_LIBS}
+libglite_lbu_db.la: ${LOBJS}
+       ${LINK} -o $@ $< ${EXT_LIBS}
+
+libglite_lbu_dbtest.la: ${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_mysql: db_test_mysql.lo libglite_lbu_dbtest.la
-       ${LINK} -o $@ $+ ${EXT_LIBS}
-
-db_test_psql: db_test_psql.lo libglite_lbu_dbtest.la
+db_test: db_test.lo libglite_lbu_dbtest.la
        ${LINK} -o $@ $+ ${EXT_LIBS}
 
 db_expire: db_expire.lo libglite_lbu_dbtest.la
@@ -111,13 +95,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_mysql db_test_psql db_expire
+examples: db_test db_expire
 
 doc:
 
 olddoc:
        cp ${top_srcdir}/doc/*.dox .
-       echo "PROJECT_NUMBER = ${module.version}" >> C.dox
+       echo "PROJECT_NUMBER = ${version}" >> C.dox
        doxygen C.dox
 
 stage: compile
@@ -126,49 +110,34 @@ stage: compile
 dist: distsrc distbin
 
 distsrc:
-       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}
+       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}
 
 distbin:
        $(MAKE) install PREFIX=`pwd`/tmpbuilddir${stagedir}
-       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${module.version}_bin.tar.gz *; cd $$save_dir
+       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz *; cd $$save_dir
        rm -rf tmpbuilddir
         
 install: all
        -mkdir -p ${PREFIX}/lib
-       -mkdir -p ${PREFIX}/share/doc/${package}-${module.version}
+       -mkdir -p ${PREFIX}/share/doc/${package}-${version}
        -mkdir -p ${PREFIX}/include/${globalprefix}/${lbutilsprefix}
-       ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${module.version}
-       -cp -r C ${PREFIX}/share/doc/${package}-${module.version}
+       ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${version}
+       -cp -r C ${PREFIX}/share/doc/${package}-${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 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 $@
+       rm -rvf db_expire db_test
 
 %.o %.lo: %.c
        ${COMPILE} -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
+db.lo: db.c db.h
+db_test.lo: libglite_lbu_dbtest.la db.h db_test.c
 
-.PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage check_soname
+.PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage
index 3830e0f..2eb3ba6 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, GLITE_LBU_DB_BACKEND_MYSQL) != 0) {
+       if (glite_lbu_InitDBContext(&ctx) != 0) {
                print_error(ctx);
                goto failctx;
        }
index e1b639f..f87aff7 100644 (file)
@@ -6,11 +6,6 @@
  *   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/trio.h>
-
-#include "db.h"
+#include "glite/lbu/db.h"
 
 #define CS "testuser/@localhost:test"
-
-#ifdef PSQL_BACKEND
+#if defined(DB_BACKEND) && DB_BACKEND == postgresql
 #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; }
 
@@ -92,7 +77,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
        int caps;
 
 #ifndef NO_PREPARED
-       char blob1[] = "Guess: blob or _string?"; blob1[15] = 0;
+       char blob1[] = "Guess: blob or \000string?";
        char blob2[] = {0, 1, 2, 3, 4, 5};
 #endif
 
@@ -107,7 +92,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
 
        // init
        dprintf(("connecting to %s...\n", cs));
-       if (glite_lbu_InitDBContext(&ctx, DB_TEST_BACKEND) != 0) goto failctx;
+       if (glite_lbu_InitDBContext(&ctx) != 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 46a17ae..bd7d7e0 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef GLITE_LBU_DB_H
 #define GLITE_LBU_DB_H
 
+#ident "$Header$"
+
+
 #include <time.h>
 #include <stdarg.h>
 
@@ -17,13 +20,12 @@ extern "C" {
  * Database modul module API (LB & JP Utils).
  *
  * There are two ways to access DB here:
- *
  * - simple:
- * SQL commands as single string. All values are incorporated in the SQL command strings. Proper escaping is required.
  *
+ * SQL commands as single string. All values are incorporated in the SQL command strings. Proper escaping is required.
  * - 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.
  *
+ * 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.
  * @{
  */
 
@@ -103,16 +105,6 @@ 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
@@ -123,21 +115,11 @@ 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 backend);
+int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx);
 
 
 /**
@@ -275,7 +257,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(glite_lbu_DBContext ctx, time_t t, char **str);
+void glite_lbu_TimeToDB(time_t t, char **str);
 
 
 /** 
@@ -286,7 +268,7 @@ void glite_lbu_TimeToDB(glite_lbu_DBContext ctx, time_t t, char **str);
  * \param[in]   t    the converted time
  * \param[out]  str  result allocated string
  */
-void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, double t, char **str);
+void glite_lbu_TimestampToDB(double t, char **str);
 
 
 /**
@@ -294,28 +276,10 @@ void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, 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(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);
+time_t glite_lbu_DBToTime(const char *str);
 
 
 /**
index 66c6aab..d5f41b3 100755 (executable)
@@ -1,11 +1,9 @@
 #! /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 "${filename}"* 2>/dev/null | head -n 1`
+               l=`find $dir -maxdepth 1 -name libmysqlclient.so* | head -n 1`
                if [ -f "$l" ]; then
                        lib=$l
                        break
@@ -17,7 +15,7 @@ for prefix in $@; do
 done
 
 if [ x"" != x"$lib" ]; then
-       readelf -d $lib | grep SONAME | sed "s/.*\(${filename}.[0-9]\{1,\}\).*/\1/"
+       readelf -d $lib | grep SONAME | sed 's/.*\(libmysqlclient.so.[0-9]\{1,\}\).*/\1/'
 else
        echo notfound
 fi
diff --git a/org.glite.lbjp-common.db/src/db.c b/org.glite.lbjp-common.db/src/db.c
new file mode 100644 (file)
index 0000000..1c91526
--- /dev/null
@@ -0,0 +1,1239 @@
+#ident "$Header$"
+
+#include <sys/types.h>
+#ifdef LBS_DB_PROFILE
+#include <sys/time.h>
+#endif
+#include <unistd.h>
+#include <time.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include <syslog.h>
+
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <mysql_version.h>
+#include <errmsg.h>
+
+#include "glite/lbu/trio.h"
+#include "db.h"
+
+
+#define GLITE_LBU_MYSQL_INDEX_VERSION 40001
+#define GLITE_LBU_MYSQL_PREPARED_VERSION 40102
+#define BUF_INSERT_ROW_ALLOC_BLOCK     1000
+#ifndef GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH
+#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 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()); \
+       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;
+       int in_transaction;     /* this flag is set whenever we are in DB transaction */
+};
+
+
+struct glite_lbu_Statement_s {
+       glite_lbu_DBContext  ctx;
+
+       /* for simple commands */
+       MYSQL_RES           *result;
+
+       /* for prepared commands */
+       MYSQL_STMT          *stmt;
+       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 */
+};
+
+
+/*
+ * mapping glite DB types to mysql types
+ */
+int glite_type_to_mysql[] = {
+       MYSQL_TYPE_NULL,
+       MYSQL_TYPE_TINY,
+       MYSQL_TYPE_LONG,
+       MYSQL_TYPE_TINY_BLOB,
+       MYSQL_TYPE_TINY_BLOB,
+       MYSQL_TYPE_BLOB,
+       MYSQL_TYPE_BLOB,
+       MYSQL_TYPE_MEDIUM_BLOB,
+       MYSQL_TYPE_MEDIUM_BLOB,
+       MYSQL_TYPE_LONG_BLOB,
+       MYSQL_TYPE_LONG_BLOB,
+       MYSQL_TYPE_VAR_STRING,
+       MYSQL_TYPE_STRING,
+       MYSQL_TYPE_DATE,
+       MYSQL_TYPE_TIME,
+       MYSQL_TYPE_DATETIME,
+       MYSQL_TYPE_TIMESTAMP,
+};
+
+
+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 = {
+       lib: NULL,
+       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 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 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);
+
+
+/* ---- 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 err = 0;
+       unsigned int ver_u;
+
+       *ctx = calloc(1, sizeof **ctx);
+       if (!*ctx) return ENOMEM;
+
+       /* 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());
+               do {
+                       LOAD(mysql_init, "mysql_init");
+                       LOAD(mysql_get_client_version, "mysql_get_client_version");
+                       LOAD(mysql_options, "mysql_options");
+                       LOAD(mysql_errno, "mysql_errno");
+                       LOAD(mysql_error, "mysql_error");
+                       LOAD(mysql_stmt_errno, "mysql_stmt_errno");
+                       LOAD(mysql_stmt_error, "mysql_stmt_error");
+                       LOAD(mysql_real_connect, "mysql_real_connect");
+                       LOAD(mysql_close, "mysql_close");
+                       LOAD(mysql_query, "mysql_query");
+                       LOAD(mysql_store_result, "mysql_store_result");
+                       LOAD(mysql_free_result, "mysql_free_result");
+                       LOAD(mysql_affected_rows, "mysql_affected_rows");
+                       LOAD(mysql_stmt_close, "mysql_stmt_close");
+                       LOAD(mysql_num_fields, "mysql_num_fields");
+                       LOAD(mysql_fetch_lengths, "mysql_fetch_lengths");
+                       LOAD(mysql_stmt_bind_result, "mysql_stmt_bind_result");
+                       LOAD(mysql_stmt_prepare, "mysql_stmt_prepare");
+                       LOAD(mysql_stmt_store_result, "mysql_stmt_store_result");
+                       LOAD(mysql_fetch_row, "mysql_fetch_row");
+                       LOAD(mysql_fetch_field, "mysql_fetch_field");
+                       LOAD(mysql_get_server_info, "mysql_get_server_info");
+                       LOAD(mysql_stmt_init, "mysql_stmt_init");
+                       LOAD(mysql_stmt_bind_param, "mysql_stmt_bind_param");
+                       LOAD(mysql_stmt_execute, "mysql_stmt_execute");
+                       LOAD(mysql_stmt_fetch, "mysql_stmt_fetch");
+                       LOAD(mysql_stmt_insert_id, "mysql_stmt_insert_id");
+                       LOAD(mysql_stmt_affected_rows, "mysql_stmt_affected_rows");
+                       LOAD(mysql_stmt_result_metadata, "mysql_stmt_result_metadata");
+                       LOAD(mysql_stmt_fetch_column, "mysql_stmt_fetch_column");
+
+                       // check the runtime version
+                       ver_u = db_handle.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);
+                       }
+
+                       pthread_mutex_unlock(&db_handle.lock);
+                       atexit(glite_lbu_DBCleanup);
+               } while(0);
+
+               if (err) {
+                       dlclose(db_handle.lib);
+                       db_handle.lib = NULL;
+                       pthread_mutex_unlock(&db_handle.lock);
+                       return err;
+               }
+       } else pthread_mutex_unlock(&db_handle.lock);
+
+       return 0;
+}
+
+
+void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) {
+       if (ctx) {
+               assert(ctx->mysql == NULL);
+               free(ctx->err.desc);
+               free(ctx);
+       }
+}
+
+
+int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+       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)
+               return STATUS(ctx);
+       else
+               return 0;
+}
+
+
+void glite_lbu_DBClose(glite_lbu_DBContext ctx) {
+       db_close(ctx->mysql);
+       ctx->mysql = NULL;
+       CLR_ERR(ctx);
+}
+
+
+int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
+       MYSQL   *m = ctx->mysql;
+       int     major,minor,sub,version,caps,origcaps;
+       const char *ver_s;
+
+       origcaps = ctx->caps;
+       caps = 0;
+
+       ver_s = db_handle.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;
+
+       if (version >= GLITE_LBU_MYSQL_INDEX_VERSION) caps |= GLITE_LBU_DB_CAP_INDEX;
+       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;
+
+       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;
+}
+
+
+int glite_lbu_Transaction(glite_lbu_DBContext ctx) {
+       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;
+               ctx->in_transaction = 1;
+       }
+err:
+       return STATUS(ctx);
+}
+
+
+int glite_lbu_Commit(glite_lbu_DBContext ctx) {
+       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;
+               ctx->in_transaction = 0;
+       }
+err:
+       return STATUS(ctx);
+}
+
+
+int glite_lbu_Rollback(glite_lbu_DBContext ctx) {
+       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;
+               ctx->in_transaction = 0;
+       }
+err:
+       return STATUS(ctx);
+}
+
+
+int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+       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);
+}
+
+
+static void glite_lbu_FreeStmt_int(glite_lbu_Statement stmt) {
+       if (stmt) {
+               if (stmt->result) db_handle.mysql_free_result(stmt->result);
+               if (stmt->stmt) db_handle.mysql_stmt_close(stmt->stmt);
+               free(stmt->sql);
+       }
+}
+
+
+void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
+       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;
+
+       size_t  i,j,ret;
+
+/* XXX: "show index from" columns. Matches at least MySQL 4.0.11 */
+       char    *sql, *showcol[12];
+       int     Key_name,Seq_in_index,Column_name,Sub_part;
+
+       char    **keys = NULL;
+       size_t  *cols = NULL;
+       char    **col_names = NULL;
+
+       size_t  nkeys = 0;
+
+       char    ***idx = NULL;
+
+       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) {
+               free(sql);
+               return STATUS(ctx);
+       }
+       free(sql);
+
+       while ((ret = glite_lbu_FetchRow(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);
+                       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;
+                               else if (!strcasecmp(col_names[i],"Column_name")) Column_name = i;
+                               else if (!strcasecmp(col_names[i],"Sub_part")) Sub_part = i;
+
+                       assert(Key_name >= 0 && Seq_in_index >= 0 && 
+                                       Column_name >= 0 && Sub_part >= 0);
+
+               }
+
+               for (i=0; i<nkeys && strcasecmp(showcol[Key_name],keys[i]); i++);
+
+               if (i == nkeys) {
+                       keys = realloc(keys,(i+2) * sizeof keys[0]);
+                       keys[i] = strdup(showcol[Key_name]);
+//printf("** KEY [%d] %s\n", i, keys[i]);
+                       keys[i+1] = NULL;
+                       cols = realloc(cols,(i+1) * sizeof cols[0]); 
+                       cols[i] = 0;
+                       idx = realloc(idx,(i+2) * sizeof idx[0]);
+                       idx[i] = idx[i+1] = NULL;
+                       nkeys++;
+               }
+
+               j = atoi(showcol[Seq_in_index])-1;
+               if (cols[i] <= j) {
+                       cols[i] = j+1;
+                       idx[i] = realloc(idx[i],(j+2)*sizeof idx[i][0]);
+                       memset(&idx[i][j+1],0,sizeof idx[i][0]);
+               }
+               idx[i][j] = strdup(showcol[Column_name]);
+//printf("****** [%d, %d] %s\n", i, j, idx[i][j]);
+//FIXME: needed?idx[i][j].value.i = atoi(showcol[Sub_part]);
+               for (i = 0; i<ret; i++) free(showcol[i]);
+       }
+
+       glite_lbu_FreeStmt(&stmt);
+       free(cols);
+       free(col_names);
+
+       if (ret == 0) CLR_ERR(ctx);
+       else {
+               free(keys);
+               keys = NULL;
+               for (i = 0; idx[i]; i++) {
+                       for (j = 0; idx[i][j]; j++) free(idx[i][j]);
+                       free(idx[i]);
+               }
+               free(idx);
+               idx = NULL;
+       }
+
+       if (key_names) *key_names = keys;
+       else {
+               for (i = 0; keys[i]; i++) free(keys[i]);
+               free(keys);
+       }
+       *column_names = idx;
+
+       return STATUS(ctx);
+}
+
+
+/* ---- simple ---- */
+
+int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt) {
+       int     merr;
+       int     retry_nr = 0;
+       int     do_reconnect = 0;
+#ifdef LBS_DB_PROFILE
+       struct timeval  start,end;
+       int     pid;
+
+       static struct timeval sum = {
+               tv_sec: 0,
+               tv_usec: 0
+       };
+#endif
+
+       CLR_ERR(ctx);
+
+       if (stmt) *stmt = NULL;
+
+#ifdef LBS_DB_PROFILE
+       gettimeofday(&start,NULL);
+#endif
+
+       while (retry_nr == 0 || do_reconnect) {
+               do_reconnect = 0;
+               if (db_handle.mysql_query(ctx->mysql, cmd)) {
+                       /* error occured */
+                       switch (merr = db_handle.mysql_errno(ctx->mysql)) {
+                               case 0:
+                                       break;
+                               case ER_DUP_ENTRY: 
+                                       ERR(ctx, EEXIST, db_handle.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));
+                                               return -1;
+                                       }
+                                       else if (retry_nr <= 0) 
+                                               do_reconnect = 1;
+                                       break;
+                               case ER_LOCK_DEADLOCK:
+                                       ERR(ctx, EDEADLOCK, db_handle.mysql_error(ctx->mysql));
+                                       return -1;
+                                       break;  
+                               default:
+                                       MY_ERR(ctx);
+                                       return -1;
+                                       break;
+                       }
+               }
+               retry_nr++;
+       }
+
+       if (stmt) {
+               *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)) {
+                               MY_ERR(ctx);
+                               *stmt = NULL;
+                               return -1;
+                       }
+               }
+       } else {
+               MYSQL_RES       *r = db_handle.mysql_store_result(ctx->mysql);
+               db_handle.mysql_free_result(r);
+       }
+#ifdef LBS_DB_PROFILE
+       pid = getpid();
+       gettimeofday(&end,NULL);
+       end.tv_usec -= start.tv_usec;
+       end.tv_sec -= start.tv_sec;
+       if (end.tv_usec < 0) { end.tv_sec--; end.tv_usec += 1000000; }
+
+       sum.tv_usec += end.tv_usec;
+       sum.tv_sec += end.tv_sec + sum.tv_usec / 1000000;
+       sum.tv_usec -= 1000000 * (sum.tv_usec / 1000000);
+       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);
+}
+
+
+int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols)
+{
+       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;
+       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 ret, retry;
+       MYSQL_RES *meta;
+
+       // init
+       *stmt = calloc(1, sizeof(**stmt));
+       (*stmt)->ctx = ctx;
+
+       // create the SQL command
+       if (((*stmt)->stmt = db_handle.mysql_stmt_init(ctx->mysql)) == NULL)
+               return MY_ERRSTMT(*stmt);
+
+       // prepare the SQL command
+       retry = 1;
+       do {
+               db_handle.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);
+       } else
+               (*stmt)->nrfields = 0;
+
+       // remember the command
+       (*stmt)->sql = strdup(sql);
+
+       return CLR_ERR(ctx);
+
+failed:
+       glite_lbu_FreeStmt(stmt);
+       return STATUS(ctx);
+}
+
+
+int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
+       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;
+       unsigned long *lens;
+       glite_lbu_Statement newstmt;
+
+       // gather parameters
+       if (n) {
+               binds = calloc(n, sizeof(MYSQL_BIND));
+               data = calloc(n, sizeof(void *));
+               lens = calloc(n, sizeof(unsigned long *));
+       }
+       for (i = 0; i < n; i++) {
+               type = va_arg(ap, glite_lbu_DBType);
+               switch (type) {
+               case GLITE_LBU_DB_TYPE_TINYINT:
+                       pchar = binds[i].buffer = data[i] = malloc(sizeof(char));
+                       *pchar = va_arg(ap, int);
+                       break;
+
+               case GLITE_LBU_DB_TYPE_INT:
+                       plint = binds[i].buffer = data[i] = malloc(sizeof(long int));
+                       *plint = va_arg(ap, long int);
+                       break;
+
+               case GLITE_LBU_DB_TYPE_BOOLEAN:
+                       pint = binds[i].buffer = data[i] = malloc(sizeof(int));
+                       *pint = va_arg(ap, int) ? 1 : 0;
+                       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:
+                       binds[i].buffer = va_arg(ap, void *);
+                       binds[i].length = &lens[i];
+                       lens[i] = va_arg(ap, unsigned long);
+                       break;
+
+               case GLITE_LBU_DB_TYPE_VARCHAR:
+               case GLITE_LBU_DB_TYPE_CHAR:
+                       binds[i].buffer = va_arg(ap, char *);
+                       binds[i].length = &lens[i];
+                       lens[i] = binds[i].buffer ? strlen((char *)binds[i].buffer) : 0;
+                       break;
+
+               case GLITE_LBU_DB_TYPE_DATE:
+               case GLITE_LBU_DB_TYPE_TIME:
+               case GLITE_LBU_DB_TYPE_DATETIME:
+                       ptime = binds[i].buffer = data[i] = malloc(sizeof(MYSQL_TIME));
+                       set_time(ptime, va_arg(ap, time_t));
+                       break;
+
+               case GLITE_LBU_DB_TYPE_TIMESTAMP:
+                       ptime = binds[i].buffer = data[i] = malloc(sizeof(MYSQL_TIME));
+                       set_time(ptime, va_arg(ap, double));
+                       break;
+
+               case GLITE_LBU_DB_TYPE_NULL:
+                       break;
+
+               default:
+                       assert("unimplemented parameter assign" == NULL);
+                       break;
+               }
+               binds[i].buffer_type = glite_type_to_mysql[type];
+       }
+
+       prepare_retry = 2;
+       do {
+               // bind parameters
+               if (n) {
+                       if (db_handle.mysql_stmt_bind_param(stmt->stmt, binds) != 0) {
+                               MY_ERRSTMT(stmt);
+                               ret = -1;
+                               goto statement_failed;
+                       }
+               }
+
+               // run
+               ctx = stmt->ctx;
+               retry = 1;
+               do {
+                       db_handle.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) {
+                               // expired the prepared command ==> restore it
+                               if (glite_lbu_PrepareStmt(stmt->ctx, stmt->sql, &newstmt) == -1) goto failed;
+                               glite_lbu_FreeStmt_int(stmt);
+                               memcpy(stmt, newstmt, sizeof(struct glite_lbu_Statement_s));
+                               prepare_retry--;
+                               ret = 0;
+                       } else goto failed;
+               }
+       } while (ret == 0 && prepare_retry > 0);
+
+       // result
+       retry = 1;
+       do {
+               db_handle.mysql_stmt_store_result(stmt->stmt);
+               ret = MY_ISOKSTMT(stmt, &retry);
+       } while (ret == 0);
+       if (ret == -1) goto failed;
+
+       // free params
+       if (n) {
+               for (i = 0; i < n; i++) free(data[i]);
+               free(data);
+               free(binds);
+               free(lens);
+       }
+       CLR_ERR(ctx);
+       return db_handle.mysql_stmt_affected_rows(stmt->stmt);
+
+failed:
+       for (i = 0; i < n; i++) free(data[i]);
+       free(data);
+       free(binds);
+       free(lens);
+       return -1;
+}
+
+
+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) {
+       my_ulonglong i;
+
+       CLR_ERR(stmt->ctx);
+       i = db_handle.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));
+}
+
+
+/*
+ * 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));
+}
+
+
+/*
+ * helping function: error handle
+ *
+ * \return -1 failed
+ * \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)) {
+               case 0:
+                       return 1;
+                       break;
+               case ER_DUP_ENTRY:
+                       lbu_err(stmt->ctx, EEXIST, source, line, db_handle.mysql_stmt_error(stmt->stmt));
+                       return -1;
+                       break;
+               case CR_SERVER_LOST:
+               case CR_SERVER_GONE_ERROR:
+                       if (*retry > 0) {
+                               (*retry)--;
+                               return 0;
+                       } else {
+                               myerrstmt(stmt, source, line);
+                               return -1;
+                       }
+                       break;
+               default:
+                       myerrstmt(stmt, source, line);
+                       return -1;
+                       break;
+       }
+}
+
+
+/*
+ * mysql connect
+ */
+static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) {
+       char    *buf = NULL;
+       char    *host,*user,*pw,*db; 
+       char    *slash,*at,*colon;
+       int      ret;
+#if MYSQL_VERSION_ID >= 50013
+       my_bool reconnect = 1;
+#endif
+
+       // needed for SQL result parameters
+       assert(sizeof(int) >= sizeof(my_bool));
+
+       if (!cs) return ERR(ctx, EINVAL, "connect string not specified");
+       
+       if (!(*mysql = db_handle.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL);
+
+       db_handle.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);
+#endif
+
+       host = user = pw = db = NULL;
+
+       buf = strdup(cs);
+       slash = strchr(buf,'/');
+       at = strrchr(buf,'@');
+       colon = strrchr(buf,':');
+
+       if (!slash || !at || !colon) {
+               free(buf);
+               db_close(*mysql);
+               *mysql = NULL;
+               return ERR(ctx, EINVAL, "Invalid DB connect string");
+       }
+
+       *slash = *at = *colon = 0;
+       host = at+1;
+       user = buf;
+       pw = slash+1;
+       db = colon+1;
+
+       /* 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);
+               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;
+       }
+       free(buf);
+
+       ctx->cs = cs;
+       return CLR_ERR(ctx);
+}
+
+
+/*
+ * mysql close
+ */
+static void db_close(MYSQL *mysql) {
+       if (mysql) db_handle.mysql_close(mysql);
+}
+
+
+/*
+ * test transactions capability:
+ */
+static int transaction_test(glite_lbu_DBContext ctx) {
+       glite_lbu_Statement stmt;
+       char *table[1] = { NULL }, *res[2] = { NULL, NULL }, *cmd = NULL;
+       int retval;
+
+       ctx->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);
+
+       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 (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;
+
+#ifdef LBS_DB_PROFILE
+       fprintf(stderr, "[%d] use_transactions = %d\n", getpid(), USE_TRANS(ctx));
+#endif
+
+quit:
+       glite_lbu_FreeStmt(&stmt);
+       free(table[0]);
+       free(res[0]);
+       free(res[1]);
+       free(cmd);
+       return STATUS(ctx);
+}
+
+
+/*
+ * simple version of the fetch
+ */
+static int FetchRowSimple(glite_lbu_DBContext 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)) {
+                       MY_ERR(ctx);
+                       return -1;
+               } else return 0;
+       }
+
+       nr = db_handle.mysql_num_fields(result);
+       len = db_handle.mysql_fetch_lengths(result);
+       for (i=0; i<nr; i++) {
+               if (lengths) lengths[i] = len[i];
+               if (len[i]) {
+                       results[i] = malloc(len[i] + 1);
+                       memcpy(results[i], row[i], len[i]);
+                       results[i][len[i]] = '\000';
+               } else
+                       results[i] = strdup("");
+       }
+
+       return nr;
+}
+
+
+/*
+ * prepared version of the fetch
+ */
+static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+       int ret, retry;
+       unsigned int i;
+       MYSQL_BIND *binds = NULL;
+       unsigned long *lens = NULL;
+
+       if (n != stmt->nrfields) {
+               ERR(ctx, EINVAL, "bad number of result fields");
+               return -1;
+       }
+
+       // space for results
+       if (n) binds = calloc(n, sizeof(MYSQL_BIND));
+       if (!lengths) {
+               lens = calloc(n, sizeof(unsigned long));
+               lengths = lens;
+       }
+       for (i = 0; i < n; i++) {
+               binds[i].buffer_type = MYSQL_TYPE_VAR_STRING;
+               binds[i].buffer_length = GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH - 1;
+               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;
+
+       // fetch data, all can be truncated
+       retry = 1;
+       do {
+               switch(db_handle.mysql_stmt_fetch(stmt->stmt)) {
+#ifdef MYSQL_DATA_TRUNCATED
+                       case MYSQL_DATA_TRUNCATED:
+#endif
+                       case 0:
+                               ret = 1; break;
+                       case 1: ret = MY_ISOKSTMT(stmt, &retry); break;
+                       case MYSQL_NO_DATA: ret = 0; goto quit; /* it's OK */
+                       default: ERR(ctx, EIO, "other fetch error"); goto failed;
+               }
+       } while (ret == 0);
+       if (ret == -1) goto failed;
+
+       // check if all fileds had enough buffer space
+       for (i = 0; i < n; i++) {
+               // fetch the rest if needed
+               if (lengths[i] > binds[i].buffer_length) {
+                       unsigned int fetched;
+
+                       fetched = binds[i].buffer_length;
+                       if ((results[i] = realloc(results[i], lengths[i] + 1)) == NULL) {
+                               ERR(ctx, ENOMEM, "insufficient memory for field data");
+                               goto failed;
+                       }
+                       results[i][lengths[i]] = '\000';
+                       binds[i].buffer = results[i] + fetched;
+                       binds[i].buffer_length = lengths[i] - fetched;
+
+                       retry = 1;
+                       do {
+                               switch (db_handle.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 */
+                                       default: ERR(ctx, EIO, "other fetch error"); goto failed;
+                               }
+                       } while (ret == 0);
+                       if (ret == -1) goto failed;
+               }
+       }
+
+       CLR_ERR(ctx);
+       free(binds);
+       free(lens);
+       return n;
+
+failedstmt:
+       MY_ERRSTMT(stmt);
+failed:
+       ret = -1;
+quit:
+       free(binds);
+       free(lens);
+       for (i = 0; i < n; i++) {
+               free(results[i]);
+               results[i] = NULL;
+       }
+       return ret;
+}
+
+
+static void set_time(MYSQL_TIME *mtime, const double time) {
+       struct tm tm;
+       time_t itime;
+
+       itime = time;
+       gmtime_r(&itime, &tm);
+       memset(mtime, 0, sizeof *mtime);
+       mtime->year = tm.tm_year + 1900;
+       mtime->month = tm.tm_mon + 1;
+       mtime->day = tm.tm_mday;
+       mtime->hour = tm.tm_hour;
+       mtime->minute = tm.tm_min;
+       mtime->second = tm.tm_sec;
+       mtime->second_part = (time - itime) * 1000;
+}
+
+
+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_unlock(&db_handle.lock);
+}
+
+