From c88615e286e2a9f7f71007aa67caefd99e8bd6a5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Tue, 22 Dec 2009 14:24:36 +0000 Subject: [PATCH] Backport new DB module and subsequent changes to branch. --- 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/configure | 29 +- 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 => db-mysql.c} | 688 +++++++++------------- org.glite.lbjp-common.db/src/db-pg.c | 603 +++++++++++++++++++ 15 files changed, 1076 insertions(+), 494 deletions(-) rename org.glite.lbjp-common.db/src/{db.c => db-mysql.c} (52%) create mode 100644 org.glite.lbjp-common.db/src/db-pg.c diff --git a/org.glite.lb.server/src/dump.c b/org.glite.lb.server/src/dump.c index 1b6771e..f8b37b3 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_TimeToDB(from, &from_s); - glite_lbu_TimeToDB(to, &to_s); + glite_lbu_TimeToStr(from, &from_s); + glite_lbu_TimeToStr(to, &to_s); trio_asprintf(&stmt, "select event,dg_jobid,code,prog,host,u.cert_subj,time_stamp,usec,level,arrived " @@ -175,7 +175,7 @@ static int handle_specials(edg_wll_Context ctx,time_t *t) case ENOENT: *t = 0; edg_wll_ResetError(ctx); break; - case 0: *t = glite_lbu_DBToTime(time_s); + case 0: *t = glite_lbu_StrToTime(time_s); assert(*t >= 0); break; default: break; @@ -190,7 +190,7 @@ static int handle_specials(edg_wll_Context ctx,time_t *t) static char *time_to_string(time_t t, char **ptr) { char *s; - glite_lbu_TimeToDB(t, &s); + glite_lbu_TimeToStr(t, &s); s[strlen(s) - 1] = '\0'; *ptr = s; diff --git a/org.glite.lb.server/src/index.c.T b/org.glite.lb.server/src/index.c.T index de8a715..3e471b5 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_TimeToDB( ((struct timeval *) (((char *) stat) + offset))->tv_sec, &out ); + glite_lbu_TimeToStr( ((struct timeval *) (((char *) stat) + offset))->tv_sec, &out ); return out; } @@ -378,17 +378,17 @@ edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context ctx, break; case EDG_WLL_QUERY_ATTR_TIME: if (stat->stateEnterTimes) - glite_lbu_TimeToDB(stat->stateEnterTimes[job_index_cols[i].qrec.attr_id.state+1], &data); + glite_lbu_TimeToStr(stat->stateEnterTimes[job_index_cols[i].qrec.attr_id.state+1], &data); else data = strdup("0"); break; case EDG_WLL_QUERY_ATTR_RESUBMITTED: asprintf(&data, "%d", stat->resubmitted); break; case EDG_WLL_QUERY_ATTR_STATEENTERTIME: - glite_lbu_TimeToDB(stat->stateEnterTime.tv_sec, &data); + glite_lbu_TimeToStr(stat->stateEnterTime.tv_sec, &data); break; case EDG_WLL_QUERY_ATTR_LASTUPDATETIME: - glite_lbu_TimeToDB(stat->lastUpdateTime.tv_sec, &data); + glite_lbu_TimeToStr(stat->lastUpdateTime.tv_sec, &data); break; case EDG_WLL_QUERY_ATTR_JDL_ATTR: // This is not the correct way to handle jdl searches. /* There's no way to index individual JDL attributes */ @@ -398,12 +398,12 @@ edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context ctx, break; /* case EDG_WLL_QUERY_ATTR_STATEENTERTIME: /// XXX: Which way of handling this is correct? if (stat->stateEnterTime) - glite_lbu_TimeToDB(stat->stateEnterTime, &data); + glite_lbu_TimeToStr(stat->stateEnterTime, &data); else data = strdup("0"); break; case EDG_WLL_QUERY_ATTR_LASTUPDATETIME: if (stat->lastUpdateTime) - glite_lbu_TimeToDB(stat->lastUpdateTime, &data); + glite_lbu_TimeToStr(stat->lastUpdateTime, &data); else data = strdup("0"); break;*/ diff --git a/org.glite.lb.server/src/notif_match.c b/org.glite.lb.server/src/notif_match.c index f351605..54264c3 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_DBToTime(jobc[2]))) { + if (now > (expires = glite_lbu_StrToTime(jobc[2]))) { edg_wll_NotifExpired(ctx,jobc[0]); if (debug) fprintf(stderr,"[%d] NOTIFY:%s expired at %s UTC\n", getpid(),jobc[0],asctime(gmtime(&expires))); diff --git a/org.glite.lb.server/src/notification.c b/org.glite.lb.server/src/notification.c index 024b932..0ca77a8 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_TimeToDB(*valid, &time_s); + glite_lbu_TimeToStr(*valid, &time_s); if ( !time_s ) { edg_wll_SetError(ctx, errno, NULL); @@ -205,7 +205,7 @@ int edg_wll_NotifBindServer( *valid = time(NULL) + ctx->notifDuration; adjust_validity(ctx,valid); - glite_lbu_TimeToDB(*valid, &time_s); + glite_lbu_TimeToStr(*valid, &time_s); if ( !time_s ) { edg_wll_SetError(ctx, errno, "Formating validity time"); @@ -379,7 +379,7 @@ int edg_wll_NotifRefreshServer( *valid = time(NULL) + ctx->notifDuration; adjust_validity(ctx,valid); - glite_lbu_TimeToDB(*valid, &time_s); + glite_lbu_TimeToStr(*valid, &time_s); if ( !time_s ) { edg_wll_SetError(ctx, errno, "Formating validity time"); @@ -685,7 +685,7 @@ static int update_notif( int expires; *v2 = 0; - expires = glite_lbu_DBToTime(v+1); + expires = glite_lbu_StrToTime(v+1); /* printf("edg_wll_NotifChangeIL(ctx, %s, %s, %d)\n", nid_s? nid_s: "nid", host, port); @@ -820,7 +820,7 @@ static int check_notif_age(edg_wll_Context ctx, const edg_wll_NotifId nid) { if ( !(nid_s = edg_wll_NotifIdGetUnique(nid)) ) goto cleanup; - glite_lbu_TimeToDB(now, &time_s); + glite_lbu_TimeToStr(now, &time_s); if ( !time_s ) { edg_wll_SetError(ctx, errno, NULL); diff --git a/org.glite.lb.server/src/openserver.c b/org.glite.lb.server/src/openserver.c index 8580ff5..28ef18d 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) != 0) { + if (!ctx->dbctx && glite_lbu_InitDBContext((glite_lbu_DBContext*) &ctx->dbctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) { char *ed; glite_lbu_DBError(ctx->dbctx, NULL, &ed); diff --git a/org.glite.lb.server/src/query.c b/org.glite.lb.server/src/query.c index 01f53e8..6a3ee43 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_TimeToDB(ec[m][n].value.t.tv_sec, &dbt); + glite_lbu_TimeToStr(ec[m][n].value.t.tv_sec, &dbt); if ( conds ) { if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN ) { trio_asprintf(&aux, "%s", dbt); free(dbt); - glite_lbu_TimeToDB(ec[m][n].value2.t.tv_sec, &dbt); + glite_lbu_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt); trio_asprintf(&out, "%s OR (e.time_stamp >= %s AND e.time_stamp <= %s)", conds, aux, dbt); free(aux); } @@ -814,7 +814,7 @@ static char *ec_to_head_where(edg_wll_Context ctx,const edg_wll_QueryRec **ec) { trio_asprintf(&aux, "%s", dbt); free(dbt); - glite_lbu_TimeToDB(ec[m][n].value2.t.tv_sec, &dbt); + glite_lbu_TimeToStr(ec[m][n].value2.t.tv_sec, &dbt); trio_asprintf(&conds, "(e.time_stamp >= %s AND e.time_stamp <= %s)", aux, dbt); free(aux); } @@ -1078,14 +1078,14 @@ static char *jc_to_head_where( *where_flags |= FL_SEL_STATUS; - glite_lbu_TimeToDB(jc[m][n].value.t.tv_sec, &dbt); + glite_lbu_TimeToStr(jc[m][n].value.t.tv_sec, &dbt); if ( conds ) { if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN ) { trio_asprintf(&aux, "%s", dbt); free(dbt); - glite_lbu_TimeToDB(jc[m][n].value2.t.tv_sec, &dbt); + glite_lbu_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt); trio_asprintf(&tmps, "%s OR (s.%s >= %s AND s.%s <= %s)", conds, cname, aux, cname, dbt); free(dbt); free(aux); @@ -1100,7 +1100,7 @@ static char *jc_to_head_where( { trio_asprintf(&aux, "%s", dbt); free(dbt); - glite_lbu_TimeToDB(jc[m][n].value2.t.tv_sec, &dbt); + glite_lbu_TimeToStr(jc[m][n].value2.t.tv_sec, &dbt); trio_asprintf(&conds, "(s.%s >= %s AND s.%s <= %s)", cname, aux, cname, dbt); free(dbt); free(aux); @@ -1374,7 +1374,7 @@ int convert_event_head(edg_wll_Context ctx,char **f,edg_wll_Event *e) e->any.user = f[4]; f[4] = NULL; - e->any.timestamp.tv_sec = glite_lbu_DBToTime(f[5]); + e->any.timestamp.tv_sec = glite_lbu_StrToTime(f[5]); free(f[5]); f[5] = NULL; e->any.timestamp.tv_usec = atoi(f[6]); @@ -1383,7 +1383,7 @@ int convert_event_head(edg_wll_Context ctx,char **f,edg_wll_Event *e) e->any.level = atoi(f[7]); free(f[7]); f[7] = NULL; - e->any.arrived.tv_sec = glite_lbu_DBToTime(f[8]); + e->any.arrived.tv_sec = glite_lbu_StrToTime(f[8]); e->any.arrived.tv_usec = 0; free(f[8]); f[8] = NULL; diff --git a/org.glite.lb.server/src/store.c.T b/org.glite.lb.server/src/store.c.T index 365268f..a83152f 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_TimeToDB(e->any.timestamp.tv_sec, &stamp); + glite_lbu_TimeToStr(e->any.timestamp.tv_sec, &stamp); ssrc = edg_wll_SourceToString(e->any.source); if ( ctx->event_load ) - glite_lbu_TimeToDB(e->any.arrived.tv_sec, &now_s); + glite_lbu_TimeToStr(e->any.arrived.tv_sec, &now_s); else - glite_lbu_TimeToDB(time(NULL), &now_s); + glite_lbu_TimeToStr(time(NULL), &now_s); edg_wll_ResetError(ctx); switch (check_auth(ctx,e)) { diff --git a/org.glite.lb/configure b/org.glite.lb/configure index 93e3eea..6c3cf92 100755 --- a/org.glite.lb/configure +++ b/org.glite.lb/configure @@ -28,7 +28,7 @@ my $sec_tag = ''; my $jobid_tag = ''; my $libdir = 'lib'; -my @nodes = qw/client server logger utils doc ws-test db jpprimary jpindex jpclient/; +my @nodes = qw/client server logger utils doc ws-test db jpprimary jpindex jpclient harvester/; my %enable_nodes; my %disable_nodes; @@ -46,13 +46,18 @@ my %extern_prefix = ( voms => '/opt/glite', gridsite => '/opt/glite', lcas => '/opt/glite', + trustmanager => '/opt/glite', ant => '/usr', jdk => '/usr', libtar => '/usr', + axis => '/usr', + log4c => '/usr', + postgresql => '/usr' ); my %jar = ( - 'commons-codec' => '/usr/share/java/commons-codec-1.3.jar', + 'commons-codec' => '/usr/share/java/commons-codec.jar', + 'commons-lang' => '/usr/share/java/commons-lang.jar', ); @@ -67,10 +72,10 @@ my %deps_type; my %topbuild; my %lbmodules = ( - 'lb' => [ qw/client common doc logger server state-machine types utils ws-interface ws-test/], + 'lb' => [ qw/client common doc logger server state-machine types utils ws-interface ws-test harvester/], 'security' => [qw/gss gsoap-plugin/], 'lbjp-common' => [qw/db maildir server-bones trio jp-interface/], - 'jobid' => [qw/api-c api-cpp/], + 'jobid' => [qw/api-c api-cpp api-java/], 'jp' => [ qw/client doc index primary server-common ws-interface/ ], ); @@ -294,7 +299,7 @@ sub mode_checkout() { BEGIN{ %need_externs_aux = ( 'lb.client' => [ qw/cppunit:B classads/ ], - 'lb.client-java' => [ qw/ant:B jglobus jdk:B/ ], + 'lb.client-java' => [ qw/ant:B jdk:B axis:B trustmanager/ ], 'lb.common' => [ qw/expat cares:B cppunit:B classads/ ], 'lb.doc' => [], 'lb.logger' => [ qw/cppunit:B/ ], @@ -304,13 +309,14 @@ BEGIN{ 'lb.ws-interface' => [], 'lb.ws-test' => [ qw/gsoap:B/ ], 'lb.types' => [ qw// ], - 'lbjp-common.db' => [ qw/mysql:R mysql-devel:B/ ], + 'lb.harvester' => [ qw/postgresql:R/ ], + 'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ], 'lbjp-common.maildir' => [ qw// ], 'lbjp-common.server-bones' => [ qw// ], 'lbjp-common.trio' => [ qw/cppunit:B/ ], 'lbjp-common.jp-interface' => [ qw/cppunit:B/ ], 'security.gss' => [ qw/globus_essentials:R globus:B cares cppunit:B/ ], - 'security.gsoap-plugin' => [ qw/cppunit:B globus_essentials:R globus:B cares gsoap:B/ ], + 'security.gsoap-plugin' => [ qw/cppunit:B globus_essentials:R globus:B cares:B gsoap:B/ ], 'jobid.api-c' => [ qw/cppunit:B/ ], 'jobid.api-cpp' => [ qw/cppunit:B/ ], 'jobid.api-java' => [ qw/ant:B jdk:B/ ], @@ -333,6 +339,7 @@ for my $ext (keys %need_externs_aux) { %need_jars = ( 'jobid.api-java' => [ qw/commons-codec/ ], + 'lb.client-java' => [ qw/commons-lang/ ], ); for my $jar (keys %need_jars) { @@ -350,6 +357,7 @@ for my $jar (keys %need_jars) { / ], 'lb.client-java' => [ qw/ lb.types:B + lb.ws-interface:B jobid.api-java / ], 'lb.common' => [ qw/ @@ -379,6 +387,10 @@ for my $jar (keys %need_jars) { 'lb.ws-test' => [ qw/security.gsoap-plugin lb.ws-interface/ ], 'lb.ws-interface' => [ qw/lb.types:B/ ], 'lb.types' => [ qw// ], + 'lb.harvester' => [ qw/ + jobid.api-c lbjp-common.trio lbjp-common.db lb.common lb.client + security.gss + / ], 'lbjp-common.db' => [ qw/lbjp-common.trio/ ], 'lbjp-common.maildir' => [ qw// ], 'lbjp-common.server-bones' => [ qw// ], @@ -451,7 +463,7 @@ sub mkinc my %aux; undef %aux; my @m=qw/ -lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java +lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java lb.harvester security.gss security.gsoap-plugin jobid.api-c jobid.api-cpp jobid.api-java lbjp-common.db lbjp-common.maildir lbjp-common.server-bones lbjp-common.trio lbjp-common.jp-interface @@ -517,6 +529,7 @@ BEGIN{ voms=>'org.glite.security.voms-api-cpp', gridsite=>'org.gridsite.shared', lcas=>'org.glite.security.lcas', + trustmanager=>'org.glite.security.trustmanager', ); %etics_projects = ( vdt=>[qw/globus globus_essentials/], diff --git a/org.glite.lbjp-common.db/Makefile b/org.glite.lbjp-common.db/Makefile index 94eb1cf..dafb817 100644 --- a/org.glite.lbjp-common.db/Makefile +++ b/org.glite.lbjp-common.db/Makefile @@ -6,13 +6,10 @@ distdir=. globalprefix=glite lbutilsprefix=lbu package=glite-lb-utils-db -version=0.2.0 PREFIX=/opt/glite -flavour=gcc32thr -glite_location=/opt/glite mysql-devel_prefix=/opt/mysql -mysql_version=4.1.11 +postgresql_prefix=/usr cppunit_prefix=/opt/cppunit thrflavour=gcc32pthr nothrflavour=gcc32 @@ -26,23 +23,33 @@ VPATH=${top_srcdir}/interface:${top_srcdir}/src:${top_srcdir}/examples DEBUG:=-g -O0 -W -Wall -MYSQL_SONAME:=$(shell ../project/get_soname.sh ${mysql-devel_prefix}/${libdir} ${mysql_prefix}/${libdir} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib) +MYSQL_SONAME:=$(shell ../project/get_soname.sh mysqlclient ${mysql-devel_prefix}/${libdir} ${mysql_prefix}/${libdir} ${mysql-devel_prefix}/lib ${mysql_prefix}/lib) +PSQL_SONAME:=$(shell ../project/get_soname.sh pq ${postgresql_prefix}/${libdir} ${postgresql_prefix}/lib) + +MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql +PSQL_CPPFLAGS:=-I${postgresql_prefix}/include -MYSQL_CPPFLAGS:=-I${mysql-devel_prefix}/include -I${mysql-devel_prefix}/include/mysql -DMYSQL_SONAME=\"${MYSQL_SONAME}\" -MYSQL_LIBS=-lz CFLAGS:= \ ${DEBUG} \ - -DVERSION=\"${version}\" \ + -DVERSION=\"${module.version}\" \ -I${stagedir}/include -I${top_srcdir}/src -I. \ -I${top_srcdir}/interface \ ${COVERAGE_FLAGS} \ - ${MYSQL_CPPFLAGS} \ - -D_GNU_SOURCE + -D_GNU_SOURCE \ + -DHAVE_SYSLOG_H=1 ifdef LBS_DB_PROFILE CFLAGS:=${CFLAGS} -DLBS_DB_PROFILE endif +ifneq (${mysql-devel_prefix},no) + OBJS:=${OBJS} db-mysql.o + CFLAGS:=${CFLAGS} -DMYSQL_SONAME=\"${MYSQL_SONAME}\" +endif +ifneq (${postgresql_prefix},no) + OBJS:=${OBJS} db-pg.o + CFLAGS:=${CFLAGS} -DPSQL_SONAME=\"${PSQL_SONAME}\" +endif TEST_LIBS:=-L${cppunit_prefix}/lib -lcppunit TEST_INC:=-I${cppunit_prefix}/include @@ -53,9 +60,9 @@ COMPILE:=libtool --mode=compile ${CC} ${CFLAGS} LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/lib ${LDFLAGS} INSTALL:=libtool --mode=install install -EXT_LIBS:=${MYSQL_LIBS} -lglite_lbu_trio -lpthread -ldl -OBJS:=db.o -TESTOBJS:=dbtest.o +EXT_LIBS:=-lglite_lbu_trio -lpthread -ldl +TESTOBJS:=${OBJS} dbtest.o +OBJS:=${OBJS} db.o HDRS:=db.h LOBJS:=${OBJS:.o=.lo} LTESTOBJS:=${TESTOBJS:.o=.lo} @@ -63,23 +70,32 @@ LTESTOBJS:=${TESTOBJS:.o=.lo} default all: compile doc check_soname: - if [ "${MYSQL_SONAME}" = notfound ]; then \ - echo "MySQL shared library not found!"; \ - false; \ + if [ "${mysql-devel_prefix}" != no ]; then \ + if [ "${MYSQL_SONAME}" = notfound ]; then \ + echo "MySQL shared library not found!"; \ + false; \ + fi \ + fi + if [ "${postgresql_prefix}" != no ]; then \ + if [ "${PSQL_SONAME}" = notfound ]; then \ + echo "PostgreSQL shared library not found!"; \ + false; \ + fi \ fi -db.lo: check_soname - -libglite_lbu_db.la: ${LOBJS} - ${LINK} -o $@ $< ${EXT_LIBS} +libglite_lbu_db.la: check_soname ${LOBJS} + ${LINK} -o $@ $+ ${EXT_LIBS} -libglite_lbu_dbtest.la: ${LTESTOBJS} - ${LINK} -o $@ $< ${EXT_LIBS} +libglite_lbu_dbtest.la: check_soname ${LTESTOBJS} + ${LINK} -o $@ $+ ${EXT_LIBS} dbtest.lo dbtest.o: db.c db.h ${COMPILE} -DGLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH=10 -c $< -o $@ -db_test: db_test.lo libglite_lbu_dbtest.la +db_test_mysql: db_test_mysql.lo libglite_lbu_dbtest.la + ${LINK} -o $@ $+ ${EXT_LIBS} + +db_test_psql: db_test_psql.lo libglite_lbu_dbtest.la ${LINK} -o $@ $+ ${EXT_LIBS} db_expire: db_expire.lo libglite_lbu_dbtest.la @@ -95,13 +111,13 @@ test_coverage: cd coverage && $(MAKE) -f ../Makefile top_srcdir=../../ COVERAGE_FLAGS="-fprofile-arcs -ftest-coverage" check cd coverage && for i in `echo ${OBJS} | tr ' ' '\012' | sort -u`; do gcov $$i ; done -examples: db_test db_expire +examples: db_test_mysql db_test_psql db_expire doc: olddoc: cp ${top_srcdir}/doc/*.dox . - echo "PROJECT_NUMBER = ${version}" >> C.dox + echo "PROJECT_NUMBER = ${module.version}" >> C.dox doxygen C.dox stage: compile @@ -110,34 +126,49 @@ stage: compile dist: distsrc distbin distsrc: - mkdir -p ${top_srcdir}/${package}-${version} - cd ${top_srcdir} && GLOBIGNORE="${package}-${version}" && cp -Rf * ${package}-${version} - cd ${top_srcdir} && tar -czf ${distdir}/${package}-${version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${version} - rm -rf ${top_srcdir}/${package}-${version} + mkdir -p ${top_srcdir}/${package}-${module.version} + cd ${top_srcdir} && GLOBIGNORE="${package}-${module.version}" && cp -Rf * ${package}-${module.version} + cd ${top_srcdir} && tar -czf ${distdir}/${package}-${module.version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${module.version} + rm -rf ${top_srcdir}/${package}-${module.version} distbin: $(MAKE) install PREFIX=`pwd`/tmpbuilddir${stagedir} - save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz *; cd $$save_dir + save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${module.version}_bin.tar.gz *; cd $$save_dir rm -rf tmpbuilddir install: all -mkdir -p ${PREFIX}/lib - -mkdir -p ${PREFIX}/share/doc/${package}-${version} + -mkdir -p ${PREFIX}/share/doc/${package}-${module.version} -mkdir -p ${PREFIX}/include/${globalprefix}/${lbutilsprefix} - ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${version} - -cp -r C ${PREFIX}/share/doc/${package}-${version} + ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${module.version} + -cp -r C ${PREFIX}/share/doc/${package}-${module.version} ${INSTALL} -m 755 "libglite_lbu_db.la" "${PREFIX}/lib/libglite_lbu_db.la"; \ ${INSTALL} -m 644 ${top_srcdir}/interface/${HDRS} ${PREFIX}/include/${globalprefix}/${lbutilsprefix} clean: rm -rvf *.o *.lo .libs lib* *.c *.h *.dox C/ CPP/ rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/ - rm -rvf db_expire db_test + rm -rvf db_expire db_test db_test_mysql db_test_psql + +db-mysql.o db-mysql.lo: db-mysql.c + ${COMPILE} ${MYSQL_CPPFLAGS} -c $< + +db-pg.o db-pg.lo: db-pg.c + ${COMPILE} ${PSQL_CPPFLAGS} -c $< + +db_test_mysql.o db_test_mysql.lo: db_test.c + ${COMPILE} -DMYSQL_BACKEND=1 -c $< -o $@ + +db_test_psql.o db_test_psql.lo: db_test.c + ${COMPILE} -DPSQL_BACKEND=1 -c $< -o $@ %.o %.lo: %.c ${COMPILE} -c $< -db.lo: db.c db.h -db_test.lo: libglite_lbu_dbtest.la db.h db_test.c +db.lo: db.c db.h db-int.h +db_test_psql.lo: libglite_lbu_dbtest.la db.h db-int.h db_test.c +db_test_mysql.lo: libglite_lbu_dbtest.la db.h db-int.h db_test.c +db-mysql.lo: db-mysql.c db-int.h db.h +db-pg.lo: db-pg.c db-int.h db.h -.PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage +.PHONY: default all compile check examples doc stage dist distsrc distbin install clean test_coverage check_soname diff --git a/org.glite.lbjp-common.db/examples/db_expire.c b/org.glite.lbjp-common.db/examples/db_expire.c index 2eb3ba6..3830e0f 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) != 0) { + if (glite_lbu_InitDBContext(&ctx, GLITE_LBU_DB_BACKEND_MYSQL) != 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 f87aff7..e1b639f 100644 --- a/org.glite.lbjp-common.db/examples/db_test.c +++ b/org.glite.lbjp-common.db/examples/db_test.c @@ -6,6 +6,11 @@ * mysqladmin -u root -p create test * mysql -u root -p -e 'GRANT ALL on test.* to testuser@localhost' * + * Or postgres: + * + * createuser -U postgres testuser + * createdb -U postgres --owner testuser test + * * Use CS environment variable when using different user/pwd@machine:dbname. */ @@ -13,10 +18,13 @@ #include #include -#include "glite/lbu/db.h" +#include + +#include "db.h" #define CS "testuser/@localhost:test" -#if defined(DB_BACKEND) && DB_BACKEND == postgresql + +#ifdef PSQL_BACKEND #define CREATE_CMD "CREATE TABLE \"data\" (\n\ \"id\" INTEGER NOT NULL,\n\ \"user\" VARCHAR(32) NOT NULL,\n\ @@ -24,7 +32,12 @@ 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\ @@ -33,12 +46,14 @@ INDEX(`user`)\n\ ) engine=innodb" #define AMP "`" +#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)" +#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?" +#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_MYSQL #endif + #define DROP_CMD "DROP TABLE " AMP "data" AMP #define INSERT_TRIO_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (%d, %s, %s)" #define SELECT_TRIO_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = '%s'" -#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)" -#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?" #define dprintf(ARGS) { printf("%s: ", name); printf ARGS; } @@ -77,7 +92,7 @@ int main(int argn __attribute((unused)), char *argv[]) { int caps; #ifndef NO_PREPARED - char blob1[] = "Guess: blob or \000string?"; + char blob1[] = "Guess: blob or _string?"; blob1[15] = 0; char blob2[] = {0, 1, 2, 3, 4, 5}; #endif @@ -92,7 +107,7 @@ int main(int argn __attribute((unused)), char *argv[]) { // init dprintf(("connecting to %s...\n", cs)); - if (glite_lbu_InitDBContext(&ctx) != 0) goto failctx; + if (glite_lbu_InitDBContext(&ctx, DB_TEST_BACKEND) != 0) goto failctx; if (glite_lbu_DBConnect(ctx, cs) != 0) goto failctx; if ((caps = glite_lbu_DBQueryCaps(ctx)) == -1) goto failcon; #ifndef NO_PREPARED diff --git a/org.glite.lbjp-common.db/interface/db.h b/org.glite.lbjp-common.db/interface/db.h index bd7d7e0..46a17ae 100644 --- a/org.glite.lbjp-common.db/interface/db.h +++ b/org.glite.lbjp-common.db/interface/db.h @@ -1,9 +1,6 @@ #ifndef GLITE_LBU_DB_H #define GLITE_LBU_DB_H -#ident "$Header$" - - #include #include @@ -20,12 +17,13 @@ extern "C" { * Database modul module API (LB & JP Utils). * * There are two ways to access DB here: - * - simple: * + * - simple: * SQL commands as single string. All values are incorporated in the SQL command strings. Proper escaping is required. - * - enhanced: * + * - enhanced: * Prepared SQL commands with separated parameters, functions PrepareStmt() and ExecPreparedStmt(). All values are delivered in separated buffers. Its faster for multiple using and more secure. + * * @{ */ @@ -105,6 +103,16 @@ typedef enum { /** + * Supported DB backends. + */ +typedef enum { + GLITE_LBU_DB_BACKEND_MYSQL = 0, + GLITE_LBU_DB_BACKEND_PSQL, + GLITE_LBU_DB_BACKEND_LAST +} glite_lbu_DBBackendNo; + + +/** * Get error state from DB context. * * \param[in] ctx context to work with @@ -115,11 +123,21 @@ int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc); /** + * Clear the error from DB context. + * + * \param[in] ctx context to work with + */ +int glite_lbu_DBClearError(glite_lbu_DBContext ctx); + + +/** * Initialize the database context. * * \param[out] ctx result context + * \param[in] backend required database backend + * \return error code */ -int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx); +int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx, int backend); /** @@ -257,7 +275,7 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k * \param[in] t the converted time * \param[out] str result allocated string */ -void glite_lbu_TimeToDB(time_t t, char **str); +void glite_lbu_TimeToDB(glite_lbu_DBContext ctx, time_t t, char **str); /** @@ -268,7 +286,7 @@ void glite_lbu_TimeToDB(time_t t, char **str); * \param[in] t the converted time * \param[out] str result allocated string */ -void glite_lbu_TimestampToDB(double t, char **str); +void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, double t, char **str); /** @@ -276,10 +294,28 @@ void glite_lbu_TimestampToDB(double t, char **str); * * String is expected in database for (ISO format). * + * \param[in] ctx context to work with * \param[in] str the converted string * \return result time */ -time_t glite_lbu_DBToTime(const char *str); +time_t glite_lbu_DBToTime(glite_lbu_DBContext ctx, const char *str); + + +/** + * Convert database-specific time string to time (double). + * + * \param[in] ctx context to work with + * \param[in] str the converted string + * \return result time + * */ +double glite_lbu_DBToTimestamp(glite_lbu_DBContext ctx, const char *str); + + +/* Generic helper time convert functions. */ +void glite_lbu_TimeToStr(time_t t, char **str); +void glite_lbu_TimestampToStr(double t, char **str); +time_t glite_lbu_StrToTime(const char *str); +double glite_lbu_StrToTimestamp(const char *str); /** diff --git a/org.glite.lbjp-common.db/project/get_soname.sh b/org.glite.lbjp-common.db/project/get_soname.sh index d5f41b3..66c6aab 100755 --- a/org.glite.lbjp-common.db/project/get_soname.sh +++ b/org.glite.lbjp-common.db/project/get_soname.sh @@ -1,9 +1,11 @@ #! /bin/sh lib="" +filename="lib$1.so" +shift for prefix in $@; do for dir in "$prefix" "$prefix/mysql"; do - l=`find $dir -maxdepth 1 -name libmysqlclient.so* | head -n 1` + l=`find $dir -maxdepth 1 -name "${filename}"* 2>/dev/null | head -n 1` if [ -f "$l" ]; then lib=$l break @@ -15,7 +17,7 @@ for prefix in $@; do done if [ x"" != x"$lib" ]; then - readelf -d $lib | grep SONAME | sed 's/.*\(libmysqlclient.so.[0-9]\{1,\}\).*/\1/' + readelf -d $lib | grep SONAME | sed "s/.*\(${filename}.[0-9]\{1,\}\).*/\1/" else echo notfound fi diff --git a/org.glite.lbjp-common.db/src/db.c b/org.glite.lbjp-common.db/src/db-mysql.c similarity index 52% rename from org.glite.lbjp-common.db/src/db.c rename to org.glite.lbjp-common.db/src/db-mysql.c index 1c91526..60468d7 100644 --- a/org.glite.lbjp-common.db/src/db.c +++ b/org.glite.lbjp-common.db/src/db-mysql.c @@ -1,5 +1,3 @@ -#ident "$Header$" - #include #ifdef LBS_DB_PROFILE #include @@ -14,7 +12,18 @@ #include #include #include +#ifdef HAVE_SYSLOG_H #include +#endif + +#ifdef WIN32 +#include +#ifndef STDCALL +#define STDCALL __stdcall +#endif +#else +#define STDCALL +#endif #include #include @@ -23,6 +32,7 @@ #include "glite/lbu/trio.h" #include "db.h" +#include "db-int.h" #define GLITE_LBU_MYSQL_INDEX_VERSION 40001 @@ -32,36 +42,31 @@ #define GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH 256 #endif -#define CLR_ERR(CTX) lbu_clrerr((CTX)) -#define ERR(CTX, CODE, ARGS...) lbu_err((CTX), (CODE), __FUNCTION__, __LINE__, ##ARGS) -#define STATUS(CTX) ((CTX)->err.code) +#define CLR_ERR(CTX) glite_lbu_DBClearError((glite_lbu_DBContext)(CTX)) +#define ERR(CTX, CODE, ARGS...) glite_lbu_DBSetError((glite_lbu_DBContext)((CTX)), (CODE), __FUNCTION__, __LINE__, ##ARGS) +#define STATUS(CTX) glite_lbu_DBError((glite_lbu_DBContext)((CTX)), NULL, NULL) #define MY_ERR(CTX) myerr((CTX), __FUNCTION__, __LINE__) #define MY_ERRSTMT(STMT) myerrstmt((STMT), __FUNCTION__, __LINE__) #define MY_ISOKSTMT(STMT, RETRY) myisokstmt((STMT), __FUNCTION__, __LINE__, (RETRY)) -#define USE_TRANS(CTX) ((CTX->caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0) -#define LOAD(SYM, SYM2) if ((*(void **)(&db_handle.SYM) = dlsym(db_handle.lib, SYM2)) == NULL) { \ - err = ERR(*ctx, ENOENT, "can't load symbol %s from mysql library (%s)", SYM2, dlerror()); \ +#define USE_TRANS(CTX) ((CTX->generic.caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0) +#define LOAD(SYM, SYM2) if ((*(void **)(&mysql_module.SYM) = dlsym(mysql_module.lib, SYM2)) == NULL) { \ + err = ERR(ctx, ENOENT, "can't load symbol '%s' from mysql library (%s)", SYM2, dlerror()); \ break; \ } -#define dprintf(CTX, FMT...) if (CTX->caps & GLITE_LBU_DB_CAP_ERRORS) fprintf(stderr, ##FMT) -struct glite_lbu_DBContext_s { - MYSQL *mysql; - const char *cs; - int caps; - struct { - int code; - char *desc; - } err; +struct glite_lbu_DBContextMysql_s { + struct glite_lbu_DBContext_s generic; int in_transaction; /* this flag is set whenever we are in DB transaction */ + MYSQL *mysql; }; +typedef struct glite_lbu_DBContextMysql_s *glite_lbu_DBContextMysql; -struct glite_lbu_Statement_s { - glite_lbu_DBContext ctx; +struct glite_lbu_StatementMysql_s { + glite_lbu_Statement_t generic; /* for simple commands */ MYSQL_RES *result; @@ -71,20 +76,7 @@ struct glite_lbu_Statement_s { unsigned long nrfields; char *sql; }; - - -struct glite_lbu_bufInsert_s { - glite_lbu_DBContext ctx; - char *table_name; - char *columns; /* names of columns to be inserted into - * (values separated with commas) */ - char **rows; /* each row hold string of one row to be inserted - * (values separated with commas) */ - long rec_num, /* actual number of rows in structure */ - rec_size; /* approx. size of a real insert string */ - long size_limit, /* size and # of records limit which trigger */ - record_limit; /* real insert; zero means unlimitted */ -}; +typedef struct glite_lbu_StatementMysql_s *glite_lbu_StatementMysql; /* @@ -115,86 +107,123 @@ typedef struct { void *lib; pthread_mutex_t lock; - void *(*mysql_init)(void *); - unsigned long (*mysql_get_client_version)(void); - int (*mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); - unsigned int (*mysql_errno)(MYSQL *mysql); - const char *(*mysql_error)(MYSQL *mysql); - unsigned int (*mysql_stmt_errno)(MYSQL_STMT *stmt); - const char *(*mysql_stmt_error)(MYSQL_STMT *stmt); - MYSQL *(*mysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag); - void (*mysql_close)(MYSQL *mysql); - int (*mysql_query)(MYSQL *mysql, const char *stmt_str); - MYSQL_RES *(*mysql_store_result)(MYSQL *mysql); - void (*mysql_free_result)(MYSQL_RES *result); - my_ulonglong (*mysql_affected_rows)(MYSQL *mysql); - my_bool (*mysql_stmt_close)(MYSQL_STMT *); - unsigned int (*mysql_num_fields)(MYSQL_RES *result); - unsigned long *(*mysql_fetch_lengths)(MYSQL_RES *result); - my_bool (*mysql_stmt_bind_result)(MYSQL_STMT *stmt, MYSQL_BIND *bind); - int (*mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, unsigned long length); - int (*mysql_stmt_store_result)(MYSQL_STMT *stmt); - MYSQL_ROW (*mysql_fetch_row)(MYSQL_RES *result); - MYSQL_FIELD *(*mysql_fetch_field)(MYSQL_RES *result); - const char *(*mysql_get_server_info)(MYSQL *mysql); - MYSQL_STMT *(*mysql_stmt_init)(MYSQL *mysql); - my_bool (*mysql_stmt_bind_param)(MYSQL_STMT *stmt, MYSQL_BIND *bind); - int (*mysql_stmt_execute)(MYSQL_STMT *stmt); - int (*mysql_stmt_fetch)(MYSQL_STMT *stmt); - my_ulonglong (*mysql_stmt_insert_id)(MYSQL_STMT *stmt); - my_ulonglong (*mysql_stmt_affected_rows)(MYSQL_STMT *stmt); - MYSQL_RES *(*mysql_stmt_result_metadata)(MYSQL_STMT *stmt); - int (*mysql_stmt_fetch_column)(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset); -} mysql_lib_handle_t; - - -static mysql_lib_handle_t db_handle = { + void * STDCALL(*mysql_init)(void *); + unsigned long STDCALL(*mysql_get_client_version)(void); + int STDCALL(*mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); + unsigned int STDCALL(*mysql_errno)(MYSQL *mysql); + const char *STDCALL(*mysql_error)(MYSQL *mysql); + unsigned int STDCALL(*mysql_stmt_errno)(MYSQL_STMT *stmt); + const char *STDCALL(*mysql_stmt_error)(MYSQL_STMT *stmt); + MYSQL * STDCALL(*mysql_real_connect)(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag); + void STDCALL(*mysql_close)(MYSQL *mysql); + int STDCALL(*mysql_query)(MYSQL *mysql, const char *stmt_str); + MYSQL_RES *STDCALL(*mysql_store_result)(MYSQL *mysql); + void STDCALL(*mysql_free_result)(MYSQL_RES *result); + my_ulonglong STDCALL(*mysql_affected_rows)(MYSQL *mysql); + my_bool STDCALL(*mysql_stmt_close)(MYSQL_STMT *); + unsigned int STDCALL(*mysql_num_fields)(MYSQL_RES *result); + unsigned long *STDCALL(*mysql_fetch_lengths)(MYSQL_RES *result); + my_bool STDCALL(*mysql_stmt_bind_result)(MYSQL_STMT *stmt, MYSQL_BIND *bind); + int STDCALL(*mysql_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, unsigned long length); + int STDCALL(*mysql_stmt_store_result)(MYSQL_STMT *stmt); + MYSQL_ROW STDCALL(*mysql_fetch_row)(MYSQL_RES *result); + MYSQL_FIELD *STDCALL(*mysql_fetch_field)(MYSQL_RES *result); + const char *STDCALL(*mysql_get_server_info)(MYSQL *mysql); + MYSQL_STMT *STDCALL(*mysql_stmt_init)(MYSQL *mysql); + my_bool STDCALL(*mysql_stmt_bind_param)(MYSQL_STMT *stmt, MYSQL_BIND *bind); + int STDCALL(*mysql_stmt_execute)(MYSQL_STMT *stmt); + int STDCALL(*mysql_stmt_fetch)(MYSQL_STMT *stmt); + my_ulonglong STDCALL(*mysql_stmt_insert_id)(MYSQL_STMT *stmt); + my_ulonglong STDCALL(*mysql_stmt_affected_rows)(MYSQL_STMT *stmt); + MYSQL_RES *STDCALL(*mysql_stmt_result_metadata)(MYSQL_STMT *stmt); + int STDCALL(*mysql_stmt_fetch_column)(MYSQL_STMT *stmt, MYSQL_BIND *bind, unsigned int column, unsigned long offset); +} mysql_module_t; + + +/* backend module declaration */ +int glite_lbu_InitDBContextMysql(glite_lbu_DBContext *ctx_gen); +void glite_lbu_FreeDBContextMysql(glite_lbu_DBContext ctx_gen); +int glite_lbu_DBConnectMysql(glite_lbu_DBContext ctx_gen, const char *cs); +void glite_lbu_DBCloseMysql(glite_lbu_DBContext ctx_gen); +int glite_lbu_DBQueryCapsMysql(glite_lbu_DBContext ctx_gen); +void glite_lbu_DBSetCapsMysql(glite_lbu_DBContext commmon_ctx, int caps); +int glite_lbu_TransactionMysql(glite_lbu_DBContext ctx_gen); +int glite_lbu_CommitMysql(glite_lbu_DBContext ctx_gen); +int glite_lbu_RollbackMysql(glite_lbu_DBContext ctx_gen); +int glite_lbu_FetchRowMysql(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results); +void glite_lbu_FreeStmtMysql(glite_lbu_Statement *stmt); +int glite_lbu_QueryIndicesMysql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names); +int glite_lbu_ExecSQLMysql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt); +int glite_lbu_QueryColumnsMysql(glite_lbu_Statement stmt_gen, char **cols); +int glite_lbu_PrepareStmtMysql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen); +int glite_lbu_ExecPreparedStmtMysql_v(glite_lbu_Statement stmt_gen, int n, va_list ap); +long int glite_lbu_LastidMysql(glite_lbu_Statement stmt_gen); + +glite_lbu_DBBackend_t mysql_backend = { + backend: GLITE_LBU_DB_BACKEND_MYSQL, + + initContext: glite_lbu_InitDBContextMysql, + freeContext: glite_lbu_FreeDBContextMysql, + connect: glite_lbu_DBConnectMysql, + close: glite_lbu_DBCloseMysql, + queryCaps: glite_lbu_DBQueryCapsMysql, + setCaps: glite_lbu_DBSetCapsMysql, + transaction: glite_lbu_TransactionMysql, + commit: glite_lbu_CommitMysql, + rollback: glite_lbu_RollbackMysql, + fetchRow: glite_lbu_FetchRowMysql, + freeStmt: glite_lbu_FreeStmtMysql, + queryIndices: glite_lbu_QueryIndicesMysql, + execSQL: glite_lbu_ExecSQLMysql, + queryColumns: glite_lbu_QueryColumnsMysql, + + timeToDB: glite_lbu_TimeToStr, + timestampToDB: glite_lbu_TimestampToStr, + DBToTime: glite_lbu_StrToTime, + DBToTimestamp: glite_lbu_StrToTimestamp, + + prepareStmt: glite_lbu_PrepareStmtMysql, + execPreparedStmt_v: glite_lbu_ExecPreparedStmtMysql_v, + lastid: glite_lbu_LastidMysql, +}; + +static mysql_module_t mysql_module = { lib: NULL, - lock: PTHREAD_MUTEX_INITIALIZER + lock: PTHREAD_MUTEX_INITIALIZER, }; -static int lbu_clrerr(glite_lbu_DBContext ctx); -static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...); -static int myerr(glite_lbu_DBContext ctx, const char *source, int line); -static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line); -static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry); -static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql); +static int myerr(glite_lbu_DBContextMysql ctx, const char *source, int line); +static int myerrstmt(glite_lbu_StatementMysql stmt, const char *source, int line); +static int myisokstmt(glite_lbu_StatementMysql stmt, const char *source, int line, int *retry); +static int db_connect(glite_lbu_DBContextMysql ctx, const char *cs, MYSQL **mysql); static void db_close(MYSQL *mysql); -static int transaction_test(glite_lbu_DBContext ctx); -static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned long *lengths, char **results); -static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results); +static int transaction_test(glite_lbu_DBContext ctx, int *caps); +static int FetchRowSimple(glite_lbu_DBContextMysql ctx, MYSQL_RES *result, unsigned long *lengths, char **results); +static int FetchRowPrepared(glite_lbu_DBContextMysql ctx, glite_lbu_StatementMysql stmt, unsigned int n, unsigned long *lengths, char **results); static void set_time(MYSQL_TIME *mtime, const double time); static void glite_lbu_DBCleanup(void); -static void glite_lbu_FreeStmt_int(glite_lbu_Statement stmt); +static void glite_lbu_FreeStmt_int(glite_lbu_StatementMysql stmt); /* ---- common ---- */ -int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc) { - if (text) *text = strdup(strerror(ctx->err.code)); - if (desc) { - if (ctx->err.desc) *desc = strdup(ctx->err.desc); - else *desc = NULL; - } - - return ctx->err.code; -} - -int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx) { +int glite_lbu_InitDBContextMysql(glite_lbu_DBContext *ctx_gen) { + glite_lbu_DBContextMysql ctx; int err = 0; - unsigned int ver_u; + int ver_u; - *ctx = calloc(1, sizeof **ctx); - if (!*ctx) return ENOMEM; + ctx = calloc(1, sizeof *ctx); + if (!ctx) return ENOMEM; + *ctx_gen = (glite_lbu_DBContext)ctx; - /* dynamic load the mysql library */ - pthread_mutex_lock(&db_handle.lock); - if (!db_handle.lib) { - db_handle.lib = dlopen(MYSQL_SONAME, RTLD_LAZY | RTLD_LOCAL); - if (!db_handle.lib) return ERR(*ctx, ENOENT, "dlopen(): " MYSQL_SONAME ": %s", dlerror()); + /* dynamic load the client library */ + pthread_mutex_lock(&mysql_module.lock); + if (!mysql_module.lib) { + mysql_module.lib = dlopen(MYSQL_SONAME, RTLD_LAZY | RTLD_LOCAL); + if (!mysql_module.lib) return ERR(ctx, ENOENT, "dlopen(): " MYSQL_SONAME ": %s", dlerror()); do { LOAD(mysql_init, "mysql_init"); LOAD(mysql_get_client_version, "mysql_get_client_version"); @@ -228,63 +257,70 @@ int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx) { LOAD(mysql_stmt_fetch_column, "mysql_stmt_fetch_column"); // check the runtime version - ver_u = db_handle.mysql_get_client_version(); + ver_u = mysql_module.mysql_get_client_version(); if (ver_u != MYSQL_VERSION_ID) { - fprintf(stderr,"Warning: MySQL library version mismatch (compiled '%lu', runtime '%lu')", MYSQL_VERSION_ID, ver_u); - syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%lu', runtime '%lu')", MYSQL_VERSION_ID, ver_u); + fprintf(stderr,"Warning: MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u); +#ifdef SYSLOG_H + syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u); +#endif } - pthread_mutex_unlock(&db_handle.lock); + pthread_mutex_unlock(&mysql_module.lock); atexit(glite_lbu_DBCleanup); } while(0); if (err) { - dlclose(db_handle.lib); - db_handle.lib = NULL; - pthread_mutex_unlock(&db_handle.lock); + dlclose(mysql_module.lib); + mysql_module.lib = NULL; + pthread_mutex_unlock(&mysql_module.lock); return err; } - } else pthread_mutex_unlock(&db_handle.lock); + } else pthread_mutex_unlock(&mysql_module.lock); return 0; } -void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) { +void glite_lbu_FreeDBContextMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + if (ctx) { assert(ctx->mysql == NULL); - free(ctx->err.desc); free(ctx); } } -int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) { +int glite_lbu_DBConnectMysql(glite_lbu_DBContext ctx_gen, const char *cs) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + if (db_connect(ctx, cs, &ctx->mysql) != 0 || - glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0 || - glite_lbu_ExecSQL(ctx, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", NULL) < 0) + glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0 || + glite_lbu_ExecSQLMysql(ctx_gen, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", NULL) < 0) return STATUS(ctx); else return 0; } -void glite_lbu_DBClose(glite_lbu_DBContext ctx) { +void glite_lbu_DBCloseMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + db_close(ctx->mysql); ctx->mysql = NULL; CLR_ERR(ctx); } -int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) { +int glite_lbu_DBQueryCapsMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; MYSQL *m = ctx->mysql; - int major,minor,sub,version,caps,origcaps; + int major,minor,sub,version,caps; const char *ver_s; - origcaps = ctx->caps; caps = 0; - ver_s = db_handle.mysql_get_server_info(m); + ver_s = mysql_module.mysql_get_server_info(m); if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub)) return ERR(ctx, EINVAL, "problem retreiving MySQL version"); version = 10000*major + 100*minor + sub; @@ -293,25 +329,25 @@ int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) { if (version >= GLITE_LBU_MYSQL_PREPARED_VERSION) caps |= GLITE_LBU_DB_CAP_PREPARED; CLR_ERR(ctx); - transaction_test(ctx); - if ((ctx->caps & GLITE_LBU_DB_CAP_TRANSACTIONS)) caps |= GLITE_LBU_DB_CAP_TRANSACTIONS; + transaction_test(ctx_gen, &caps); - ctx->caps = origcaps; if (STATUS(ctx) == 0) return caps; else return -1; } -void glite_lbu_DBSetCaps(glite_lbu_DBContext ctx, int caps) { - ctx->caps = caps; +void glite_lbu_DBSetCapsMysql(glite_lbu_DBContext ctx_gen, int caps) { + ctx_gen->caps = caps; } -int glite_lbu_Transaction(glite_lbu_DBContext ctx) { +int glite_lbu_TransactionMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + CLR_ERR(ctx); if (USE_TRANS(ctx)) { - if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=0", NULL) < 0) goto err; - if (glite_lbu_ExecSQL(ctx, "BEGIN", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=0", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "BEGIN", NULL) < 0) goto err; ctx->in_transaction = 1; } err: @@ -319,11 +355,13 @@ err: } -int glite_lbu_Commit(glite_lbu_DBContext ctx) { +int glite_lbu_CommitMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + CLR_ERR(ctx); if (USE_TRANS(ctx)) { - if (glite_lbu_ExecSQL(ctx, "COMMIT", NULL) < 0) goto err; - if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "COMMIT", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err; ctx->in_transaction = 0; } err: @@ -331,11 +369,13 @@ err: } -int glite_lbu_Rollback(glite_lbu_DBContext ctx) { +int glite_lbu_RollbackMysql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + CLR_ERR(ctx); if (USE_TRANS(ctx)) { - if (glite_lbu_ExecSQL(ctx, "ROLLBACK", NULL) < 0) goto err; - if (glite_lbu_ExecSQL(ctx, "SET AUTOCOMMIT=1", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "ROLLBACK", NULL) < 0) goto err; + if (glite_lbu_ExecSQLMysql(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0) goto err; ctx->in_transaction = 0; } err: @@ -343,31 +383,37 @@ err: } -int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) { +int glite_lbu_FetchRowMysql(glite_lbu_Statement stmt_gen, unsigned int n, unsigned long *lengths, char **results) { + glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen; + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)stmt->generic.ctx; + memset(results, 0, n * sizeof(*results)); - if (stmt->result) return FetchRowSimple(stmt->ctx, stmt->result, lengths, results); - else return FetchRowPrepared(stmt->ctx, stmt, n, lengths, results); + if (stmt->result) return FetchRowSimple(ctx, stmt->result, lengths, results); + else return FetchRowPrepared(ctx, stmt, n, lengths, results); } -static void glite_lbu_FreeStmt_int(glite_lbu_Statement stmt) { +static void glite_lbu_FreeStmt_int(glite_lbu_StatementMysql stmt) { if (stmt) { - if (stmt->result) db_handle.mysql_free_result(stmt->result); - if (stmt->stmt) db_handle.mysql_stmt_close(stmt->stmt); + if (stmt->result) mysql_module.mysql_free_result(stmt->result); + if (stmt->stmt) mysql_module.mysql_stmt_close(stmt->stmt); free(stmt->sql); } } -void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) { +void glite_lbu_FreeStmtMysql(glite_lbu_Statement *stmt_gen) { + glite_lbu_StatementMysql *stmt = (glite_lbu_StatementMysql*)stmt_gen; + glite_lbu_FreeStmt_int(*stmt); free(*stmt); *stmt = NULL; } -int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names) { - glite_lbu_Statement stmt = NULL; +int glite_lbu_QueryIndicesMysql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + glite_lbu_Statement stmt; size_t i,j,ret; @@ -386,18 +432,18 @@ int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***k Key_name = Seq_in_index = Column_name = Sub_part = -1; asprintf(&sql, "show index from %s", table); - if (glite_lbu_ExecSQL(ctx,sql,&stmt)<0) { + if (glite_lbu_ExecSQLMysql(ctx_gen,sql,&stmt)<0) { free(sql); return STATUS(ctx); } free(sql); - while ((ret = glite_lbu_FetchRow(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) { + while ((ret = glite_lbu_FetchRowMysql(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) { assert(ret <= (int)(sizeof showcol/sizeof showcol[0])); if (!col_names) { col_names = malloc(ret * sizeof col_names[0]); - glite_lbu_QueryColumns(stmt,col_names); + glite_lbu_QueryColumnsMysql(stmt,col_names); for (i=0; imysql, cmd)) { + if (mysql_module.mysql_query(ctx->mysql, cmd)) { /* error occured */ - switch (merr = db_handle.mysql_errno(ctx->mysql)) { + switch (merr = mysql_module.mysql_errno(ctx->mysql)) { case 0: break; case ER_DUP_ENTRY: - ERR(ctx, EEXIST, db_handle.mysql_error(ctx->mysql)); + ERR(ctx, EEXIST, mysql_module.mysql_error(ctx->mysql)); return -1; break; case CR_SERVER_LOST: case CR_SERVER_GONE_ERROR: if (ctx->in_transaction) { - ERR(ctx, ERESTART, db_handle.mysql_error(ctx->mysql)); + ERR(ctx, ERESTART, mysql_module.mysql_error(ctx->mysql)); return -1; } else if (retry_nr <= 0) do_reconnect = 1; break; case ER_LOCK_DEADLOCK: - ERR(ctx, EDEADLOCK, db_handle.mysql_error(ctx->mysql)); + ERR(ctx, EDEADLOCK, mysql_module.mysql_error(ctx->mysql)); return -1; break; default: @@ -519,24 +568,25 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem retry_nr++; } - if (stmt) { - *stmt = calloc(1, sizeof(**stmt)); - if (!*stmt) { + if (stmt_gen) { + stmt = calloc(1, sizeof(*stmt)); + if (!stmt) { ERR(ctx, ENOMEM, NULL); return -1; } - (**stmt).ctx = ctx; - (**stmt).result = db_handle.mysql_store_result(ctx->mysql); - if (!(**stmt).result) { - if (db_handle.mysql_errno(ctx->mysql)) { + stmt->generic.ctx = ctx_gen; + stmt->result = mysql_module.mysql_store_result(ctx->mysql); + if (!stmt->result) { + if (mysql_module.mysql_errno(ctx->mysql)) { MY_ERR(ctx); - *stmt = NULL; + free(stmt); return -1; } } + *stmt_gen = (glite_lbu_Statement)stmt; } else { - MYSQL_RES *r = db_handle.mysql_store_result(ctx->mysql); - db_handle.mysql_free_result(r); + MYSQL_RES *r = mysql_module.mysql_store_result(ctx->mysql); + mysql_module.mysql_free_result(r); } #ifdef LBS_DB_PROFILE pid = getpid(); @@ -551,102 +601,76 @@ int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statem fprintf(stderr,"[%d] %s\n[%d] %3ld.%06ld (sum: %3ld.%06ld)\n",pid,cmd,pid,end.tv_sec,end.tv_usec,sum.tv_sec,sum.tv_usec); #endif - return db_handle.mysql_affected_rows(ctx->mysql); + return mysql_module.mysql_affected_rows(ctx->mysql); } -int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols) +int glite_lbu_QueryColumnsMysql(glite_lbu_Statement stmt_gen, char **cols) { + glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen; int i = 0; MYSQL_FIELD *f; - CLR_ERR(stmt->ctx); - if (!stmt->result) return ERR(stmt->ctx, EINVAL, "QueryColumns implemented only for simple API"); - while ((f = db_handle.mysql_fetch_field(stmt->result))) cols[i++] = f->name; + CLR_ERR(stmt->generic.ctx); + if (!stmt->result) return ERR(stmt->generic.ctx, ENOTSUP, "QueryColumns implemented only for simple API"); + while ((f = mysql_module.mysql_fetch_field(stmt->result))) cols[i++] = f->name; return i == 0; } -void glite_lbu_TimeToDB(time_t t, char **str) { - struct tm *tm = gmtime(&t); - - asprintf(str,"'%4d-%02d-%02d %02d:%02d:%02d'",tm->tm_year+1900,tm->tm_mon+1, - tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); -} - - -void glite_lbu_TimestampToDB(double t, char **str) { - time_t tsec = t; - struct tm *tm = gmtime(&tsec); - - t = t - tsec + tm->tm_sec; - asprintf(str,"'%4d-%02d-%02d %02d:%02d:%02.09f'",tm->tm_year+1900,tm->tm_mon+1, - tm->tm_mday,tm->tm_hour,tm->tm_min,t); -} - - -time_t glite_lbu_DBToTime(const char *str) { - struct tm tm; - - memset(&tm,0,sizeof(tm)); - setenv("TZ","UTC",1); tzset(); - sscanf(str,"%4d-%02d-%02d %02d:%02d:%02d", - &tm.tm_year,&tm.tm_mon,&tm.tm_mday, - &tm.tm_hour,&tm.tm_min,&tm.tm_sec); - tm.tm_year -= 1900; - tm.tm_mon--; - - return mktime(&tm); -} - /* ---- prepared --- */ -int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmt) { +int glite_lbu_PrepareStmtMysql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen) { + glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen; + glite_lbu_StatementMysql stmt; int ret, retry; MYSQL_RES *meta; // init - *stmt = calloc(1, sizeof(**stmt)); - (*stmt)->ctx = ctx; + stmt = calloc(1, sizeof(*stmt)); + stmt->generic.ctx = ctx_gen; + *stmt_gen = NULL; // create the SQL command - if (((*stmt)->stmt = db_handle.mysql_stmt_init(ctx->mysql)) == NULL) - return MY_ERRSTMT(*stmt); + if ((stmt->stmt = mysql_module.mysql_stmt_init(ctx->mysql)) == NULL) + return STATUS(ctx); // prepare the SQL command retry = 1; do { - db_handle.mysql_stmt_prepare((*stmt)->stmt, sql, strlen(sql)); - ret = MY_ISOKSTMT(*stmt, &retry); + mysql_module.mysql_stmt_prepare(stmt->stmt, sql, strlen(sql)); + ret = MY_ISOKSTMT(stmt, &retry); } while (ret == 0); if (ret == -1) goto failed; // number of fields (0 for no results) - if ((meta = db_handle.mysql_stmt_result_metadata((*stmt)->stmt)) != NULL) { - (*stmt)->nrfields = db_handle.mysql_num_fields(meta); - db_handle.mysql_free_result(meta); + if ((meta = mysql_module.mysql_stmt_result_metadata(stmt->stmt)) != NULL) { + stmt->nrfields = mysql_module.mysql_num_fields(meta); + mysql_module.mysql_free_result(meta); } else - (*stmt)->nrfields = 0; + stmt->nrfields = 0; // remember the command - (*stmt)->sql = strdup(sql); + stmt->sql = strdup(sql); + *stmt_gen = (glite_lbu_Statement)stmt; return CLR_ERR(ctx); failed: - glite_lbu_FreeStmt(stmt); + glite_lbu_FreeStmt_int(stmt); + free(stmt); return STATUS(ctx); } -int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) { +int glite_lbu_ExecPreparedStmtMysql_v(glite_lbu_Statement stmt_gen, int n, va_list ap) { + glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen; int i, prepare_retry; glite_lbu_DBType type; char *pchar; int *pint; long int *plint; MYSQL_TIME *ptime; - glite_lbu_DBContext ctx; int ret, retry; MYSQL_BIND *binds = NULL; void **data = NULL; @@ -723,7 +747,7 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) { do { // bind parameters if (n) { - if (db_handle.mysql_stmt_bind_param(stmt->stmt, binds) != 0) { + if (mysql_module.mysql_stmt_bind_param(stmt->stmt, binds) != 0) { MY_ERRSTMT(stmt); ret = -1; goto statement_failed; @@ -731,19 +755,18 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) { } // run - ctx = stmt->ctx; retry = 1; do { - db_handle.mysql_stmt_execute(stmt->stmt); + mysql_module.mysql_stmt_execute(stmt->stmt); ret = MY_ISOKSTMT(stmt, &retry); } while (ret == 0); statement_failed: if (ret == -1) { - if (db_handle.mysql_stmt_errno(stmt->stmt) == ER_UNKNOWN_STMT_HANDLER) { + if (mysql_module.mysql_stmt_errno(stmt->stmt) == ER_UNKNOWN_STMT_HANDLER) { // expired the prepared command ==> restore it - if (glite_lbu_PrepareStmt(stmt->ctx, stmt->sql, &newstmt) == -1) goto failed; + if (glite_lbu_PrepareStmtMysql(stmt->generic.ctx, stmt->sql, &newstmt) == -1) goto failed; glite_lbu_FreeStmt_int(stmt); - memcpy(stmt, newstmt, sizeof(struct glite_lbu_Statement_s)); + memcpy(stmt, newstmt, sizeof(struct glite_lbu_StatementMysql_s)); prepare_retry--; ret = 0; } else goto failed; @@ -753,7 +776,7 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) { // result retry = 1; do { - db_handle.mysql_stmt_store_result(stmt->stmt); + mysql_module.mysql_stmt_store_result(stmt->stmt); ret = MY_ISOKSTMT(stmt, &retry); } while (ret == 0); if (ret == -1) goto failed; @@ -765,8 +788,8 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) { free(binds); free(lens); } - CLR_ERR(ctx); - return db_handle.mysql_stmt_affected_rows(stmt->stmt); + CLR_ERR(stmt->generic.ctx); + return mysql_module.mysql_stmt_affected_rows(stmt->stmt); failed: for (i = 0; i < n; i++) free(data[i]); @@ -777,167 +800,30 @@ failed: } -int glite_lbu_ExecPreparedStmt(glite_lbu_Statement stmt, int n, ...) { - va_list ap; - int retval; - - va_start(ap, n); - retval = glite_lbu_ExecPreparedStmt_v(stmt, n, ap); - va_end(ap); - - return retval; -} - - -int glite_lbu_bufferedInsertInit(glite_lbu_DBContext ctx, glite_lbu_bufInsert *bi, const char *table_name, long size_limit, long record_limit, const char *columns) -{ - *bi = calloc(1, sizeof(*bi)); - (*bi)->ctx = ctx; - (*bi)->table_name = strdup(table_name); - (*bi)->columns = strdup(columns); - (*bi)->rec_num = 0; - (*bi)->rec_size = 0; - (*bi)->rows = calloc(record_limit, sizeof(*((*bi)->rows)) ); - (*bi)->size_limit = size_limit; - (*bi)->record_limit = record_limit; - - return CLR_ERR(ctx); -} - - -static int flush_bufferd_insert(glite_lbu_bufInsert bi) -{ - char *stmt, *vals, *temp; - long i; - - - if (!bi->rec_num) - return STATUS(bi->ctx); - - asprintf(&vals,"(%s)", bi->rows[0]); - for (i=1; i < bi->rec_num; i++) { - // XXX: use string add (preallocated memory) - asprintf(&temp,"%s,(%s)", vals, bi->rows[i]); - free(vals); vals = temp; temp = NULL; - free(bi->rows[i]); - bi->rows[i] = NULL; - } - - trio_asprintf(&stmt, "insert into %|Ss(%|Ss) values %s;", - bi->table_name, bi->columns, vals); - - if (glite_lbu_ExecSQL(bi->ctx,stmt,NULL) < 0) { - if (STATUS(bi->ctx) == EEXIST) - CLR_ERR(bi->ctx); - } - - /* reset bi counters */ - bi->rec_size = 0; - bi->rec_num = 0; - - free(vals); - free(stmt); - - return STATUS(bi->ctx); -} - - -int glite_lbu_bufferedInsert(glite_lbu_bufInsert bi, const char *row) -{ - bi->rows[bi->rec_num++] = strdup(row); - bi->rec_size += strlen(row); - - if ((bi->size_limit && bi->rec_size >= bi->size_limit) || - (bi->record_limit && bi->rec_num >= bi->record_limit)) - { - if (flush_bufferd_insert(bi)) - return STATUS(bi->ctx); - } - - return CLR_ERR(bi->ctx); -} - - -static void free_buffered_insert(glite_lbu_bufInsert bi) { - long i; - - free(bi->table_name); - free(bi->columns); - for (i=0; i < bi->rec_num; i++) { - free(bi->rows[i]); - } - free(bi->rows); -} - - -int glite_lbu_bufferedInsertClose(glite_lbu_bufInsert bi) -{ - if (flush_bufferd_insert(bi)) - return STATUS(bi->ctx); - free_buffered_insert(bi); - - return CLR_ERR(bi->ctx); -} - - -long int glite_lbu_Lastid(glite_lbu_Statement stmt) { +long int glite_lbu_LastidMysql(glite_lbu_Statement stmt_gen) { + glite_lbu_StatementMysql stmt = (glite_lbu_StatementMysql)stmt_gen; my_ulonglong i; - CLR_ERR(stmt->ctx); - i = db_handle.mysql_stmt_insert_id(stmt->stmt); + CLR_ERR(stmt_gen->ctx); + i = mysql_module.mysql_stmt_insert_id(stmt->stmt); assert(i < ((unsigned long int)-1) >> 1); return (long int)i; } /* - * helping compatibility function: clear error from the context - */ -static int lbu_clrerr(glite_lbu_DBContext ctx) { - ctx->err.code = 0; - if (ctx->err.desc) { - free(ctx->err.desc); - ctx->err.desc = NULL; - } - return 0; -} - - -/* - * helping compatibility function: sets error on the context - */ -static int lbu_err(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...) { - va_list ap; - - if (code) { - ctx->err.code = code; - free(ctx->err.desc); - if (desc) { - va_start(ap, desc); - vasprintf(&ctx->err.desc, desc, ap); - va_end(ap); - } else - ctx->err.desc = NULL; - dprintf(ctx, "[db %d] %s:%d %s\n", getpid(), func, line, desc ? ctx->err.desc : ""); - return code; - } else - return ctx->err.code; -} - - -/* * helping function: find oud mysql error and sets on the context */ -static int myerr(glite_lbu_DBContext ctx, const char *source, int line) { - return lbu_err(ctx, EIO, source, line, db_handle.mysql_error(ctx->mysql)); +static int myerr(glite_lbu_DBContextMysql ctx, const char *source, int line) { + return glite_lbu_DBSetError(&ctx->generic, EIO, source, line, mysql_module.mysql_error(ctx->mysql)); } /* * helping function: find oud mysql stmt error and sets on the context */ -static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line) { - return lbu_err(stmt->ctx, EIO, source, line, db_handle.mysql_stmt_error(stmt->stmt)); +static int myerrstmt(glite_lbu_StatementMysql stmt, const char *source, int line) { + return glite_lbu_DBSetError(stmt->generic.ctx, EIO, source, line, mysql_module.mysql_stmt_error(stmt->stmt)); } @@ -948,13 +834,13 @@ static int myerrstmt(glite_lbu_Statement stmt, const char *source, int line) { * \return 0 retry * \return 1 OK */ -static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, int *retry) { - switch (db_handle.mysql_stmt_errno(stmt->stmt)) { +static int myisokstmt(glite_lbu_StatementMysql stmt, const char *source, int line, int *retry) { + switch (mysql_module.mysql_stmt_errno(stmt->stmt)) { case 0: return 1; break; case ER_DUP_ENTRY: - lbu_err(stmt->ctx, EEXIST, source, line, db_handle.mysql_stmt_error(stmt->stmt)); + glite_lbu_DBSetError(stmt->generic.ctx, EEXIST, source, line, mysql_module.mysql_stmt_error(stmt->stmt)); return -1; break; case CR_SERVER_LOST: @@ -978,7 +864,7 @@ static int myisokstmt(glite_lbu_Statement stmt, const char *source, int line, in /* * mysql connect */ -static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) { +static int db_connect(glite_lbu_DBContextMysql ctx, const char *cs, MYSQL **mysql) { char *buf = NULL; char *host,*user,*pw,*db; char *slash,*at,*colon; @@ -992,12 +878,12 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) { if (!cs) return ERR(ctx, EINVAL, "connect string not specified"); - if (!(*mysql = db_handle.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL); + if (!(*mysql = mysql_module.mysql_init(NULL))) return ERR(ctx, ENOMEM, NULL); - db_handle.mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my"); + mysql_module.mysql_options(*mysql, MYSQL_READ_DEFAULT_FILE, "my"); #if MYSQL_VERSION_ID >= 50013 /* XXX: may result in weird behaviour in the middle of transaction */ - db_handle.mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect); + mysql_module.mysql_options(*mysql, MYSQL_OPT_RECONNECT, &reconnect); #endif host = user = pw = db = NULL; @@ -1023,19 +909,15 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) { /* ljocha: CLIENT_FOUND_ROWS added to make authorization check * working in update_notif(). * Hope it does not break anything else */ - if (!db_handle.mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) { - char *desc; - free(buf); + if (!mysql_module.mysql_real_connect(*mysql,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) { ret = MY_ERR(ctx); - desc = ctx->err.desc; - ctx->err.desc = NULL; - glite_lbu_DBClose(ctx); - ctx->err.desc = desc; - return ctx->err.code = ret; + db_close(*mysql); + *mysql = NULL; + free(buf); + return ret; } free(buf); - ctx->cs = cs; return CLR_ERR(ctx); } @@ -1044,39 +926,39 @@ static int db_connect(glite_lbu_DBContext ctx, const char *cs, MYSQL **mysql) { * mysql close */ static void db_close(MYSQL *mysql) { - if (mysql) db_handle.mysql_close(mysql); + if (mysql) mysql_module.mysql_close(mysql); } /* * test transactions capability: */ -static int transaction_test(glite_lbu_DBContext ctx) { +static int transaction_test(glite_lbu_DBContext ctx, int *caps) { glite_lbu_Statement stmt; char *table[1] = { NULL }, *res[2] = { NULL, NULL }, *cmd = NULL; int retval; - ctx->caps &= ~GLITE_LBU_DB_CAP_TRANSACTIONS; + (*caps) &= ~GLITE_LBU_DB_CAP_TRANSACTIONS; - if ((retval = glite_lbu_ExecSQL(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRow(stmt, 1, NULL, table) < 0) goto quit; - glite_lbu_FreeStmt(&stmt); + if ((retval = glite_lbu_ExecSQLMysql(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRowMysql(stmt, 1, NULL, table) < 0) goto quit; + glite_lbu_FreeStmtMysql(&stmt); trio_asprintf(&cmd, "SHOW CREATE TABLE %|Ss", table[0]); - if (glite_lbu_ExecSQL(ctx, cmd, &stmt) <= 0 || (retval = glite_lbu_FetchRow(stmt, 2, NULL, res)) < 0 ) goto quit; + if (glite_lbu_ExecSQLMysql(ctx, cmd, &stmt) <= 0 || (retval = glite_lbu_FetchRowMysql(stmt, 2, NULL, res)) < 0 ) goto quit; if (retval != 2 || strcmp(res[0], table[0])) { ERR(ctx, EIO, "unexpected show create result"); goto quit; } if (strstr(res[1],"ENGINE=InnoDB")) - ctx->caps |= GLITE_LBU_DB_CAP_TRANSACTIONS; + (*caps) |= GLITE_LBU_DB_CAP_TRANSACTIONS; #ifdef LBS_DB_PROFILE fprintf(stderr, "[%d] use_transactions = %d\n", getpid(), USE_TRANS(ctx)); #endif quit: - glite_lbu_FreeStmt(&stmt); + glite_lbu_FreeStmtMysql(&stmt); free(table[0]); free(res[0]); free(res[1]); @@ -1088,22 +970,22 @@ quit: /* * simple version of the fetch */ -static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned long *lengths, char **results) { +static int FetchRowSimple(glite_lbu_DBContextMysql ctx, MYSQL_RES *result, unsigned long *lengths, char **results) { MYSQL_ROW row; unsigned int nr, i; unsigned long *len; CLR_ERR(ctx); - if (!(row = db_handle.mysql_fetch_row(result))) { - if (db_handle.mysql_errno((MYSQL *) ctx->mysql)) { + if (!(row = mysql_module.mysql_fetch_row(result))) { + if (mysql_module.mysql_errno((MYSQL *) ctx->mysql)) { MY_ERR(ctx); return -1; } else return 0; } - nr = db_handle.mysql_num_fields(result); - len = db_handle.mysql_fetch_lengths(result); + nr = mysql_module.mysql_num_fields(result); + len = mysql_module.mysql_fetch_lengths(result); for (i=0; istmt, binds) != 0) goto failedstmt; + if (mysql_module.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt; // fetch data, all can be truncated retry = 1; do { - switch(db_handle.mysql_stmt_fetch(stmt->stmt)) { + switch(mysql_module.mysql_stmt_fetch(stmt->stmt)) { #ifdef MYSQL_DATA_TRUNCATED case MYSQL_DATA_TRUNCATED: #endif @@ -1179,7 +1061,7 @@ static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, u retry = 1; do { - switch (db_handle.mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) { + switch (mysql_module.mysql_stmt_fetch_column(stmt->stmt, binds + i, i, fetched)) { case 0: ret = 1; break; case 1: ret = MY_ISOKSTMT(stmt, &retry); break; case MYSQL_NO_DATA: ret = 0; goto quit; /* it's OK */ @@ -1228,12 +1110,12 @@ static void set_time(MYSQL_TIME *mtime, const double time) { static void glite_lbu_DBCleanup(void) { - pthread_mutex_lock(&db_handle.lock); - if (db_handle.lib) { - dlclose(db_handle.lib); - db_handle.lib = NULL; + pthread_mutex_lock(&mysql_module.lock); + if (mysql_module.lib) { + dlclose(mysql_module.lib); + mysql_module.lib = NULL; } - pthread_mutex_unlock(&db_handle.lock); + pthread_mutex_unlock(&mysql_module.lock); } diff --git a/org.glite.lbjp-common.db/src/db-pg.c b/org.glite.lbjp-common.db/src/db-pg.c new file mode 100644 index 0000000..7d8d743 --- /dev/null +++ b/org.glite.lbjp-common.db/src/db-pg.c @@ -0,0 +1,603 @@ +/** + * Simple postgres module with org.glite.lbjp-common.db interface. + * + * PostgreSQL limitations: + * - prepared commands requires server >= 8.2 + * - binary data need to be handled manually (libpq limitation) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "glite/lbu/trio.h" +#include "db.h" +#include "db-int.h" + +#ifdef WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +#define DB_CONNECT_TIMEOUT "20" + +#ifdef LOG + #define lprintf(FMT...) fprintf(stdout, "[db-pg] %s: ", __FUNCTION__); fprintf(stdout, ##FMT); +#else + #define lprintf(FMT...) +#endif + +#define set_error(CTX, CODE, DESC...) glite_lbu_DBSetError((glite_lbu_DBContext)(CTX), (CODE), __FUNCTION__, __LINE__, ##DESC) + +#define LOAD(SYM, SYM2) if ((*(void **)(&psql_module.SYM) = dlsym(psql_module.lib, SYM2)) == NULL) { \ + err = set_error(ctx, ENOENT, "can't load symbol '%s' from psql library (%s)", SYM2, dlerror()); \ + break; \ +} + + +struct glite_lbu_DBContextPsql_s { + struct glite_lbu_DBContext_s generic; + PGconn *conn; + int prepared_counts[4]; +}; +typedef struct glite_lbu_DBContextPsql_s *glite_lbu_DBContextPsql; + +struct glite_lbu_StatementPsql_s { + glite_lbu_Statement_t generic; + PGresult *res; + int row, nrows; + char *sql, *name; +}; +typedef struct glite_lbu_StatementPsql_s *glite_lbu_StatementPsql; + +typedef struct { + void *lib; + pthread_mutex_t lock; + + /* functions from 8.3.8 client library version (libpq-fe.h) */ + PGconn *STDCALL(*PQconnectdb)(const char *conninfo); + ConnStatusType STDCALL(*PQstatus)(const PGconn *conn); + void STDCALL(*PQfinish)(PGconn *conn); + char *STDCALL(*PQerrorMessage)(const PGconn *conn); + int STDCALL(*PQnfields)(const PGresult *res); + char *STDCALL(*PQgetvalue)(const PGresult *res, int tup_num, int field_num); + int STDCALL(*PQgetlength)(const PGresult *res, int tup_num, int field_num); + void STDCALL(*PQclear)(PGresult *res); + PGresult *STDCALL(*PQexec)(PGconn *conn, const char *query); + ExecStatusType STDCALL(*PQresultStatus)(const PGresult *res); + char *STDCALL(*PQresultErrorMessage)(const PGresult *res); + char *STDCALL(*PQcmdTuples)(PGresult *res); + int STDCALL(*PQntuples)(const PGresult *res); + char *STDCALL(*PQfname)(const PGresult *res, int field_num); + size_t STDCALL(*PQescapeStringConn)(PGconn *conn, + char *to, const char *from, size_t length, + int *error); + void STDCALL(*PQfreemem)(void *ptr); +} psql_module_t; + + +static void glite_lbu_DBCleanup(void); + +/* backend module declaration */ +int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen); +void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen); +int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs); +void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen); +int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen); +void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext commmon_ctx, int caps); +int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen); +int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen); +int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen); +int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results); +void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt); +//int glite_lbu_QueryIndicesPsql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names); +int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt); +int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols); +int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen); +int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap); +//long int glite_lbu_LastidPsql(glite_lbu_Statement stmt_gen); + +glite_lbu_DBBackend_t psql_backend = { + backend: GLITE_LBU_DB_BACKEND_PSQL, + + initContext: glite_lbu_InitDBContextPsql, + freeContext: glite_lbu_FreeDBContextPsql, + connect: glite_lbu_DBConnectPsql, + close: glite_lbu_DBClosePsql, + queryCaps: glite_lbu_DBQueryCapsPsql, + setCaps: glite_lbu_DBSetCapsPsql, + transaction: glite_lbu_TransactionPsql, + commit: glite_lbu_CommitPsql, + rollback: glite_lbu_RollbackPsql, + fetchRow: glite_lbu_FetchRowPsql, + freeStmt: glite_lbu_FreeStmtPsql, + queryIndices: NULL /*glite_lbu_QueryIndicesPsql*/, + execSQL: glite_lbu_ExecSQLPsql, + queryColumns: glite_lbu_QueryColumnsPsql, + + timeToDB: glite_lbu_TimeToStr, + timestampToDB: glite_lbu_TimestampToStr, + DBToTime: glite_lbu_StrToTime, + DBToTimestamp: glite_lbu_StrToTimestamp, + + prepareStmt: glite_lbu_PrepareStmtPsql, + execPreparedStmt_v: glite_lbu_ExecPreparedStmtPsql_v, + lastid: NULL/*glite_lbu_LastidPsql*/, +}; + +static psql_module_t psql_module = { + lib: NULL, + lock: PTHREAD_MUTEX_INITIALIZER, +}; + + +/* nicer identifiers in PREPARE/EXECUTE commands */ +static const char *prepared_names[4] = {"select", "update", "insert", "other"}; + + +int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen) { + glite_lbu_DBContextPsql ctx; + int err = 0; + + ctx = calloc(1, sizeof *ctx); + if (!ctx) return ENOMEM; + *ctx_gen = (glite_lbu_DBContext)ctx; + + /* dynamic load of the client library */ + pthread_mutex_lock(&psql_module.lock); + if (!psql_module.lib) { + psql_module.lib = dlopen(PSQL_SONAME, RTLD_LAZY | RTLD_LOCAL); + if (!psql_module.lib) return set_error(ctx, ENOENT, "dlopen(): " PSQL_SONAME ": %s", dlerror()); + do { + LOAD(PQconnectdb, "PQconnectdb"); + LOAD(PQstatus, "PQstatus"); + LOAD(PQfinish, "PQfinish"); + LOAD(PQerrorMessage, "PQerrorMessage"); + LOAD(PQnfields, "PQnfields"); + LOAD(PQgetvalue, "PQgetvalue"); + LOAD(PQgetlength, "PQgetlength"); + LOAD(PQclear, "PQclear"); + LOAD(PQexec, "PQexec"); + LOAD(PQresultStatus, "PQresultStatus"); + LOAD(PQresultErrorMessage, "PQresultErrorMessage"); + LOAD(PQcmdTuples, "PQcmdTuples"); + LOAD(PQntuples, "PQntuples"); + LOAD(PQfname, "PQfname"); + LOAD(PQescapeStringConn, "PQescapeStringConn"); + LOAD(PQfreemem, "PQfreemem"); + + pthread_mutex_unlock(&psql_module.lock); + atexit(glite_lbu_DBCleanup); + } while(0); + + if (err) { + dlclose(psql_module.lib); + psql_module.lib = NULL; + pthread_mutex_unlock(&psql_module.lock); + return err; + } + } else pthread_mutex_unlock(&psql_module.lock); + + return 0; +} + + +void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + + if (ctx) { + assert(ctx->conn == NULL); + free(ctx); + } +} + + +int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + char *buf, *slash, *at, *colon, *host, *user, *pw, *db, *pgcsbuf, *pgcs; + char *err; + + host = user = pw = db = NULL; + buf = strdup(cs); + slash = strchr(buf,'/'); + at = strrchr(buf,'@'); + colon = strrchr(buf,':'); + if (!slash || !at || !colon) { + free(buf); + return set_error(ctx, EINVAL, "Invalid DB connect string"); + } + *slash = *at = *colon = 0; + host = at+1; + user = buf; + pw = slash+1; + db = colon+1; + + trio_asprintf(&pgcsbuf, "host='%|Ss' dbname='%|Ss' user='%|Ss' password='%|Ss' connect_timeout=" DB_CONNECT_TIMEOUT, host, db, user, pw); + /* simulate socket acces via localhost similar to MySQL */ + if (strcmp(host, "localhost") == 0) pgcs = pgcsbuf + strlen("host='localhost' "); + else pgcs = pgcsbuf; + free(buf); + + lprintf("connection string = %s\n", pgcs); + ctx->conn = psql_module.PQconnectdb(pgcs); + free(pgcsbuf); + if (!ctx->conn) return ENOMEM; + + + + if (psql_module.PQstatus(ctx->conn) != CONNECTION_OK) { + asprintf(&err, "Can't connect, %s", psql_module.PQerrorMessage(ctx->conn)); + psql_module.PQfinish(ctx->conn); + ctx->conn = NULL; + set_error(ctx, EIO, err); + free(err); + return EIO; + } + + return 0; +} + + +void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + + if (ctx->conn) { + psql_module.PQfinish(ctx->conn); + ctx->conn = NULL; + } +} + + +int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + glite_lbu_Statement stmt; + int major, minor, sub, version; + int has_prepared = 0; + char *res = NULL; + + if (glite_lbu_ExecSQLPsql(ctx_gen, "SHOW server_version", &stmt) == -1) return -1; + switch (glite_lbu_FetchRowPsql(stmt, 1, NULL, &res)) { + case 1: + break; + case -1: + has_prepared = -1; + goto quit; + default: + goto quit; + } + if (sscanf(res, "%d.%d.%d", &major, &minor, &sub) != 3) { + set_error(ctx, EIO, "can't parse PostgreSQL version string"); + goto quit; + } + version = 10000*major + 100*minor + sub; + has_prepared = version >= 80200 ? GLITE_LBU_DB_CAP_PREPARED : 0; + +quit: + free(res); + glite_lbu_FreeStmtPsql(&stmt); + return has_prepared; +} + + +void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext ctx_gen, int caps) { + ctx_gen->caps = caps; +} + + +int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) { + return 0; +} + + +int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) { + return 0; +} + + +int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) { + return 0; +} + + +int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt_gen, unsigned int maxn, unsigned long *lengths, char **results) { + glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen; + unsigned int i, n; + char *s; + + if (stmt->row >= stmt->nrows) return 0; + + n = psql_module.PQnfields(stmt->res); + if (n <= 0) { + set_error(stmt->generic.ctx, EINVAL, "Result set w/o columns"); + return -1; + } + if (n > maxn) { + set_error(stmt->generic.ctx, EINVAL, "Not enough room for the result"); + return -1; + } + for (i = 0; i < n; i++) { + /* sanity check for internal error (NULL when invalid row) */ + s = psql_module.PQgetvalue(stmt->res, stmt->row, i) ? : ""; + results[i] = strdup(s); + if (lengths) lengths[i] = strlen(s); + } + + stmt->row++; + return n; +} + + +void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt_gen) { + glite_lbu_DBContextPsql ctx; + glite_lbu_StatementPsql stmt; + char *sql; + + if (!*stmt_gen) return; + stmt = (glite_lbu_StatementPsql)(*stmt_gen); + ctx = (glite_lbu_DBContextPsql)stmt->generic.ctx; + if (stmt->res) psql_module.PQclear(stmt->res); + if (stmt->name) { + asprintf(&sql, "DEALLOCATE %s", stmt->name); + stmt->res = psql_module.PQexec(ctx->conn, sql); + free(sql); + psql_module.PQclear(stmt->res); + } + free(stmt->name); + free(stmt->sql); + free(stmt); + *stmt_gen = NULL; +} + + +int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt_out) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + glite_lbu_StatementPsql stmt = NULL; + int status, n; + char *nstr, *errmsg, *pos; + PGresult *res; + + lprintf("command = %s\n", cmd); + if (stmt_out) *stmt_out = NULL; + if ((res = psql_module.PQexec(ctx->conn, cmd)) == NULL) { + ctx->generic.err.code = ENOMEM; + return -1; + } + + status = psql_module.PQresultStatus(res); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + errmsg = psql_module.PQresultErrorMessage(res); + if (errmsg) { + errmsg = strdup(errmsg); + if ((pos = strrchr(errmsg, '\n')) != NULL) pos[0] = '\0'; + } + set_error(ctx, EIO, errmsg); + free(errmsg); + psql_module.PQclear(res); + return -1; + } + + nstr = psql_module.PQcmdTuples(res); + if (nstr && nstr[0]) n = atoi(nstr); + else n = psql_module.PQntuples(res); + if (stmt_out) { + stmt = calloc(1, sizeof(*stmt)); + stmt->generic.ctx = ctx_gen; + stmt->res = res; + stmt->nrows = n; + *stmt_out = (glite_lbu_Statement)stmt; + } else { + psql_module.PQclear(res); + } + return n; +} + + +int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols) { + glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen; + int n, i; + + n = psql_module.PQnfields(stmt->res); + for (i = 0; i < n; i++) { + cols[i] = psql_module.PQfname(stmt->res, i); + } + return -1; +} + + +int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_out) { + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen; + int i, retval = -1; + const char *selectp, *updatep, *insertp, *minp; + char *sqlPrep = NULL, *s = NULL; + glite_lbu_StatementPsql stmt; + PGresult *res = NULL; + + // init + stmt = calloc(1, sizeof(*stmt)); + stmt->generic.ctx = ctx_gen; + stmt->sql = strdup(sql); + + // name of the prepared command used as ID in postgres + i = -1; + minp = stmt->sql + strlen(stmt->sql); + selectp = strcasestr(stmt->sql, "SELECT"); + updatep = strcasestr(stmt->sql, "UPDATE"); + insertp = strcasestr(stmt->sql, "INSERT"); + if (selectp && selectp < minp) { minp = selectp; i = 0; } + if (updatep && updatep < minp) { minp = updatep; i = 1; } + if (insertp && insertp < minp) { minp = insertp; i = 2; } + if (i == -1 || minp[0] == '\0') i = 3; + asprintf(&stmt->name, "%s%d", prepared_names[i], ++ctx->prepared_counts[i]); + + asprintf(&sqlPrep, "PREPARE %s AS %s", stmt->name, stmt->sql); + lprintf("prepare = %s\n", sqlPrep); + res = psql_module.PQexec(ctx->conn, sqlPrep); + if (psql_module.PQresultStatus(res) != PGRES_COMMAND_OK) { + asprintf(&s, "error preparing command: %s", psql_module.PQerrorMessage(ctx->conn)); + set_error(ctx, EIO, s); + free(s); s = NULL; + goto quit; + } + + *stmt_out = (glite_lbu_Statement)stmt; + retval = 0; + +quit: + free(sqlPrep); + if (res) psql_module.PQclear(res); + if (!retval) return 0; + + free(stmt->name); + free(stmt->sql); + free(stmt); + return retval; +} + + +int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap) { + glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen; + glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)stmt_gen->ctx; + int i, retval = -1, status; + char **tmpdata = NULL; + char *sql = NULL, *s, *nstr; + size_t data_len = 0; + + glite_lbu_DBType type; + + if (!stmt || !stmt->sql || !stmt->name) + return set_error(ctx, EINVAL, "PrepareStmt() not called"); + + if (stmt->res) { + psql_module.PQclear(stmt->res); + stmt->res = NULL; + } + + // gather parameters + if (n) { + tmpdata = calloc(n, sizeof(char *)); + } + for (i = 0; i < n; i++) { + type = va_arg(ap, glite_lbu_DBType); + + switch(type) { + case GLITE_LBU_DB_TYPE_TINYINT: + asprintf(&tmpdata[i], "%d", va_arg(ap, int)); + break; + + case GLITE_LBU_DB_TYPE_INT: + asprintf(&tmpdata[i], "%ld", va_arg(ap, long int)); + break; + + case GLITE_LBU_DB_TYPE_TINYBLOB: + case GLITE_LBU_DB_TYPE_TINYTEXT: + case GLITE_LBU_DB_TYPE_BLOB: + case GLITE_LBU_DB_TYPE_TEXT: + case GLITE_LBU_DB_TYPE_MEDIUMBLOB: + case GLITE_LBU_DB_TYPE_MEDIUMTEXT: + case GLITE_LBU_DB_TYPE_LONGBLOB: + case GLITE_LBU_DB_TYPE_LONGTEXT: { + char *tmp, *s; + unsigned long binary_len; + + s = va_arg(ap, char *); + binary_len = va_arg(ap, unsigned long); + lprintf("blob, len = %lu, ptr = %p\n", binary_len, s); + if (s) { + tmp = malloc(2*binary_len + 1); + psql_module.PQescapeStringConn(ctx->conn, tmp, s, binary_len, NULL); + asprintf(&tmpdata[i], "'%s'", tmp); + lprintf("escaped: '%s'\n", tmpdata[i]); + free(tmp); + } else + tmpdata[i] = strdup("NULL"); + break; + } + + + case GLITE_LBU_DB_TYPE_VARCHAR: + case GLITE_LBU_DB_TYPE_CHAR: + s = va_arg(ap, char *); + if (s) trio_asprintf(&tmpdata[i], "'%|Ss'", s); + else tmpdata[i] = strdup("NULL"); + break; + + case GLITE_LBU_DB_TYPE_DATE: + case GLITE_LBU_DB_TYPE_TIME: + case GLITE_LBU_DB_TYPE_DATETIME: + glite_lbu_TimeToStr(va_arg(ap, time_t), &tmpdata[i]); + break; + + case GLITE_LBU_DB_TYPE_TIMESTAMP: + glite_lbu_TimestampToStr(va_arg(ap, double), &tmpdata[i]); + break; + + case GLITE_LBU_DB_TYPE_NULL: + tmpdata[i] = strdup("NULL"); + break; + + case GLITE_LBU_DB_TYPE_BOOLEAN: + tmpdata[i] = strdup(va_arg(ap, int) ? "true" : "false"); + break; + + default: + lprintf("unknown type %d\n", type); + set_error(ctx, EINVAL, "unimplemented type"); + goto quit; + } + + data_len += strlen(tmpdata[i]); + } + + asprintf(&sql, "EXECUTE %s", stmt->name); + s = realloc(sql, strlen(sql) + (2 * n - 2) + strlen(" ()") + data_len + 1); + if (!s) goto quit; + sql = s; + for (i = 0; i < n; i++) { + strcat(sql, i ? ", " : " (" ); s += 2; + strcat(sql, tmpdata[i]); + } + if (n) strcat(sql, ")"); + + lprintf("exec prepared: n = %d, sql = '%s'\n", n, sql); + stmt->res = psql_module.PQexec(ctx->conn, sql); + status = psql_module.PQresultStatus(stmt->res); + if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + asprintf(&s, "error executing prepared command '%s' parameters '%s': %s", stmt->sql, sql, psql_module.PQerrorMessage(ctx->conn)); + set_error(ctx, EIO, s); + free(s); s = NULL; + goto quit; + } + nstr = psql_module.PQcmdTuples(stmt->res); + //lprintf("cmdtuples: '%s'\n", nstr); + if (nstr && nstr[0]) retval = atoi(nstr); + else retval = psql_module.PQntuples(stmt->res); + stmt->nrows = retval; + stmt->row = 0; + //lprintf("ntuples/retval: %d\n", retval); + +quit: + for (i = 0; i < n; i++) free(tmpdata[i]); + free(tmpdata); + free(sql); + return retval; +} + + +static void glite_lbu_DBCleanup(void) { + pthread_mutex_lock(&psql_module.lock); + if (psql_module.lib) { + dlclose(psql_module.lib); + psql_module.lib = NULL; + } + pthread_mutex_unlock(&psql_module.lock); +} -- 1.8.2.3