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