2 Bacula® - The Network Backup Solution
4 Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula Catalog Database routines specific to Ingres
30 * These are Ingres specific routines
32 * Stefan Reddig, June 2009
35 /* The following is necessary so that we do not include
36 * the dummy external definition of DB.
38 #define __SQL_C /* indicate that this is sql.c */
44 EXEC SQL INCLUDE SQLCA;
45 EXEC SQL INCLUDE SQLDA;
54 * ---Implementations---
58 return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
61 short INGgetCols(B_DB *mdb, const char *query)
63 bool stmt_free = false;
64 EXEC SQL BEGIN DECLARE SECTION;
66 EXEC SQL END DECLARE SECTION;
71 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
72 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
77 * See if we need to run this through the limit_filter.
79 if (strstr(query, "LIMIT") != NULL) {
80 stmt = mdb->limit_filter->replace(query);
82 stmt = bstrdup(query);
86 EXEC SQL PREPARE s1 from :stmt;
92 EXEC SQL DESCRIBE s1 into :sqlda;
108 static inline IISQLDA *INGgetDescriptor(B_DB *mdb, short numCols, const char *query)
110 bool stmt_free = false;
111 EXEC SQL BEGIN DECLARE SECTION;
113 EXEC SQL END DECLARE SECTION;
118 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
119 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
121 sqlda->sqln = numCols;
124 * See if we need to run this through the limit_filter.
126 if (strstr(query, "LIMIT") != NULL) {
127 stmt = mdb->limit_filter->replace(query);
129 stmt = bstrdup(query);
133 EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
139 for (i = 0; i < sqlda->sqld; ++i) {
141 * Negative type indicates nullable coulumns, so an indicator
142 * is allocated, otherwise it's null
144 if (sqlda->sqlvar[i].sqltype > 0) {
145 sqlda->sqlvar[i].sqlind = NULL;
147 sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
150 * Alloc space for variable like indicated in sqllen
151 * for date types sqllen is always 0 -> allocate by type
153 switch (abs(sqlda->sqlvar[i].sqltype)) {
155 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
158 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
160 case IISQ_TSTMP_TYPE:
161 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
165 * plus one to avoid zero mem allocs
167 sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
175 static void INGfreeDescriptor(IISQLDA *sqlda)
183 for (i = 0; i < sqlda->sqld; ++i) {
184 if (sqlda->sqlvar[i].sqldata) {
185 free(sqlda->sqlvar[i].sqldata);
187 if (sqlda->sqlvar[i].sqlind) {
188 free(sqlda->sqlvar[i].sqlind);
195 static inline int INGgetTypeSize(IISQLVAR *ingvar)
200 * TODO: add date types (at least TSTMP,TSW TSWO)
202 switch (ingvar->sqltype) {
210 inglength = ingvar->sqllen;
217 static inline INGresult *INGgetINGresult(IISQLDA *sqlda)
220 INGresult *result = NULL;
226 result = (INGresult *)malloc(sizeof(INGresult));
227 memset(result, 0, sizeof(INGresult));
229 result->sqlda = sqlda;
230 result->num_fields = sqlda->sqld;
231 result->num_rows = 0;
232 result->first_row = NULL;
233 result->status = ING_EMPTY_RESULT;
234 result->act_row = NULL;
235 memset(result->numrowstring, 0, sizeof(result->numrowstring));
237 if (result->num_fields) {
238 result->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * result->num_fields);
239 memset(result->fields, 0, sizeof(INGRES_FIELD) * result->num_fields);
241 for (i = 0; i < result->num_fields; ++i) {
242 memset(result->fields[i].name, 0, 34);
243 bstrncpy(result->fields[i].name, sqlda->sqlvar[i].sqlname.sqlnamec, sqlda->sqlvar[i].sqlname.sqlnamel);
244 result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]);
245 result->fields[i].type = abs(sqlda->sqlvar[i].sqltype);
246 result->fields[i].flags = (sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
253 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
257 if (row == NULL || sqlda == NULL) {
261 for (i = 0; i < sqlda->sqld; ++i) {
262 if (row->sqlvar[i].sqldata) {
263 free(row->sqlvar[i].sqldata);
265 if (row->sqlvar[i].sqlind) {
266 free(row->sqlvar[i].sqlind);
273 static void INGfreeINGresult(INGresult *ing_res)
283 * Free all rows and fields, then res, not descriptor!
285 * Use of rows is a nasty workaround til I find the reason,
286 * why aggregates like max() don't work
288 rows = ing_res->num_rows;
289 ing_res->act_row = ing_res->first_row;
290 while (ing_res->act_row != NULL && rows > 0) {
291 rowtemp = ing_res->act_row->next;
292 INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
293 ing_res->act_row = rowtemp;
296 if (ing_res->fields) {
297 free(ing_res->fields);
303 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
306 unsigned short len; /* used for VARCHAR type length */
307 IISQLDA *sqlda = ing_res->sqlda;
309 IISQLVAR *vars = NULL;
311 row = (ING_ROW *)malloc(sizeof(ING_ROW));
312 memset(row, 0, sizeof(ING_ROW));
314 vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
315 memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
320 for (i = 0; i < sqlda->sqld; ++i) {
322 * Make strings out of the data, then the space and assign
323 * (why string? at least it seems that way, looking into the sources)
325 vars[i].sqlind = (short *)malloc(sizeof(short));
326 if (sqlda->sqlvar[i].sqlind) {
327 memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
329 *vars[i].sqlind = NULL;
332 * if sqlind pointer exists AND points to -1 -> column is 'null'
334 if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
335 vars[i].sqldata = NULL;
337 switch (ing_res->fields[i].type) {
339 len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
340 vars[i].sqldata = (char *)malloc(len+1);
341 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len);
342 vars[i].sqldata[len] = '\0';
345 vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1);
346 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
347 vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
350 vars[i].sqldata = (char *)malloc(20);
351 memset(vars[i].sqldata, 0, 20);
352 switch (sqlda->sqlvar[i].sqllen) {
354 bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
357 bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
360 bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
364 case IISQ_TSTMP_TYPE:
365 vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
366 vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
369 vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
370 vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
373 vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
374 vars[i].sqldata[IISQ_TSW_LEN] = '\0';
382 static inline int INGfetchAll(const char *query, INGresult *ing_res)
389 desc = ing_res->sqlda;
391 EXEC SQL DECLARE c2 CURSOR FOR s2;
392 if ((check = INGcheck()) < 0) {
397 if ((check = INGcheck()) < 0) {
401 /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
403 EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
405 if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
406 row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
409 * Initialize list when encountered first time
411 if (ing_res->first_row == 0) {
412 ing_res->first_row = row; /* head of the list */
413 ing_res->first_row->next = NULL;
414 ing_res->act_row = ing_res->first_row;
416 ing_res->act_row->next = row; /* append row to old act_row */
417 ing_res->act_row = row; /* set row as act_row */
418 row->row_number = linecount;
421 } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
425 ing_res->status = ING_COMMAND_OK;
426 ing_res->num_rows = linecount;
430 static inline ING_STATUS INGresultStatus(INGresult *res)
433 return ING_NO_RESULT;
439 static void INGrowSeek(INGresult *res, int row_number)
441 ING_ROW *trow = NULL;
443 if (res->act_row->row_number == row_number) {
448 * TODO: real error handling
450 if (row_number<0 || row_number>res->num_rows) {
454 for (trow = res->first_row; trow->row_number != row_number; trow = trow->next) ;
457 * Note - can be null - if row_number not found, right?
461 char *INGgetvalue(INGresult *res, int row_number, int column_number)
463 if (row_number != res->act_row->row_number) {
464 INGrowSeek(res, row_number);
467 return res->act_row->sqlvar[column_number].sqldata;
470 int INGgetisnull(INGresult *res, int row_number, int column_number)
472 if (row_number != res->act_row->row_number) {
473 INGrowSeek(res, row_number);
476 return (*res->act_row->sqlvar[column_number].sqlind == -1) ? 1 : 0;
479 int INGntuples(const INGresult *res)
481 return res->num_rows;
484 int INGnfields(const INGresult *res)
486 return res->num_fields;
489 char *INGfname(const INGresult *res, int column_number)
491 if ((column_number > res->num_fields) || (column_number < 0)) {
494 return res->fields[column_number].name;
498 short INGftype(const INGresult *res, int column_number)
500 return res->fields[column_number].type;
503 int INGexec(B_DB *mdb, INGconn *conn, const char *query)
505 bool stmt_free = false;
507 EXEC SQL BEGIN DECLARE SECTION;
510 EXEC SQL END DECLARE SECTION;
513 * See if we need to run this through the limit_filter.
515 if (strstr(query, "LIMIT") != NULL) {
516 stmt = mdb->limit_filter->replace(query);
518 stmt = bstrdup(query);
523 EXEC SQL EXECUTE IMMEDIATE :stmt;
529 if ((check = INGcheck()) < 0) {
533 EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
534 if ((check = INGcheck()) < 0) {
541 INGresult *INGquery(B_DB *mdb, INGconn *conn, const char *query)
544 * TODO: error handling
546 IISQLDA *desc = NULL;
547 INGresult *res = NULL;
549 int cols = INGgetCols(mdb, query);
551 desc = INGgetDescriptor(mdb, cols, query);
555 res = INGgetINGresult(desc);
559 rows = INGfetchAll(query, res);
562 INGfreeINGresult(res);
563 INGfreeDescriptor(desc);
569 void INGclear(INGresult *res)
575 INGfreeINGresult(res);
576 INGfreeDescriptor(res->sqlda);
579 INGconn *INGconnectDB(char *dbname, char *user, char *passwd)
583 if (dbname == NULL || strlen(dbname) == 0) {
587 dbconn = (INGconn *)malloc(sizeof(INGconn));
588 memset(dbconn, 0, sizeof(INGconn));
590 EXEC SQL BEGIN DECLARE SECTION;
596 EXEC SQL END DECLARE SECTION;
598 bstrncpy(ingdbname, dbname, sizeof(ingdbname));
601 bstrncpy(ingdbuser, user, sizeof(ingdbuser));
602 if (passwd != NULL) {
603 bstrncpy(ingdbpasw, passwd, sizeof(ingdbpasw));
605 memset(ingdbpasw, 0, sizeof(ingdbpasw));
609 identified by :ingdbuser
610 dbms_password = :ingdbpasw;
612 EXEC SQL CONNECT :ingdbname;
615 EXEC SQL INQUIRE_SQL(:conn_name = connection_name);
616 EXEC SQL INQUIRE_SQL(:sess_id = session);
618 bstrncpy(dbconn->dbname, ingdbname, sizeof(dbconn->dbname));
619 bstrncpy(dbconn->user, ingdbuser, sizeof(dbconn->user));
620 bstrncpy(dbconn->password, ingdbpasw, sizeof(dbconn->password));
621 bstrncpy(dbconn->connection_name, conn_name, sizeof(dbconn->connection_name));
622 dbconn->session_id = sess_id;
623 dbconn->msg = (char*)malloc(257);
624 memset(dbconn->msg, 0, 257);
629 void INGdisconnectDB(INGconn *dbconn)
632 * TODO: check for any real use of dbconn: maybe whenn multithreaded?
635 if (dbconn != NULL) {
641 char *INGerrorMessage(const INGconn *conn)
643 EXEC SQL BEGIN DECLARE SECTION;
645 EXEC SQL END DECLARE SECTION;
647 EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
648 memcpy(conn->msg, &errbuf, 256);
652 char *INGcmdTuples(INGresult *res)
654 return res->numrowstring;
658 int INGputCopyEnd(INGconn *conn, const char *errormsg);
659 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);