Initial import of common module for server side. It depends on mysql.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 15 Sep 2005 16:53:25 +0000 (16:53 +0000)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 15 Sep 2005 16:53:25 +0000 (16:53 +0000)
org.glite.jp.server-common/Makefile [new file with mode: 0644]
org.glite.jp.server-common/build.xml [new file with mode: 0755]
org.glite.jp.server-common/interface/db.h [new file with mode: 0644]
org.glite.jp.server-common/project/configure.properties.xml [new file with mode: 0644]
org.glite.jp.server-common/project/properties.xml [new file with mode: 0755]
org.glite.jp.server-common/project/tar_exclude [new file with mode: 0644]
org.glite.jp.server-common/project/version.properties [new file with mode: 0644]
org.glite.jp.server-common/src/db.c [new file with mode: 0644]

diff --git a/org.glite.jp.server-common/Makefile b/org.glite.jp.server-common/Makefile
new file mode 100644 (file)
index 0000000..cb1f049
--- /dev/null
@@ -0,0 +1,106 @@
+# defaults
+top_srcdir=.
+builddir=build
+top_builddir=${top_srcdir}/${builddir}
+stagedir=.
+distdir=.
+globalprefix=glite
+jpprefix=jp
+package=glite-jp-server-common
+version=0.0.0
+PREFIX=/opt/glite
+
+glite_location=/opt/glite
+globus_prefix=/opt/globus
+nothrflavour=gcc32
+thrflavour=gcc32pthr
+expat_prefix=/opt/expat
+ares_prefix=/opt/ares
+gsoap_prefix=/software/gsoap-2.6
+
+CC=gcc
+
+-include Makefile.inc
+
+
+VPATH=${top_srcdir}/src:${top_srcdir}/examples:${top_srcdir}/test:${top_srcdir}/project:${jpproject}:${stagedir}/interface
+
+DEBUG:=-g -O0 -W -Wall -DDEBUG
+CPPFLAGS:=-I. -I${top_srcdir}/interface -I${top_srcdir}/src -I${stagedir}/include -I${mysql_prefix}/include -I${mysql_prefix}/include/mysql
+CFLAGS:=${DEBUG} -D_GNU_SOURCE
+LDFLAGS:=-L${stagedir}/lib
+
+COMPILE:=libtool --mode=compile ${CC} ${CPPFLAGS} ${CFLAGS}
+LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/lib ${LDFLAGS} 
+INSTALL:=libtool --mode=install install
+
+# FIXME: to use libtool versioning correcty, we should have:
+#
+# current = major + minor + offset
+# revision = patch
+# age = minor
+#
+# where offset is a sum of maximal released minor's of all previous major's
+# 
+version_info=-version-info `echo ${version} | cut -d. -f1,2 | tr . :`
+
+STATICLIB:=libglite_jp_server_common.a
+LTLIB:=libglite_jp_server_common.la
+
+
+SRCS:=db.c
+HDRS:=db.h
+OBJS:=${SRCS:.c=.o}
+LOBJS:=${OBJS:.o=.lo}
+
+ifneq (${mysql_prefix},/usr)
+       ifeq ($(shell echo ${mysql_version} | cut -d. -f1,2),4.1)
+               MYSQLIB := -L${mysql_prefix}/lib/mysql -lmysqlclient
+       else
+               MYSQLIB := -L${mysql_prefix}/lib -lmysqlclient
+       endif
+else
+       MYSQLIB := -lmysqlclient
+endif
+
+
+default all: compile
+
+compile: ${LTLIB} ${STATICLIB}
+
+${LTLIB} ${STATICLIB}: ${OBJS}
+       ${LINK} ${version_info} -o $@ ${LOBJS} ${MYSQLIB}
+
+check: 
+       -echo nothing yet
+
+doc:
+
+stage: compile
+       $(MAKE) install PREFIX=${stagedir}
+
+dist: distsrc distbin
+
+distsrc:
+       mkdir -p ${top_srcdir}/${package}-${version}
+       cd ${top_srcdir} && GLOBIGNORE="${package}-${version}" && cp -Rf * ${package}-${version}
+       cd ${top_srcdir} && tar -czf ${distdir}/${package}-${version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${version}
+       rm -rf ${top_srcdir}/${package}-${version}
+
+distbin:
+       $(MAKE) install PREFIX=`pwd`/tmpbuilddir${stagedir}
+       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz *; cd $$save_dir
+       rm -rf tmpbuilddir
+       
+install:
+       ${INSTALL} -m 755 ${LTLIB} ${PREFIX}/lib
+       for f in ${HDRS}; do \
+               ${INSTALL} -m 644 ${top_srcdir}/interface/"$$f" ${PREFIX}/include/${globalprefix}/${jpprefix}; \
+       done
+
+clean:
+
+%.o: %.c
+       ${COMPILE} -c $< -o $@
+
+.PHONY: default all compile check doc stage dist distsrc distbin install clean
diff --git a/org.glite.jp.server-common/build.xml b/org.glite.jp.server-common/build.xml
new file mode 100755 (executable)
index 0000000..33d20cf
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+       Copyright (c) Members of the EGEE Collaboration. 2004 
+       See http://eu-egee.org/partners/ for details on the copyright holders
+       For license conditions see the license file or http://eu-egee.org/license.html
+
+       Build file for the gLite JP Server Common module
+       
+       Authors: Ales Krenek <ljocha@ics.muni.cz>
+       Version info: $Id$
+       Release: $Name$
+
+       Revision history:
+       $Log$
+       Revision 1.3  2005/05/26 15:13:28  zurek
+       inserted module.build.file
+       
+       Revision 1.2  2004/11/22 14:00:37  dimeglio
+       Updated to use standard files
+       Fixed names (was using common instead of real module name)
+       
+       Revision 1.1.1.1  2004/10/15 09:49:13  akrenek
+-->
+
+<project name="server-common" default="dist">
+       
+       <!-- =========================================
+                Builds the GLite JP Common Server Module
+            ========================================= -->
+       
+       <!-- =========================================
+            Import properties (order is important)
+            ========================================= -->
+
+       <!-- import baseline & user properties -->
+       <import file="../org.glite/project/baseline.properties.xml" />
+
+       <!-- import component build properties,
+                       component properties &
+                       component common properties -->
+       <import file="./project/properties.xml"/>
+       
+       <!-- import subsystem build properties,
+                       subsystem properties &
+                       subsystem common properties -->
+       <import file="${subsystem.properties.file}"/>
+
+       <!-- import global build properties &
+                       global properties -->
+       <import file="${global.properties.file}" />
+               
+       <!-- =========================================
+                Load dependency property files (order is important)
+            ========================================= -->
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <!-- =========================================
+                 Load configure options (order is important)
+             ========================================= -->
+        <import file="${global.configure.options.file}"/>
+        <import file="${component.configure.options.file}"/>
+       
+       <!-- =========================================
+                Import task definitions (order is important)
+            ========================================= -->
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+                       
+       <!-- =========================================
+                Load common targets
+            ========================================= -->
+       <import file="${global.targets-simple_make.file}" />
+
+       <!-- =========================================
+                Load version file 
+            ========================================= -->
+       <property file="${module.version.file}"/>
+       <property file="${module.build.file}"/>
+               
+       <!-- ==============================================
+                Local private targets
+            ============================================== -->
+       
+       <target name="localinit"
+               description="Module specific initialization tasks">
+
+               <antcall target="lbmakefiles" />
+       </target>
+               
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.jp.server-common/interface/db.h b/org.glite.jp.server-common/interface/db.h
new file mode 100644 (file)
index 0000000..c5eca7f
--- /dev/null
@@ -0,0 +1,178 @@
+#ifndef _DB_H
+#define _DB_H
+
+#ident "$Header$"
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct _glite_jp_db_stmt_t *glite_jp_db_stmt_t;
+
+
+/**
+ * Connect to the database.
+ *
+ * \param[inout] cxt   context to work with
+ * \param[in] cs       connect string user/password@host:database
+ *
+  \return      JP error code
+ */
+int glite_jp_db_connect(glite_jp_context_t,const char *);
+
+
+/**
+ * Close the connection to database.
+ *
+ * \param[inout] ctx   context to work with
+ */
+void glite_jp_db_close(glite_jp_context_t);
+
+
+/**
+ * Parse and execute SQL statement.
+ *
+ * \param[inout] ctx   context to work with
+ * \param[in] txt      SQL statement
+ * \param[out] stmt    statement handle, usable for select only
+ *
+ * \return     number of rows selected, created or affected by update, or -1 on error
+ */
+int glite_jp_db_execstmt(glite_jp_context_t, const char *, glite_jp_db_stmt_t *);
+
+
+/** Fetch next row of select statement. 
+ * All columns are returned as fresh allocated strings 
+ *
+ * \param[inout] stmt  statement from glite_jp_db_execstmt()
+ * \param[out] array of fetched values.
+ *              As number of columns is fixed and known,
+ *              expects allocated array of pointers here.
+ *
+ * \retval >0  number of fields of the retrieved row
+ * \retval 0   no more rows
+ * \retval -1  error
+ *
+ * Errors are stored in context passed to previous glite_jp_db_execstmt()
+ */
+int glite_jp_db_fetchrow(glite_jp_db_stmt_t, char **);
+
+
+/**
+ * Retrieve column names of a query statement
+ *
+ * \param[inout] stmt  statement
+ * \param[out] cols    result set column names. Expects allocated array.
+ *
+ * \return     0 if OK, nonzero on error
+ */
+int glite_jp_db_querycolumns(glite_jp_db_stmt_t, char **);
+
+
+/**
+ * Free the statement structure
+ *
+ * \param[inout] stmt  statement
+ */
+void glite_jp_db_freestmt(glite_jp_db_stmt_t *);
+
+
+/** 
+ * Convert time_t into database-specific time string.
+ *
+ * \param[in] t        the converted time
+ * \return     XXX: pointer to static area that is changed by subsequent calls
+ */
+char *glite_jp_db_timetodb(time_t);
+
+
+/** 
+ * Convert database-specific time string into time_t.
+ *
+ * \param[in] t        the converted string
+ *
+ * \return     result time
+ */
+time_t glite_jp_db_dbtotime(const char *);
+
+
+/**
+ * Check database version.
+ *
+ * \param[inout] ctx   context to work with
+ *
+ * \return JP error code
+ */
+int glite_jp_db_dbcheckversion(glite_jp_context_t);
+
+
+/**
+ * Assign parameters to mysql bind structure.
+ *
+ * \param[inout] param mysql bind strusture array
+ * \param[in] type     mysql type
+ *
+ * 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:        -
+ */
+void glite_jp_db_assign_param(MYSQL_BIND *param, enum enum_field_types type, ...);
+
+
+/**
+ * Assign result variables to mysql bind structure.
+ *
+ * \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
+ */
+void glite_jp_db_assign_result(MYSQL_BIND *result, enum enum_field_types type, my_bool *is_null, ...);
+
+/**
+ * Prepare the SQL statement. Use glite_jp_db_freestmt() to free it.
+ *
+ * \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
+ *
+ * \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);
+
+/**
+ * Execute prepared SQL statement.
+ *
+ * \param[inout] jpstmt        JP SQL statement
+ *
+ * \return     number of affected rows, -1 on error
+ */
+int glite_jp_db_execute(glite_jp_db_stmt_t jpstmt);
+
+/**
+ *
+ * \param[inout] jpstmt        JP SQL statement
+ *
+ * \return JP error code (ENODATA when no more row are available)
+ */
+int glite_jp_db_fetch(glite_jp_db_stmt_t jpstmt);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.jp.server-common/project/configure.properties.xml b/org.glite.jp.server-common/project/configure.properties.xml
new file mode 100644 (file)
index 0000000..b8d50bd
--- /dev/null
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+       Copyright (c) Members of the EGEE Collaboration. 2004 
+       See http://eu-egee.org/partners/ for details on the copyright holders
+       For license conditions see the license file or http://eu-egee.org/license.html
+
+       Configuration options for the gLite JP Index module
+       
+       Authors: Ales Krenek <ljocha@ics.muni.cz>
+       Version info: $Id$
+       Release: $Name$
+
+       Revision history:
+       $Log$
+       Revision 1.4  2005/08/12 10:56:25  mmulac
+       void IS server
+       - seems compiling
+       
+       Revision 1.3  2004/12/01 18:45:38  zsalvet
+       *** empty log message ***
+       
+       Revision 1.2  2004/11/22 14:00:37  dimeglio
+       Updated to use standard files
+       Fixed names (was using common instead of real module name)
+       
+       Revision 1.1.1.1  2004/10/15 09:49:13  akrenek
+-->
+
+       <!-- ======================================================
+         Define extra properties here ...
+         ====================================================== -->
+        
+       <project name="JP Common configuration options">                                                                        
+               <target name="lbmakefiles">
+                       <exec executable="ln" failonerror="true">
+                               <arg line="-fs ${component.dir}/Makefile ${module.build.dir}/Makefile"/>
+                       </exec>
+                       <echo file="${module.build.dir}/Makefile.inc">
+top_srcdir=..
+builddir=build
+stagedir=${stage.abs.dir}
+distdir=${dist.dir}
+globalprefix=${global.prefix}
+jpprefix=${subsystem.prefix}
+package=${module.package.name}
+PREFIX=${install.dir}
+version=${module.version}
+glite_location=${with.glite.location}
+globus_prefix=${with.globus.prefix}
+expat_prefix=${with.expat.prefix}
+gsoap_prefix=${with.gsoap.prefix}
+ares_prefix=${with.ares.prefix}
+mysql_prefix=${with.mysql.prefix}
+mysql_version=${ext.mysql.version}
+thrflavour=${with.globus.thr.flavor}
+nothrflavour=${with.globus.nothr.flavor}
+cppunit=${with.cppunit.prefix}
+jpproject=${subsystem.project.dir}
+project=${component.project.dir}
+                       </echo>
+           </target>
+       </project>
diff --git a/org.glite.jp.server-common/project/properties.xml b/org.glite.jp.server-common/project/properties.xml
new file mode 100755 (executable)
index 0000000..bd0829c
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+       Copyright (c) Members of the EGEE Collaboration. 2004 
+       See http://eu-egee.org/partners/ for details on the copyright holders
+       For license conditions see the license file or http://eu-egee.org/license.html
+
+       Common build properties file for the gLite JP Common Server component
+       
+       Authors: Ales Krenek <ljocha@ics.muni.cz>
+       Version info: $Id$
+       Release: $Name$ 
+       
+       Revision history:
+       $Log$
+       Revision 1.2  2004/11/22 14:00:37  dimeglio
+       Updated to use standard files
+       Fixed names (was using common instead of real module name)
+       
+       Revision 1.1.1.1  2004/10/15 09:49:13  akrenek
+-->
+
+<project name="JP Common Server component common properties">
+
+       <!-- Include build properties to allow overwriting 
+            of properties for subsystem                    -->
+       <property file="project/build.properties" />    
+
+       <!-- ======================================================
+          Define corresponding subsystem properties
+                ====================================================== -->
+
+       <!-- Subsystem name -->
+       <property name="subsystem.name" value="${jp.subsystem.name}"/>
+               
+       <!-- Subsystem prefix -->
+       <property name="subsystem.prefix" value="${jp.subsystem.prefix}"/>
+
+       <!-- ======================================================
+          Define component properties
+                ====================================================== -->
+                               
+       <!-- Component name prefix -->
+       <property name="component.prefix" value="server-common" />
+                       
+       <!-- ======================================================
+          Define general component properties
+                ====================================================== -->
+       
+       <import file="${component.general.properties.file}" />
+                                               
+       <!-- ======================================================
+                Define extra properties here ...
+                ====================================================== -->
+                
+                                                               
+</project>
diff --git a/org.glite.jp.server-common/project/tar_exclude b/org.glite.jp.server-common/project/tar_exclude
new file mode 100644 (file)
index 0000000..e1fcd1a
--- /dev/null
@@ -0,0 +1,10 @@
+tar_exclude
+CVS
+build.xml
+build
+build.properties
+properties.xml
+configure.properties.xml
+.cvsignore
+.project
+.cdtproject
diff --git a/org.glite.jp.server-common/project/version.properties b/org.glite.jp.server-common/project/version.properties
new file mode 100644 (file)
index 0000000..cd1e9e7
--- /dev/null
@@ -0,0 +1,2 @@
+module.version=1.0.0
+module.age=1
diff --git a/org.glite.jp.server-common/src/db.c b/org.glite.jp.server-common/src/db.c
new file mode 100644 (file)
index 0000000..de024dd
--- /dev/null
@@ -0,0 +1,489 @@
+#ident "$Header$"
+
+#include "mysql.h"
+#include "mysqld_error.h"
+#include "errmsg.h"
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "glite/jp/types.h"
+#include "glite/jp/context.h"
+
+#include "db.h"
+
+#define GLITE_JP_LB_MYSQL_VERSION 40018
+
+#define JP_ERR(CTX, CODE, DESC) jp_err((CTX), (CODE), (DESC), __FUNCTION__, __LINE__)
+#define MY_ERR(CTX) my_err((CTX), __FUNCTION__, __LINE__)
+#define MY_ERRSTMT(JPSTMT) my_errstmt((JPSTMT), __FUNCTION__, __LINE__)
+#define MY_ISOKSTMT(JPSTMT, RETRY) my_isokstmt((JPSTMT), __FUNCTION__, __LINE__, (RETRY))
+
+
+struct _glite_jp_db_stmt_t {
+       glite_jp_context_t      ctx;
+       MYSQL_RES               *result;
+       MYSQL_STMT              *stmt;
+};
+
+
+static int jp_err(glite_jp_context_t ctx, int code, const char *desc, const char *source, int line)
+{
+       glite_jp_error_t err; 
+       char *fullsource;
+       int ret;
+
+       asprintf(&fullsource, "%s:%d", source, line);
+       memset(&err,0,sizeof err); 
+       err.code = code;
+       err.source = fullsource;
+       err.desc = desc; 
+
+       ret = glite_jp_stack_error(ctx,&err); 
+       free(fullsource);
+       return ret;
+}
+
+
+static int my_err(glite_jp_context_t ctx, const char *source, int line)
+{      
+       return jp_err(ctx, EIO, mysql_error((MYSQL *)ctx->dbhandle), source, line);
+}
+
+
+static int my_errstmt(glite_jp_db_stmt_t jpstmt, const char *source, int line) {       
+       return jp_err(jpstmt->ctx, EIO, mysql_stmt_error(jpstmt->stmt), source, line);
+}
+
+
+/*
+ * Error handle.
+ *
+ * \return -1 failed
+ * \return  0 retry
+ * \return  1 OK
+ */
+static int my_isokstmt(glite_jp_db_stmt_t jpstmt, const char *source, int line, int *retry) {
+       switch (mysql_stmt_errno(jpstmt->stmt)) {
+               case 0:
+                       return 1;
+                       break;
+               case ER_DUP_ENTRY:
+                       jp_err(jpstmt->ctx, EEXIST, mysql_stmt_error(jpstmt->stmt), source, line);
+                       return -1;
+                       break;
+               case CR_SERVER_LOST:
+                       if (*retry > 0) {
+                               (*retry)--;
+                               return 0;
+                       } else
+                               return -1;
+                       break;
+               default:
+                       my_errstmt(jpstmt, source, line);
+                       return -1;
+                       break;
+       }
+}
+
+
+int glite_jp_db_connect(glite_jp_context_t ctx,const char *cs)
+{
+       char    *buf = NULL;
+       char    *host,*user,*pw,*db; 
+       char    *slash,*at,*colon;
+       int      ret;
+
+       glite_jp_clear_error(ctx);
+
+       if (!cs) return JP_ERR(ctx, EINVAL, "connect string not specified");
+
+       if (!(ctx->dbhandle = (void *) mysql_init(NULL))) return JP_ERR(ctx, ENOMEM, NULL);
+
+       mysql_options(ctx->dbhandle, MYSQL_READ_DEFAULT_FILE, "my");
+
+       host = user = pw = db = NULL;
+
+       buf = strdup(cs);
+       slash = strchr(buf,'/');
+       at = strrchr(buf,'@');
+       colon = strrchr(buf,':');
+
+       if (!slash || !at || !colon) {
+               free(buf);
+               mysql_close((MYSQL *)ctx->dbhandle);
+               return JP_ERR(ctx, EINVAL, "Invalid DB connect string");
+       }
+
+       *slash = *at = *colon = 0;
+       host = at+1;
+       user = buf;
+       pw = slash+1;
+       db = colon+1;
+
+       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);
+               return ret;
+       }
+
+       free(buf);
+       return 0;
+}
+
+
+void glite_jp_db_close(glite_jp_context_t ctx)
+{
+       mysql_close((MYSQL *) ctx->dbhandle);
+       ctx->dbhandle = NULL;
+}
+
+
+int glite_jp_db_execstmt(glite_jp_context_t ctx,const char *txt,glite_jp_db_stmt_t *stmt)
+{
+       int     merr;
+       int     retry_nr = 0;
+       int     do_reconnect = 0;
+
+       glite_jp_clear_error(ctx);
+
+       if (stmt) {
+               *stmt = NULL;
+       }
+
+       while (retry_nr == 0 || do_reconnect) {
+               do_reconnect = 0;
+               if (mysql_query((MYSQL *) ctx->dbhandle,txt)) {
+                       /* error occured */
+                       switch (merr = mysql_errno((MYSQL *) ctx->dbhandle)) {
+                               case 0:
+                                       break;
+                               case ER_DUP_ENTRY: 
+                                       JP_ERR(ctx, EEXIST, mysql_error((MYSQL *) ctx->dbhandle));
+                                       return -1;
+                                       break;
+                               case CR_SERVER_LOST:
+                                       if (retry_nr <= 0) 
+                                               do_reconnect = 1;
+                                       break;
+                               default:
+                                       MY_ERR(ctx);
+                                       return -1;
+                                       break;
+                       }
+               }
+               retry_nr++;
+       }
+
+       if (stmt) {
+               *stmt = malloc(sizeof(**stmt));
+               if (!*stmt) {
+                       JP_ERR(ctx, ENOMEM, NULL);
+                       return -1;
+               }
+               memset(*stmt,0,sizeof(**stmt));
+               (**stmt).ctx = ctx;
+               (**stmt).result = mysql_store_result((MYSQL *) ctx->dbhandle);
+               if (!(**stmt).result) {
+                       if (mysql_errno((MYSQL *) ctx->dbhandle)) {
+                               MY_ERR(ctx);
+                               return -1;
+                       }
+               }
+       } else {
+               MYSQL_RES       *r = mysql_store_result((MYSQL *) ctx->dbhandle);
+               mysql_free_result(r);
+       }
+       
+       return mysql_affected_rows((MYSQL *) ctx->dbhandle);
+}
+
+
+int glite_jp_db_fetchrow(glite_jp_db_stmt_t stmt,char **res)
+{
+       MYSQL_ROW       row;
+       glite_jp_context_t      ctx = stmt->ctx;
+       int             nr,i;
+       unsigned long   *len;
+
+       glite_jp_clear_error(ctx);
+
+       if (!stmt->result) return 0;
+
+       if (!(row = mysql_fetch_row(stmt->result))) {
+               if (mysql_errno((MYSQL *) ctx->dbhandle)) {
+                       MY_ERR(ctx);
+                       return -1;
+               } else return 0;
+       }
+
+       nr = mysql_num_fields(stmt->result);
+       len = mysql_fetch_lengths(stmt->result);
+       for (i=0; i<nr; i++) res[i] = len[i] ? strdup(row[i]) : strdup("");
+
+       return nr;
+}
+
+
+int glite_jp_db_querycolumns(glite_jp_db_stmt_t stmt,char **cols)
+{
+       int     i = 0;
+       MYSQL_FIELD     *f;
+
+       while ((f = mysql_fetch_field(stmt->result))) cols[i++] = f->name;
+       return i == 0;
+}
+
+
+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);
+               free(*stmt);
+               *stmt = NULL;
+       }
+}
+
+
+char *glite_jp_db_timetodb(time_t t)
+{
+       struct tm       *tm = gmtime(&t);
+       char    tbuf[256];
+
+       /* XXX: the very end of our days */
+       if (!tm && t == (time_t) LONG_MAX) return strdup("9999-12-31 23:59:59");
+
+       sprintf(tbuf,"'%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);
+       
+       return strdup(tbuf);
+}
+
+
+time_t glite_jp_db_dbtotime(const char *t)
+{
+       struct tm       tm;
+
+       memset(&tm,0,sizeof(tm));
+       setenv("TZ","UTC",1); tzset();
+       sscanf(t,"%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_jp_db_dbcheckversion(glite_jp_context_t ctx)
+{
+       MYSQL   *m = (MYSQL *) ctx->dbhandle;
+       const   char *ver_s = mysql_get_server_info(m);
+       int     major,minor,sub,version;
+
+       glite_jp_clear_error(ctx);
+
+       if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub)) {
+               return JP_ERR(ctx, EINVAL, "problem checking MySQL version");
+       }
+
+       version = 10000*major + 100*minor + sub;
+
+       if (version < GLITE_JP_LB_MYSQL_VERSION) {
+               char    msg[300];
+
+               return JP_ERR(ctx, EINVAL, msg);
+       }
+
+       return 0;
+}
+
+
+void glite_jp_db_assign_param(MYSQL_BIND *param, enum enum_field_types type, ...) {
+       va_list ap;
+
+       memset(param, 0, sizeof(*param));
+       param->buffer_type = type;
+
+       va_start(ap, type);
+
+       switch (type) {
+       case MYSQL_TYPE_TINY:
+               param->buffer = va_arg(ap, char *);
+               break;
+
+       case MYSQL_TYPE_LONG:
+               param->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 MYSQL_TYPE_VAR_STRING:
+       case MYSQL_TYPE_STRING:
+               param->buffer = va_arg(ap, char *);
+               param->length = va_arg(ap, unsigned long *);
+               break;
+
+       case MYSQL_TYPE_NULL:
+               break;
+
+       default:
+               assert("unimplemented parameter assign" == NULL);
+               break;
+       }
+
+       va_end(ap);
+}
+
+void glite_jp_db_assign_result(MYSQL_BIND *param, enum enum_field_types type, my_bool *is_null, ...) {
+       va_list ap;
+
+       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);
+       }
+       if (param->buffer && param->buffer_length) memset(param->buffer, 0, param->buffer_length);
+
+       va_end(ap);
+}
+
+
+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) {
+       int ret, retry;
+
+       glite_jp_clear_error(ctx);
+
+       // init
+       *jpstmt = malloc(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);
+
+       // prepare the SQL command
+       retry = 1;
+       do {
+               mysql_stmt_prepare((*jpstmt)->stmt, sql, strlen(sql));
+               ret = MY_ISOKSTMT(*jpstmt, &retry);
+       } 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);
+       }
+
+       return 0;
+
+failed:
+       return ctx->error->code;
+}
+
+
+int glite_jp_db_execute(glite_jp_db_stmt_t jpstmt) {
+       glite_jp_context_t ctx;
+       int ret, retry;
+
+       ctx = jpstmt->ctx;
+       glite_jp_clear_error(ctx);
+
+       // run
+       retry = 1;
+       do {
+               mysql_stmt_execute(jpstmt->stmt);
+               ret = MY_ISOKSTMT(jpstmt, &retry);
+       } while (ret == 0);
+       if (ret == -1) goto failed;
+
+       // result
+       mysql_stmt_store_result(jpstmt->stmt);
+       if (mysql_stmt_errno(jpstmt->stmt)) {
+               MY_ERRSTMT(jpstmt);
+               goto failed;
+       }
+
+       return mysql_stmt_affected_rows(jpstmt->stmt);
+
+failed:
+       return -1;
+}
+
+
+int glite_jp_db_fetch(glite_jp_db_stmt_t jpstmt) {
+       int ret, retry;
+
+       glite_jp_clear_error(jpstmt->ctx);
+
+       retry = 1;
+       do {
+               switch(mysql_stmt_fetch(jpstmt->stmt)) {
+               case 0: ret = 1; break;
+               case 1: ret = MY_ISOKSTMT(jpstmt, &retry); break;
+               case MYSQL_NO_DATA: JP_ERR(jpstmt->ctx, ENODATA, "no more rows"); ret = -1; break;
+               default: JP_ERR(jpstmt->ctx, EIO, "other fetch error"); ret = -1; break;
+               }
+       } while (ret == 0);
+       if (ret == -1) goto failed;
+
+       return 0;
+
+failed:
+       return jpstmt->ctx->error->code;
+}