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 "
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;
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;
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;
}
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 */
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;*/
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)));
*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);
*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");
*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");
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);
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);
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);
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);
}
{
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);
}
*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);
{
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);
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]);
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;
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)) {
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;
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',
);
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/ ],
);
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/ ],
'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/ ],
%need_jars = (
'jobid.api-java' => [ qw/commons-codec/ ],
+ 'lb.client-java' => [ qw/commons-lang/ ],
);
for my $jar (keys %need_jars) {
/ ],
'lb.client-java' => [ qw/
lb.types:B
+ lb.ws-interface:B
jobid.api-java
/ ],
'lb.common' => [ qw/
'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// ],
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
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/],
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
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
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}
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
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
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
// 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;
}
* mysqladmin -u root -p create test
* mysql -u root -p -e 'GRANT ALL on test.* to testuser@localhost'
*
+ * Or postgres:
+ *
+ * createuser -U postgres testuser
+ * createdb -U postgres --owner testuser test
+ *
* Use CS environment variable when using different user/pwd@machine:dbname.
*/
#include <stdlib.h>
#include <string.h>
-#include "glite/lbu/db.h"
+#include <glite/lbu/trio.h>
+
+#include "db.h"
#define CS "testuser/@localhost:test"
-#if defined(DB_BACKEND) && DB_BACKEND == postgresql
+
+#ifdef PSQL_BACKEND
#define CREATE_CMD "CREATE TABLE \"data\" (\n\
\"id\" INTEGER NOT NULL,\n\
\"user\" VARCHAR(32) NOT NULL,\n\
PRIMARY KEY (\"id\")\n\
)"
#define AMP "\""
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES ($1, $2, $3)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = $1"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_PSQL
+
#else
+
#define CREATE_CMD "CREATE TABLE data (\n\
`id` INT NOT NULL,\n\
`user` VARCHAR(32) NOT NULL,\n\
INDEX(`user`)\n\
) engine=innodb"
#define AMP "`"
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_MYSQL
#endif
+
#define DROP_CMD "DROP TABLE " AMP "data" AMP
#define INSERT_TRIO_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (%d, %s, %s)"
#define SELECT_TRIO_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = '%s'"
-#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
-#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
#define dprintf(ARGS) { printf("%s: ", name); printf ARGS; }
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
// 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
#ifndef GLITE_LBU_DB_H
#define GLITE_LBU_DB_H
-#ident "$Header$"
-
-
#include <time.h>
#include <stdarg.h>
* 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.
+ *
* @{
*/
/**
+ * 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
/**
+ * 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);
/**
* \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);
/**
* \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);
/**
*
* 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);
/**
#! /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
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
-#ident "$Header$"
-
#include <sys/types.h>
#ifdef LBS_DB_PROFILE
#include <sys/time.h>
#include <stdarg.h>
#include <dlfcn.h>
#include <pthread.h>
+#ifdef HAVE_SYSLOG_H
#include <syslog.h>
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#ifndef STDCALL
+#define STDCALL __stdcall
+#endif
+#else
+#define STDCALL
+#endif
#include <mysql.h>
#include <mysqld_error.h>
#include "glite/lbu/trio.h"
#include "db.h"
+#include "db-int.h"
#define GLITE_LBU_MYSQL_INDEX_VERSION 40001
#define GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH 256
#endif
-#define CLR_ERR(CTX) lbu_clrerr((CTX))
-#define ERR(CTX, CODE, ARGS...) lbu_err((CTX), (CODE), __FUNCTION__, __LINE__, ##ARGS)
-#define STATUS(CTX) ((CTX)->err.code)
+#define CLR_ERR(CTX) glite_lbu_DBClearError((glite_lbu_DBContext)(CTX))
+#define ERR(CTX, CODE, ARGS...) glite_lbu_DBSetError((glite_lbu_DBContext)((CTX)), (CODE), __FUNCTION__, __LINE__, ##ARGS)
+#define STATUS(CTX) glite_lbu_DBError((glite_lbu_DBContext)((CTX)), NULL, NULL)
#define MY_ERR(CTX) myerr((CTX), __FUNCTION__, __LINE__)
#define MY_ERRSTMT(STMT) myerrstmt((STMT), __FUNCTION__, __LINE__)
#define MY_ISOKSTMT(STMT, RETRY) myisokstmt((STMT), __FUNCTION__, __LINE__, (RETRY))
-#define USE_TRANS(CTX) ((CTX->caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0)
-#define LOAD(SYM, SYM2) if ((*(void **)(&db_handle.SYM) = dlsym(db_handle.lib, SYM2)) == NULL) { \
- err = ERR(*ctx, ENOENT, "can't load symbol %s from mysql library (%s)", SYM2, dlerror()); \
+#define USE_TRANS(CTX) ((CTX->generic.caps & GLITE_LBU_DB_CAP_TRANSACTIONS) != 0)
+#define LOAD(SYM, SYM2) if ((*(void **)(&mysql_module.SYM) = dlsym(mysql_module.lib, SYM2)) == NULL) { \
+ err = ERR(ctx, ENOENT, "can't load symbol '%s' from mysql library (%s)", SYM2, dlerror()); \
break; \
}
-#define dprintf(CTX, FMT...) if (CTX->caps & GLITE_LBU_DB_CAP_ERRORS) fprintf(stderr, ##FMT)
-struct glite_lbu_DBContext_s {
- MYSQL *mysql;
- const char *cs;
- int caps;
- struct {
- int code;
- char *desc;
- } err;
+struct glite_lbu_DBContextMysql_s {
+ struct glite_lbu_DBContext_s generic;
int in_transaction; /* this flag is set whenever we are in DB transaction */
+ MYSQL *mysql;
};
+typedef struct glite_lbu_DBContextMysql_s *glite_lbu_DBContextMysql;
-struct glite_lbu_Statement_s {
- glite_lbu_DBContext ctx;
+struct glite_lbu_StatementMysql_s {
+ glite_lbu_Statement_t generic;
/* for simple commands */
MYSQL_RES *result;
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;
/*
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");
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;
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:
}
-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:
}
-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:
}
-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;
Key_name = Seq_in_index = Column_name = Sub_part = -1;
asprintf(&sql, "show index from %s", table);
- if (glite_lbu_ExecSQL(ctx,sql,&stmt)<0) {
+ if (glite_lbu_ExecSQLMysql(ctx_gen,sql,&stmt)<0) {
free(sql);
return STATUS(ctx);
}
free(sql);
- while ((ret = glite_lbu_FetchRow(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) {
+ while ((ret = glite_lbu_FetchRowMysql(stmt,sizeof(showcol)/sizeof(showcol[0]),NULL,showcol)) > 0) {
assert(ret <= (int)(sizeof showcol/sizeof showcol[0]));
if (!col_names) {
col_names = malloc(ret * sizeof col_names[0]);
- glite_lbu_QueryColumns(stmt,col_names);
+ glite_lbu_QueryColumnsMysql(stmt,col_names);
for (i=0; i<ret; i++)
if (!strcasecmp(col_names[i],"Key_name")) Key_name = i;
else if (!strcasecmp(col_names[i],"Seq_in_index")) Seq_in_index = i;
for (i = 0; i<ret; i++) free(showcol[i]);
}
- glite_lbu_FreeStmt(&stmt);
+ glite_lbu_FreeStmtMysql(&stmt);
free(cols);
free(col_names);
/* ---- simple ---- */
-int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt) {
+int glite_lbu_ExecSQLMysql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt_gen) {
+ glite_lbu_DBContextMysql ctx = (glite_lbu_DBContextMysql)ctx_gen;
+ glite_lbu_StatementMysql stmt;
int merr;
int retry_nr = 0;
int do_reconnect = 0;
CLR_ERR(ctx);
- if (stmt) *stmt = NULL;
+ if (stmt_gen) *stmt_gen = NULL;
+ stmt = NULL;
#ifdef LBS_DB_PROFILE
gettimeofday(&start,NULL);
while (retry_nr == 0 || do_reconnect) {
do_reconnect = 0;
- if (db_handle.mysql_query(ctx->mysql, cmd)) {
+ if (mysql_module.mysql_query(ctx->mysql, cmd)) {
/* error occured */
- switch (merr = db_handle.mysql_errno(ctx->mysql)) {
+ switch (merr = mysql_module.mysql_errno(ctx->mysql)) {
case 0:
break;
case ER_DUP_ENTRY:
- ERR(ctx, EEXIST, db_handle.mysql_error(ctx->mysql));
+ ERR(ctx, EEXIST, mysql_module.mysql_error(ctx->mysql));
return -1;
break;
case CR_SERVER_LOST:
case CR_SERVER_GONE_ERROR:
if (ctx->in_transaction) {
- ERR(ctx, ERESTART, db_handle.mysql_error(ctx->mysql));
+ ERR(ctx, ERESTART, mysql_module.mysql_error(ctx->mysql));
return -1;
}
else if (retry_nr <= 0)
do_reconnect = 1;
break;
case ER_LOCK_DEADLOCK:
- ERR(ctx, EDEADLOCK, db_handle.mysql_error(ctx->mysql));
+ ERR(ctx, EDEADLOCK, mysql_module.mysql_error(ctx->mysql));
return -1;
break;
default:
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();
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;
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;
}
// 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;
// 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;
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]);
}
-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));
}
* \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:
/*
* 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;
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;
/* 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);
}
* 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]);
/*
* simple version of the fetch
*/
-static int FetchRowSimple(glite_lbu_DBContext ctx, MYSQL_RES *result, unsigned long *lengths, char **results) {
+static int FetchRowSimple(glite_lbu_DBContextMysql ctx, MYSQL_RES *result, unsigned long *lengths, char **results) {
MYSQL_ROW row;
unsigned int nr, i;
unsigned long *len;
CLR_ERR(ctx);
- if (!(row = db_handle.mysql_fetch_row(result))) {
- if (db_handle.mysql_errno((MYSQL *) ctx->mysql)) {
+ if (!(row = mysql_module.mysql_fetch_row(result))) {
+ if (mysql_module.mysql_errno((MYSQL *) ctx->mysql)) {
MY_ERR(ctx);
return -1;
} else return 0;
}
- nr = db_handle.mysql_num_fields(result);
- len = db_handle.mysql_fetch_lengths(result);
+ nr = mysql_module.mysql_num_fields(result);
+ len = mysql_module.mysql_fetch_lengths(result);
for (i=0; i<nr; i++) {
if (lengths) lengths[i] = len[i];
if (len[i]) {
/*
* prepared version of the fetch
*/
-static int FetchRowPrepared(glite_lbu_DBContext ctx, glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+static int FetchRowPrepared(glite_lbu_DBContextMysql ctx, glite_lbu_StatementMysql stmt, unsigned int n, unsigned long *lengths, char **results) {
int ret, retry;
unsigned int i;
MYSQL_BIND *binds = NULL;
binds[i].length = &lengths[i];
binds[i].buffer = results[i] = calloc(1, GLITE_LBU_DEFAULT_RESULT_BUFFER_LENGTH);
}
- if (db_handle.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
+ if (mysql_module.mysql_stmt_bind_result(stmt->stmt, binds) != 0) goto failedstmt;
// fetch data, all can be truncated
retry = 1;
do {
- switch(db_handle.mysql_stmt_fetch(stmt->stmt)) {
+ switch(mysql_module.mysql_stmt_fetch(stmt->stmt)) {
#ifdef MYSQL_DATA_TRUNCATED
case MYSQL_DATA_TRUNCATED:
#endif
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 */
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);
}
--- /dev/null
+/**
+ * Simple postgres module with org.glite.lbjp-common.db interface.
+ *
+ * PostgreSQL limitations:
+ * - prepared commands requires server >= 8.2
+ * - binary data need to be handled manually (libpq limitation)
+ */
+
+#include <sys/types.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libpq-fe.h>
+
+#include "glite/lbu/trio.h"
+#include "db.h"
+#include "db-int.h"
+
+#ifdef WIN32
+#define STDCALL __stdcall
+#else
+#define STDCALL
+#endif
+
+#define DB_CONNECT_TIMEOUT "20"
+
+#ifdef LOG
+ #define lprintf(FMT...) fprintf(stdout, "[db-pg] %s: ", __FUNCTION__); fprintf(stdout, ##FMT);
+#else
+ #define lprintf(FMT...)
+#endif
+
+#define set_error(CTX, CODE, DESC...) glite_lbu_DBSetError((glite_lbu_DBContext)(CTX), (CODE), __FUNCTION__, __LINE__, ##DESC)
+
+#define LOAD(SYM, SYM2) if ((*(void **)(&psql_module.SYM) = dlsym(psql_module.lib, SYM2)) == NULL) { \
+ err = set_error(ctx, ENOENT, "can't load symbol '%s' from psql library (%s)", SYM2, dlerror()); \
+ break; \
+}
+
+
+struct glite_lbu_DBContextPsql_s {
+ struct glite_lbu_DBContext_s generic;
+ PGconn *conn;
+ int prepared_counts[4];
+};
+typedef struct glite_lbu_DBContextPsql_s *glite_lbu_DBContextPsql;
+
+struct glite_lbu_StatementPsql_s {
+ glite_lbu_Statement_t generic;
+ PGresult *res;
+ int row, nrows;
+ char *sql, *name;
+};
+typedef struct glite_lbu_StatementPsql_s *glite_lbu_StatementPsql;
+
+typedef struct {
+ void *lib;
+ pthread_mutex_t lock;
+
+ /* functions from 8.3.8 client library version (libpq-fe.h) */
+ PGconn *STDCALL(*PQconnectdb)(const char *conninfo);
+ ConnStatusType STDCALL(*PQstatus)(const PGconn *conn);
+ void STDCALL(*PQfinish)(PGconn *conn);
+ char *STDCALL(*PQerrorMessage)(const PGconn *conn);
+ int STDCALL(*PQnfields)(const PGresult *res);
+ char *STDCALL(*PQgetvalue)(const PGresult *res, int tup_num, int field_num);
+ int STDCALL(*PQgetlength)(const PGresult *res, int tup_num, int field_num);
+ void STDCALL(*PQclear)(PGresult *res);
+ PGresult *STDCALL(*PQexec)(PGconn *conn, const char *query);
+ ExecStatusType STDCALL(*PQresultStatus)(const PGresult *res);
+ char *STDCALL(*PQresultErrorMessage)(const PGresult *res);
+ char *STDCALL(*PQcmdTuples)(PGresult *res);
+ int STDCALL(*PQntuples)(const PGresult *res);
+ char *STDCALL(*PQfname)(const PGresult *res, int field_num);
+ size_t STDCALL(*PQescapeStringConn)(PGconn *conn,
+ char *to, const char *from, size_t length,
+ int *error);
+ void STDCALL(*PQfreemem)(void *ptr);
+} psql_module_t;
+
+
+static void glite_lbu_DBCleanup(void);
+
+/* backend module declaration */
+int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen);
+void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs);
+void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen);
+void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext commmon_ctx, int caps);
+int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen);
+int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results);
+void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt);
+//int glite_lbu_QueryIndicesPsql(glite_lbu_DBContext ctx_gen, const char *table, char ***key_names, char ****column_names);
+int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt);
+int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols);
+int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_gen);
+int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap);
+//long int glite_lbu_LastidPsql(glite_lbu_Statement stmt_gen);
+
+glite_lbu_DBBackend_t psql_backend = {
+ backend: GLITE_LBU_DB_BACKEND_PSQL,
+
+ initContext: glite_lbu_InitDBContextPsql,
+ freeContext: glite_lbu_FreeDBContextPsql,
+ connect: glite_lbu_DBConnectPsql,
+ close: glite_lbu_DBClosePsql,
+ queryCaps: glite_lbu_DBQueryCapsPsql,
+ setCaps: glite_lbu_DBSetCapsPsql,
+ transaction: glite_lbu_TransactionPsql,
+ commit: glite_lbu_CommitPsql,
+ rollback: glite_lbu_RollbackPsql,
+ fetchRow: glite_lbu_FetchRowPsql,
+ freeStmt: glite_lbu_FreeStmtPsql,
+ queryIndices: NULL /*glite_lbu_QueryIndicesPsql*/,
+ execSQL: glite_lbu_ExecSQLPsql,
+ queryColumns: glite_lbu_QueryColumnsPsql,
+
+ timeToDB: glite_lbu_TimeToStr,
+ timestampToDB: glite_lbu_TimestampToStr,
+ DBToTime: glite_lbu_StrToTime,
+ DBToTimestamp: glite_lbu_StrToTimestamp,
+
+ prepareStmt: glite_lbu_PrepareStmtPsql,
+ execPreparedStmt_v: glite_lbu_ExecPreparedStmtPsql_v,
+ lastid: NULL/*glite_lbu_LastidPsql*/,
+};
+
+static psql_module_t psql_module = {
+ lib: NULL,
+ lock: PTHREAD_MUTEX_INITIALIZER,
+};
+
+
+/* nicer identifiers in PREPARE/EXECUTE commands */
+static const char *prepared_names[4] = {"select", "update", "insert", "other"};
+
+
+int glite_lbu_InitDBContextPsql(glite_lbu_DBContext *ctx_gen) {
+ glite_lbu_DBContextPsql ctx;
+ int err = 0;
+
+ ctx = calloc(1, sizeof *ctx);
+ if (!ctx) return ENOMEM;
+ *ctx_gen = (glite_lbu_DBContext)ctx;
+
+ /* dynamic load of the client library */
+ pthread_mutex_lock(&psql_module.lock);
+ if (!psql_module.lib) {
+ psql_module.lib = dlopen(PSQL_SONAME, RTLD_LAZY | RTLD_LOCAL);
+ if (!psql_module.lib) return set_error(ctx, ENOENT, "dlopen(): " PSQL_SONAME ": %s", dlerror());
+ do {
+ LOAD(PQconnectdb, "PQconnectdb");
+ LOAD(PQstatus, "PQstatus");
+ LOAD(PQfinish, "PQfinish");
+ LOAD(PQerrorMessage, "PQerrorMessage");
+ LOAD(PQnfields, "PQnfields");
+ LOAD(PQgetvalue, "PQgetvalue");
+ LOAD(PQgetlength, "PQgetlength");
+ LOAD(PQclear, "PQclear");
+ LOAD(PQexec, "PQexec");
+ LOAD(PQresultStatus, "PQresultStatus");
+ LOAD(PQresultErrorMessage, "PQresultErrorMessage");
+ LOAD(PQcmdTuples, "PQcmdTuples");
+ LOAD(PQntuples, "PQntuples");
+ LOAD(PQfname, "PQfname");
+ LOAD(PQescapeStringConn, "PQescapeStringConn");
+ LOAD(PQfreemem, "PQfreemem");
+
+ pthread_mutex_unlock(&psql_module.lock);
+ atexit(glite_lbu_DBCleanup);
+ } while(0);
+
+ if (err) {
+ dlclose(psql_module.lib);
+ psql_module.lib = NULL;
+ pthread_mutex_unlock(&psql_module.lock);
+ return err;
+ }
+ } else pthread_mutex_unlock(&psql_module.lock);
+
+ return 0;
+}
+
+
+void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+
+ if (ctx) {
+ assert(ctx->conn == NULL);
+ free(ctx);
+ }
+}
+
+
+int glite_lbu_DBConnectPsql(glite_lbu_DBContext ctx_gen, const char *cs) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+ char *buf, *slash, *at, *colon, *host, *user, *pw, *db, *pgcsbuf, *pgcs;
+ char *err;
+
+ host = user = pw = db = NULL;
+ buf = strdup(cs);
+ slash = strchr(buf,'/');
+ at = strrchr(buf,'@');
+ colon = strrchr(buf,':');
+ if (!slash || !at || !colon) {
+ free(buf);
+ return set_error(ctx, EINVAL, "Invalid DB connect string");
+ }
+ *slash = *at = *colon = 0;
+ host = at+1;
+ user = buf;
+ pw = slash+1;
+ db = colon+1;
+
+ trio_asprintf(&pgcsbuf, "host='%|Ss' dbname='%|Ss' user='%|Ss' password='%|Ss' connect_timeout=" DB_CONNECT_TIMEOUT, host, db, user, pw);
+ /* simulate socket acces via localhost similar to MySQL */
+ if (strcmp(host, "localhost") == 0) pgcs = pgcsbuf + strlen("host='localhost' ");
+ else pgcs = pgcsbuf;
+ free(buf);
+
+ lprintf("connection string = %s\n", pgcs);
+ ctx->conn = psql_module.PQconnectdb(pgcs);
+ free(pgcsbuf);
+ if (!ctx->conn) return ENOMEM;
+
+
+
+ if (psql_module.PQstatus(ctx->conn) != CONNECTION_OK) {
+ asprintf(&err, "Can't connect, %s", psql_module.PQerrorMessage(ctx->conn));
+ psql_module.PQfinish(ctx->conn);
+ ctx->conn = NULL;
+ set_error(ctx, EIO, err);
+ free(err);
+ return EIO;
+ }
+
+ return 0;
+}
+
+
+void glite_lbu_DBClosePsql(glite_lbu_DBContext ctx_gen) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+
+ if (ctx->conn) {
+ psql_module.PQfinish(ctx->conn);
+ ctx->conn = NULL;
+ }
+}
+
+
+int glite_lbu_DBQueryCapsPsql(glite_lbu_DBContext ctx_gen) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+ glite_lbu_Statement stmt;
+ int major, minor, sub, version;
+ int has_prepared = 0;
+ char *res = NULL;
+
+ if (glite_lbu_ExecSQLPsql(ctx_gen, "SHOW server_version", &stmt) == -1) return -1;
+ switch (glite_lbu_FetchRowPsql(stmt, 1, NULL, &res)) {
+ case 1:
+ break;
+ case -1:
+ has_prepared = -1;
+ goto quit;
+ default:
+ goto quit;
+ }
+ if (sscanf(res, "%d.%d.%d", &major, &minor, &sub) != 3) {
+ set_error(ctx, EIO, "can't parse PostgreSQL version string");
+ goto quit;
+ }
+ version = 10000*major + 100*minor + sub;
+ has_prepared = version >= 80200 ? GLITE_LBU_DB_CAP_PREPARED : 0;
+
+quit:
+ free(res);
+ glite_lbu_FreeStmtPsql(&stmt);
+ return has_prepared;
+}
+
+
+void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext ctx_gen, int caps) {
+ ctx_gen->caps = caps;
+}
+
+
+int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+ return 0;
+}
+
+
+int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+ return 0;
+}
+
+
+int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
+ return 0;
+}
+
+
+int glite_lbu_FetchRowPsql(glite_lbu_Statement stmt_gen, unsigned int maxn, unsigned long *lengths, char **results) {
+ glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+ unsigned int i, n;
+ char *s;
+
+ if (stmt->row >= stmt->nrows) return 0;
+
+ n = psql_module.PQnfields(stmt->res);
+ if (n <= 0) {
+ set_error(stmt->generic.ctx, EINVAL, "Result set w/o columns");
+ return -1;
+ }
+ if (n > maxn) {
+ set_error(stmt->generic.ctx, EINVAL, "Not enough room for the result");
+ return -1;
+ }
+ for (i = 0; i < n; i++) {
+ /* sanity check for internal error (NULL when invalid row) */
+ s = psql_module.PQgetvalue(stmt->res, stmt->row, i) ? : "";
+ results[i] = strdup(s);
+ if (lengths) lengths[i] = strlen(s);
+ }
+
+ stmt->row++;
+ return n;
+}
+
+
+void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt_gen) {
+ glite_lbu_DBContextPsql ctx;
+ glite_lbu_StatementPsql stmt;
+ char *sql;
+
+ if (!*stmt_gen) return;
+ stmt = (glite_lbu_StatementPsql)(*stmt_gen);
+ ctx = (glite_lbu_DBContextPsql)stmt->generic.ctx;
+ if (stmt->res) psql_module.PQclear(stmt->res);
+ if (stmt->name) {
+ asprintf(&sql, "DEALLOCATE %s", stmt->name);
+ stmt->res = psql_module.PQexec(ctx->conn, sql);
+ free(sql);
+ psql_module.PQclear(stmt->res);
+ }
+ free(stmt->name);
+ free(stmt->sql);
+ free(stmt);
+ *stmt_gen = NULL;
+}
+
+
+int glite_lbu_ExecSQLPsql(glite_lbu_DBContext ctx_gen, const char *cmd, glite_lbu_Statement *stmt_out) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+ glite_lbu_StatementPsql stmt = NULL;
+ int status, n;
+ char *nstr, *errmsg, *pos;
+ PGresult *res;
+
+ lprintf("command = %s\n", cmd);
+ if (stmt_out) *stmt_out = NULL;
+ if ((res = psql_module.PQexec(ctx->conn, cmd)) == NULL) {
+ ctx->generic.err.code = ENOMEM;
+ return -1;
+ }
+
+ status = psql_module.PQresultStatus(res);
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+ errmsg = psql_module.PQresultErrorMessage(res);
+ if (errmsg) {
+ errmsg = strdup(errmsg);
+ if ((pos = strrchr(errmsg, '\n')) != NULL) pos[0] = '\0';
+ }
+ set_error(ctx, EIO, errmsg);
+ free(errmsg);
+ psql_module.PQclear(res);
+ return -1;
+ }
+
+ nstr = psql_module.PQcmdTuples(res);
+ if (nstr && nstr[0]) n = atoi(nstr);
+ else n = psql_module.PQntuples(res);
+ if (stmt_out) {
+ stmt = calloc(1, sizeof(*stmt));
+ stmt->generic.ctx = ctx_gen;
+ stmt->res = res;
+ stmt->nrows = n;
+ *stmt_out = (glite_lbu_Statement)stmt;
+ } else {
+ psql_module.PQclear(res);
+ }
+ return n;
+}
+
+
+int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols) {
+ glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+ int n, i;
+
+ n = psql_module.PQnfields(stmt->res);
+ for (i = 0; i < n; i++) {
+ cols[i] = psql_module.PQfname(stmt->res, i);
+ }
+ return -1;
+}
+
+
+int glite_lbu_PrepareStmtPsql(glite_lbu_DBContext ctx_gen, const char *sql, glite_lbu_Statement *stmt_out) {
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+ int i, retval = -1;
+ const char *selectp, *updatep, *insertp, *minp;
+ char *sqlPrep = NULL, *s = NULL;
+ glite_lbu_StatementPsql stmt;
+ PGresult *res = NULL;
+
+ // init
+ stmt = calloc(1, sizeof(*stmt));
+ stmt->generic.ctx = ctx_gen;
+ stmt->sql = strdup(sql);
+
+ // name of the prepared command used as ID in postgres
+ i = -1;
+ minp = stmt->sql + strlen(stmt->sql);
+ selectp = strcasestr(stmt->sql, "SELECT");
+ updatep = strcasestr(stmt->sql, "UPDATE");
+ insertp = strcasestr(stmt->sql, "INSERT");
+ if (selectp && selectp < minp) { minp = selectp; i = 0; }
+ if (updatep && updatep < minp) { minp = updatep; i = 1; }
+ if (insertp && insertp < minp) { minp = insertp; i = 2; }
+ if (i == -1 || minp[0] == '\0') i = 3;
+ asprintf(&stmt->name, "%s%d", prepared_names[i], ++ctx->prepared_counts[i]);
+
+ asprintf(&sqlPrep, "PREPARE %s AS %s", stmt->name, stmt->sql);
+ lprintf("prepare = %s\n", sqlPrep);
+ res = psql_module.PQexec(ctx->conn, sqlPrep);
+ if (psql_module.PQresultStatus(res) != PGRES_COMMAND_OK) {
+ asprintf(&s, "error preparing command: %s", psql_module.PQerrorMessage(ctx->conn));
+ set_error(ctx, EIO, s);
+ free(s); s = NULL;
+ goto quit;
+ }
+
+ *stmt_out = (glite_lbu_Statement)stmt;
+ retval = 0;
+
+quit:
+ free(sqlPrep);
+ if (res) psql_module.PQclear(res);
+ if (!retval) return 0;
+
+ free(stmt->name);
+ free(stmt->sql);
+ free(stmt);
+ return retval;
+}
+
+
+int glite_lbu_ExecPreparedStmtPsql_v(glite_lbu_Statement stmt_gen, int n, va_list ap) {
+ glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
+ glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)stmt_gen->ctx;
+ int i, retval = -1, status;
+ char **tmpdata = NULL;
+ char *sql = NULL, *s, *nstr;
+ size_t data_len = 0;
+
+ glite_lbu_DBType type;
+
+ if (!stmt || !stmt->sql || !stmt->name)
+ return set_error(ctx, EINVAL, "PrepareStmt() not called");
+
+ if (stmt->res) {
+ psql_module.PQclear(stmt->res);
+ stmt->res = NULL;
+ }
+
+ // gather parameters
+ if (n) {
+ tmpdata = calloc(n, sizeof(char *));
+ }
+ for (i = 0; i < n; i++) {
+ type = va_arg(ap, glite_lbu_DBType);
+
+ switch(type) {
+ case GLITE_LBU_DB_TYPE_TINYINT:
+ asprintf(&tmpdata[i], "%d", va_arg(ap, int));
+ break;
+
+ case GLITE_LBU_DB_TYPE_INT:
+ asprintf(&tmpdata[i], "%ld", va_arg(ap, long int));
+ break;
+
+ case GLITE_LBU_DB_TYPE_TINYBLOB:
+ case GLITE_LBU_DB_TYPE_TINYTEXT:
+ case GLITE_LBU_DB_TYPE_BLOB:
+ case GLITE_LBU_DB_TYPE_TEXT:
+ case GLITE_LBU_DB_TYPE_MEDIUMBLOB:
+ case GLITE_LBU_DB_TYPE_MEDIUMTEXT:
+ case GLITE_LBU_DB_TYPE_LONGBLOB:
+ case GLITE_LBU_DB_TYPE_LONGTEXT: {
+ char *tmp, *s;
+ unsigned long binary_len;
+
+ s = va_arg(ap, char *);
+ binary_len = va_arg(ap, unsigned long);
+ lprintf("blob, len = %lu, ptr = %p\n", binary_len, s);
+ if (s) {
+ tmp = malloc(2*binary_len + 1);
+ psql_module.PQescapeStringConn(ctx->conn, tmp, s, binary_len, NULL);
+ asprintf(&tmpdata[i], "'%s'", tmp);
+ lprintf("escaped: '%s'\n", tmpdata[i]);
+ free(tmp);
+ } else
+ tmpdata[i] = strdup("NULL");
+ break;
+ }
+
+
+ case GLITE_LBU_DB_TYPE_VARCHAR:
+ case GLITE_LBU_DB_TYPE_CHAR:
+ s = va_arg(ap, char *);
+ if (s) trio_asprintf(&tmpdata[i], "'%|Ss'", s);
+ else tmpdata[i] = strdup("NULL");
+ break;
+
+ case GLITE_LBU_DB_TYPE_DATE:
+ case GLITE_LBU_DB_TYPE_TIME:
+ case GLITE_LBU_DB_TYPE_DATETIME:
+ glite_lbu_TimeToStr(va_arg(ap, time_t), &tmpdata[i]);
+ break;
+
+ case GLITE_LBU_DB_TYPE_TIMESTAMP:
+ glite_lbu_TimestampToStr(va_arg(ap, double), &tmpdata[i]);
+ break;
+
+ case GLITE_LBU_DB_TYPE_NULL:
+ tmpdata[i] = strdup("NULL");
+ break;
+
+ case GLITE_LBU_DB_TYPE_BOOLEAN:
+ tmpdata[i] = strdup(va_arg(ap, int) ? "true" : "false");
+ break;
+
+ default:
+ lprintf("unknown type %d\n", type);
+ set_error(ctx, EINVAL, "unimplemented type");
+ goto quit;
+ }
+
+ data_len += strlen(tmpdata[i]);
+ }
+
+ asprintf(&sql, "EXECUTE %s", stmt->name);
+ s = realloc(sql, strlen(sql) + (2 * n - 2) + strlen(" ()") + data_len + 1);
+ if (!s) goto quit;
+ sql = s;
+ for (i = 0; i < n; i++) {
+ strcat(sql, i ? ", " : " (" ); s += 2;
+ strcat(sql, tmpdata[i]);
+ }
+ if (n) strcat(sql, ")");
+
+ lprintf("exec prepared: n = %d, sql = '%s'\n", n, sql);
+ stmt->res = psql_module.PQexec(ctx->conn, sql);
+ status = psql_module.PQresultStatus(stmt->res);
+ if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+ asprintf(&s, "error executing prepared command '%s' parameters '%s': %s", stmt->sql, sql, psql_module.PQerrorMessage(ctx->conn));
+ set_error(ctx, EIO, s);
+ free(s); s = NULL;
+ goto quit;
+ }
+ nstr = psql_module.PQcmdTuples(stmt->res);
+ //lprintf("cmdtuples: '%s'\n", nstr);
+ if (nstr && nstr[0]) retval = atoi(nstr);
+ else retval = psql_module.PQntuples(stmt->res);
+ stmt->nrows = retval;
+ stmt->row = 0;
+ //lprintf("ntuples/retval: %d\n", retval);
+
+quit:
+ for (i = 0; i < n; i++) free(tmpdata[i]);
+ free(tmpdata);
+ free(sql);
+ return retval;
+}
+
+
+static void glite_lbu_DBCleanup(void) {
+ pthread_mutex_lock(&psql_module.lock);
+ if (psql_module.lib) {
+ dlclose(psql_module.lib);
+ psql_module.lib = NULL;
+ }
+ pthread_mutex_unlock(&psql_module.lock);
+}