From fc232c2e0d1aff1a88631018706f337730852793 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Thu, 29 Oct 2009 20:07:39 +0000 Subject: [PATCH] Finish postgress backend module: - dynamic client library opening - using internal DB module API - tests --- org.glite.lbjp-common.db/Makefile | 18 +- org.glite.lbjp-common.db/examples/db_expire.c | 2 +- org.glite.lbjp-common.db/examples/db_test.c | 25 +- org.glite.lbjp-common.db/src/db-pg.c | 442 +++++++++++++++----------- org.glite.lbjp-common.db/src/db.c | 8 +- 5 files changed, 296 insertions(+), 199 deletions(-) diff --git a/org.glite.lbjp-common.db/Makefile b/org.glite.lbjp-common.db/Makefile index 738e8dc..989598d 100644 --- a/org.glite.lbjp-common.db/Makefile +++ b/org.glite.lbjp-common.db/Makefile @@ -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 diff --git a/org.glite.lbjp-common.db/examples/db_expire.c b/org.glite.lbjp-common.db/examples/db_expire.c index 2eb3ba6..3830e0f 100644 --- a/org.glite.lbjp-common.db/examples/db_expire.c +++ b/org.glite.lbjp-common.db/examples/db_expire.c @@ -80,7 +80,7 @@ int main(int argn __attribute((unused)), char *argv[]) { // init dprintf(("connecting to %s...\n", cs)); - if (glite_lbu_InitDBContext(&ctx) != 0) { + if (glite_lbu_InitDBContext(&ctx, GLITE_LBU_DB_BACKEND_MYSQL) != 0) { print_error(ctx); goto failctx; } diff --git a/org.glite.lbjp-common.db/examples/db_test.c b/org.glite.lbjp-common.db/examples/db_test.c index 2ae6738..3e541f9 100644 --- a/org.glite.lbjp-common.db/examples/db_test.c +++ b/org.glite.lbjp-common.db/examples/db_test.c @@ -6,6 +6,11 @@ * mysqladmin -u root -p create test * mysql -u root -p -e 'GRANT ALL on test.* to testuser@localhost' * + * Or postgres: + * + * createuser -U postgres testuser + * createdb -U postgres --owner testuser test + * * Use CS environment variable when using different user/pwd@machine:dbname. */ @@ -13,10 +18,11 @@ #include #include -#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\ @@ -24,7 +30,12 @@ PRIMARY KEY (\"id\")\n\ )" #define AMP "\"" +#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES ($1, $2, $3)" +#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = $1" +#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_PSQL + #else + #define CREATE_CMD "CREATE TABLE data (\n\ `id` INT NOT NULL,\n\ `user` VARCHAR(32) NOT NULL,\n\ @@ -33,12 +44,14 @@ INDEX(`user`)\n\ ) engine=innodb" #define AMP "`" +#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)" +#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?" +#define DB_TEST_BACKEND GLITE_LBU_DB_BACKEND_MYSQL #endif + #define DROP_CMD "DROP TABLE " AMP "data" AMP #define INSERT_TRIO_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (%d, %s, %s)" #define SELECT_TRIO_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = '%s'" -#define INSERT_CMD "INSERT INTO " AMP "data" AMP " (" AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP ") VALUES (?, ?, ?)" -#define SELECT_CMD "SELECT " AMP "id" AMP ", " AMP "user" AMP ", " AMP "info" AMP " FROM " AMP "data" AMP " WHERE " AMP "user" AMP " = ?" #define dprintf(ARGS) { printf("%s: ", name); printf ARGS; } @@ -77,7 +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 diff --git a/org.glite.lbjp-common.db/src/db-pg.c b/org.glite.lbjp-common.db/src/db-pg.c index 6cf1c2e..411177e 100644 --- a/org.glite.lbjp-common.db/src/db-pg.c +++ b/org.glite.lbjp-common.db/src/db-pg.c @@ -9,7 +9,10 @@ */ #include +#include +#include #include +#include #include #include #include @@ -19,83 +22,188 @@ #include #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) {} -*/ diff --git a/org.glite.lbjp-common.db/src/db.c b/org.glite.lbjp-common.db/src/db.c index db60d2d..2b19aff 100644 --- a/org.glite.lbjp-common.db/src/db.c +++ b/org.glite.lbjp-common.db/src/db.c @@ -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; -- 1.8.2.3