]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/cats/myingres.sc
Fix get_basename() -- rewrite
[bacula/bacula] / bacula / src / cats / myingres.sc
index 89a25623f33d95048a02cb112560c8df0de876d3..d91087b910ed99f8037fd5dffb3073c8e90b2f1b 100644 (file)
@@ -1,3 +1,40 @@
+/*
+   Bacula® - The Network Backup Solution
+
+   Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
+
+   The main author of Bacula is Kern Sibbald, with contributions from
+   many others, a complete list can be found in the file AUTHORS.
+   This program is Free Software; you can redistribute it and/or
+   modify it under the terms of version two of the GNU General Public
+   License as published by the Free Software Foundation and included
+   in the file LICENSE.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA.
+
+   Bacula® is a registered trademark of Kern Sibbald.
+   The licensor of Bacula is the Free Software Foundation Europe
+   (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+   Switzerland, email:ftf@fsfeurope.org.
+*/
+/*
+ * Bacula Catalog Database routines specific to Ingres
+ *   These are Ingres specific routines
+ *
+ *    Stefan Reddig, June 2009 with help of Marco van Wieringen April 2010
+ */
+
+#include "bacula.h"
+
+#ifdef HAVE_INGRES
 EXEC SQL INCLUDE SQLCA;
 EXEC SQL INCLUDE SQLDA;
 
@@ -7,503 +44,811 @@ EXEC SQL INCLUDE SQLDA;
 
 #include "myingres.h"
 
-#define INGRES_DEBUG 0
-#define DEBB(x) if (INGRES_DEBUG >= x) {
-#define DEBE }
-
-/* ---Implementations--- */
-int INGcheck()
+/*
+ * ---Implementations---
+ */
+int INGgetCols(INGconn *dbconn, const char *query, bool explicit_commit)
 {
-    EXEC SQL BEGIN DECLARE SECTION;
-               char errbuf[256];
-    EXEC SQL END DECLARE SECTION;
-    
-       if (sqlca.sqlcode < 0) 
-       {
-               EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
-               printf("Ingres-DBMS-Fehler: %s\n", errbuf);
-               return sqlca.sqlcode;
-    } 
-       else 
-               return 0;
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   char *stmt;
+   EXEC SQL END DECLARE SECTION;
+   IISQLDA *sqlda;
+   int number = -1;
+
+   sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + IISQDA_VAR_SIZE);
+   memset(sqlda, 0, (IISQDA_HEAD_SIZE + IISQDA_VAR_SIZE));
+   
+   sqlda->sqln = number;
+
+   stmt = bstrdup(query);
+
+   EXEC SQL WHENEVER SQLERROR GOTO bail_out;
+
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+   EXEC SQL SET_SQL (SESSION = :sess_id);
+
+   EXEC SQL PREPARE s1 INTO :sqlda FROM :stmt;
+
+   EXEC SQL WHENEVER SQLERROR CONTINUE;
+     
+   number = sqlda->sqld;
+
+bail_out:
+   /*
+    * If explicit_commit is set we commit our work now.
+    */
+   if (explicit_commit) {
+      EXEC SQL COMMIT WORK;
+   }
+
+   /*
+    * Switch to no default session for this thread.
+    */
+   EXEC SQL SET_SQL (SESSION = NONE);
+   free(stmt);
+   free(sqlda);
+   return number;
 }
 
-short INGgetCols(const char *stmt)
+static inline IISQLDA *INGgetDescriptor(int numCols, const char *query)
 {
-    short number = 1;
-    IISQLDA *sqlda;
-    sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
-    if (sqlda == (IISQLDA *)0)
-       { printf("Failure allocating %d SQLDA elements\n",number); }
-    sqlda->sqln = number;
-    
-    EXEC SQL BEGIN DECLARE SECTION;
-       char stmt_buffer[2000];
-    EXEC SQL END DECLARE SECTION;
-    
-    strcpy(stmt_buffer,stmt);
-    
-    EXEC SQL PREPARE s1 from :stmt_buffer;
-    EXEC SQL DESCRIBE s1 into :sqlda;
-    
-    number = sqlda->sqld;
-    free(sqlda);
-    return number;
+   EXEC SQL BEGIN DECLARE SECTION;
+   char *stmt;
+   EXEC SQL END DECLARE SECTION;
+   int i;
+   IISQLDA *sqlda;
+
+   sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
+   memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
+   
+   sqlda->sqln = numCols;
+   
+   stmt = bstrdup(query);
+  
+   EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
+
+   for (i = 0; i < sqlda->sqld; ++i) {
+      /*
+       * Negative type indicates nullable columns, so an indicator
+       * is allocated, otherwise it's null
+       */
+      if (sqlda->sqlvar[i].sqltype > 0) {
+         sqlda->sqlvar[i].sqlind = NULL;
+      } else {
+         sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
+      }
+      /*
+       * Alloc space for variable like indicated in sqllen
+       * for date types sqllen is always 0 -> allocate by type
+       */
+      switch (abs(sqlda->sqlvar[i].sqltype)) {
+      case IISQ_TSW_TYPE:
+         sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
+         break;
+      case IISQ_TSWO_TYPE:
+         sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
+         break;
+      case IISQ_TSTMP_TYPE:
+         sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
+         break;
+      default:
+         /*
+          * plus one to avoid zero mem allocs
+          */
+         sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen + 1);
+         break;
+      }
+   }
+   
+   free(stmt);
+   return sqlda;
 }
 
-IISQLDA *INGgetDescriptor(short numCols, const char *stmt)
+static void INGfreeDescriptor(IISQLDA *sqlda)
 {
-    IISQLDA *sqlda;
-    sqlda = (IISQLDA *)calloc(1,IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
-    if (sqlda == (IISQLDA *)0) 
-       { printf("Failure allocating %d SQLDA elements\n",numCols); }
-    
-    sqlda->sqln = numCols;
-    
-    EXEC SQL BEGIN DECLARE SECTION;
-       char stmt_buffer[2000];
-    EXEC SQL END DECLARE SECTION;
-
-    strcpy(stmt_buffer,stmt);
-
-    EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt_buffer;
-
-    int i;
-    for (i=0;i<sqlda->sqld;++i)
-    {
-       sqlda->sqlvar[i].sqldata =
-           (char *)malloc(sqlda->sqlvar[i].sqllen);
-       if (sqlda->sqlvar[i].sqldata == (char *)0)
-           { printf("Failure allocating %d bytes for SQLVAR data\n",sqlda->sqlvar[i].sqllen); }
-
-       sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
-       if (sqlda->sqlvar[i].sqlind == (short *)0) 
-           { printf("Failure allocating sqlind\n"); }
-    }
-    
-    return sqlda;
+   int i;
+
+   if (!sqlda) {
+      return;
+   }
+
+   for (i = 0; i < sqlda->sqld; ++i) {
+      if (sqlda->sqlvar[i].sqldata) {
+         free(sqlda->sqlvar[i].sqldata);
+      }
+      if (sqlda->sqlvar[i].sqlind) {
+         free(sqlda->sqlvar[i].sqlind);
+      }
+   }
+   free(sqlda);
 }
-void INGfreeDescriptor(IISQLDA *sqlda)
+
+static inline int INGgetTypeSize(IISQLVAR *ingvar)
 {
-    int i;
-    for ( i = 0 ; i < sqlda->sqld ; ++i )
-    {
-       free(sqlda->sqlvar[i].sqldata);
-       free(sqlda->sqlvar[i].sqlind);
-    }
-    free(sqlda);
-    sqlda = NULL;
+   int inglength = 0;
+   
+   switch (ingvar->sqltype) {
+   case IISQ_TSWO_TYPE:
+      inglength = 20;
+      break;
+   case IISQ_TSW_TYPE:
+      inglength = 20;
+      break;
+   case IISQ_DTE_TYPE:
+      inglength = 25;
+      break;
+   case IISQ_MNY_TYPE:
+      inglength = 8;
+      break;
+   default:
+      inglength = ingvar->sqllen;
+      break;
+   }
+   
+   return inglength;
 }
 
-int INGgetTypeSize(IISQLVAR *ingvar)
+static inline INGresult *INGgetINGresult(int numCols, const char *query)
 {
-    int inglength = 0;
-    
-       switch (ingvar->sqltype)
-    {
-               case IISQ_DTE_TYPE:
-               inglength = 25;
-                   break;
-               case IISQ_MNY_TYPE:
-                   inglength = 8;
-                   break;
-               default:
-                   inglength = ingvar->sqllen;
-    }
-    
-       return inglength;
+   int i;
+   INGresult *ing_res;
+   
+   ing_res = (INGresult *)malloc(sizeof(INGresult));
+   memset(ing_res, 0, sizeof(INGresult));
+   
+   if ((ing_res->sqlda = INGgetDescriptor(numCols, query)) == NULL) {
+      return NULL;
+   }
+
+   ing_res->num_fields = ing_res->sqlda->sqld;
+   ing_res->num_rows = 0;
+   ing_res->first_row = NULL;
+   ing_res->status = ING_EMPTY_RESULT;
+   ing_res->act_row = NULL;
+   
+   if (ing_res->num_fields) {
+      ing_res->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * ing_res->num_fields);
+      memset(ing_res->fields, 0, sizeof(INGRES_FIELD) * ing_res->num_fields);
+
+      for (i = 0; i < ing_res->num_fields; ++i) {
+         ing_res->fields[i].name = (char *)malloc(ing_res->sqlda->sqlvar[i].sqlname.sqlnamel + 1);
+         bstrncpy(ing_res->fields[i].name, ing_res->sqlda->sqlvar[i].sqlname.sqlnamec, ing_res->sqlda->sqlvar[i].sqlname.sqlnamel + 1);
+         ing_res->fields[i].name[ing_res->sqlda->sqlvar[i].sqlname.sqlnamel] = '\0';
+         ing_res->fields[i].max_length = INGgetTypeSize(&ing_res->sqlda->sqlvar[i]);
+         ing_res->fields[i].type = abs(ing_res->sqlda->sqlvar[i].sqltype);
+         ing_res->fields[i].flags = (ing_res->sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
+      }
+   }
+
+   return ing_res;
 }
 
-INGresult *INGgetINGresult(IISQLDA *sqlda)
+static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
 {
-    INGresult *result = NULL;
-    
-    result = (INGresult *)calloc(1, sizeof(INGresult));
-    if (result == (INGresult *)0) 
-       { printf("Failure allocating INGresult\n"); }
-    
-    result->sqlda      = sqlda;
-    result->num_fields = sqlda->sqld;
-    result->num_rows   = 0;
-    result->first_row  = NULL;
-    result->status     = ING_EMPTY_RESULT;
-    result->act_row    = NULL;
-    strcpy(result->numrowstring,"");
-    
-    result->fields = (INGRES_FIELD *)calloc(1, sizeof(INGRES_FIELD) * result->num_fields);
-    if (result->fields == (INGRES_FIELD *)0)
-       { printf("Failure allocating %d INGRES_FIELD elements\n",result->num_fields); }
-
-    DEBB(2)
-       printf("INGgetINGresult, before loop over %d fields\n", result->num_fields);
-    DEBE
-
-    int i;
-    for (i=0;i<result->num_fields;++i)
-    {
-       memset(result->fields[i].name,'\0',34);
-       strncpy(result->fields[i].name,
-           sqlda->sqlvar[i].sqlname.sqlnamec,
-           sqlda->sqlvar[i].sqlname.sqlnamel);
-       result->fields[i].max_length    = INGgetTypeSize(&sqlda->sqlvar[i]);
-       result->fields[i].type          = abs(sqlda->sqlvar[i].sqltype);
-       result->fields[i].flags         = (abs(sqlda->sqlvar[i].sqltype)<0) ? 1 : 0;
-    }
-
-    return result;
+   int i;
+
+   if (row == NULL || sqlda == NULL) {
+      return;
+   }
+
+   for (i = 0; i < sqlda->sqld; ++i) {
+      if (row->sqlvar[i].sqldata) {
+         free(row->sqlvar[i].sqldata);
+      }
+      if (row->sqlvar[i].sqlind) {
+         free(row->sqlvar[i].sqlind);
+      }
+   }
+   free(row->sqlvar);
+   free(row);
 }
 
-void INGfreeINGresult(INGresult *ing_res)
+static void INGfreeINGresult(INGresult *ing_res)
 {
-    /* TODO: free all rows and fields, then res, not descriptor! */
-    if( ing_res != NULL )
-    {
-       /* use of rows is a nasty workaround til I find the reason,
-          why aggregates like max() don't work
-        */
-       int rows = ing_res->num_rows;
-       ING_ROW *rowtemp;
-       ing_res->act_row = ing_res->first_row;
-       while (ing_res->act_row != NULL && rows > 0)
-       {
-           rowtemp = ing_res->act_row->next;
-           INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
-           ing_res->act_row = rowtemp;
-           --rows;
-       }
-       free(ing_res->fields);
-    }
-    free(ing_res);
-    ing_res = NULL;
+   int i;
+   int rows;
+   ING_ROW *rowtemp;
+
+   if (!ing_res) {
+      return;
+   }
+
+   /*
+    * Use of rows is a nasty workaround til I find the reason,
+    * why aggregates like max() don't work
+    */
+   rows = ing_res->num_rows;
+   ing_res->act_row = ing_res->first_row;
+   while (ing_res->act_row != NULL && rows > 0) {
+      rowtemp = ing_res->act_row->next;
+      INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
+      ing_res->act_row = rowtemp;
+      --rows;
+   }
+
+   if (ing_res->fields) {
+      for (i = 0; i < ing_res->num_fields; ++i) {
+         free(ing_res->fields[i].name);
+      }
+
+      free(ing_res->fields);
+   }
+
+   INGfreeDescriptor(ing_res->sqlda);
+
+   free(ing_res);
 }
 
-ING_ROW *INGgetRowSpace(INGresult *ing_res)
+static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
 {
-    IISQLDA *sqlda = ing_res->sqlda;
-    ING_ROW *row = NULL;
-    IISQLVAR *vars = NULL;
-    row = (ING_ROW *)calloc(1,sizeof(ING_ROW));
-    if (row == (ING_ROW *)0)
-       { printf("Failure allocating ING_ROW\n"); }
-
-    vars = (IISQLVAR *)calloc(1,sizeof(IISQLVAR) * sqlda->sqld);
-    if (vars == (IISQLVAR *)0)
-       { printf("Failure allocating %d SQLVAR elements\n",sqlda->sqld); }
-
-    row->sqlvar = vars;
-    row->next = NULL;
-
-    int i;
-    unsigned short len; /* used for VARCHAR type length */
-    for (i=0;i<sqlda->sqld;++i)
-    {
-       /* make strings out of the data, then the space and assign 
-           (why string? at least it seems that way, looking into the sources)
-        */
-       switch (abs(ing_res->fields[i].type))
-       {
-           case IISQ_VCH_TYPE:
-               len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
-               DEBB(2)
-                   printf("length of varchar: %d\n", len);
-               DEBE
-               vars[i].sqldata = (char *)malloc(len+1);
-               if (vars[i].sqldata == (char *)0)
-                   { printf("Failure allocating %d bytes for SQLVAR data\n",len+1); }
-               memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len);
-               vars[i].sqldata[len] = '\0';
-               break;
-           case IISQ_CHA_TYPE:
-               vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1);
-               if (vars[i].sqldata == (char *)0)
-                   { printf("Failure allocating %d bytes for SQLVAR data\n",ing_res->fields[i].max_length); }
-               memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
-               vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
-               break;
-           case IISQ_INT_TYPE:
-               vars[i].sqldata = (char *)malloc(20);
-               memset(vars[i].sqldata,'\0',20);
-               sprintf(vars[i].sqldata,"%d",*(int*)sqlda->sqlvar[i].sqldata);
-               break;
-       }
-       vars[i].sqlind = (short *)malloc(sizeof(short));
-       if (sqlda->sqlvar[i].sqlind == (short *)0) 
-           { printf("Failure allocating sqlind\n"); }
-       memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
-       DEBB(2)
-           printf("INGgetRowSpace, Field %d, type %d, length %d, name %s\n",
-           i, sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqllen, ing_res->fields[i].name);
-       DEBE
-    }
-    
-    return row;
+   int i;
+   unsigned short len; /* used for VARCHAR type length */
+   unsigned short th, tm, ts;
+   IISQLDA *sqlda;
+   ING_ROW *row = NULL;
+   ING_TIMESTAMP *tsp;
+   IISQLVAR *vars = NULL;
+
+   row = (ING_ROW *)malloc(sizeof(ING_ROW));
+   memset(row, 0, sizeof(ING_ROW));
+
+   sqlda = ing_res->sqlda;
+   vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
+   memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
+
+   row->sqlvar = vars;
+   row->next = NULL;
+
+   for (i = 0; i < sqlda->sqld; ++i) {
+      /*
+       * Make strings out of the data, then the space and assign 
+       * (why string? at least it seems that way, looking into the sources)
+       */
+      vars[i].sqlind = (short *)malloc(sizeof(short));
+      if (sqlda->sqlvar[i].sqlind) {
+         memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
+      } else {
+         *vars[i].sqlind = NULL;
+      }
+      /*
+       * if sqlind pointer exists AND points to -1 -> column is 'null'
+       */
+      if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
+         vars[i].sqldata = NULL;
+      } else {
+         switch (ing_res->fields[i].type) {
+         case IISQ_VCH_TYPE:
+         case IISQ_LVCH_TYPE:
+         case IISQ_VBYTE_TYPE:
+         case IISQ_LBYTE_TYPE:
+         case IISQ_NVCHR_TYPE:
+         case IISQ_LNVCHR_TYPE:
+            len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
+            vars[i].sqldata = (char *)malloc(len + 1);
+            memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata + 2,len);
+            vars[i].sqldata[len] = '\0';
+            break;
+         case IISQ_CHA_TYPE:
+         case IISQ_BYTE_TYPE:
+         case IISQ_NCHR_TYPE:
+            vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length + 1);
+            memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
+            vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
+            break;
+         case IISQ_INT_TYPE:
+            switch (sqlda->sqlvar[i].sqllen) {
+            case 2:
+               vars[i].sqldata = (char *)malloc(6);
+               memset(vars[i].sqldata, 0, 6);
+               bsnprintf(vars[i].sqldata, 6, "%d",*(int16_t *)sqlda->sqlvar[i].sqldata);
+               break;
+            case 4:
+               vars[i].sqldata = (char *)malloc(11);
+               memset(vars[i].sqldata, 0, 11);
+               bsnprintf(vars[i].sqldata, 11, "%ld",*(int32_t *)sqlda->sqlvar[i].sqldata);
+               break;
+            case 8:
+               vars[i].sqldata = (char *)malloc(20);
+               memset(vars[i].sqldata, 0, 20);
+               bsnprintf(vars[i].sqldata, 20, "%lld",*(int64_t *)sqlda->sqlvar[i].sqldata);
+               break;
+            }
+            break;
+         case IISQ_TSTMP_TYPE:
+            vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN + 1);
+            vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
+            break;
+         case IISQ_TSWO_TYPE:
+            tsp = (ING_TIMESTAMP *)sqlda->sqlvar[i].sqldata;
+            th = tsp->secs / 3600; /* hours */
+            tm = tsp->secs % 3600; /* remaining seconds */
+            tm = tm / 60; /* minutes */
+            ts = tsp->secs - (th * 3600) - (tm * 60); /* seconds */
+            vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN + 1);
+            bsnprintf(vars[i].sqldata, IISQ_TSWO_LEN + 1,
+                      "%04u-%02u-%02u %02u:%02u:%02u",
+                      tsp->year, tsp->month, tsp->day, th, tm, ts);
+            break;
+         case IISQ_TSW_TYPE:
+            tsp = (ING_TIMESTAMP *)sqlda->sqlvar[i].sqldata;
+            th = tsp->secs / 3600; /* hours */
+            tm = tsp->secs % 3600; /* remaining seconds */
+            tm = tm / 60; /* minutes */
+            ts = tsp->secs - (th * 3600) - (tm * 60); /* seconds */
+            vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN + 1);
+            bsnprintf(vars[i].sqldata, IISQ_TSW_LEN + 1,
+                      "%04u-%02u-%02u %02u:%02u:%02u",
+                      tsp->year, tsp->month, tsp->day, th, tm, ts);
+            break;
+         default:
+            Jmsg(NULL, M_FATAL, 0,
+                 "INGgetRowSpace: encountered unhandled database datatype %d please report this as a bug\n",
+                 ing_res->fields[i].type);
+            break;
+         }
+      }
+   }
+   return row;
 }
 
+static inline int INGfetchAll(INGresult *ing_res)
+{
+   ING_ROW *row;
+   IISQLDA *desc;
+   int linecount = -1;
+   
+   desc = ing_res->sqlda;
+   
+   EXEC SQL WHENEVER SQLERROR GOTO bail_out;
+
+   EXEC SQL DECLARE c2 CURSOR FOR s2;
+   EXEC SQL OPEN c2;
+      
+   EXEC SQL WHENEVER SQLERROR CONTINUE;
+
+   linecount = 0;
+   do {
+      EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
+
+      if (sqlca.sqlcode == 0 || sqlca.sqlcode == -40202) {
+         /*
+          * Allocate space for fetched row
+          */
+         row = INGgetRowSpace(ing_res);
+            
+         /*
+          * Initialize list when encountered first time
+          */
+         if (ing_res->first_row == 0) {
+            ing_res->first_row = row; /* head of the list */
+            ing_res->first_row->next = NULL;
+            ing_res->act_row = ing_res->first_row;
+         }      
+
+         ing_res->act_row->next = row; /* append row to old act_row */
+         ing_res->act_row = row; /* set row as act_row */
+         row->row_number = linecount++;
+      }
+   } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
+   
+   EXEC SQL CLOSE c2;
+   
+   ing_res->status = ING_COMMAND_OK;
+   ing_res->num_rows = linecount;
+
+bail_out:
+   return linecount;
+}
 
-void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
+static inline ING_STATUS INGresultStatus(INGresult *ing_res)
 {
-    int i;
-    if (row == NULL || sqlda == NULL)
-    {
-       printf("INGfreeRowSpace: one argument is NULL!\n");
-       return;
-    }
-
-    for ( i = 0 ; i < sqlda->sqld ; ++i )
-    {
-       free(row->sqlvar[i].sqldata);
-       free(row->sqlvar[i].sqlind);
-    }
-    free(row->sqlvar);
-    free(row);
+   if (ing_res == NULL) {
+      return ING_NO_RESULT;
+   } else {
+      return ing_res->status;
+   }
 }
 
-int INGfetchAll(const char *stmt, INGresult *ing_res)
+static void INGrowSeek(INGresult *ing_res, int row_number)
 {
-    int linecount = 0;
-    ING_ROW *row;
-    IISQLDA *desc;
-    
-    EXEC SQL BEGIN DECLARE SECTION;
-       char    stmt_buffer[2000];
-    EXEC SQL END DECLARE SECTION;
-    
-    strcpy(stmt_buffer,stmt);
-    desc = ing_res->sqlda;
-    
-    EXEC SQL DECLARE c2 CURSOR FOR s2;
-    INGcheck();
-    
-    EXEC SQL OPEN c2;
-    INGcheck();
-        
-    /* for (linecount=0;sqlca.sqlcode==0;++linecount) */
-    while(sqlca.sqlcode==0)
-    {
-        EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
-       INGcheck();
-
-       if (sqlca.sqlcode == 0)
-       {
-           row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
-               
-           /* initialize list when encountered first time */
-           if (ing_res->first_row == 0) 
-           {
-               ing_res->first_row = row; /* head of the list */
-               ing_res->first_row->next = NULL;
-               ing_res->act_row = ing_res->first_row;
-           }   
-           ing_res->act_row->next = row; /* append row to old act_row */
-           ing_res->act_row = row; /* set row as act_row */
-           row->row_number = linecount;
-           ++linecount;
-               
-           DEBB(2)
-           int i;
-           printf("Row %d ", linecount);
-           for (i=0;i<ing_res->num_fields;++i)
-               { printf("F%d:%s ",i,row->sqlvar[i].sqldata); }
-           printf("\n");
-           DEBE
-       
-       }
-    }
-    
-    EXEC SQL CLOSE c2;
-    
-    ing_res->status = ING_COMMAND_OK;
-    ing_res->num_rows = linecount;
-    return linecount;
+   ING_ROW *trow = NULL;
+
+   if (ing_res->act_row->row_number == row_number) {
+      return;
+   }
+   
+   /*
+    * TODO: real error handling
+    */
+   if (row_number < 0 || row_number > ing_res->num_rows) {
+      return;
+   }
+
+   for (trow = ing_res->first_row; trow->row_number != row_number; trow = trow->next) ;
+   ing_res->act_row = trow;
+   /*
+    * Note - can be null - if row_number not found, right?
+    */
 }
 
-ING_STATUS INGresultStatus(INGresult *res)
+char *INGgetvalue(INGresult *ing_res, int row_number, int column_number)
 {
-    if (res == NULL) {return ING_NO_RESULT;}
-    return res->status;
+   if (row_number != ing_res->act_row->row_number) {
+      INGrowSeek(ing_res, row_number);
+   }
+
+   return ing_res->act_row->sqlvar[column_number].sqldata;
 }
 
-void INGrowSeek(INGresult *res, int row_number)
+bool INGgetisnull(INGresult *ing_res, int row_number, int column_number)
 {
-    if (res->act_row->row_number == row_number) { return; }
-    
-    /* TODO: real error handling */
-    if (row_number<0 || row_number>res->num_rows) { return;}
-
-    ING_ROW *trow = res->first_row;
-    while ( trow->row_number != row_number )
-    { trow = trow->next; }
-    res->act_row = trow;
-    /* note - can be null - if row_number not found, right? */
+   if (row_number != ing_res->act_row->row_number) {
+      INGrowSeek(ing_res, row_number);
+   }
+
+   return (*ing_res->act_row->sqlvar[column_number].sqlind == -1) ? true : false;
 }
 
-char *INGgetvalue(INGresult *res, int row_number, int column_number)
+int INGntuples(const INGresult *ing_res)
 {
-    if (row_number != res->act_row->row_number)
-       { INGrowSeek(res, row_number); }
-    return res->act_row->sqlvar[column_number].sqldata;
+   return ing_res->num_rows;
 }
 
-int INGgetisnull(INGresult *res, int row_number, int column_number)
+int INGnfields(const INGresult *ing_res)
 {
-    if (row_number != res->act_row->row_number)
-       { INGrowSeek(res, row_number); }
-    return (short)*res->act_row->sqlvar[column_number].sqlind;
+   return ing_res->num_fields;
 }
 
-int INGntuples(const INGresult *res)
+char *INGfname(const INGresult *ing_res, int column_number)
 {
-    return res->num_rows;
+   if ((column_number > ing_res->num_fields) || (column_number < 0)) {
+      return NULL;
+   } else {
+      return ing_res->fields[column_number].name;
+   }
 }
 
-int INGnfields(const INGresult *res)
+short INGftype(const INGresult *ing_res, int column_number)
 {
-    return res->num_fields;
+   return ing_res->fields[column_number].type;
 }
 
-char *INGfname(const INGresult *res, int column_number)
+int INGexec(INGconn *dbconn, const char *query, bool explicit_commit)
 {
-    if ( (column_number > res->num_fields) || (column_number < 0) )
-       { return NULL; }
-    else
-       { return res->fields[column_number].name; }
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   int rowcount;
+   int errors;
+   char *stmt;
+   EXEC SQL END DECLARE SECTION;
+   
+   rowcount = -1;
+   stmt = bstrdup(query);
+
+   EXEC SQL WHENEVER SQLERROR GOTO bail_out;
+
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+   EXEC SQL SET_SQL (SESSION = :sess_id);
+
+   EXEC SQL EXECUTE IMMEDIATE :stmt;
+   EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
+
+   /*
+    * See if the negative rowcount is due to errors.
+    */
+   if (rowcount < 0) {
+      EXEC SQL INQUIRE_INGRES(:errors = DBMSERROR);
+
+      /*
+       * If the number of errors is 0 we got a negative rowcount
+       * because the statement we executed doesn't give a rowcount back.
+       * Lets pretend we have a rowcount of 1 then.
+       */
+      if (errors == 0) {
+         rowcount = 1;
+      }
+   }
+
+   EXEC SQL WHENEVER SQLERROR CONTINUE;
+
+bail_out:
+   /*
+    * If explicit_commit is set we commit our work now.
+    */
+   if (explicit_commit) {
+      EXEC SQL COMMIT WORK;
+   }
+
+   /*
+    * Switch to no default session for this thread.
+    */
+   EXEC SQL SET_SQL (SESSION = NONE);
+   free(stmt);
+   return rowcount;
 }
 
-short INGftype(const INGresult *res, int column_number)
+INGresult *INGquery(INGconn *dbconn, const char *query, bool explicit_commit)
 {
-    return res->fields[column_number].type;
+   /*
+    * TODO: error handling
+    */
+   INGresult *ing_res = NULL;
+   int rows;
+   int cols;
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   EXEC SQL END DECLARE SECTION;
+
+   cols = INGgetCols(dbconn, query, explicit_commit);
+
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+   EXEC SQL SET_SQL (SESSION = :sess_id);
+
+   ing_res = INGgetINGresult(cols, query);
+   if (!ing_res) {
+      goto bail_out;
+   }
+
+   rows = INGfetchAll(ing_res);
+
+   if (rows < 0) {
+      INGfreeINGresult(ing_res);
+      ing_res = NULL;
+      goto bail_out;
+   }
+
+bail_out:
+   /*
+    * If explicit_commit is set we commit our work now.
+    */
+   if (explicit_commit) {
+      EXEC SQL COMMIT WORK;
+   }
+
+   /*
+    * Switch to no default session for this thread.
+    */
+   EXEC SQL SET_SQL (SESSION = NONE);
+   return ing_res;
 }
 
-INGresult *INGexec(INGconn *conn, const char *query)
+void INGclear(INGresult *ing_res)
 {
-    /* TODO: error handling -> res->status? */
-    IISQLDA *desc = NULL;
-    INGresult *res = NULL;
-    int cols = -1;
-
-    EXEC SQL BEGIN DECLARE SECTION;
-        char stmt[2000];
-    EXEC SQL END DECLARE SECTION;
-    strncpy(stmt,query,strlen(query));
-    stmt[strlen(query)]='\0';
-
-    DEBB(1)
-       printf("INGexec: query is >>%s<<\n",stmt);
-    DEBE
-
-    if ((cols = INGgetCols(query)) == 0)
-    {
-       DEBB(1)
-           printf("INGexec: non-select\n");
-       DEBE
-       /* non-select statement - TODO: EXECUTE IMMEDIATE */
-       EXEC SQL EXECUTE IMMEDIATE :stmt;
-    }
-    else
-    {
-       DEBB(1)
-           printf("INGexec: select\n");
-       DEBE
-       /* select statement */
-       desc = INGgetDescriptor(cols, query);
-       res = INGgetINGresult(desc);
-       INGfetchAll(query, res);
-    }
-    return res;
+   if (ing_res == NULL) {
+      return;
+   }
+
+   INGfreeINGresult(ing_res);
 }
 
-void INGclear(INGresult *res)
+void INGcommit(const INGconn *dbconn)
 {
-    if (res == NULL) { return; }
-    IISQLDA *desc = res->sqlda;
-    INGfreeINGresult(res);
-    INGfreeDescriptor(desc);
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   EXEC SQL END DECLARE SECTION;
+
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+      EXEC SQL SET_SQL (SESSION = :sess_id);
+
+      /*
+       * Commit our work.
+       */
+      EXEC SQL COMMIT WORK;
+
+      /*
+       * Switch to no default session for this thread.
+       */
+      EXEC SQL SET_SQL (SESSION = NONE);
+   }
 }
 
-INGconn *INGconnectDB(char *dbname, char *user, char *passwd)
+INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id)
 {
-    if (dbname == NULL || strlen(dbname) == 0)
-       { return NULL; }
-
-    INGconn *dbconn = (INGconn *)calloc(1,sizeof(INGconn));
-    if (dbconn == (INGconn *)0)
-       { printf("Failure allocating INGconn\n"); }
-
-    EXEC SQL BEGIN DECLARE SECTION;
-       char    ingdbname[24];
-       char    ingdbuser[32];
-       char    ingdbpasw[32];
-       char    conn_name[32];
-       int     sess_id;
-    EXEC SQL END DECLARE SECTION;
-
-    strcpy(ingdbname,dbname);
-    
-    if ( user != NULL)
-    {
-       DEBB(1)
-           printf("Connection: with user/passwd\n");
-       DEBE
-        strcpy(ingdbuser,user);
-       if ( passwd != NULL)
-           { strcpy(ingdbpasw,passwd); }
-       else
-           { strcpy(ingdbpasw, ""); }
-       EXEC SQL CONNECT
-           :ingdbname
-           identified by :ingdbuser
-           dbms_password = :ingdbpasw;
-    }
-    else
-    {
-       DEBB(1)
-           printf("Connection: w/ user/passwd\n");
-       DEBE
-       EXEC SQL CONNECT :ingdbname;
-    }   
+   EXEC SQL BEGIN DECLARE SECTION;
+   char *ingdbname;
+   char *ingdbuser = NULL;
+   char *ingdbpasswd = NULL;
+   int sess_id;
+   EXEC SQL END DECLARE SECTION;
+   INGconn *dbconn = NULL;
+
+   if (dbname == NULL || strlen(dbname) == 0) {
+      return NULL;
+   }
+
+   sess_id = session_id;
+   ingdbname = dbname;
+   
+   EXEC SQL WHENEVER SQLERROR GOTO bail_out;
+
+   if (user != NULL) {
+      ingdbuser = user;
+      if (passwd != NULL) {
+         ingdbpasswd = passwd;
+         EXEC SQL CONNECT
+            :ingdbname
+            SESSION :sess_id
+            IDENTIFIED BY :ingdbuser
+            DBMS_PASSWORD = :ingdbpasswd;
+      } else {
+         EXEC SQL CONNECT
+            :ingdbname
+            SESSION :sess_id
+            IDENTIFIED BY :ingdbuser;
+      }
+   } else {
+      EXEC SQL CONNECT
+         :ingdbname
+         SESSION :sess_id;
+   }   
    
-    EXEC SQL INQUIRE_SQL(:conn_name = connection_name);
-    EXEC SQL INQUIRE_SQL(:sess_id = session);
-    
-    strcpy(dbconn->dbname,ingdbname);
-    strcpy(dbconn->user,ingdbuser);
-    strcpy(dbconn->password,ingdbpasw);
-    strcpy(dbconn->connection_name,conn_name);
-    dbconn->session_id = sess_id;
-
-    DEBB(1)
-       printf("Connected to '%s' with user/passwd %s/%s, sessID/name %i/%s\n",
-           dbconn->dbname,
-           dbconn->user,
-           dbconn->password,
-           dbconn->session_id,
-           dbconn->connection_name
-       );
-    DEBE
-
-    return dbconn;
+   EXEC SQL WHENEVER SQLERROR CONTINUE;
+
+   dbconn = (INGconn *)malloc(sizeof(INGconn));
+   memset(dbconn, 0, sizeof(INGconn));
+
+   dbconn->dbname = bstrdup(ingdbname);
+   if (user != NULL) {
+      dbconn->user = bstrdup(ingdbuser);
+      dbconn->password = bstrdup(ingdbpasswd);
+   }
+   dbconn->session_id = sess_id;
+   dbconn->msg = (char *)malloc(257);
+   memset(dbconn->msg, 0, 257);
+
+   /*
+    * Switch to no default session for this thread undo default settings from SQL CONNECT.
+    */
+   EXEC SQL SET_SQL (SESSION = NONE);
+
+bail_out:
+   return dbconn;
 }
 
-void INGdisconnectDB(INGconn *dbconn)
+void INGsetDefaultLockingMode(INGconn *dbconn)
 {
-    /* TODO: use of dbconn */
-    EXEC SQL DISCONNECT;
-    free(dbconn);
+   /*
+    * Set the default Ingres session locking mode:
+    *
+    * SET LOCKMODE provides four different parameters to govern
+    * the nature of locking in an INGRES session:
+    *
+    * Level: This refers to the level of granularity desired when
+    * the table is accessed. You can specify any of the following
+    * locking levels:
+    *
+    * row     Specifies locking at the level of the row (subject to
+    *         escalation criteria; see below)
+    * page    Specifies locking at the level of the data page (subject to
+    *         escalation criteria; see below)
+    * table   Specifies table-level locking in the database
+    * session Specifies the current default for your INGRES session
+    * system  Specifies that INGRES will start with page-level locking,
+    *         unless it estimates that more than Maxlocks pages will be
+    *         referenced, in which case table-level locking will be used.
+    *
+    * Readlock: This refers to locking in situations where table access
+    *           is required for reading data only (as opposed to updating
+    *           data). You can specify any of the following Readlock modes:
+    *
+    *    nolock     Specifies no locking when reading data
+    *    shared     Specifies the default mode of locking when reading data
+    *    exclusive  Specifies exclusive locking when reading data (useful in
+    *               "select-for-update" processing within a multi-statement
+    *               transaction)
+    *    system     Specifies the general Readlock default for the INGRES system
+    *
+    * Maxlocks: This refers to an escalation factor, or number of locks on
+    *           data pages, at which locking escalates from page-level
+    *           to table-level. The number of locks available to you is
+    *           dependent upon your system configuration. You can specify the
+    *           following Maxlocks escalation factors:
+    *
+    *    n       A specific (integer) number of page locks to allow before
+    *            escalating to table-level locking. The default "n" is 10,
+    *            and "n" must be greater than 0.
+    *    session Specifies the current Maxlocks default for your INGRES
+    *            session
+    *    system  Specifies the general Maxlocks default for the INGRES system
+    *
+    * Note: If you specify page-level locking, and the number of locks granted
+    * during a query exceeds the system-wide lock limit, or if the operating
+    * system's locking resources are depleted, locking escalates to table-level.
+    * This escalation occurs automatically and is independent of the user.
+    *
+    * Timeout: This refers to a time limit, expressed in seconds, for which
+    * a lock request should remain pending. If INGRES cannot grant the lock
+    * request within the specified time, then the query that requested the
+    * lock aborts. You can specify the following timeout characteristics:
+    *
+    *    n       A specific (integer) number of seconds to wait for a lock
+    *            (setting "n" to 0 requires INGRES to wait indefinitely for
+    *            the lock)
+    *    session Specifies the current timeout default for your INGRES
+    *            session (which is also the INGRES default)
+    *    system  Specifies the general timeout default for the INGRES system
+    *
+    */
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   EXEC SQL END DECLARE SECTION;
+
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+      EXEC SQL SET_SQL (SESSION = :sess_id);
+
+      EXEC SQL SET LOCKMODE SESSION WHERE level = row, readlock = nolock;
+
+      /*
+       * Switch to no default session for this thread.
+       */
+      EXEC SQL SET_SQL (SESSION = NONE);
+   }
 }
 
-char *INGerrorMessage(const INGconn *conn)
+void INGdisconnectDB(INGconn *dbconn)
 {
-    return NULL;
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   EXEC SQL END DECLARE SECTION;
+
+   if (dbconn != NULL) {
+      sess_id = dbconn->session_id;
+      EXEC SQL DISCONNECT SESSION :sess_id;
+
+      free(dbconn->dbname);
+      if (dbconn->user) {
+         free(dbconn->user);
+      }
+      if (dbconn->password) {
+         free(dbconn->password);
+      }
+      free(dbconn->msg);
+      free(dbconn);
+   }
 }
 
-char   *INGcmdTuples(INGresult *res)
+char *INGerrorMessage(const INGconn *dbconn)
 {
-    return res->numrowstring;
+   EXEC SQL BEGIN DECLARE SECTION;
+   int sess_id;
+   char errbuf[256];
+   EXEC SQL END DECLARE SECTION;
+
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+      EXEC SQL SET_SQL (SESSION = :sess_id);
+
+      EXEC SQL INQUIRE_INGRES (:errbuf = ERRORTEXT);
+      strncpy(dbconn->msg, errbuf, sizeof(dbconn->msg));
+
+      /*
+       * Switch to no default session for this thread.
+       */
+      EXEC SQL SET_SQL (SESSION = NONE);
+   }
+
+   return dbconn->msg;
 }
 
-
-/*      TODO?
-char *INGerrorMessage(const INGconn *conn);
-int INGputCopyEnd(INGconn *conn, const char *errormsg);
-int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);
-*/
+#endif