Database access routines adapted for JP environment
authorZdeněk Salvet <salvet@ics.muni.cz>
Fri, 6 May 2005 06:48:19 +0000 (06:48 +0000)
committerZdeněk Salvet <salvet@ics.muni.cz>
Fri, 6 May 2005 06:48:19 +0000 (06:48 +0000)
(new names, error handling, and default connect string).

org.glite.jp.primary/src/db.h [new file with mode: 0644]
org.glite.jp.primary/src/mysql.c [new file with mode: 0644]

diff --git a/org.glite.jp.primary/src/db.h b/org.glite.jp.primary/src/db.h
new file mode 100644 (file)
index 0000000..0b9f730
--- /dev/null
@@ -0,0 +1,83 @@
+#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;
+
+int glite_jp_db_connect(
+       glite_jp_context_t,     /* INOUT: */
+       char *          /* IN: connect string user/password@host:database */
+);
+
+void glite_jp_db_close(glite_jp_context_t);
+
+
+/* Parse and execute SQL statement. Returns number of rows selected, created 
+ * or affected by update, or -1 on error */
+
+int glite_jp_db_execstmt(
+       glite_jp_context_t,     /* INOUT: */
+       char *,         /* IN: SQL statement */
+       glite_jp_db_stmt_t *    /* OUT: statement handle. Usable for
+                                       select only */
+);
+
+
+/* Fetch next row of select statement. 
+ * All columns are returned as fresh allocated strings 
+ *
+ * return values:
+ *     >0 - number of fields of the retrieved row
+ *      0 - no more rows
+ *     -1 - error
+ *
+ * Errors are stored in context passed to previous glite_jp_db_execstmt() */
+
+int glite_jp_db_fetchrow(
+       glite_jp_db_stmt_t,     /* IN: statement */
+       char **         /* OUT: array of fetched values. 
+                        *      As number of columns is fixed and known,
+                        *      expects allocated array of pointers here */
+);
+
+/* Retrieve column names of a query statement */
+
+int glite_jp_db_querycolumns(
+       glite_jp_db_stmt_t,     /* IN: statement */
+       char **         /* OUT: result set column names. Expects allocated array. */
+);
+
+/* Free the statement structure */
+
+void glite_jp_db_freestmt(
+       glite_jp_db_stmt_t *    /* INOUT: statement */
+);
+
+
+/* convert time_t into database-specific time string 
+ * returns pointer to static area that is changed by subsequent calls */
+
+char *glite_jp_db_timetodb(time_t);
+time_t glite_jp_db_dbtotime(char *);
+
+
+/**
+ * Check database version.
+ */
+int glite_jp_db_dbcheckversion(glite_jp_context_t);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.jp.primary/src/mysql.c b/org.glite.jp.primary/src/mysql.c
new file mode 100644 (file)
index 0000000..591d8e1
--- /dev/null
@@ -0,0 +1,260 @@
+#ident "$Header$"
+
+#include "mysql.h"     // MySql header file
+#include "mysqld_error.h"
+#include "errmsg.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "glite/jp/types.h"
+#include "glite/jp/context.h"
+
+#include "db.h"
+
+#define DEFAULTCS      "jpps/@localhost:jpps1"
+
+static int  my_err(glite_jp_context_t ctx, char *function)
+{      
+       glite_jp_error_t err; 
+
+       glite_jp_clear_error(ctx); 
+       memset(&err,0,sizeof err); 
+       err.source = function;
+       err.code = EIO; /* XXX */
+       err.desc = mysql_error((MYSQL *) ctx->dbhandle); 
+       return glite_jp_stack_error(ctx,&err); 
+}
+
+struct _glite_jp_db_stmt_t {
+       MYSQL_RES               *result;
+       glite_jp_context_t      ctx;
+};
+
+int glite_jp_db_connect(glite_jp_context_t ctx,char *cs)
+{
+       char    *buf = NULL;
+       char    *host,*user,*pw,*db; 
+       char    *slash,*at,*colon;
+
+       glite_jp_error_t err;
+
+       glite_jp_clear_error(ctx);
+       memset(&err,0,sizeof err);
+       err.source = __FUNCTION__;
+
+       if (!cs) cs = DEFAULTCS;
+
+       if (!(ctx->dbhandle = (void *) mysql_init(NULL))) {
+               err.code = ENOMEM;
+               return glite_jp_stack_error(ctx,&err);
+       }
+
+       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);
+               err.code = EINVAL;
+               err.desc = "Invalid DB connect string");
+               return glite_jp_stack_error(ctx,&err);
+       }
+
+       *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);
+               return my_err(ctx, __FUNCTION__);
+       }
+
+       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,char *txt,glite_jp_db_stmt_t *stmt)
+{
+       int     err;
+       int     retry_nr = 0;
+       int     do_reconnect = 0;
+
+       glite_jp_error_t err;
+
+       glite_jp_clear_error(ctx);
+       memset(&err,0,sizeof err);
+       err.source = __FUNCTION__;
+
+       if (stmt) {
+               *stmt = NULL;
+       }
+
+       while (retry_nr == 0 || do_reconnect) {
+               do_reconnect = 0;
+               if (mysql_query((MYSQL *) ctx->dbhandle,txt)) {
+                       /* error occured */
+                       switch (err = mysql_errno((MYSQL *) ctx->dbhandle)) {
+                               case 0:
+                                       break;
+                               case ER_DUP_ENTRY: 
+                                       err.code = EEXIST;
+                                       err.desc = mysql_error((MYSQL *) ctx->dbhandle));
+                                       glite_jp_stack_error(ctx,&err);
+                                       return -1;
+                                       break;
+                               case CR_SERVER_LOST:
+                                       if (retry_nr <= 0) 
+                                               do_reconnect = 1;
+                                       break;
+                               default:
+                                       my_err(ctx, __FUNCTION__);
+                                       return -1;
+                                       break;
+                       }
+               }
+               retry_nr++;
+       }
+
+       if (stmt) {
+               *stmt = malloc(sizeof(**stmt));
+               if (!*stmt) {
+                       err.code = ENOMEM;
+                       glite_jp_stack_error(ctx,&err);
+                       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, __FUNCTION__);
+                               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, __FUNCTION__);
+                       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);
+               free(*stmt);
+               *stmt = NULL;
+       }
+}
+
+
+char *glite_jp_db_timetodb(time_t t)
+{
+       struct tm       *tm = gmtime(&t);
+       char    tbuf[256];
+
+       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(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_error_t err;
+
+       glite_jp_clear_error(ctx);
+       memset(&err,0,sizeof err);
+       err.source = __FUNCTION__;
+
+       if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub)) {
+               err.code = EINVAL;
+               err.desc = "problem checking MySQL version";
+               return glite_jp_stack_error(ctx,&err);
+       }
+
+       version = 10000*major + 100*minor + sub;
+
+       if (version < GLITE_JP_LB_MYSQL_VERSION) {
+               char    msg[300];
+
+               snprintf(msg,sizeof msg,"Your MySQL version is %d. At least %d required.",version, GLITE_JP_LB_MYSQL_VERSION);
+               err.code = EINVAL;
+               err.desc = msg;
+               return glite_jp_stack_error(ctx,&err);
+       }
+
+       return 0;
+}