]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/myingres.sc
Let INGgetisnull return an bool
[bacula/bacula] / bacula / src / cats / myingres.sc
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2009-2010 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Bacula Catalog Database routines specific to Ingres
30  *   These are Ingres specific routines
31  *
32  *    Stefan Reddig, June 2009
33  */
34
35 /* The following is necessary so that we do not include
36  * the dummy external definition of DB.
37  */
38 #define __SQL_C                       /* indicate that this is sql.c */
39
40 #include "bacula.h"
41 #include "cats.h"
42
43 #ifdef HAVE_INGRES
44 EXEC SQL INCLUDE SQLCA;
45 EXEC SQL INCLUDE SQLDA;
46
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50
51 #include "myingres.h"
52
53 /*
54  * ---Implementations---
55  */
56 int INGcheck()
57 {
58    return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
59 }
60
61 short INGgetCols(B_DB *mdb, const char *query)
62 {
63    bool stmt_free = false;
64    EXEC SQL BEGIN DECLARE SECTION;
65    char *stmt;
66    EXEC SQL END DECLARE SECTION;
67    
68    short number = 1;
69    IISQLDA *sqlda;
70
71    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
72    memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
73    
74    sqlda->sqln = number;
75
76    /*
77     * See if we need to run this through the limit_filter.
78     */
79    if (strstr(query, "LIMIT") != NULL) {
80       stmt = mdb->limit_filter->replace(query);
81    } else {
82       stmt = bstrdup(query);
83       stmt_free = true;
84    }
85      
86    EXEC SQL PREPARE s1 from :stmt;
87    if (INGcheck() < 0) {
88       number = -1;
89       goto bail_out;
90    }
91
92    EXEC SQL DESCRIBE s1 into :sqlda;
93    if (INGcheck() < 0) {
94       number = -1;
95       goto bail_out;
96    }
97      
98    number = sqlda->sqld;
99
100 bail_out:
101    if (stmt_free) {
102       free(stmt);
103    }
104    free(sqlda);
105    return number;
106 }
107
108 static inline IISQLDA *INGgetDescriptor(B_DB *mdb, short numCols, const char *query)
109 {
110    bool stmt_free = false;
111    EXEC SQL BEGIN DECLARE SECTION;
112    char *stmt;
113    EXEC SQL END DECLARE SECTION;
114
115    int i;
116    IISQLDA *sqlda;
117
118    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
119    memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
120    
121    sqlda->sqln = numCols;
122    
123    /*
124     * See if we need to run this through the limit_filter.
125     */
126    if (strstr(query, "LIMIT") != NULL) {
127       stmt = mdb->limit_filter->replace(query);
128    } else {
129       stmt = bstrdup(query);
130       stmt_free = true;
131    }
132   
133    EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
134   
135    if (stmt_free) {
136       free(stmt);
137    }
138
139    for (i = 0; i < sqlda->sqld; ++i) {
140       /*
141        * Negative type indicates nullable coulumns, so an indicator
142        * is allocated, otherwise it's null
143        */
144       if (sqlda->sqlvar[i].sqltype > 0) {
145          sqlda->sqlvar[i].sqlind = NULL;
146       } else {
147          sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
148       }
149       /*
150        * Alloc space for variable like indicated in sqllen
151        * for date types sqllen is always 0 -> allocate by type
152        */
153       switch (abs(sqlda->sqlvar[i].sqltype)) {
154       case IISQ_TSW_TYPE:
155          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
156          break;
157       case IISQ_TSWO_TYPE:
158          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
159          break;
160       case IISQ_TSTMP_TYPE:
161          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
162          break;
163       default:
164          /*
165           * plus one to avoid zero mem allocs
166           */
167          sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
168          break;
169       }
170    }
171    
172    return sqlda;
173 }
174
175 static void INGfreeDescriptor(IISQLDA *sqlda)
176 {
177    int i;
178
179    if (!sqlda) {
180       return;
181    }
182
183    for (i = 0; i < sqlda->sqld; ++i) {
184       if (sqlda->sqlvar[i].sqldata) {
185          free(sqlda->sqlvar[i].sqldata);
186       }
187       if (sqlda->sqlvar[i].sqlind) {
188          free(sqlda->sqlvar[i].sqlind);
189       }
190    }
191    free(sqlda);
192    sqlda = NULL;
193 }
194
195 static inline int INGgetTypeSize(IISQLVAR *ingvar)
196 {
197    int inglength = 0;
198    
199    /*
200     * TODO: add date types (at least TSTMP,TSW TSWO)
201     */
202    switch (ingvar->sqltype) {
203    case IISQ_DTE_TYPE:
204       inglength = 25;
205       break;
206    case IISQ_MNY_TYPE:
207       inglength = 8;
208       break;
209    default:
210       inglength = ingvar->sqllen;
211       break;
212    }
213    
214    return inglength;
215 }
216
217 static inline INGresult *INGgetINGresult(IISQLDA *sqlda)
218 {
219    int i;
220    INGresult *result = NULL;
221    
222    if (!sqlda) {
223       return NULL;
224    }
225
226    result = (INGresult *)malloc(sizeof(INGresult));
227    memset(result, 0, sizeof(INGresult));
228    
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));
236    
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);
240
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;
247       }
248    }
249
250    return result;
251 }
252
253 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
254 {
255    int i;
256
257    if (row == NULL || sqlda == NULL) {
258       return;
259    }
260
261    for (i = 0; i < sqlda->sqld; ++i) {
262       if (row->sqlvar[i].sqldata) {
263          free(row->sqlvar[i].sqldata);
264       }
265       if (row->sqlvar[i].sqlind) {
266          free(row->sqlvar[i].sqlind);
267       }
268    }
269    free(row->sqlvar);
270    free(row);
271 }
272
273 static void INGfreeINGresult(INGresult *ing_res)
274 {
275    int rows;
276    ING_ROW *rowtemp;
277
278    if (!ing_res) {
279       return;
280    }
281
282    /*
283     * Free all rows and fields, then res, not descriptor!
284     *
285     * Use of rows is a nasty workaround til I find the reason,
286     * why aggregates like max() don't work
287     */
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;
294       --rows;
295    }
296    if (ing_res->fields) {
297       free(ing_res->fields);
298    }
299    free(ing_res);
300    ing_res = NULL;
301 }
302
303 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
304 {
305    int i;
306    unsigned short len; /* used for VARCHAR type length */
307    IISQLDA *sqlda = ing_res->sqlda;
308    ING_ROW *row = NULL;
309    IISQLVAR *vars = NULL;
310
311    row = (ING_ROW *)malloc(sizeof(ING_ROW));
312    memset(row, 0, sizeof(ING_ROW));
313
314    vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
315    memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
316
317    row->sqlvar = vars;
318    row->next = NULL;
319
320    for (i = 0; i < sqlda->sqld; ++i) {
321       /*
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)
324        */
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));
328       } else {
329          *vars[i].sqlind = NULL;
330       }
331       /*
332        * if sqlind pointer exists AND points to -1 -> column is 'null'
333        */
334       if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
335          vars[i].sqldata = NULL;
336       } else {
337          switch (ing_res->fields[i].type) {
338          case IISQ_VCH_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';
343             break;
344          case IISQ_CHA_TYPE:
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';
348             break;
349          case IISQ_INT_TYPE:
350             vars[i].sqldata = (char *)malloc(20);
351             memset(vars[i].sqldata, 0, 20);
352             switch (sqlda->sqlvar[i].sqllen) {
353             case 2:
354                bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
355                break;
356             case 4:
357                bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
358                break;
359             case 8:
360                bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
361                break;
362             }
363             break;
364          case IISQ_TSTMP_TYPE:
365             vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
366             vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
367             break;
368          case IISQ_TSWO_TYPE:
369             vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
370             vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
371             break;
372          case IISQ_TSW_TYPE:
373             vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
374             vars[i].sqldata[IISQ_TSW_LEN] = '\0';
375             break;
376          }
377       }
378    }
379    return row;
380 }
381
382 static inline int INGfetchAll(const char *query, INGresult *ing_res)
383 {
384    int linecount = 0;
385    ING_ROW *row;
386    IISQLDA *desc;
387    int check = -1;
388    
389    desc = ing_res->sqlda;
390    
391    EXEC SQL DECLARE c2 CURSOR FOR s2;
392    if ((check = INGcheck()) < 0) {
393       return check;
394    }
395    
396    EXEC SQL OPEN c2;
397    if ((check = INGcheck()) < 0) {
398       return check;
399    }
400       
401    /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
402    do {
403       EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
404
405       if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
406          row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
407             
408          /*
409           * Initialize list when encountered first time
410           */
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;
415          }      
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;
419          ++linecount;
420       }
421    } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
422    
423    EXEC SQL CLOSE c2;
424    
425    ing_res->status = ING_COMMAND_OK;
426    ing_res->num_rows = linecount;
427    return linecount;
428 }
429
430 static inline ING_STATUS INGresultStatus(INGresult *res)
431 {
432    if (res == NULL) {
433       return ING_NO_RESULT;
434    } else {
435       return res->status;
436    }
437 }
438
439 static void INGrowSeek(INGresult *res, int row_number)
440 {
441    ING_ROW *trow = NULL;
442
443    if (res->act_row->row_number == row_number) {
444       return;
445    }
446    
447    /*
448     * TODO: real error handling
449     */
450    if (row_number<0 || row_number>res->num_rows) {
451       return;
452    }
453
454    for (trow = res->first_row; trow->row_number != row_number; trow = trow->next) ;
455    res->act_row = trow;
456    /*
457     * Note - can be null - if row_number not found, right?
458     */
459 }
460
461 char *INGgetvalue(INGresult *res, int row_number, int column_number)
462 {
463    if (row_number != res->act_row->row_number) {
464       INGrowSeek(res, row_number);
465    }
466
467    return res->act_row->sqlvar[column_number].sqldata;
468 }
469
470 bool INGgetisnull(INGresult *res, int row_number, int column_number)
471 {
472    if (row_number != res->act_row->row_number) {
473       INGrowSeek(res, row_number);
474    }
475
476    return (*res->act_row->sqlvar[column_number].sqlind == -1) ? true : false;
477 }
478
479 int INGntuples(const INGresult *res)
480 {
481    return res->num_rows;
482 }
483
484 int INGnfields(const INGresult *res)
485 {
486    return res->num_fields;
487 }
488
489 char *INGfname(const INGresult *res, int column_number)
490 {
491    if ((column_number > res->num_fields) || (column_number < 0)) {
492       return NULL;
493    } else {
494       return res->fields[column_number].name;
495    }
496 }
497
498 short INGftype(const INGresult *res, int column_number)
499 {
500    return res->fields[column_number].type;
501 }
502
503 int INGexec(B_DB *mdb, INGconn *conn, const char *query)
504 {
505    bool stmt_free = false;
506    int check;
507    EXEC SQL BEGIN DECLARE SECTION;
508    int rowcount;
509    char *stmt;
510    EXEC SQL END DECLARE SECTION;
511    
512    /*
513     * See if we need to run this through the limit_filter.
514     */
515    if (strstr(query, "LIMIT") != NULL) {
516       stmt = mdb->limit_filter->replace(query);
517    } else {
518       stmt = bstrdup(query);
519       stmt_free = true;
520    }
521    rowcount = -1;
522
523    EXEC SQL EXECUTE IMMEDIATE :stmt;
524
525    if (stmt_free) {
526       free(stmt);
527    }
528
529    if ((check = INGcheck()) < 0) {
530       return check;
531    }
532
533    EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
534    if ((check = INGcheck()) < 0) {
535       return check;
536    }
537    
538    return rowcount;
539 }
540
541 INGresult *INGquery(B_DB *mdb, INGconn *conn, const char *query)
542 {
543    /*
544     * TODO: error handling
545     */
546    IISQLDA *desc = NULL;
547    INGresult *res = NULL;
548    int rows = -1;
549    int cols = INGgetCols(mdb, query);
550
551    desc = INGgetDescriptor(mdb, cols, query);
552    if (!desc) {
553       return NULL;
554    }
555    res = INGgetINGresult(desc);
556    if (!res) {
557       return NULL;
558    }
559    rows = INGfetchAll(query, res);
560
561    if (rows < 0) {
562      INGfreeINGresult(res);
563      INGfreeDescriptor(desc);
564      return NULL;
565    }
566    return res;
567 }
568
569 void INGclear(INGresult *res)
570 {
571    if (res == NULL) {
572       return;
573    }
574
575    INGfreeINGresult(res);
576    INGfreeDescriptor(res->sqlda);
577 }
578
579 INGconn *INGconnectDB(char *dbname, char *user, char *passwd)
580 {
581    INGconn *dbconn;
582
583    if (dbname == NULL || strlen(dbname) == 0) {
584       return NULL;
585    }
586
587    dbconn = (INGconn *)malloc(sizeof(INGconn));
588    memset(dbconn, 0, sizeof(INGconn));
589
590    EXEC SQL BEGIN DECLARE SECTION;
591    char ingdbname[24];
592    char ingdbuser[32];
593    char ingdbpasw[32];
594    char conn_name[32];
595    int sess_id;
596    EXEC SQL END DECLARE SECTION;
597
598    bstrncpy(ingdbname, dbname, sizeof(ingdbname));
599    
600    if (user != NULL) {
601       bstrncpy(ingdbuser, user, sizeof(ingdbuser));
602       if (passwd != NULL) {
603          bstrncpy(ingdbpasw, passwd, sizeof(ingdbpasw));
604       } else {
605          memset(ingdbpasw, 0, sizeof(ingdbpasw));
606       }
607       EXEC SQL CONNECT
608          :ingdbname
609          identified by :ingdbuser
610          dbms_password = :ingdbpasw;
611    } else {
612       EXEC SQL CONNECT :ingdbname;
613    }   
614    
615    EXEC SQL INQUIRE_SQL(:conn_name = connection_name);
616    EXEC SQL INQUIRE_SQL(:sess_id = session);
617    
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);
625
626    return dbconn;
627 }
628
629 void INGdisconnectDB(INGconn *dbconn)
630 {
631    /*
632     * TODO: check for any real use of dbconn: maybe whenn multithreaded?
633     */
634    EXEC SQL DISCONNECT;
635    if (dbconn != NULL) {
636       free(dbconn->msg);
637       free(dbconn);
638    }
639 }
640
641 char *INGerrorMessage(const INGconn *conn)
642 {
643    EXEC SQL BEGIN DECLARE SECTION;
644    char errbuf[256];
645    EXEC SQL END DECLARE SECTION;
646
647    EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
648    memcpy(conn->msg, &errbuf, 256);
649    return conn->msg;
650 }
651
652 char *INGcmdTuples(INGresult *res)
653 {
654    return res->numrowstring;
655 }
656
657 /* TODO?
658 int INGputCopyEnd(INGconn *conn, const char *errormsg);
659 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);
660 */
661
662 #endif