-#ident "$Header$"
-
#include <sys/types.h>
#ifdef LBS_DB_PROFILE
#include <sys/time.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;
/*
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;
-
+} 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_lib_handle_t db_handle = {
+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());
+ 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);
+ syslog(LOG_WARNING,"MySQL library version mismatch (compiled '%d', runtime '%d')", MYSQL_VERSION_ID, ver_u);
}
- 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_ExecSQL(ctx_gen, "SET AUTOCOMMIT=1", NULL) < 0 ||
+ glite_lbu_ExecSQL(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_ExecSQL(ctx_gen, "SET AUTOCOMMIT=0", NULL) < 0) goto err;
+ if (glite_lbu_ExecSQL(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_ExecSQL(ctx_gen, "COMMIT", NULL) < 0) goto err;
+ if (glite_lbu_ExecSQL(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_ExecSQL(ctx_gen, "ROLLBACK", NULL) < 0) goto err;
+ if (glite_lbu_ExecSQL(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_PrepareStmt(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;
+ if ((retval = glite_lbu_ExecSQLMysql(ctx, "SHOW TABLES", &stmt)) <= 0 || glite_lbu_FetchRowMysql(stmt, 1, NULL, table) < 0) goto quit;
glite_lbu_FreeStmt(&stmt);
trio_asprintf(&cmd, "SHOW CREATE TABLE %|Ss", table[0]);
- if (glite_lbu_ExecSQL(ctx, cmd, &stmt) <= 0 || (retval = glite_lbu_FetchRow(stmt, 2, NULL, res)) < 0 ) goto quit;
+ if (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
+#include <sys/types.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glite/lbu/trio.h>
+
+#include "db.h"
+#include "db-int.h"
+
+
+#define VALID(BACKEND) ((BACKEND) >= 0 && (BACKEND) < GLITE_LBU_DB_BACKEND_LAST)
+
+
+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 */
+};
+
+
+/* possible backends */
+#ifdef MYSQL_SONAME
+extern glite_lbu_DBBackend_t mysql_backend;
+#else
+#define mysql_backend no_backend
+#endif
+#ifdef PSQL_SONAME
+extern glite_lbu_DBBackend_t psql_backend;
+#else
+#define psql_backend no_backend
+#endif
+
+glite_lbu_DBBackend_t no_backend = {
+ backend: -1,
+
+ /* functions unspecified */
+};
+
+glite_lbu_DBBackend_t *backends[GLITE_LBU_DB_BACKEND_LAST] = {
+ &mysql_backend,
+ &psql_backend
+};
+
+
+/* --- internal functions ---- */
+
+int glite_lbu_DBClearError(glite_lbu_DBContext ctx) {
+ ctx->err.code = 0;
+ if (ctx->err.desc) {
+ free(ctx->err.desc);
+ ctx->err.desc = NULL;
+ }
+ return 0;
+}
+
+
+int glite_lbu_DBSetError(glite_lbu_DBContext ctx, int code, const char *func, int line, const char *desc, ...) {
+ va_list ap;
+
+ if (!code) return ctx->err.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, ctx->err.desc);
+ return code;
+}
+
+
+void glite_lbu_TimeToStrGeneric(time_t t, char **str, const char *amp) {
+ struct tm *tm = gmtime(&t);
+
+ asprintf(str,"%s%4d-%02d-%02d %02d:%02d:%02d%s",amp,tm->tm_year+1900,tm->tm_mon+1,
+ tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,amp);
+}
+
+
+void glite_lbu_TimeToStr(time_t t, char **str) {
+ glite_lbu_TimeToStrGeneric(t, str, "'");
+}
+
+
+void glite_lbu_TimestampToStrGeneric(double t, char **str, const char *amp) {
+ time_t tsec = t;
+ struct tm *tm = gmtime(&tsec);
+
+ t = t - tsec + tm->tm_sec;
+ asprintf(str,"%s%4d-%02d-%02d %02d:%02d:%02.09f%s",amp,tm->tm_year+1900,tm->tm_mon+1,
+ tm->tm_mday,tm->tm_hour,tm->tm_min,t,amp);
+}
+
+
+void glite_lbu_TimestampToStr(double t, char **str) {
+ glite_lbu_TimestampToStrGeneric(t, str, "'");
+}
+
+
+time_t glite_lbu_StrToTime(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);
+}
+
+
+double glite_lbu_StrToTimestamp(const char *str) {
+ struct tm tm;
+ double sec;
+
+ memset(&tm,0,sizeof(tm));
+ setenv("TZ","UTC",1); tzset();
+ sscanf(str,"%4d-%02d-%02d %02d:%02d:%lf",
+ &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
+ &tm.tm_hour,&tm.tm_min,&sec);
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+ tm.tm_sec = sec;
+
+ return (sec - tm.tm_sec) + mktime(&tm);
+}
+
+
+/* ---- API ---- */
+
+
+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 backend) {
+ if (!VALID(backend)) return EINVAL;
+ if (backends[backend]->backend != backend) return ENOTSUP;
+ return backends[backend]->initContext(ctx);
+}
+
+
+void glite_lbu_FreeDBContext(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return;
+
+ free(ctx->err.desc);
+ ctx->err.desc = NULL;
+ backends[ctx->backend]->freeContext(ctx);
+}
+
+
+int glite_lbu_DBConnect(glite_lbu_DBContext ctx, const char *cs) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->connect(ctx, cs);
+}
+
+
+void glite_lbu_DBClose(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return;
+ backends[ctx->backend]->close(ctx);
+}
+
+
+int glite_lbu_DBQueryCaps(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->queryCaps(ctx);
+}
+
+
+void glite_lbu_DBSetCaps(glite_lbu_DBContext ctx, int caps) {
+ if (!VALID(ctx->backend)) return;
+ return backends[ctx->backend]->setCaps(ctx, caps);
+}
+
+
+int glite_lbu_Transaction(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->transaction(ctx);
+}
+
+
+int glite_lbu_Commit(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->commit(ctx);
+}
+
+
+int glite_lbu_Rollback(glite_lbu_DBContext ctx) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->rollback(ctx);
+}
+
+
+int glite_lbu_FetchRow(glite_lbu_Statement stmt, unsigned int n, unsigned long *lengths, char **results) {
+ if (!VALID(stmt->ctx->backend)) return EINVAL;
+ return backends[stmt->ctx->backend]->fetchRow(stmt, n, lengths, results);
+}
+
+
+void glite_lbu_FreeStmt(glite_lbu_Statement *stmt) {
+ if (!VALID((*stmt)->ctx->backend)) return;
+ return backends[(*stmt)->ctx->backend]->freeStmt(stmt);
+}
+
+
+int glite_lbu_QueryIndices(glite_lbu_DBContext ctx, const char *table, char ***key_names, char ****column_names) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->queryIndices(ctx, table, key_names, column_names);
+}
+
+
+int glite_lbu_ExecSQL(glite_lbu_DBContext ctx, const char *cmd, glite_lbu_Statement *stmt) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->execSQL(ctx, cmd, stmt);
+}
+
+
+int glite_lbu_QueryColumns(glite_lbu_Statement stmt, char **cols) {
+ if (!VALID(stmt->ctx->backend)) return EINVAL;
+ return backends[stmt->ctx->backend]->queryColumns(stmt, cols);
+}
+
+
+int glite_lbu_PrepareStmt(glite_lbu_DBContext ctx, const char *sql, glite_lbu_Statement *stmt) {
+ if (!VALID(ctx->backend)) return EINVAL;
+ return backends[ctx->backend]->prepareStmt(ctx, sql, stmt);
+}
+
+
+int glite_lbu_ExecPreparedStmt_v(glite_lbu_Statement stmt, int n, va_list ap) {
+ if (!VALID(stmt->ctx->backend)) return EINVAL;
+ return backends[stmt->ctx->backend]->execPreparedStmt_v(stmt, n, ap);
+}
+
+
+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;
+}
+
+
+long int glite_lbu_Lastid(glite_lbu_Statement stmt) {
+ if (!VALID(stmt->ctx->backend)) return EINVAL;
+ return backends[stmt->ctx->backend]->lastid(stmt);
+}
+
+
+void glite_lbu_TimeToDB(glite_lbu_DBContext ctx, time_t t, char **str) {
+ if (!VALID(ctx->backend)) return;
+ return backends[ctx->backend]->timeToDB(t, str);
+}
+
+
+void glite_lbu_TimestampToDB(glite_lbu_DBContext ctx, double t, char **str) {
+ if (!VALID(ctx->backend)) return;
+ return backends[ctx->backend]->timestampToDB(t, str);
+}
+
+
+time_t glite_lbu_DBToTime(glite_lbu_DBContext ctx, const char *str) {
+ if (!VALID(ctx->backend)) return (time_t)-1;
+ return backends[ctx->backend]->DBToTime(str);
+}
+
+
+double glite_lbu_DBToTimestamp(glite_lbu_DBContext ctx, const char *str) {
+ if (!VALID(ctx->backend)) return -1;
+ return backends[ctx->backend]->DBToTimestamp(str);
+}
+
+
+#define STATUS(CTX) ((CTX)->err.code)
+#define CLR_ERR(CTX) glite_lbu_DBClearError((CTX))
+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);
+}