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
38 EXEC SQL INCLUDE SQLCA;
39 EXEC SQL INCLUDE SQLDA;
48 * ---Implementations---
52 return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
55 short INGgetCols(INGconn *conn, const char *query, bool transaction)
57 EXEC SQL BEGIN DECLARE SECTION;
60 EXEC SQL END DECLARE SECTION;
65 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
66 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
70 stmt = bstrdup(query);
73 * Switch to the correct default session for this thread.
75 sess_id = conn->session_id;
76 EXEC SQL SET_SQL (SESSION = :sess_id);
78 EXEC SQL PREPARE s1 from :stmt;
84 EXEC SQL DESCRIBE s1 into :sqlda;
94 * If we are not in a transaction we commit our work now.
100 * Switch to no default session for this thread.
102 EXEC SQL SET_SQL (SESSION = NONE);
108 static inline IISQLDA *INGgetDescriptor(short numCols, const char *query)
110 EXEC SQL BEGIN DECLARE SECTION;
112 EXEC SQL END DECLARE SECTION;
117 sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
118 memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
120 sqlda->sqln = numCols;
122 stmt = bstrdup(query);
124 EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
128 for (i = 0; i < sqlda->sqld; ++i) {
130 * Negative type indicates nullable coulumns, so an indicator
131 * is allocated, otherwise it's null
133 if (sqlda->sqlvar[i].sqltype > 0) {
134 sqlda->sqlvar[i].sqlind = NULL;
136 sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
139 * Alloc space for variable like indicated in sqllen
140 * for date types sqllen is always 0 -> allocate by type
142 switch (abs(sqlda->sqlvar[i].sqltype)) {
144 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
147 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
149 case IISQ_TSTMP_TYPE:
150 sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
154 * plus one to avoid zero mem allocs
156 sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
164 static void INGfreeDescriptor(IISQLDA *sqlda)
172 for (i = 0; i < sqlda->sqld; ++i) {
173 if (sqlda->sqlvar[i].sqldata) {
174 free(sqlda->sqlvar[i].sqldata);
176 if (sqlda->sqlvar[i].sqlind) {
177 free(sqlda->sqlvar[i].sqlind);
184 static inline int INGgetTypeSize(IISQLVAR *ingvar)
189 * TODO: add date types (at least TSTMP,TSW TSWO)
191 switch (ingvar->sqltype) {
199 inglength = ingvar->sqllen;
206 static inline INGresult *INGgetINGresult(IISQLDA *sqlda)
209 INGresult *result = NULL;
215 result = (INGresult *)malloc(sizeof(INGresult));
216 memset(result, 0, sizeof(INGresult));
218 result->sqlda = sqlda;
219 result->num_fields = sqlda->sqld;
220 result->num_rows = 0;
221 result->first_row = NULL;
222 result->status = ING_EMPTY_RESULT;
223 result->act_row = NULL;
224 memset(result->numrowstring, 0, sizeof(result->numrowstring));
226 if (result->num_fields) {
227 result->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * result->num_fields);
228 memset(result->fields, 0, sizeof(INGRES_FIELD) * result->num_fields);
230 for (i = 0; i < result->num_fields; ++i) {
231 memset(result->fields[i].name, 0, 34);
232 bstrncpy(result->fields[i].name, sqlda->sqlvar[i].sqlname.sqlnamec, sqlda->sqlvar[i].sqlname.sqlnamel);
233 result->fields[i].max_length = INGgetTypeSize(&sqlda->sqlvar[i]);
234 result->fields[i].type = abs(sqlda->sqlvar[i].sqltype);
235 result->fields[i].flags = (sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
242 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
246 if (row == NULL || sqlda == NULL) {
250 for (i = 0; i < sqlda->sqld; ++i) {
251 if (row->sqlvar[i].sqldata) {
252 free(row->sqlvar[i].sqldata);
254 if (row->sqlvar[i].sqlind) {
255 free(row->sqlvar[i].sqlind);
262 static void INGfreeINGresult(INGresult *ing_res)
272 * Free all rows and fields, then res, not descriptor!
274 * Use of rows is a nasty workaround til I find the reason,
275 * why aggregates like max() don't work
277 rows = ing_res->num_rows;
278 ing_res->act_row = ing_res->first_row;
279 while (ing_res->act_row != NULL && rows > 0) {
280 rowtemp = ing_res->act_row->next;
281 INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
282 ing_res->act_row = rowtemp;
285 if (ing_res->fields) {
286 free(ing_res->fields);
291 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
294 unsigned short len; /* used for VARCHAR type length */
295 IISQLDA *sqlda = ing_res->sqlda;
297 IISQLVAR *vars = NULL;
299 row = (ING_ROW *)malloc(sizeof(ING_ROW));
300 memset(row, 0, sizeof(ING_ROW));
302 vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
303 memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
308 for (i = 0; i < sqlda->sqld; ++i) {
310 * Make strings out of the data, then the space and assign
311 * (why string? at least it seems that way, looking into the sources)
313 vars[i].sqlind = (short *)malloc(sizeof(short));
314 if (sqlda->sqlvar[i].sqlind) {
315 memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
317 *vars[i].sqlind = NULL;
320 * if sqlind pointer exists AND points to -1 -> column is 'null'
322 if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
323 vars[i].sqldata = NULL;
325 switch (ing_res->fields[i].type) {
327 len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
328 vars[i].sqldata = (char *)malloc(len+1);
329 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata+2,len);
330 vars[i].sqldata[len] = '\0';
333 vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length+1);
334 memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
335 vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
338 vars[i].sqldata = (char *)malloc(20);
339 memset(vars[i].sqldata, 0, 20);
340 switch (sqlda->sqlvar[i].sqllen) {
342 bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
345 bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
348 bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
352 case IISQ_TSTMP_TYPE:
353 vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
354 vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
357 vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
358 vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
361 vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
362 vars[i].sqldata[IISQ_TSW_LEN] = '\0';
370 static inline int INGfetchAll(const char *query, INGresult *ing_res)
377 desc = ing_res->sqlda;
379 EXEC SQL DECLARE c2 CURSOR FOR s2;
380 if ((check = INGcheck()) < 0) {
385 if ((check = INGcheck()) < 0) {
389 /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
391 EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
393 if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
394 row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
397 * Initialize list when encountered first time
399 if (ing_res->first_row == 0) {
400 ing_res->first_row = row; /* head of the list */
401 ing_res->first_row->next = NULL;
402 ing_res->act_row = ing_res->first_row;
404 ing_res->act_row->next = row; /* append row to old act_row */
405 ing_res->act_row = row; /* set row as act_row */
406 row->row_number = linecount;
409 } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
413 ing_res->status = ING_COMMAND_OK;
414 ing_res->num_rows = linecount;
418 static inline ING_STATUS INGresultStatus(INGresult *res)
421 return ING_NO_RESULT;
427 static void INGrowSeek(INGresult *res, int row_number)
429 ING_ROW *trow = NULL;
431 if (res->act_row->row_number == row_number) {
436 * TODO: real error handling
438 if (row_number<0 || row_number>res->num_rows) {
442 for (trow = res->first_row; trow->row_number != row_number; trow = trow->next) ;
445 * Note - can be null - if row_number not found, right?
449 char *INGgetvalue(INGresult *res, int row_number, int column_number)
451 if (row_number != res->act_row->row_number) {
452 INGrowSeek(res, row_number);
455 return res->act_row->sqlvar[column_number].sqldata;
458 bool INGgetisnull(INGresult *res, int row_number, int column_number)
460 if (row_number != res->act_row->row_number) {
461 INGrowSeek(res, row_number);
464 return (*res->act_row->sqlvar[column_number].sqlind == -1) ? true : false;
467 int INGntuples(const INGresult *res)
469 return res->num_rows;
472 int INGnfields(const INGresult *res)
474 return res->num_fields;
477 char *INGfname(const INGresult *res, int column_number)
479 if ((column_number > res->num_fields) || (column_number < 0)) {
482 return res->fields[column_number].name;
486 short INGftype(const INGresult *res, int column_number)
488 return res->fields[column_number].type;
491 int INGexec(INGconn *conn, const char *query, bool transaction)
494 EXEC SQL BEGIN DECLARE SECTION;
498 EXEC SQL END DECLARE SECTION;
500 stmt = bstrdup(query);
504 * Switch to the correct default session for this thread.
506 sess_id = conn->session_id;
507 EXEC SQL SET_SQL (SESSION = :sess_id);
508 EXEC SQL EXECUTE IMMEDIATE :stmt;
512 if ((check = INGcheck()) < 0) {
517 EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
518 if ((check = INGcheck()) < 0) {
525 * If we are not in a transaction we commit our work now.
528 EXEC SQL COMMIT WORK;
531 * Switch to no default session for this thread.
533 EXEC SQL SET_SQL (SESSION = NONE);
537 INGresult *INGquery(INGconn *conn, const char *query, bool transaction)
540 * TODO: error handling
542 IISQLDA *desc = NULL;
543 INGresult *res = NULL;
545 int cols = INGgetCols(conn, query, transaction);
546 EXEC SQL BEGIN DECLARE SECTION;
548 EXEC SQL END DECLARE SECTION;
551 * Switch to the correct default session for this thread.
553 sess_id = conn->session_id;
554 EXEC SQL SET_SQL (SESSION = :sess_id);
556 desc = INGgetDescriptor(cols, query);
561 res = INGgetINGresult(desc);
566 rows = INGfetchAll(query, res);
569 INGfreeDescriptor(desc);
570 INGfreeINGresult(res);
577 * If we are not in a transaction we commit our work now.
580 EXEC SQL COMMIT WORK;
583 * Switch to no default session for this thread.
585 EXEC SQL SET_SQL (SESSION = NONE);
589 void INGclear(INGresult *res)
595 INGfreeDescriptor(res->sqlda);
596 INGfreeINGresult(res);
599 void INGcommit(const INGconn *conn)
601 EXEC SQL BEGIN DECLARE SECTION;
603 EXEC SQL END DECLARE SECTION;
605 if (dbconn != NULL) {
606 sess_id = dbconn->session_id;
607 EXEC SQL DISCONNECT SESSION :sess_id;
612 EXEC SQL COMMIT WORK;
615 * Switch to no default session for this thread.
617 EXEC SQL SET_SQL (SESSION = NONE);
621 INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id)
625 if (dbname == NULL || strlen(dbname) == 0) {
629 dbconn = (INGconn *)malloc(sizeof(INGconn));
630 memset(dbconn, 0, sizeof(INGconn));
632 EXEC SQL BEGIN DECLARE SECTION;
635 char ingdbpasswd[32];
637 EXEC SQL END DECLARE SECTION;
639 sess_id = session_id;
640 bstrncpy(ingdbname, dbname, sizeof(ingdbname));
643 bstrncpy(ingdbuser, user, sizeof(ingdbuser));
644 if (passwd != NULL) {
645 bstrncpy(ingdbpasswd, passwd, sizeof(ingdbpasswd));
647 memset(ingdbpasswd, 0, sizeof(ingdbpasswd));
652 IDENTIFIED BY :ingdbuser
653 DBMS_PASSWORD = :ingdbpasswd;
659 if (INGcheck() < 0) {
663 bstrncpy(dbconn->dbname, ingdbname, sizeof(dbconn->dbname));
664 bstrncpy(dbconn->user, ingdbuser, sizeof(dbconn->user));
665 bstrncpy(dbconn->password, ingdbpasswd, sizeof(dbconn->password));
666 dbconn->session_id = sess_id;
667 dbconn->msg = (char*)malloc(257);
668 memset(dbconn->msg, 0, 257);
671 * Switch to no default session for this thread undo default settings from SQL CONNECT.
673 EXEC SQL SET_SQL (SESSION = NONE);
678 void INGdisconnectDB(INGconn *dbconn)
680 EXEC SQL BEGIN DECLARE SECTION;
682 EXEC SQL END DECLARE SECTION;
684 if (dbconn != NULL) {
685 sess_id = dbconn->session_id;
686 EXEC SQL DISCONNECT SESSION :sess_id;
693 char *INGerrorMessage(const INGconn *conn)
695 EXEC SQL BEGIN DECLARE SECTION;
697 EXEC SQL END DECLARE SECTION;
699 EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
700 memcpy(conn->msg, &errbuf, 256);
704 char *INGcmdTuples(INGresult *res)
706 return res->numrowstring;
710 int INGputCopyEnd(INGconn *conn, const char *errormsg);
711 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);