From bf4f504e53fd29cd21d822c1fb6cb6f2427be6c9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Franti=C5=A1ek=20Dvo=C5=99=C3=A1k?= Date: Thu, 15 Sep 2005 16:53:25 +0000 Subject: [PATCH] Initial import of common module for server side. It depends on mysql. --- org.glite.jp.server-common/Makefile | 106 +++++ org.glite.jp.server-common/build.xml | 101 +++++ org.glite.jp.server-common/interface/db.h | 178 ++++++++ .../project/configure.properties.xml | 62 +++ org.glite.jp.server-common/project/properties.xml | 56 +++ org.glite.jp.server-common/project/tar_exclude | 10 + .../project/version.properties | 2 + org.glite.jp.server-common/src/db.c | 489 +++++++++++++++++++++ 8 files changed, 1004 insertions(+) create mode 100644 org.glite.jp.server-common/Makefile create mode 100755 org.glite.jp.server-common/build.xml create mode 100644 org.glite.jp.server-common/interface/db.h create mode 100644 org.glite.jp.server-common/project/configure.properties.xml create mode 100755 org.glite.jp.server-common/project/properties.xml create mode 100644 org.glite.jp.server-common/project/tar_exclude create mode 100644 org.glite.jp.server-common/project/version.properties create mode 100644 org.glite.jp.server-common/src/db.c diff --git a/org.glite.jp.server-common/Makefile b/org.glite.jp.server-common/Makefile new file mode 100644 index 0000000..cb1f049 --- /dev/null +++ b/org.glite.jp.server-common/Makefile @@ -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 index 0000000..33d20cf --- /dev/null +++ b/org.glite.jp.server-common/build.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.jp.server-common/interface/db.h b/org.glite.jp.server-common/interface/db.h new file mode 100644 index 0000000..c5eca7f --- /dev/null +++ b/org.glite.jp.server-common/interface/db.h @@ -0,0 +1,178 @@ +#ifndef _DB_H +#define _DB_H + +#ident "$Header$" + +#include +#include + +#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 index 0000000..b8d50bd --- /dev/null +++ b/org.glite.jp.server-common/project/configure.properties.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + +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} + + + diff --git a/org.glite.jp.server-common/project/properties.xml b/org.glite.jp.server-common/project/properties.xml new file mode 100755 index 0000000..bd0829c --- /dev/null +++ b/org.glite.jp.server-common/project/properties.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.jp.server-common/project/tar_exclude b/org.glite.jp.server-common/project/tar_exclude new file mode 100644 index 0000000..e1fcd1a --- /dev/null +++ b/org.glite.jp.server-common/project/tar_exclude @@ -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 index 0000000..cd1e9e7 --- /dev/null +++ b/org.glite.jp.server-common/project/version.properties @@ -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 index 0000000..de024dd --- /dev/null +++ b/org.glite.jp.server-common/src/db.c @@ -0,0 +1,489 @@ +#ident "$Header$" + +#include "mysql.h" +#include "mysqld_error.h" +#include "errmsg.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iresult))) 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; +} -- 1.8.2.3