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