More changes from out of CVS tree:
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Mon, 10 Oct 2005 09:10:41 +0000 (09:10 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Mon, 10 Oct 2005 09:10:41 +0000 (09:10 +0000)
 - get rid of mysql from public header
 - added time types
 - added testing example
 - runtime and compile check for minimal mysql version: 4.1.2
 - more free on error in db_prepare, NULLing on DB close

org.glite.jp.server-common/Makefile
org.glite.jp.server-common/examples/db-test-int.c [new file with mode: 0644]
org.glite.jp.server-common/interface/db.h
org.glite.jp.server-common/src/db.c

index cb1f049..e916efd 100644 (file)
@@ -34,6 +34,10 @@ COMPILE:=libtool --mode=compile ${CC} ${CPPFLAGS} ${CFLAGS}
 LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/lib ${LDFLAGS} 
 INSTALL:=libtool --mode=install install
 
+GLOBUS_LIBS:=-L${globus_prefix}/lib \
+       -lglobus_common_${nothrflavour} \
+       -lglobus_gssapi_gsi_${nothrflavour}
+
 # FIXME: to use libtool versioning correcty, we should have:
 #
 # current = major + minor + offset
@@ -64,13 +68,18 @@ else
 endif
 
 
-default all: compile
+default all: compile examples
 
 compile: ${LTLIB} ${STATICLIB}
 
 ${LTLIB} ${STATICLIB}: ${OBJS}
        ${LINK} ${version_info} -o $@ ${LOBJS} ${MYSQLIB}
 
+examples: db-test-int
+
+db-test-int: db-test-int.o
+       ${LINK} -o $@ $+ ${STATICLIB} ${MYSQLIB} -lglite_jp_common ${GLOBUS_LIBS}
+
 check: 
        -echo nothing yet
 
@@ -103,4 +112,4 @@ clean:
 %.o: %.c
        ${COMPILE} -c $< -o $@
 
-.PHONY: default all compile check doc stage dist distsrc distbin install clean
+.PHONY: default all compile examples check doc stage dist distsrc distbin install clean
diff --git a/org.glite.jp.server-common/examples/db-test-int.c b/org.glite.jp.server-common/examples/db-test-int.c
new file mode 100644 (file)
index 0000000..e58b5c2
--- /dev/null
@@ -0,0 +1,112 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "db.h"
+
+
+static void print_err(glite_jp_context_t ctx) {
+       glite_jp_error_t *e;
+
+       e = ctx->error;
+       while (e) {
+               printf("%s(%s)\n", e->desc, e->source);
+               e = e->reason;
+       }
+       printf("\n");
+}
+
+
+int main() {
+       glite_jp_context_t ctx;
+       glite_jp_db_stmt_t jpstmt;
+
+       glite_jp_init_context(&ctx);
+
+       printf("connecting...\n");
+       if (glite_jp_db_connect(ctx, "jpis/@localhost:jpis1") != 0) goto fail;
+
+       // "trio" queries
+{
+       int nr, i;
+       char **res;
+
+       printf("selecting...\n");
+       if ((glite_jp_db_execstmt(ctx, "SELECT uniqueid, feedid, state, source, condition FROM feeds", &jpstmt)) == -1) goto fail;
+
+       printf("fetching...\n");
+       res = calloc(4, sizeof(char *));
+       while ((nr = glite_jp_db_fetchrow(jpstmt, res)) > 0) {
+               printf("Result: n=%d, res=%p\n", nr, res);
+               i = 0;
+               if (res) while(i < nr) {printf("p=%p(%s)\n", res[i], res[i]);free(res[i]);i++;}
+       }
+       free(res);
+       printf("closing stmt...\n");
+       glite_jp_db_freestmt(&jpstmt);
+}
+
+       // param queries
+{
+       char res_feedid[33];
+       long int res_state;
+       char res_source[256];
+       char res_condition[1024];
+       unsigned long res_condition_length;
+       long int param_state;
+
+       void *my_res, *my_param;
+
+       glite_jp_db_create_params(&my_param, 1, GLITE_JP_DB_TYPE_INT, &param_state);
+       glite_jp_db_create_results(&my_res, 4,
+               GLITE_JP_DB_TYPE_VARCHAR, NULL, res_feedid, sizeof(res_feedid), NULL,
+               GLITE_JP_DB_TYPE_INT, NULL, &res_state,
+               GLITE_JP_DB_TYPE_VARCHAR, NULL, res_source, sizeof(res_source), NULL,
+               GLITE_JP_DB_TYPE_MEDIUMBLOB, NULL, res_condition, sizeof(res_condition), &res_condition_length
+       );
+       printf("preparing...\n");
+       if ((glite_jp_db_prepare(ctx, "SELECT feedid, state, source, condition FROM feeds WHERE state = ?", &jpstmt, my_param, my_res)) != 0) goto fail_close;
+
+       param_state = 1;
+       printf("executing state %ld...\n", param_state);
+       if (glite_jp_db_execute(jpstmt) == -1) {
+               glite_jp_db_freestmt(&jpstmt);
+               goto fail_stmtclose;
+       }
+       printf("fetching...\n");
+       while (glite_jp_db_fetch(jpstmt) == 0) {
+               printf("feedid:%s, state:%ld, source:%s, condition:%s\n", res_feedid, res_state, res_source, res_condition);
+       }
+
+       param_state = 2;
+       printf("executing state %ld...\n", param_state);
+       if (glite_jp_db_execute(jpstmt) == -1) {
+               glite_jp_db_freestmt(&jpstmt);
+               goto fail_stmtclose;
+       }
+       printf("fetching...\n");
+       while (glite_jp_db_fetch(jpstmt) == 0) {
+               printf("feedid:%s, state:%ld, source:%s, condition:%s\n", res_feedid, res_state, res_source, res_condition);
+       }
+}
+
+       printf("closing stmt...\n");
+       glite_jp_db_freestmt(&jpstmt);
+       printf("closing...\n");
+       glite_jp_db_close(ctx);
+
+       glite_jp_free_context(ctx);
+       return 0;
+
+fail_stmtclose:
+       printf("closing stmt...\n");
+       glite_jp_db_freestmt(&jpstmt);
+fail_close:
+       printf("closing...\n");
+       glite_jp_db_close(ctx);
+fail:
+       printf("failed\n");
+       print_err(ctx);
+       glite_jp_free_context(ctx);
+
+       return 1;
+}
index c5eca7f..7e51553 100644 (file)
 extern "C" {
 #endif
 
+#include <glite/jp/types.h>
+#include <glite/jp/context.h>
 
 typedef struct _glite_jp_db_stmt_t *glite_jp_db_stmt_t;
 
+typedef enum {
+       GLITE_JP_DB_TYPE_NULL = 0,
+       GLITE_JP_DB_TYPE_TINYINT = 1,
+       GLITE_JP_DB_TYPE_INT = 2,
+       GLITE_JP_DB_TYPE_TINYBLOB = 3,
+       GLITE_JP_DB_TYPE_TINYTEXT = 4,
+       GLITE_JP_DB_TYPE_BLOB = 5,
+       GLITE_JP_DB_TYPE_TEXT = 6,
+       GLITE_JP_DB_TYPE_MEDIUMBLOB = 7,
+       GLITE_JP_DB_TYPE_MEDIUMTEXT = 8,
+       GLITE_JP_DB_TYPE_LONGBLOB = 9,
+       GLITE_JP_DB_TYPE_LONGTEXT = 10,
+       GLITE_JP_DB_TYPE_VARCHAR = 11,
+       GLITE_JP_DB_TYPE_CHAR = 12,
+       GLITE_JP_DB_TYPE_DATE = 13,
+       GLITE_JP_DB_TYPE_TIME = 14,
+       GLITE_JP_DB_TYPE_DATETIME = 15,
+       GLITE_JP_DB_TYPE_TIMESTAMP = 16,
+       GLITE_JP_DB_TYPE_LAST = 17
+} glite_jp_db_type_t;
 
 /**
  * Connect to the database.
@@ -22,7 +44,7 @@ typedef struct _glite_jp_db_stmt_t *glite_jp_db_stmt_t;
  *
   \return      JP error code
  */
-int glite_jp_db_connect(glite_jp_context_t,const char *);
+int glite_jp_db_connect(glite_jp_context_t, const char *);
 
 
 /**
@@ -74,7 +96,7 @@ int glite_jp_db_querycolumns(glite_jp_db_stmt_t, char **);
 
 
 /**
- * Free the statement structure
+ * Free the statement structure and destroy its parameters.
  *
  * \param[inout] stmt  statement
  */
@@ -111,35 +133,82 @@ int glite_jp_db_dbcheckversion(glite_jp_context_t);
 
 
 /**
- * Assign parameters to mysql bind structure.
+ * Create and assign parameters for mysql prepared commands.
  *
- * \param[inout] param mysql bind strusture array
- * \param[in] type     mysql type
+ * \param[out] params  internal structure array
  *
  * Variable parameters:
- *     MYSQL_TYPE_TINY:        char *buffer
- *     MYSQL_TYPE_LONG:        long int *buffer
- *     MYSQL_TYPE_*_BLOB:      void *buffer, unsigned long *length
- *     MYSQL_TYPE_*STRING:     char *buffer, unsigned long *length
- *     MYSQL_TYPE_NULL:        -
+ * always:
+ *     glite_jp_db_type_t type         DB item type
+ * then one of them:
+ *     GLITE_JP_DB_TYPE_TINYINT:       char *buffer
+ *     GLITE_JP_DB_TYPE_INT:   int *buffer
+ *     GLITE_JP_DB_TYPE_*BLOB/TEXT:    void *buffer, unsigned long *length
+ *     GLITE_JP_DB_TYPE_[VAR]CHAR:     char *buffer, unsigned long *length
+ *     GLITE_JP_DB_TYPE_DATE:  void **buffer
+ *     GLITE_JP_DB_TYPE_TIME:  void **buffer
+ *     GLITE_JP_DB_TYPE_DATETIME:      void **buffer
+ *     GLITE_JP_DB_TYPE_TIMESTAMP:     void **buffer
+ *     GLITE_JP_DB_TYPE_NULL:  -
  */
-void glite_jp_db_assign_param(MYSQL_BIND *param, enum enum_field_types type, ...);
-
+void glite_jp_db_create_params(void **params, int n, ...);
 
 /**
- * Assign result variables to mysql bind structure.
+ * Create and assign result variables for mysql prepared commands.
  *
  * \param[inout] result        mysql bind strusture array
- * \param[in] type     mysql type
- * \param[in] is_null  pointer to is_null boolean
  *
  * Variable parameters:
- *     MYSQL_TYPE_TINY:        char *buffer
- *     MYSQL_TYPE_LONG:        long int *buffer
- *     MYSQL_TYPE_*_BLOB:      void *buffer, unsigned long max_length, unsigned long *length
- *     MYSQL_TYPE_*STRING:     char *buffer, unsigned long max_length, unsigned long *length
+ * always:
+ * \param[in] glite_jp_db_type_t type  DB item type
+ * \param[in] int *is_null     pointer to is_null boolean or NULL
+ * then one of them:
+ *     GLITE_JP_DB_TYPE_TINYINT:       char *buffer
+ *     GLITE_JP_DB_TYPE_INT:           long int *buffer
+ *     GLITE_JP_DB_TYPE_*BLOB/TEXT:    void *buffer, unsigned long max_length, unsigned long *length
+ *     GLITE_JP_DB_TYPE_[VAR]CHAR:     char *buffer, unsigned long max_length, unsigned long *length
+ *     GLITE_JP_DB_TYPE_DATE:          void **buffer
+ *     GLITE_JP_DB_TYPE_TIME:          void **buffer
+ *     GLITE_JP_DB_TYPE_DATETIME:      void **buffer
+ *     GLITE_JP_DB_TYPE_TIMESTAMP:     void **buffer
+ */
+void glite_jp_db_create_results(void **results, int n, ...);
+
+/**
+ * Destroy prepared parameters.
+ */
+void glite_jp_db_destroy_params(void *params);
+
+/**
+ * Destroy prepared results.
  */
+void glite_jp_db_destroy_results(void *results);
+
+#if 0
+void glite_jp_db_assign_param(MYSQL_BIND *param, enum enum_field_types type, ...);
 void glite_jp_db_assign_result(MYSQL_BIND *result, enum enum_field_types type, my_bool *is_null, ...);
+#endif
+
+/**
+ * Assign time_t to buffer.
+ */
+void glite_jp_db_set_time(void *buffer, const time_t time);
+
+/**
+ * Get the time from buffer.
+ */
+time_t glite_jp_db_get_time(const void *buffer);
+
+/**
+ * Rebind the parameters and/or results.
+ *
+ * \param[inout] jpstmt        JP SQL statement to work with
+ * \param[inout] params        mysql static structure with parameters or NULL
+ * \param[inout] cols  mysql static structure with result buffer or NULL
+ *
+ * \return JP error code
+ */
+int glite_jp_db_rebind(glite_jp_db_stmt_t jpstmt, void *params, void *cols);
 
 /**
  * Prepare the SQL statement. Use glite_jp_db_freestmt() to free it.
@@ -147,12 +216,12 @@ void glite_jp_db_assign_result(MYSQL_BIND *result, enum enum_field_types type, m
  * \param[inout] ctx   context to work with
  * \param[in] sql      SQL command
  * \param[out] jpstmt  returned JP SQL statement
- * \param[inout] params        mysql static structure with parameters
- * \param[inout] cols  mysql static structure with result buffer
+ * \param[inout] params        mysql static structure with parameters or NULL
+ * \param[inout] cols  mysql static structure with result buffer or NULL
  *
  * \return JP error code
  */
-int glite_jpis_db_prepare(glite_jp_context_t ctx, const char *sql, glite_jp_db_stmt_t *jpstmt, MYSQL_BIND *params, MYSQL_BIND *cols);
+int glite_jp_db_prepare(glite_jp_context_t ctx, const char *sql, glite_jp_db_stmt_t *jpstmt, void *params, void *cols);
 
 /**
  * Execute prepared SQL statement.
index de024dd..3ef2738 100644 (file)
@@ -1,8 +1,8 @@
 #ident "$Header$"
 
-#include "mysql.h"
-#include "mysqld_error.h"
-#include "errmsg.h"
+#include <mysql.h>
+#include <mysqld_error.h>
+#include <errmsg.h>
 
 #include <sys/types.h>
 #include <stdio.h>
 
 #include "db.h"
 
-#define GLITE_JP_LB_MYSQL_VERSION 40018
+
+#define GLITE_JP_DB_MYSQL_VERSION 40102
+
+#if !defined(MYSQL_VERSION_ID) || MYSQL_VERSION_ID < GLITE_JP_DB_MYSQL_VERSION
+#error required MySQL version 4.1.2
+#endif
 
 #define JP_ERR(CTX, CODE, DESC) jp_err((CTX), (CODE), (DESC), __FUNCTION__, __LINE__)
 #define MY_ERR(CTX) my_err((CTX), __FUNCTION__, __LINE__)
 #define MY_ISOKSTMT(JPSTMT, RETRY) my_isokstmt((JPSTMT), __FUNCTION__, __LINE__, (RETRY))
 
 
+typedef struct {
+       int             n;
+       MYSQL_BIND      params[1];
+} params_t;
+
 struct _glite_jp_db_stmt_t {
        glite_jp_context_t      ctx;
        MYSQL_RES               *result;
        MYSQL_STMT              *stmt;
+       params_t                *params, *results;
+};
+
+
+static int glite_to_mysql_type[] = {
+       MYSQL_TYPE_NULL,
+       MYSQL_TYPE_TINY,
+       MYSQL_TYPE_LONG,
+       MYSQL_TYPE_TINY_BLOB,
+       MYSQL_TYPE_TINY_BLOB,
+       MYSQL_TYPE_BLOB,
+       MYSQL_TYPE_BLOB,
+       MYSQL_TYPE_MEDIUM_BLOB,
+       MYSQL_TYPE_MEDIUM_BLOB,
+       MYSQL_TYPE_LONG_BLOB,
+       MYSQL_TYPE_LONG_BLOB,
+       MYSQL_TYPE_VAR_STRING,
+       MYSQL_TYPE_STRING,
+       MYSQL_TYPE_DATE,
+       MYSQL_TYPE_TIME,
+       MYSQL_TYPE_DATETIME,
+       MYSQL_TYPE_TIMESTAMP,
 };
 
 
@@ -101,6 +133,9 @@ int glite_jp_db_connect(glite_jp_context_t ctx,const char *cs)
        char    *slash,*at,*colon;
        int      ret;
 
+       // needed for SQL result parameters
+       assert(sizeof(int) >= sizeof(my_bool));
+
        glite_jp_clear_error(ctx);
 
        if (!cs) return JP_ERR(ctx, EINVAL, "connect string not specified");
@@ -118,7 +153,7 @@ int glite_jp_db_connect(glite_jp_context_t ctx,const char *cs)
 
        if (!slash || !at || !colon) {
                free(buf);
-               mysql_close((MYSQL *)ctx->dbhandle);
+               glite_jp_db_close(ctx);
                return JP_ERR(ctx, EINVAL, "Invalid DB connect string");
        }
 
@@ -131,19 +166,26 @@ int glite_jp_db_connect(glite_jp_context_t ctx,const char *cs)
        if (!mysql_real_connect((MYSQL *) ctx->dbhandle,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) {
                free(buf);
                ret = MY_ERR(ctx);
-               mysql_close((MYSQL *)ctx->dbhandle);
+               glite_jp_db_close(ctx);
                return ret;
        }
-
        free(buf);
+
+       if ((ret = glite_jp_db_dbcheckversion(ctx)) != 0) {
+               glite_jp_db_close(ctx);
+               return ret;
+       }
+
        return 0;
 }
 
 
 void glite_jp_db_close(glite_jp_context_t ctx)
 {
-       mysql_close((MYSQL *) ctx->dbhandle);
-       ctx->dbhandle = NULL;
+       if (ctx->dbhandle) {
+               mysql_close((MYSQL *) ctx->dbhandle);
+               ctx->dbhandle = NULL;
+       }
 }
 
 
@@ -248,6 +290,8 @@ void glite_jp_db_freestmt(glite_jp_db_stmt_t *stmt)
        if (*stmt) {
                if ((**stmt).result) mysql_free_result((**stmt).result);
                if ((*stmt)->stmt) mysql_stmt_close((*stmt)->stmt);
+               if ((*stmt)->params) glite_jp_db_destroy_params((*stmt)->params);
+               if ((*stmt)->results) glite_jp_db_destroy_results((*stmt)->results);
                free(*stmt);
                *stmt = NULL;
        }
@@ -299,7 +343,7 @@ int glite_jp_db_dbcheckversion(glite_jp_context_t ctx)
 
        version = 10000*major + 100*minor + sub;
 
-       if (version < GLITE_JP_LB_MYSQL_VERSION) {
+       if (version < GLITE_JP_DB_MYSQL_VERSION) {
                char    msg[300];
 
                return JP_ERR(ctx, EINVAL, msg);
@@ -309,106 +353,240 @@ int glite_jp_db_dbcheckversion(glite_jp_context_t ctx)
 }
 
 
-void glite_jp_db_assign_param(MYSQL_BIND *param, enum enum_field_types type, ...) {
+void glite_jp_db_create_params(void **params, int n, ...) {
+       params_t *myparams;
+       MYSQL_BIND *myparam;
+       MYSQL_TIME **mytime;
+       int i;
        va_list ap;
+       glite_jp_db_type_t type;
 
-       memset(param, 0, sizeof(*param));
-       param->buffer_type = type;
+       myparams = calloc(n, sizeof(params_t) + (n - 1) * sizeof(MYSQL_BIND));
+       va_start(ap, n);
 
-       va_start(ap, type);
+       for (i = 0; i < n; i++) {
+               myparam = myparams->params + i;
+               type = va_arg(ap, glite_jp_db_type_t);
 
-       switch (type) {
-       case MYSQL_TYPE_TINY:
-               param->buffer = va_arg(ap, char *);
-               break;
+               switch (type) {
+               case GLITE_JP_DB_TYPE_TINYINT:
+                       myparam->buffer = va_arg(ap, char *);
+                       break;
 
-       case MYSQL_TYPE_LONG:
-               param->buffer = va_arg(ap, long int *);
-               break;
+               case GLITE_JP_DB_TYPE_INT:
+                       myparam->buffer = va_arg(ap, long int *);
+                       break;
 
-       case MYSQL_TYPE_TINY_BLOB:
-       case MYSQL_TYPE_MEDIUM_BLOB:
-       case MYSQL_TYPE_LONG_BLOB:
-       case MYSQL_TYPE_BLOB:
-               param->buffer = va_arg(ap, void *);
-               param->length = va_arg(ap, unsigned long *);
-               break;
+               case GLITE_JP_DB_TYPE_TINYBLOB:
+               case GLITE_JP_DB_TYPE_TINYTEXT:
+               case GLITE_JP_DB_TYPE_BLOB:
+               case GLITE_JP_DB_TYPE_TEXT:
+               case GLITE_JP_DB_TYPE_MEDIUMBLOB:
+               case GLITE_JP_DB_TYPE_MEDIUMTEXT:
+               case GLITE_JP_DB_TYPE_LONGBLOB:
+               case GLITE_JP_DB_TYPE_LONGTEXT:
+                       myparam->buffer = va_arg(ap, void *);
+                       myparam->length = va_arg(ap, unsigned long *);
+                       break;
+
+               case GLITE_JP_DB_TYPE_VARCHAR:
+               case GLITE_JP_DB_TYPE_CHAR:
+                       myparam->buffer = va_arg(ap, char *);
+                       myparam->length = va_arg(ap, unsigned long *);
+                       break;
 
-       case MYSQL_TYPE_VAR_STRING:
-       case MYSQL_TYPE_STRING:
-               param->buffer = va_arg(ap, char *);
-               param->length = va_arg(ap, unsigned long *);
-               break;
+               case GLITE_JP_DB_TYPE_DATE:
+               case GLITE_JP_DB_TYPE_TIME:
+               case GLITE_JP_DB_TYPE_DATETIME:
+               case GLITE_JP_DB_TYPE_TIMESTAMP:
+                       mytime = (MYSQL_TIME **)va_arg(ap, void **);
+                       *mytime = calloc(1, sizeof(MYSQL_TIME));
+                       myparam->buffer = *mytime;
+                       break;
 
-       case MYSQL_TYPE_NULL:
-               break;
+               case GLITE_JP_DB_TYPE_NULL:
+                       break;
 
-       default:
-               assert("unimplemented parameter assign" == NULL);
-               break;
+               default:
+                       assert("unimplemented parameter assign" == NULL);
+                       break;
+               }
+               myparam->buffer_type = glite_to_mysql_type[type];
        }
+       myparams->n = n;
 
        va_end(ap);
+       *params = myparams;
 }
 
-void glite_jp_db_assign_result(MYSQL_BIND *param, enum enum_field_types type, my_bool *is_null, ...) {
+
+void glite_jp_db_create_results(void **results, int n, ...) {
+       params_t *myresults;
+       MYSQL_BIND *myresult;
+       MYSQL_TIME **mytime;
        va_list ap;
+       int i;
+       glite_jp_db_type_t type;
+       int *is_null;
+
+       myresults = calloc(n, sizeof(params_t) + (n - 1) * sizeof(MYSQL_BIND));
+       va_start(ap, n);
+
+       for (i = 0; i < n; i++) {
+               myresult = myresults->params + i;
+               type = va_arg(ap, glite_jp_db_type_t);
+               is_null = va_arg(ap, int *);
+               myresult->is_null = (my_bool *)is_null;
+               if (is_null) *is_null = 0;
+
+               switch(type) {
+               case GLITE_JP_DB_TYPE_TINYINT:
+                       myresult->buffer = va_arg(ap, char *);
+                       myresult->buffer_length = sizeof(char);
+                       break;
+
+               case GLITE_JP_DB_TYPE_INT:
+                       myresult->buffer = va_arg(ap, long int *);
+                       myresult->buffer_length = sizeof(long int);
+                       break;
+
+               case GLITE_JP_DB_TYPE_TINYBLOB:
+               case GLITE_JP_DB_TYPE_TINYTEXT:
+               case GLITE_JP_DB_TYPE_BLOB:
+               case GLITE_JP_DB_TYPE_TEXT:
+               case GLITE_JP_DB_TYPE_MEDIUMBLOB:
+               case GLITE_JP_DB_TYPE_MEDIUMTEXT:
+               case GLITE_JP_DB_TYPE_LONGBLOB:
+               case GLITE_JP_DB_TYPE_LONGTEXT:
+                       myresult->buffer = va_arg(ap, void *);
+                       myresult->buffer_length = va_arg(ap, unsigned long);
+                       myresult->length = va_arg(ap, unsigned long *);
+                       break;
 
-       memset(param, 0, sizeof(*param));
-       param->buffer_type = type;
-       param->is_null = is_null;
-
-       va_start(ap, is_null);
-
-       switch(type) {
-       case MYSQL_TYPE_TINY:
-               param->buffer = va_arg(ap, char *);
-               param->buffer_length = sizeof(char);
-               break;
-
-       case MYSQL_TYPE_LONG:
-               param->buffer = va_arg(ap, long int *);
-               param->buffer_length = sizeof(long int);
-               break;
-
-       case MYSQL_TYPE_TINY_BLOB:
-       case MYSQL_TYPE_MEDIUM_BLOB:
-       case MYSQL_TYPE_LONG_BLOB:
-       case MYSQL_TYPE_BLOB:
-               param->buffer = va_arg(ap, void *);
-               param->buffer_length = va_arg(ap, unsigned long);
-               param->length = va_arg(ap, unsigned long *);
-               break;
-
-       case MYSQL_TYPE_VAR_STRING:
-       case MYSQL_TYPE_STRING:
-               param->buffer = va_arg(ap, char *);
-               param->buffer_length = va_arg(ap, unsigned long);
-               param->length = va_arg(ap, unsigned long *);
-               break;
-
-       default:
-               assert("unimplemented result assign" == NULL);
+               case GLITE_JP_DB_TYPE_VARCHAR:
+               case GLITE_JP_DB_TYPE_CHAR:
+                       myresult->buffer = va_arg(ap, char *);
+                       myresult->buffer_length = va_arg(ap, unsigned long);
+                       myresult->length = va_arg(ap, unsigned long *);
+                       break;
+
+               case GLITE_JP_DB_TYPE_DATE:
+               case GLITE_JP_DB_TYPE_TIME:
+               case GLITE_JP_DB_TYPE_DATETIME:
+               case GLITE_JP_DB_TYPE_TIMESTAMP:
+                       mytime = (MYSQL_TIME **)va_arg(ap, void **);
+                       *mytime = calloc(1, sizeof(MYSQL_TIME));
+                       myresult->buffer = *mytime;
+                       break;
+
+               default:
+                       assert("unimplemented result assign" == NULL);
+               }
+               myresult->buffer_type = glite_to_mysql_type[type];
+               if (myresult->buffer && myresult->buffer_length) memset(myresult->buffer, 0, myresult->buffer_length);
        }
-       if (param->buffer && param->buffer_length) memset(param->buffer, 0, param->buffer_length);
+       myresults->n = n;
 
        va_end(ap);
+       *results = myresults;
 }
 
 
-int glite_jp_db_prepare(glite_jp_context_t ctx, const char *sql, glite_jp_db_stmt_t *jpstmt, MYSQL_BIND *params, MYSQL_BIND *cols) {
+static void glite_jp_db_destroy_respam(params_t *params) {
+       MYSQL_BIND *myparam;
+       int i;
+       enum enum_field_types type;
+
+       for (i = 0; i < params->n; i++) {
+               myparam = params->params + i;
+               type = myparam->buffer_type;
+               if (type == MYSQL_TYPE_DATE || type == MYSQL_TYPE_TIME || type == MYSQL_TYPE_DATETIME || type == MYSQL_TYPE_TIMESTAMP) {
+                       free(myparam->buffer);
+                       myparam->buffer = NULL;
+               }
+       }
+       free(params);
+}
+
+
+void glite_jp_db_destroy_params(void *params) {
+       glite_jp_db_destroy_respam(params);
+}
+
+
+void glite_jp_db_destroy_results(void *results) {
+       glite_jp_db_destroy_respam(results);
+}
+
+
+void glite_jp_db_set_time(void *buffer, const time_t time) {
+       MYSQL_TIME *mybuffer;
+       struct tm tm;
+
+       mybuffer = (MYSQL_TIME *)buffer;
+       gmtime_r(&time, &tm);
+       mybuffer->year = tm.tm_year + 1900;
+       mybuffer->month = tm.tm_mon + 1;
+       mybuffer->day = tm.tm_mday;
+       mybuffer->hour = tm.tm_hour;
+       mybuffer->minute = tm.tm_min;
+       mybuffer->second = tm.tm_sec;
+}
+
+
+time_t glite_jp_db_get_time(const void *buffer) {
+       MYSQL_TIME *mybuffer;
+       struct tm tm;
+
+       mybuffer = (MYSQL_TIME *)buffer;
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_year = mybuffer->year - 1900;
+       tm.tm_mon = mybuffer->month - 1;
+       tm.tm_mday = mybuffer->day;
+       tm.tm_hour = mybuffer->hour;
+       tm.tm_min = mybuffer->minute;
+       tm.tm_sec = mybuffer->second;
+
+       return mktime(&tm);
+}
+
+
+int glite_jp_db_rebind(glite_jp_db_stmt_t jpstmt, void *params, void *cols) {
+       if (jpstmt->params) {
+               glite_jp_db_destroy_params(jpstmt->params);
+               jpstmt->params = NULL;
+       }
+       if (jpstmt->results) {
+               glite_jp_db_destroy_results(jpstmt->results);
+               jpstmt->results = NULL;
+       }
+       if (params) {
+               jpstmt->params = (params_t *)params;
+               if (mysql_stmt_bind_param(jpstmt->stmt, jpstmt->params->params) != 0) return MY_ERRSTMT(jpstmt);
+       }
+       if (cols) {
+               jpstmt->results = (params_t *)cols;
+               if (mysql_stmt_bind_result(jpstmt->stmt, jpstmt->results->params) != 0) return MY_ERRSTMT(jpstmt);
+       }
+
+       return 0;
+}
+
+
+int glite_jp_db_prepare(glite_jp_context_t ctx, const char *sql, glite_jp_db_stmt_t *jpstmt, void *params, void *cols) {
        int ret, retry;
 
        glite_jp_clear_error(ctx);
 
        // init
-       *jpstmt = malloc(sizeof(struct _glite_jp_db_stmt_t));
+       *jpstmt = calloc(1, sizeof(struct _glite_jp_db_stmt_t));
        (*jpstmt)->ctx = ctx;
-       (*jpstmt)->result = NULL;
 
        // create the SQL command
-       if (((*jpstmt)->stmt = mysql_stmt_init((MYSQL *)ctx->dbhandle)) == NULL)
-               return MY_ERRSTMT(*jpstmt);
+       if (((*jpstmt)->stmt = mysql_stmt_init((MYSQL *)ctx->dbhandle)) == NULL) {
+               ret = MY_ERRSTMT(*jpstmt);
+               goto failed;
+       }
 
        // prepare the SQL command
        retry = 1;
@@ -418,21 +596,15 @@ int glite_jp_db_prepare(glite_jp_context_t ctx, const char *sql, glite_jp_db_stm
        } while (ret == 0);
        if (ret == -1) goto failed;
 
-       // parameters
-       if (params) {
-               if (mysql_stmt_bind_param((*jpstmt)->stmt, params) != 0)
-                       return MY_ERRSTMT(*jpstmt);
-       }
-
-       // results
-       if (cols) {
-               if (mysql_stmt_bind_result((*jpstmt)->stmt, cols) != 0)
-                       return MY_ERRSTMT(*jpstmt);
-       }
+       // parameters and results
+       if ((ret = glite_jp_db_rebind(*jpstmt, params, cols)) != 0) goto failed;
 
        return 0;
 
 failed:
+       if (params) glite_jp_db_destroy_params(params);
+       if (cols) glite_jp_db_destroy_params(cols);
+       glite_jp_db_freestmt(jpstmt);
        return ctx->error->code;
 }