From 6efa83bb27977cb14258257346081f8316b4a230 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Fri, 8 Jan 2010 11:56:29 +0000 Subject: [PATCH] Revert DB module API changes for branch 2.0, second stage - DB module with original API and subsequent updates. --- org.glite.lb.server/src/dump.c | 8 +- org.glite.lb.server/src/index.c.T | 12 +- org.glite.lb.server/src/notif_match.c | 2 +- org.glite.lb.server/src/notification.c | 10 +- org.glite.lb.server/src/openserver.c | 2 +- org.glite.lb.server/src/query.c | 16 +- org.glite.lb.server/src/store.c.T | 6 +- org.glite.lb.server/test/test_query_events.cpp | 6 +- org.glite.lbjp-common.db/Makefile | 105 +- org.glite.lbjp-common.db/examples/db_expire.c | 2 +- org.glite.lbjp-common.db/examples/db_test.c | 27 +- org.glite.lbjp-common.db/interface/db.h | 54 +- org.glite.lbjp-common.db/project/get_soname.sh | 6 +- org.glite.lbjp-common.db/src/db.c | 1239 ++++++++++++++++++++++++ 14 files changed, 1325 insertions(+), 170 deletions(-) create mode 100644 org.glite.lbjp-common.db/src/db.c diff --git a/org.glite.lb.server/src/dump.c b/org.glite.lb.server/src/dump.c index f8b37b3..1b6771e 100644 --- a/org.glite.lb.server/src/dump.c +++ b/org.glite.lb.server/src/dump.c @@ -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; diff --git a/org.glite.lb.server/src/index.c.T b/org.glite.lb.server/src/index.c.T index 3e471b5..de8a715 100644 --- a/org.glite.lb.server/src/index.c.T +++ b/org.glite.lb.server/src/index.c.T @@ -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;*/ diff --git a/org.glite.lb.server/src/notif_match.c b/org.glite.lb.server/src/notif_match.c index 54264c3..f351605 100644 --- a/org.glite.lb.server/src/notif_match.c +++ b/org.glite.lb.server/src/notif_match.c @@ -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))); diff --git a/org.glite.lb.server/src/notification.c b/org.glite.lb.server/src/notification.c index 0ca77a8..024b932 100644 --- a/org.glite.lb.server/src/notification.c +++ b/org.glite.lb.server/src/notification.c @@ -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); diff --git a/org.glite.lb.server/src/openserver.c b/org.glite.lb.server/src/openserver.c index 28ef18d..8580ff5 100644 --- a/org.glite.lb.server/src/openserver.c +++ b/org.glite.lb.server/src/openserver.c @@ -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); diff --git a/org.glite.lb.server/src/query.c b/org.glite.lb.server/src/query.c index 6a3ee43..01f53e8 100644 --- a/org.glite.lb.server/src/query.c +++ b/org.glite.lb.server/src/query.c @@ -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; diff --git a/org.glite.lb.server/src/store.c.T b/org.glite.lb.server/src/store.c.T index a83152f..365268f 100644 --- a/org.glite.lb.server/src/store.c.T +++ b/org.glite.lb.server/src/store.c.T @@ -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)) { diff --git a/org.glite.lb.server/test/test_query_events.cpp b/org.glite.lb.server/test/test_query_events.cpp index ab2a9a3..5224281 100644 --- a/org.glite.lb.server/test/test_query_events.cpp +++ b/org.glite.lb.server/test/test_query_events.cpp @@ -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; } diff --git a/org.glite.lbjp-common.db/Makefile b/org.glite.lbjp-common.db/Makefile index dafb817..94eb1cf 100644 --- a/org.glite.lbjp-common.db/Makefile +++ b/org.glite.lbjp-common.db/Makefile @@ -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 diff --git a/org.glite.lbjp-common.db/examples/db_expire.c b/org.glite.lbjp-common.db/examples/db_expire.c index 3830e0f..2eb3ba6 100644 --- a/org.glite.lbjp-common.db/examples/db_expire.c +++ b/org.glite.lbjp-common.db/examples/db_expire.c @@ -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; } diff --git a/org.glite.lbjp-common.db/examples/db_test.c b/org.glite.lbjp-common.db/examples/db_test.c index e1b639f..f87aff7 100644 --- a/org.glite.lbjp-common.db/examples/db_test.c +++ b/org.glite.lbjp-common.db/examples/db_test.c @@ -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. */ @@ -18,13 +13,10 @@ #include #include -#include - -#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\ @@ -32,12 +24,7 @@ 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\ @@ -46,14 +33,12 @@ 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 diff --git a/org.glite.lbjp-common.db/interface/db.h b/org.glite.lbjp-common.db/interface/db.h index 46a17ae..bd7d7e0 100644 --- a/org.glite.lbjp-common.db/interface/db.h +++ b/org.glite.lbjp-common.db/interface/db.h @@ -1,6 +1,9 @@ #ifndef GLITE_LBU_DB_H #define GLITE_LBU_DB_H +#ident "$Header$" + + #include #include @@ -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); /** diff --git a/org.glite.lbjp-common.db/project/get_soname.sh b/org.glite.lbjp-common.db/project/get_soname.sh index 66c6aab..d5f41b3 100755 --- a/org.glite.lbjp-common.db/project/get_soname.sh +++ b/org.glite.lbjp-common.db/project/get_soname.sh @@ -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 index 0000000..1c91526 --- /dev/null +++ b/org.glite.lbjp-common.db/src/db.c @@ -0,0 +1,1239 @@ +#ident "$Header$" + +#include +#ifdef LBS_DB_PROFILE +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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= 0 && Seq_in_index >= 0 && + Column_name >= 0 && Sub_part >= 0); + + } + + for (i=0; imysql, 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; inrfields) { + 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); +} + + -- 1.8.2.3