--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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