Finish postgress backend module:
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 29 Oct 2009 20:07:39 +0000 (20:07 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 29 Oct 2009 20:07:39 +0000 (20:07 +0000)
- dynamic client library opening
- using internal DB module API
- tests

org.glite.lbjp-common.db/Makefile
org.glite.lbjp-common.db/examples/db_expire.c
org.glite.lbjp-common.db/examples/db_test.c
org.glite.lbjp-common.db/src/db-pg.c
org.glite.lbjp-common.db/src/db.c

index 738e8dc..989598d 100644 (file)
@@ -97,7 +97,10 @@ libglite_lbu_dbtest.la: check_soname ${LTESTOBJS}
 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
@@ -113,7 +116,7 @@ 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:
 
@@ -150,7 +153,7 @@ install: all
 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 $<
@@ -158,11 +161,18 @@ db-mysql.o db-mysql.lo: db-mysql.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-int.h
-db_test.lo: libglite_lbu_dbtest.la db.h db-int.h db_test.c
+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
 
index 2eb3ba6..3830e0f 100644 (file)
@@ -80,7 +80,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
 
        // init
        dprintf(("connecting to %s...\n", cs));
-       if (glite_lbu_InitDBContext(&ctx) != 0) {
+       if (glite_lbu_InitDBContext(&ctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) {
                print_error(ctx);
                goto failctx;
        }
index 2ae6738..3e541f9 100644 (file)
@@ -6,6 +6,11 @@
  *   mysqladmin -u root -p create test
  *   mysql -u root -p -e 'GRANT ALL on test.* to testuser@localhost'
  *
+ * Or postgres:
+ *
+ *   createuser -U postgres testuser
+ *   createdb -U postgres --owner testuser test
+ *
  * Use CS environment variable when using different user/pwd@machine:dbname.
  */
 
 #include <stdlib.h>
 #include <string.h>
 
-#include "glite/lbu/db.h"
+#include "db.h"
 
 #define CS "testuser/@localhost:test"
-#if defined(DB_BACKEND) && DB_BACKEND == postgresql
+
+#ifdef PSQL_BACKEND
 #define CREATE_CMD "CREATE TABLE \"data\" (\n\
     \"id\"    INTEGER NOT NULL,\n\
     \"user\"  VARCHAR(32) NOT NULL,\n\
     PRIMARY KEY (\"id\")\n\
 )"
 #define AMP "\""
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES ($1, $2, $3)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = $1"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_PSQL
+
 #else
+
 #define CREATE_CMD "CREATE TABLE data (\n\
     `id`    INT NOT NULL,\n\
     `user`  VARCHAR(32) NOT NULL,\n\
     INDEX(`user`)\n\
 ) engine=innodb"
 #define AMP "`"
+#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
+#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
+#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_MYSQL
 #endif
+
 #define DROP_CMD "DROP TABLE " AMP "data" AMP
 #define INSERT_TRIO_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (%d, %s, %s)"
 #define SELECT_TRIO_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = '%s'"
-#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)"
-#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?"
 
 #define dprintf(ARGS) { printf("%s: ", name); printf ARGS; }
 
@@ -77,7 +90,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 +105,7 @@ int main(int argn __attribute((unused)), char *argv[]) {
 
        // init
        dprintf(("connecting to %s...\n", cs));
-       if (glite_lbu_InitDBContext(&ctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) goto failctx;
+       if (glite_lbu_InitDBContext(&ctx, DB_TEST_BACKEND) != 0) goto failctx;
        if (glite_lbu_DBConnect(ctx, cs) != 0) goto failctx;
        if ((caps = glite_lbu_DBQueryCaps(ctx)) == -1) goto failcon;
 #ifndef NO_PREPARED
index 6cf1c2e..411177e 100644 (file)
@@ -9,7 +9,10 @@
  */
 
 #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 <libpq-fe.h>
 
 #include "glite/lbu/trio.h"
-#include "glite/lbu/db.h"
+#include "db.h"
+#include "db-int.h"
 
 #define DB_CONNECT_TIMEOUT "20"
 
+#define LOG 1
 #ifdef LOG
-  #define lprintf(ARGS...) printf("[db-pg] %s: ", __FUNCTION__); printf(##ARGS)
+  #define lprintf(FMT...) fprintf(stdout, "[db-pg] %s: ", __FUNCTION__); fprintf(stdout, ##FMT);
 #else
-  #define lprintf(ARGS...)
+  #define lprintf(FMT...)
 #endif
 
+#define set_error(CTX, CODE, DESC...) glite_lbu_DBSetError((glite_lbu_DBContext)(CTX), (CODE), __FUNCTION__, __LINE__, ##DESC)
 
-struct glite_lbu_DBContext_s {
-       int caps;
-       struct {
-               int code;
-               char *desc;
-       } err;
+#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_Statement_s {
-        glite_lbu_DBContext  ctx;
+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 *(*PQconnectdb)(const char *conninfo);
+       ConnStatusType (*PQstatus)(const PGconn *conn);
+       void (*PQfinish)(PGconn *conn);
+       char *(*PQerrorMessage)(const PGconn *conn);
+       int (*PQnfields)(const PGresult *res);
+       char *(*PQgetvalue)(const PGresult *res, int tup_num, int field_num);
+       int (*PQgetlength)(const PGresult *res, int tup_num, int field_num);
+       void (*PQclear)(PGresult *res);
+       PGresult *(*PQexec)(PGconn *conn, const char *query);
+       ExecStatusType (*PQresultStatus)(const PGresult *res);
+       char *(*PQresultErrorMessage)(const PGresult *res);
+       char *(*PQcmdTuples)(PGresult *res);
+       int (*PQntuples)(const PGresult *res);
+       char *(*PQfname)(const PGresult *res, int field_num);
+       unsigned char *(*PQescapeByteaConn)(PGconn *conn,
+               const unsigned char *from, size_t from_length,
+               size_t *to_length);
+       unsigned char *(*PQunescapeBytea)(const unsigned char *strtext,
+               size_t *retbuflen);
+       void (*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"};
 
-//static void time2str(time_t, char **str);
-
-
-#define set_error(CTX, CODE, DESC) set_error_func((CTX), (CODE), (DESC), __FUNCTION__, __LINE__)
-static int set_error_func(glite_lbu_DBContext ctx, int code, const char *desc, const char *func, int line) {
-       char *pos;
-
-       if (ctx->err.desc) free(ctx->err.desc);
-       ctx->err.code = code;
-       ctx->err.desc = strdup(desc ? desc : "(null)");
-       pos = strrchr(desc, '\n'); if (pos) pos[0] = '\0';
-
-       if ((ctx->caps & GLITE_LBU_DB_CAP_ERRORS) != 0) fprintf(stderr, "[db %d] %s:%d %s\n", getpid(), func, line, desc);
-       return code;
-}
-
-
-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) {
-       *ctx = calloc(1, sizeof **ctx);
-       if (!*ctx) return ENOMEM;
+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(PQescapeByteaConn, "PQescapeByteaConn");
+                       LOAD(PQunescapeBytea, "PQunescapeBytea");
+                       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_FreeDBContext(glite_lbu_DBContext ctx) {
+void glite_lbu_FreeDBContextPsql(glite_lbu_DBContext ctx_gen) {
+       glite_lbu_DBContextPsql ctx = (glite_lbu_DBContextPsql)ctx_gen;
+
        if (ctx) {
-               free(ctx->err.desc);
+               assert(ctx->conn == NULL);
                free(ctx);
        }
 }
 
 
-int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+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;
 
@@ -121,38 +229,44 @@ int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
        free(buf);
 
        lprintf("connection string = %s\n", pgcs);
-       ctx->conn = PQconnectdb(pgcs);
+       ctx->conn = psql_module.PQconnectdb(pgcs);
        free(pgcsbuf);
        if (!ctx->conn) return ENOMEM;
 
        
 
-       if (PQstatus(ctx->conn) != CONNECTION_OK) {
-               asprintf(&err, "Can't connect, %s", PQerrorMessage(ctx->conn));
-               PQfinish(ctx->conn);
+       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 ctx->err.code;
+               return EIO;
        }
 
        return 0;
 }
 
 
-void glite_lbu_DBClose(glite_lbu_DBContext ctx) {
-       if (ctx->conn) PQfinish(ctx->conn);
+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_DBQueryCaps(glite_lbu_DBContext ctx) {
+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_ExecSQL(ctx, "SHOW server_version", &stmt) == -1) return -1;
-       switch (glite_lbu_FetchRow(stmt, 1, NULL, &res)) {
+       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:
@@ -170,50 +284,51 @@ int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
 
 quit:
        free(res);
-       glite_lbu_FreeStmt(&stmt);
+       glite_lbu_FreeStmtPsql(&stmt);
        return has_prepared;
 }
 
 
-void glite_lbu_DBSetCaps(glite_lbu_DBContext ctx, int caps) {
-       ctx->caps = caps;
+void glite_lbu_DBSetCapsPsql(glite_lbu_DBContext ctx_gen, int caps) {
+       ctx_gen->caps = caps;
 }
 
 
-int glite_lbu_Transaction(glite_lbu_DBContext ctx) {
+int glite_lbu_TransactionPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
        return 0;
 }
 
 
-int glite_lbu_Commit(glite_lbu_DBContext ctx) {
+int glite_lbu_CommitPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
        return 0;
 }
 
 
-int glite_lbu_Rollback(glite_lbu_DBContext ctx) {
+int glite_lbu_RollbackPsql(glite_lbu_DBContext ctx_gen __attribute((unused))) {
        return 0;
 }
 
 
-int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int maxn, unsigned long *lengths, char **results) {
+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;
 
        if (stmt->row >= stmt->nrows) return 0;
 
-       n = PQnfields(stmt->res);
+       n = psql_module.PQnfields(stmt->res);
        if (n <= 0) {
-               set_error(stmt->ctx, EINVAL, "Result set w/o columns");
+               set_error(stmt->generic.ctx, EINVAL, "Result set w/o columns");
                return -1;
        }
        if (n > maxn) {
-               set_error(stmt->ctx, EINVAL, "Not enough room for the result");
+               set_error(stmt->generic.ctx, EINVAL, "Not enough room for the result");
                return -1;
        }
        for (i = 0; i < n; i++) {
-               results[i] = PQgetvalue(stmt->res, stmt->row, i);
+               results[i] = psql_module.PQgetvalue(stmt->res, stmt->row, i);
                /* sanity check for internal error (NULL when invalid row) */
                results[i] = strdup(results[i] ? : "");
-               if (lengths) lengths[i] = PQgetlength(stmt->res, stmt->row, i);
+               if (lengths) lengths[i] = psql_module.PQgetlength(stmt->res, stmt->row, i);
        }
 
        stmt->row++;
@@ -221,130 +336,94 @@ int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int maxn, unsigned lon
 }
 
 
-void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
+void glite_lbu_FreeStmtPsql(glite_lbu_Statement *stmt_gen) {
+       glite_lbu_DBContextPsql ctx;
+       glite_lbu_StatementPsql stmt;
        char *sql;
 
-       if (!*stmt) return;
-       if ((*stmt)->res) PQclear((*stmt)->res);
-       if ((*stmt)->name) {
-               asprintf(&sql, "DEALLOCATE %s", (*stmt)->name);
-               (*stmt)->res = PQexec((*stmt)->ctx->conn, 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);
-               PQclear((*stmt)->res);
+               psql_module.PQclear(stmt->res);
        }
-       free((*stmt)->name);
-       free((*stmt)->sql);
-       free(*stmt);
-       *stmt = NULL;
+       free(stmt->name);
+       free(stmt->sql);
+       free(stmt);
+       *stmt_gen = NULL;
 }
 
 
-int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt_out) {
-       glite_lbu_Statement stmt = 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;
+       char *nstr, *errmsg, *pos;
        PGresult *res;
 
        lprintf("command = %s\n", cmd);
        if (stmt_out) *stmt_out = NULL;
-       if ((res = PQexec(ctx->conn, cmd)) == NULL) {
-               ctx->err.code = ENOMEM;
+       if ((res = psql_module.PQexec(ctx->conn, cmd)) == NULL) {
+               ctx->generic.err.code = ENOMEM;
                return -1;
        }
 
-       status = PQresultStatus(res);
+       status = psql_module.PQresultStatus(res);
        if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
-               set_error(ctx, EIO, PQresultErrorMessage(res));
-               PQclear(res);
+               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 = PQcmdTuples(res);
+       nstr = psql_module.PQcmdTuples(res);
        if (nstr && nstr[0]) n = atoi(nstr);
-       else n = PQntuples(res);
+       else n = psql_module.PQntuples(res);
        if (stmt_out) {
                stmt = calloc(1, sizeof(*stmt));
-               stmt->ctx = ctx;
+               stmt->generic.ctx = ctx_gen;
                stmt->res = res;
                stmt->nrows = n;
-               *stmt_out = stmt;
+               *stmt_out = (glite_lbu_Statement)stmt;
        } else {
-               PQclear(res);
+               psql_module.PQclear(res);
        }
        return n;
 }
 
 
-int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols) {
+int glite_lbu_QueryColumnsPsql(glite_lbu_Statement stmt_gen, char **cols) {
+       glite_lbu_StatementPsql stmt = (glite_lbu_StatementPsql)stmt_gen;
        int n, i;
 
-       n = PQnfields(stmt->res);
+       n = psql_module.PQnfields(stmt->res);
        for (i = 0; i < n; i++) {
-               cols[i] = PQfname(stmt->res, i);
+               cols[i] = psql_module.PQfname(stmt->res, i);
        }
        return -1;
 }
 
 
-#if 0
-static void time2str(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);
-}
-#endif
-
-
-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);
-}
-
-
-/*
-int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names) { }
-long int glite_lbu_Lastid(glite_lbu_Statement stmt) {}
-*/
-
-
-int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmtout) {
+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_Statement stmt;
+       glite_lbu_StatementPsql stmt;
        PGresult *res = NULL;
 
        // init
        stmt = calloc(1, sizeof(*stmt));
-       stmt->ctx = ctx;
+       stmt->generic.ctx = ctx_gen;
        stmt->sql = strdup(sql);
 
        // name of the prepared command used as ID in postgres
@@ -357,24 +436,24 @@ int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_St
        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], ++stmt->ctx->prepared_counts[i]);
+       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 = PQexec(stmt->ctx->conn, sqlPrep);
-       if (PQresultStatus(res) != PGRES_COMMAND_OK) {
-               asprintf(&s, "error preparing command: %s", PQerrorMessage(stmt->ctx->conn));
-               set_error(stmt->ctx, EIO, s);
+       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;
        }
 
-       *stmtout = stmt;
+       *stmt_out = (glite_lbu_Statement)stmt;
        retval = 0;
 
 quit:
        free(sqlPrep);
-       if (res) PQclear(res);
+       if (res) psql_module.PQclear(res);
        if (!retval) return 0;
 
        free(stmt->name);
@@ -384,7 +463,9 @@ quit:
 }
 
 
-int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
+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;
@@ -393,10 +474,10 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
        glite_lbu_DBType type;
 
        if (!stmt || !stmt->sql || !stmt->name)
-               return set_error(stmt->ctx, EINVAL, "PrepareStmt() not called");
+               return set_error(ctx, EINVAL, "PrepareStmt() not called");
 
        if (stmt->res) {
-               PQclear(stmt->res);
+               psql_module.PQclear(stmt->res);
                stmt->res = NULL;
        }
 
@@ -430,10 +511,12 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
 
                        s = (unsigned char *)va_arg(ap, char *);
                        binary_len = va_arg(ap, unsigned long);
+                       lprintf("blob, len = %lu, ptr = %p\n", binary_len, s);
                        if (s) {
-                               tmp = PQescapeByteaConn(stmt->ctx->conn, s, binary_len, &result_len);
+                               tmp = psql_module.PQescapeByteaConn(ctx->conn, s, binary_len, &result_len);
                                asprintf(&tmpdata[i], "'%s'", tmp);
-                               PQfreemem(tmp);
+                               lprintf("escaped: '%s'\n", tmpdata[i]);
+                               psql_module.PQfreemem(tmp);
                        } else
                                tmpdata[i] = strdup("NULL");
                        break;
@@ -450,11 +533,11 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
                case GLITE_LBU_DB_TYPE_DATE:
                case GLITE_LBU_DB_TYPE_TIME:
                case GLITE_LBU_DB_TYPE_DATETIME:
-                       glite_lbu_TimeToDB(va_arg(ap, time_t), &tmpdata[i]);
+                       glite_lbu_TimeToStr(va_arg(ap, time_t), &tmpdata[i]);
                        break;
 
                case GLITE_LBU_DB_TYPE_TIMESTAMP:
-                       glite_lbu_TimestampToDB(va_arg(ap, double), &tmpdata[i]);
+                       glite_lbu_TimestampToStr(va_arg(ap, double), &tmpdata[i]);
                        break;
 
                case GLITE_LBU_DB_TYPE_NULL:
@@ -467,7 +550,7 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
 
                default:
                        lprintf("unknown type %d\n", type);
-                       set_error(stmt->ctx, EINVAL, "unimplemented type");
+                       set_error(ctx, EINVAL, "unimplemented type");
                        goto quit;
                }
 
@@ -485,18 +568,18 @@ int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
        if (n) strcat(sql, ")");
 
        lprintf("exec prepared: n = %d, sql = '%s'\n", n, sql);
-       stmt->res = PQexec(stmt->ctx->conn, sql);
-       status = PQresultStatus(stmt->res);
+       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, PQerrorMessage(stmt->ctx->conn));
-               set_error(stmt->ctx, EIO, s);
+               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 = PQcmdTuples(stmt->res);
+       nstr = psql_module.PQcmdTuples(stmt->res);
        //lprintf("cmdtuples: '%s'\n", nstr);
        if (nstr && nstr[0]) retval = atoi(nstr);
-       else retval = PQntuples(stmt->res);
+       else retval = psql_module.PQntuples(stmt->res);
        stmt->nrows = retval;
        stmt->row = 0;
        //lprintf("ntuples/retval: %d\n", retval);
@@ -509,24 +592,11 @@ quit:
 }
 
 
-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;
-}
-
-
-/*
-struct glite_lbu_bufInsert_s {
-        glite_lbu_DBContext ctx;
+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);
 }
-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)
-{}
-int glite_lbu_bufferedInsert(glite_lbu_bufInsert bi, const char *row) {}
-int glite_lbu_bufferedInsertClose(glite_lbu_bufInsert bi) {}
-*/
index db60d2d..2b19aff 100644 (file)
@@ -158,14 +158,18 @@ int glite_lbu_DBError(glite_lbu_DBContext ctx, char **text, char **desc) {
 
 
 int glite_lbu_InitDBContext(glite_lbu_DBContext *ctx, int backend) {
+       int ret;
+
        if (!VALID(backend)) return EINVAL;
        if (backends[backend]->backend != backend) return ENOTSUP;
-       return backends[backend]->initContext(ctx);
+       ret = backends[backend]->initContext(ctx);
+       if (ctx && *ctx) (*ctx)->backend = backend;
+       return ret;
 }
 
 
 void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) {
-       if (!VALID(ctx->backend)) return;
+       if (!ctx || !VALID(ctx->backend)) return;
 
        free(ctx->err.desc);
        ctx->err.desc = NULL;