]> git.sur5r.net Git - bacula/bacula/commitdiff
Add more ingres code
authorEric Bollengier <eric@eb.homelinux.org>
Tue, 25 Jan 2011 15:21:35 +0000 (16:21 +0100)
committerKern Sibbald <kern@sibbald.com>
Sat, 20 Apr 2013 12:39:54 +0000 (14:39 +0200)
bacula/src/cats/myingres.c [new file with mode: 0644]
bacula/src/cats/myingres.h [new file with mode: 0644]
bacula/src/cats/myingres.sc [new file with mode: 0644]
bacula/src/cats/myingres.sh [new file with mode: 0644]

diff --git a/bacula/src/cats/myingres.c b/bacula/src/cats/myingres.c
new file mode 100644 (file)
index 0000000..e13e751
--- /dev/null
@@ -0,0 +1,912 @@
+/*
+   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 three of the GNU Affero 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 Affero 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"
+/* # line 37 "myingres.sc" */  
+#ifdef HAVE_INGRES
+#include <eqpname.h>
+#include <eqdefcc.h>
+#include <eqsqlca.h>
+#include <eqsqlda.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "myingres.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+IISQLCA *IIsqlca();
+#ifdef __cplusplus
+}
+#endif
+#define sqlca (*(IIsqlca()))
+
+/*
+ * ---Implementations---
+ */
+int INGgetCols(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+/* # line 52 "myingres.sc" */  
+  
+  int sess_id;
+  char *stmt;
+/* # line 55 "myingres.sc" */  
+  
+   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);
+/* # line 68 "myingres.sc" */  /* host code */
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+/* # line 72 "myingres.sc" */  /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 74 "myingres.sc" */  /* prepare */
+  {
+    IIsqInit(&sqlca);
+    IIsqPrepare(0,(char *)"s1",sqlda,0,stmt);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 78 "myingres.sc" */  /* host code */
+   number = sqlda->sqld;
+bail_out:
+   /*
+    * If explicit_commit is set we commit our work now.
+    */
+   if (explicit_commit) {
+/* # line 85 "myingres.sc" */  /* commit */
+  {
+    IIsqInit(&sqlca);
+    IIxact(3);
+  }
+/* # line 86 "myingres.sc" */  /* host code */
+   }
+   /*
+    * Switch to no default session for this thread.
+    */
+/* # line 91 "myingres.sc" */  /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 92 "myingres.sc" */  /* host code */
+   free(stmt);
+   free(sqlda);
+   return number;
+}
+static inline IISQLDA *INGgetDescriptor(int numCols, const char *query)
+{
+/* # line 99 "myingres.sc" */  
+  
+  char *stmt;
+/* # line 101 "myingres.sc" */ 
+  
+   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);
+/* # line 112 "myingres.sc" */ /* prepare */
+  {
+    IIsqInit(&sqlca);
+    IIsqPrepare(0,(char *)"s2",sqlda,0,stmt);
+  }
+/* # line 114 "myingres.sc" */ /* host code */
+   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;
+}
+static void INGfreeDescriptor(IISQLDA *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);
+}
+static inline int INGgetTypeSize(IISQLVAR *ingvar)
+{
+   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;
+}
+static inline INGresult *INGgetINGresult(int numCols, const char *query)
+{
+   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;
+}
+static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
+{
+   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);
+}
+static void INGfreeINGresult(INGresult *ing_res)
+{
+   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);
+}
+static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
+{
+   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;
+/* # line 409 "myingres.sc" */ /* open */
+  {
+    IIsqInit(&sqlca);
+    IIcsOpen((char *)"c2",20273,8927);
+    IIwritio(0,(short *)0,1,32,0,(char *)"s2");
+    IIcsQuery((char *)"c2",20273,8927);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 413 "myingres.sc" */ /* host code */
+   linecount = 0;
+   do {
+/* # line 415 "myingres.sc" */ /* fetch */
+  {
+    IIsqInit(&sqlca);
+    if (IIcsRetScroll((char *)"c2",20273,8927,-1,-1) != 0) {
+      IIcsDaGet(0,desc);
+      IIcsERetrieve();
+    } /* IIcsRetrieve */
+  }
+/* # line 417 "myingres.sc" */ /* host code */
+      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) );
+/* # line 438 "myingres.sc" */ /* close */
+  {
+    IIsqInit(&sqlca);
+    IIcsClose((char *)"c2",20273,8927);
+  }
+/* # line 440 "myingres.sc" */ /* host code */
+   ing_res->status = ING_COMMAND_OK;
+   ing_res->num_rows = linecount;
+bail_out:
+   return linecount;
+}
+static inline ING_STATUS INGresultStatus(INGresult *ing_res)
+{
+   if (ing_res == NULL) {
+      return ING_NO_RESULT;
+   } else {
+      return ing_res->status;
+   }
+}
+static void INGrowSeek(INGresult *ing_res, int row_number)
+{
+   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?
+    */
+}
+char *INGgetvalue(INGresult *ing_res, int row_number, int column_number)
+{
+   if (row_number != ing_res->act_row->row_number) {
+      INGrowSeek(ing_res, row_number);
+   }
+   return ing_res->act_row->sqlvar[column_number].sqldata;
+}
+bool INGgetisnull(INGresult *ing_res, int row_number, int column_number)
+{
+   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;
+}
+int INGntuples(const INGresult *ing_res)
+{
+   return ing_res->num_rows;
+}
+int INGnfields(const INGresult *ing_res)
+{
+   return ing_res->num_fields;
+}
+char *INGfname(const INGresult *ing_res, int column_number)
+{
+   if ((column_number > ing_res->num_fields) || (column_number < 0)) {
+      return NULL;
+   } else {
+      return ing_res->fields[column_number].name;
+   }
+}
+short INGftype(const INGresult *ing_res, int column_number)
+{
+   return ing_res->fields[column_number].type;
+}
+int INGexec(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+/* # line 522 "myingres.sc" */ 
+  
+  int sess_id;
+  int rowcount;
+  int errors;
+  char *stmt;
+/* # line 527 "myingres.sc" */ 
+  
+   rowcount = -1;
+   stmt = bstrdup(query);
+/* # line 534 "myingres.sc" */ /* host code */
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+/* # line 538 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 540 "myingres.sc" */ /* execute */
+  {
+    IIsqInit(&sqlca);
+    IIsqExImmed(stmt);
+    IIsyncup((char *)0,0);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 541 "myingres.sc" */ /* inquire_ingres */
+  {
+    IILQisInqSqlio((short *)0,1,30,sizeof(rowcount),&rowcount,8);
+  }
+/* # line 543 "myingres.sc" */ /* host code */
+   /*
+    * See if the negative rowcount is due to errors.
+    */
+   if (rowcount < 0) {
+/* # line 547 "myingres.sc" */ /* inquire_ingres */
+  {
+    IILQisInqSqlio((short *)0,1,30,sizeof(errors),&errors,0);
+  }
+/* # line 549 "myingres.sc" */ /* host code */
+      /*
+       * 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;
+      }
+   }
+/* # line 561 "myingres.sc" */ /* host code */
+bail_out:
+   /*
+    * If explicit_commit is set we commit our work now.
+    */
+   if (explicit_commit) {
+/* # line 566 "myingres.sc" */ /* commit */
+  {
+    IIsqInit(&sqlca);
+    IIxact(3);
+  }
+/* # line 567 "myingres.sc" */ /* host code */
+   }
+   /*
+    * Switch to no default session for this thread.
+    */
+/* # line 572 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 573 "myingres.sc" */ /* host code */
+   free(stmt);
+   return rowcount;
+}
+INGresult *INGquery(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+   /*
+    * TODO: error handling
+    */
+   INGresult *ing_res = NULL;
+   int rows;
+   int cols;
+/* # line 585 "myingres.sc" */ 
+  
+  int sess_id;
+/* # line 587 "myingres.sc" */ 
+  
+   cols = INGgetCols(dbconn, query, explicit_commit);
+   /*
+    * Switch to the correct default session for this thread.
+    */
+   sess_id = dbconn->session_id;
+/* # line 595 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 597 "myingres.sc" */ /* host code */
+   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) {
+/* # line 615 "myingres.sc" */ /* commit */
+  {
+    IIsqInit(&sqlca);
+    IIxact(3);
+  }
+/* # line 616 "myingres.sc" */ /* host code */
+   }
+   /*
+    * Switch to no default session for this thread.
+    */
+/* # line 621 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 622 "myingres.sc" */ /* host code */
+   return ing_res;
+}
+void INGclear(INGresult *ing_res)
+{
+   if (ing_res == NULL) {
+      return;
+   }
+   INGfreeINGresult(ing_res);
+}
+void INGcommit(const INGconn *dbconn)
+{
+/* # line 636 "myingres.sc" */ 
+  
+  int sess_id;
+/* # line 638 "myingres.sc" */ 
+  
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+/* # line 645 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 647 "myingres.sc" */ /* host code */
+      /*
+       * Commit our work.
+       */
+/* # line 650 "myingres.sc" */ /* commit */
+  {
+    IIsqInit(&sqlca);
+    IIxact(3);
+  }
+/* # line 652 "myingres.sc" */ /* host code */
+      /*
+       * Switch to no default session for this thread.
+       */
+/* # line 655 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 656 "myingres.sc" */ /* host code */
+   }
+}
+INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id)
+{
+/* # line 661 "myingres.sc" */ 
+  
+  char *ingdbname;
+  char *ingdbuser = NULL;
+  char *ingdbpasswd = NULL;
+  int sess_id;
+/* # line 666 "myingres.sc" */ 
+  
+   INGconn *dbconn = NULL;
+   if (dbname == NULL || strlen(dbname) == 0) {
+      return NULL;
+   }
+   sess_id = session_id;
+   ingdbname = dbname;
+/* # line 678 "myingres.sc" */ /* host code */
+   if (user != NULL) {
+      ingdbuser = user;
+      if (passwd != NULL) {
+         ingdbpasswd = passwd;
+/* # line 682 "myingres.sc" */ /* connect */
+  {
+    IIsqInit(&sqlca);
+    IILQsidSessID(sess_id);
+    IIsqUser(ingdbuser);
+    IIsqConnect(0,ingdbname,(char *)"-dbms_password",ingdbpasswd,(char *)0, 
+    (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, 
+    (char *)0, (char *)0, (char *)0, (char *)0);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 687 "myingres.sc" */ /* host code */
+      } else {
+/* # line 688 "myingres.sc" */ /* connect */
+  {
+    IIsqInit(&sqlca);
+    IILQsidSessID(sess_id);
+    IIsqUser(ingdbuser);
+    IIsqConnect(0,ingdbname,(char *)0, (char *)0, (char *)0, (char *)0, 
+    (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, 
+    (char *)0, (char *)0, (char *)0);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 692 "myingres.sc" */ /* host code */
+      }
+   } else {
+/* # line 694 "myingres.sc" */ /* connect */
+  {
+    IIsqInit(&sqlca);
+    IILQsidSessID(sess_id);
+    IIsqConnect(0,ingdbname,(char *)0, (char *)0, (char *)0, (char *)0, 
+    (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, 
+    (char *)0, (char *)0, (char *)0);
+    if (sqlca.sqlcode < 0) 
+      goto bail_out;
+  }
+/* # line 697 "myingres.sc" */ /* host code */
+   }   
+/* # line 701 "myingres.sc" */ /* host code */
+   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.
+    */
+/* # line 716 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 718 "myingres.sc" */ /* host code */
+bail_out:
+   return dbconn;
+}
+void INGsetDefaultLockingMode(INGconn *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
+    *
+    */
+/* # line 786 "myingres.sc" */ 
+  
+  int sess_id;
+/* # line 788 "myingres.sc" */ 
+  
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+/* # line 795 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 797 "myingres.sc" */ /* set */
+  {
+    IIsqInit(&sqlca);
+    IIwritio(0,(short *)0,1,32,0,(char *)
+"set LOCKMODE session where level=row, readlock=nolock");
+    IIsyncup((char *)0,0);
+  }
+/* # line 799 "myingres.sc" */ /* host code */
+      /*
+       * Switch to no default session for this thread.
+       */
+/* # line 802 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 803 "myingres.sc" */ /* host code */
+   }
+}
+void INGdisconnectDB(INGconn *dbconn)
+{
+/* # line 808 "myingres.sc" */ 
+  
+  int sess_id;
+/* # line 810 "myingres.sc" */ 
+  
+   if (dbconn != NULL) {
+      sess_id = dbconn->session_id;
+/* # line 814 "myingres.sc" */ /* disconnect */
+  {
+    IIsqInit(&sqlca);
+    IILQsidSessID(sess_id);
+    IIsqDisconnect();
+  }
+/* # line 816 "myingres.sc" */ /* host code */
+      free(dbconn->dbname);
+      if (dbconn->user) {
+         free(dbconn->user);
+      }
+      if (dbconn->password) {
+         free(dbconn->password);
+      }
+      free(dbconn->msg);
+      free(dbconn);
+   }
+}
+char *INGerrorMessage(const INGconn *dbconn)
+{
+/* # line 830 "myingres.sc" */ 
+  
+  int sess_id;
+  char errbuf[256];
+/* # line 833 "myingres.sc" */ 
+  
+   if (dbconn != NULL) {
+      /*
+       * Switch to the correct default session for this thread.
+       */
+      sess_id = dbconn->session_id;
+/* # line 840 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(sess_id),&sess_id);
+  }
+/* # line 842 "myingres.sc" */ /* inquire_ingres */
+  {
+    IILQisInqSqlio((short *)0,1,32,255,errbuf,63);
+  }
+/* # line 843 "myingres.sc" */ /* host code */
+      strncpy(dbconn->msg, errbuf, sizeof(dbconn->msg));
+      /*
+       * Switch to no default session for this thread.
+       */
+/* # line 848 "myingres.sc" */ /* set_sql */
+  {
+    IILQssSetSqlio(11,(short *)0,1,30,sizeof(-97),(void *)IILQint(-97));
+  }
+/* # line 849 "myingres.sc" */ /* host code */
+   }
+   return dbconn->msg;
+}
+/* # line 854 "myingres.sc" */ 
+#endif
diff --git a/bacula/src/cats/myingres.h b/bacula/src/cats/myingres.h
new file mode 100644 (file)
index 0000000..ed3a685
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+   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 three of the GNU Affero 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 Affero 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.
+*/
+#ifndef _MYINGRES_SH
+#define _MYINGRES_SH
+#include <eqpname.h>
+#include <eqdefcc.h>
+#include <eqsqlda.h>
+
+/* ---typedefs--- */
+typedef struct ing_timestamp {
+   unsigned short year;
+   unsigned char month;
+   unsigned char day;
+   unsigned int secs;
+   unsigned int nsecs;
+   unsigned char tzh;
+   unsigned char tzm;
+} ING_TIMESTAMP;
+typedef struct ing_field {
+   char *name;
+   int max_length;
+   unsigned int type;
+   unsigned int flags;         /* 1 == not null */
+} INGRES_FIELD;
+typedef struct ing_row {
+   IISQLVAR *sqlvar;           /* ptr to sqlvar[sqld] for one row */
+   struct ing_row *next;
+   int row_number;
+} ING_ROW;
+typedef enum ing_status {
+   ING_COMMAND_OK,
+   ING_TUPLES_OK,
+   ING_NO_RESULT,
+   ING_NO_ROWS_PROCESSED,
+   ING_EMPTY_RESULT,
+   ING_ERROR
+} ING_STATUS;
+typedef struct ing_varchar {
+   short len;
+   char* value;
+} ING_VARCHAR;
+/* It seems, Bacula needs the complete query result stored in one data structure */
+typedef struct ing_result {
+   IISQLDA *sqlda;             /* descriptor */
+   INGRES_FIELD *fields;
+   int num_rows;
+   int num_fields;
+   ING_STATUS status;
+   ING_ROW *first_row;
+   ING_ROW *act_row;           /* just for iterating */
+} INGresult;
+typedef struct ing_conn {
+   char *dbname;
+   char *user;
+   char *password;
+   int session_id;
+   char *msg;
+} INGconn;
+/* ---Prototypes--- */
+int INGgetCols(INGconn *dbconn, const char *query, bool transaction);
+char *INGgetvalue(INGresult *ing_res, int row_number, int column_number);
+bool INGgetisnull(INGresult *ing_res, int row_number, int column_number);
+int INGntuples(const INGresult *ing_res);
+int INGnfields(const INGresult *ing_res);
+char *INGfname(const INGresult *ing_res, int column_number);
+short INGftype(const INGresult *ing_res, int column_number);
+int INGexec(INGconn *dbconn, const char *query, bool transaction);
+INGresult *INGquery(INGconn *dbconn, const char *query, bool transaction);
+void INGclear(INGresult *ing_res);
+void INGcommit(const INGconn *dbconn);
+INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id);
+void INGsetDefaultLockingMode(INGconn *dbconn);
+void INGdisconnectDB(INGconn *dbconn);
+char *INGerrorMessage(const INGconn *dbconn);
+char *INGcmdTuples(INGresult *ing_res);
+/* # line 109 "myingres.sh" */ 
+#endif /* _MYINGRES_SH */
diff --git a/bacula/src/cats/myingres.sc b/bacula/src/cats/myingres.sc
new file mode 100644 (file)
index 0000000..d91087b
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+   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;
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "myingres.h"
+
+/*
+ * ---Implementations---
+ */
+int INGgetCols(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+   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;
+}
+
+static inline IISQLDA *INGgetDescriptor(int numCols, const char *query)
+{
+   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;
+}
+
+static void INGfreeDescriptor(IISQLDA *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);
+}
+
+static inline int INGgetTypeSize(IISQLVAR *ingvar)
+{
+   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;
+}
+
+static inline INGresult *INGgetINGresult(int numCols, const char *query)
+{
+   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;
+}
+
+static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
+{
+   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);
+}
+
+static void INGfreeINGresult(INGresult *ing_res)
+{
+   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);
+}
+
+static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
+{
+   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;
+}
+
+static inline ING_STATUS INGresultStatus(INGresult *ing_res)
+{
+   if (ing_res == NULL) {
+      return ING_NO_RESULT;
+   } else {
+      return ing_res->status;
+   }
+}
+
+static void INGrowSeek(INGresult *ing_res, int row_number)
+{
+   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?
+    */
+}
+
+char *INGgetvalue(INGresult *ing_res, int row_number, int column_number)
+{
+   if (row_number != ing_res->act_row->row_number) {
+      INGrowSeek(ing_res, row_number);
+   }
+
+   return ing_res->act_row->sqlvar[column_number].sqldata;
+}
+
+bool INGgetisnull(INGresult *ing_res, int row_number, int column_number)
+{
+   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;
+}
+
+int INGntuples(const INGresult *ing_res)
+{
+   return ing_res->num_rows;
+}
+
+int INGnfields(const INGresult *ing_res)
+{
+   return ing_res->num_fields;
+}
+
+char *INGfname(const INGresult *ing_res, int column_number)
+{
+   if ((column_number > ing_res->num_fields) || (column_number < 0)) {
+      return NULL;
+   } else {
+      return ing_res->fields[column_number].name;
+   }
+}
+
+short INGftype(const INGresult *ing_res, int column_number)
+{
+   return ing_res->fields[column_number].type;
+}
+
+int INGexec(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+   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;
+}
+
+INGresult *INGquery(INGconn *dbconn, const char *query, bool explicit_commit)
+{
+   /*
+    * 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;
+}
+
+void INGclear(INGresult *ing_res)
+{
+   if (ing_res == NULL) {
+      return;
+   }
+
+   INGfreeINGresult(ing_res);
+}
+
+void INGcommit(const INGconn *dbconn)
+{
+   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, int session_id)
+{
+   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 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 INGsetDefaultLockingMode(INGconn *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);
+   }
+}
+
+void INGdisconnectDB(INGconn *dbconn)
+{
+   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 *INGerrorMessage(const INGconn *dbconn)
+{
+   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;
+}
+
+#endif
diff --git a/bacula/src/cats/myingres.sh b/bacula/src/cats/myingres.sh
new file mode 100644 (file)
index 0000000..cc9cf3d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+   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.
+*/
+#ifndef _MYINGRES_SH
+#define _MYINGRES_SH
+
+EXEC SQL INCLUDE SQLDA;
+
+/* ---typedefs--- */
+
+typedef struct ing_timestamp {
+   unsigned short year;
+   unsigned char month;
+   unsigned char day;
+   unsigned int secs;
+   unsigned int nsecs;
+   unsigned char tzh;
+   unsigned char tzm;
+} ING_TIMESTAMP;
+
+typedef struct ing_field {
+   char *name;
+   int max_length;
+   unsigned int type;
+   unsigned int flags;         /* 1 == not null */
+} INGRES_FIELD;
+
+typedef struct ing_row {
+   IISQLVAR *sqlvar;           /* ptr to sqlvar[sqld] for one row */
+   struct ing_row *next;
+   int row_number;
+} ING_ROW;
+
+typedef enum ing_status {
+   ING_COMMAND_OK,
+   ING_TUPLES_OK,
+   ING_NO_RESULT,
+   ING_NO_ROWS_PROCESSED,
+   ING_EMPTY_RESULT,
+   ING_ERROR
+} ING_STATUS;
+
+typedef struct ing_varchar {
+   short len;
+   char* value;
+} ING_VARCHAR;
+
+/* It seems, Bacula needs the complete query result stored in one data structure */
+typedef struct ing_result {
+   IISQLDA *sqlda;             /* descriptor */
+   INGRES_FIELD *fields;
+   int num_rows;
+   int num_fields;
+   ING_STATUS status;
+   ING_ROW *first_row;
+   ING_ROW *act_row;           /* just for iterating */
+} INGresult;
+
+typedef struct ing_conn {
+   char *dbname;
+   char *user;
+   char *password;
+   int session_id;
+   char *msg;
+} INGconn;
+
+/* ---Prototypes--- */
+int INGgetCols(INGconn *dbconn, const char *query, bool transaction);
+char *INGgetvalue(INGresult *ing_res, int row_number, int column_number);
+bool INGgetisnull(INGresult *ing_res, int row_number, int column_number);
+int INGntuples(const INGresult *ing_res);
+int INGnfields(const INGresult *ing_res);
+char *INGfname(const INGresult *ing_res, int column_number);
+short INGftype(const INGresult *ing_res, int column_number);
+int INGexec(INGconn *dbconn, const char *query, bool transaction);
+INGresult *INGquery(INGconn *dbconn, const char *query, bool transaction);
+void INGclear(INGresult *ing_res);
+void INGcommit(const INGconn *dbconn);
+INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id);
+void INGsetDefaultLockingMode(INGconn *dbconn);
+void INGdisconnectDB(INGconn *dbconn);
+char *INGerrorMessage(const INGconn *dbconn);
+char *INGcmdTuples(INGresult *ing_res);
+
+#endif /* _MYINGRES_SH */