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