]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/myingres.sc
Fix typos
[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 #include "bacula.h"
36
37 #ifdef HAVE_INGRES
38 EXEC SQL INCLUDE SQLCA;
39 EXEC SQL INCLUDE SQLDA;
40
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44
45 #include "myingres.h"
46
47 /*
48  * ---Implementations---
49  */
50 int INGcheck()
51 {
52    return (sqlca.sqlcode < 0) ? sqlca.sqlcode : 0;
53 }
54
55 short INGgetCols(INGconn *conn, const char *query, bool transaction)
56 {
57    EXEC SQL BEGIN DECLARE SECTION;
58    int sess_id;
59    char *stmt;
60    EXEC SQL END DECLARE SECTION;
61    
62    short number = 1;
63    IISQLDA *sqlda;
64
65    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE));
66    memset(sqlda, 0, (IISQDA_HEAD_SIZE + (number * IISQDA_VAR_SIZE)));
67    
68    sqlda->sqln = number;
69
70    stmt = bstrdup(query);
71
72    /*
73     * Switch to the correct default session for this thread.
74     */
75    sess_id = conn->session_id;
76    EXEC SQL SET_SQL (SESSION = :sess_id);
77      
78    EXEC SQL PREPARE s1 from :stmt;
79    if (INGcheck() < 0) {
80       number = -1;
81       goto bail_out;
82    }
83
84    EXEC SQL DESCRIBE s1 into :sqlda;
85    if (INGcheck() < 0) {
86       number = -1;
87       goto bail_out;
88    }
89      
90    number = sqlda->sqld;
91
92 bail_out:
93    /*
94     * If we are not in a transaction we commit our work now.
95     */
96    if (!transaction) {
97       EXEC SQL COMMIT WORK;
98    }
99    /*
100     * Switch to no default session for this thread.
101     */
102    EXEC SQL SET_SQL (SESSION = NONE);
103    free(stmt);
104    free(sqlda);
105    return number;
106 }
107
108 static inline IISQLDA *INGgetDescriptor(short numCols, const char *query)
109 {
110    EXEC SQL BEGIN DECLARE SECTION;
111    char *stmt;
112    EXEC SQL END DECLARE SECTION;
113
114    int i;
115    IISQLDA *sqlda;
116
117    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
118    memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
119    
120    sqlda->sqln = numCols;
121    
122    stmt = bstrdup(query);
123   
124    EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
125   
126    free(stmt);
127
128    for (i = 0; i < sqlda->sqld; ++i) {
129       /*
130        * Negative type indicates nullable coulumns, so an indicator
131        * is allocated, otherwise it's null
132        */
133       if (sqlda->sqlvar[i].sqltype > 0) {
134          sqlda->sqlvar[i].sqlind = NULL;
135       } else {
136          sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
137       }
138       /*
139        * Alloc space for variable like indicated in sqllen
140        * for date types sqllen is always 0 -> allocate by type
141        */
142       switch (abs(sqlda->sqlvar[i].sqltype)) {
143       case IISQ_TSW_TYPE:
144          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
145          break;
146       case IISQ_TSWO_TYPE:
147          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
148          break;
149       case IISQ_TSTMP_TYPE:
150          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
151          break;
152       default:
153          /*
154           * plus one to avoid zero mem allocs
155           */
156          sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen+1);
157          break;
158       }
159    }
160    
161    return sqlda;
162 }
163
164 static void INGfreeDescriptor(IISQLDA *sqlda)
165 {
166    int i;
167
168    if (!sqlda) {
169       return;
170    }
171
172    for (i = 0; i < sqlda->sqld; ++i) {
173       if (sqlda->sqlvar[i].sqldata) {
174          free(sqlda->sqlvar[i].sqldata);
175       }
176       if (sqlda->sqlvar[i].sqlind) {
177          free(sqlda->sqlvar[i].sqlind);
178       }
179    }
180    free(sqlda);
181    sqlda = NULL;
182 }
183
184 static inline int INGgetTypeSize(IISQLVAR *ingvar)
185 {
186    int inglength = 0;
187    
188    /*
189     * TODO: add date types (at least TSTMP,TSW TSWO)
190     */
191    switch (ingvar->sqltype) {
192    case IISQ_DTE_TYPE:
193       inglength = 25;
194       break;
195    case IISQ_MNY_TYPE:
196       inglength = 8;
197       break;
198    default:
199       inglength = ingvar->sqllen;
200       break;
201    }
202    
203    return inglength;
204 }
205
206 static inline INGresult *INGgetINGresult(IISQLDA *sqlda)
207 {
208    int i;
209    INGresult *result = NULL;
210    
211    if (!sqlda) {
212       return NULL;
213    }
214
215    result = (INGresult *)malloc(sizeof(INGresult));
216    memset(result, 0, sizeof(INGresult));
217    
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));
225    
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);
229
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;
236       }
237    }
238
239    return result;
240 }
241
242 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
243 {
244    int i;
245
246    if (row == NULL || sqlda == NULL) {
247       return;
248    }
249
250    for (i = 0; i < sqlda->sqld; ++i) {
251       if (row->sqlvar[i].sqldata) {
252          free(row->sqlvar[i].sqldata);
253       }
254       if (row->sqlvar[i].sqlind) {
255          free(row->sqlvar[i].sqlind);
256       }
257    }
258    free(row->sqlvar);
259    free(row);
260 }
261
262 static void INGfreeINGresult(INGresult *ing_res)
263 {
264    int rows;
265    ING_ROW *rowtemp;
266
267    if (!ing_res) {
268       return;
269    }
270
271    /*
272     * Free all rows and fields, then res, not descriptor!
273     *
274     * Use of rows is a nasty workaround til I find the reason,
275     * why aggregates like max() don't work
276     */
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;
283       --rows;
284    }
285    if (ing_res->fields) {
286       free(ing_res->fields);
287    }
288    free(ing_res);
289 }
290
291 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
292 {
293    int i;
294    unsigned short len; /* used for VARCHAR type length */
295    IISQLDA *sqlda = ing_res->sqlda;
296    ING_ROW *row = NULL;
297    IISQLVAR *vars = NULL;
298
299    row = (ING_ROW *)malloc(sizeof(ING_ROW));
300    memset(row, 0, sizeof(ING_ROW));
301
302    vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
303    memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
304
305    row->sqlvar = vars;
306    row->next = NULL;
307
308    for (i = 0; i < sqlda->sqld; ++i) {
309       /*
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)
312        */
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));
316       } else {
317          *vars[i].sqlind = NULL;
318       }
319       /*
320        * if sqlind pointer exists AND points to -1 -> column is 'null'
321        */
322       if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
323          vars[i].sqldata = NULL;
324       } else {
325          switch (ing_res->fields[i].type) {
326          case IISQ_VCH_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';
331             break;
332          case IISQ_CHA_TYPE:
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';
336             break;
337          case IISQ_INT_TYPE:
338             vars[i].sqldata = (char *)malloc(20);
339             memset(vars[i].sqldata, 0, 20);
340             switch (sqlda->sqlvar[i].sqllen) {
341             case 2:
342                bsnprintf(vars[i].sqldata, 20, "%d",*(short*)sqlda->sqlvar[i].sqldata);
343                break;
344             case 4:
345                bsnprintf(vars[i].sqldata, 20, "%ld",*(int*)sqlda->sqlvar[i].sqldata);
346                break;
347             case 8:
348                bsnprintf(vars[i].sqldata, 20, "%lld",*(long*)sqlda->sqlvar[i].sqldata);
349                break;
350             }
351             break;
352          case IISQ_TSTMP_TYPE:
353             vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN+1);
354             vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
355             break;
356          case IISQ_TSWO_TYPE:
357             vars[i].sqldata = (char *)malloc(IISQ_TSWO_LEN+1);
358             vars[i].sqldata[IISQ_TSWO_LEN] = '\0';
359             break;
360          case IISQ_TSW_TYPE:
361             vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN+1);
362             vars[i].sqldata[IISQ_TSW_LEN] = '\0';
363             break;
364          }
365       }
366    }
367    return row;
368 }
369
370 static inline int INGfetchAll(const char *query, INGresult *ing_res)
371 {
372    int linecount = 0;
373    ING_ROW *row;
374    IISQLDA *desc;
375    int check = -1;
376    
377    desc = ing_res->sqlda;
378    
379    EXEC SQL DECLARE c2 CURSOR FOR s2;
380    if ((check = INGcheck()) < 0) {
381       return check;
382    }
383    
384    EXEC SQL OPEN c2;
385    if ((check = INGcheck()) < 0) {
386       return check;
387    }
388       
389    /* for (linecount = 0; sqlca.sqlcode == 0; ++linecount) */
390    do {
391       EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
392
393       if ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) ) {
394          row = INGgetRowSpace(ing_res); /* alloc space for fetched row */
395             
396          /*
397           * Initialize list when encountered first time
398           */
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;
403          }      
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;
407          ++linecount;
408       }
409    } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
410    
411    EXEC SQL CLOSE c2;
412    
413    ing_res->status = ING_COMMAND_OK;
414    ing_res->num_rows = linecount;
415    return linecount;
416 }
417
418 static inline ING_STATUS INGresultStatus(INGresult *res)
419 {
420    if (res == NULL) {
421       return ING_NO_RESULT;
422    } else {
423       return res->status;
424    }
425 }
426
427 static void INGrowSeek(INGresult *res, int row_number)
428 {
429    ING_ROW *trow = NULL;
430
431    if (res->act_row->row_number == row_number) {
432       return;
433    }
434    
435    /*
436     * TODO: real error handling
437     */
438    if (row_number<0 || row_number>res->num_rows) {
439       return;
440    }
441
442    for (trow = res->first_row; trow->row_number != row_number; trow = trow->next) ;
443    res->act_row = trow;
444    /*
445     * Note - can be null - if row_number not found, right?
446     */
447 }
448
449 char *INGgetvalue(INGresult *res, int row_number, int column_number)
450 {
451    if (row_number != res->act_row->row_number) {
452       INGrowSeek(res, row_number);
453    }
454
455    return res->act_row->sqlvar[column_number].sqldata;
456 }
457
458 bool INGgetisnull(INGresult *res, int row_number, int column_number)
459 {
460    if (row_number != res->act_row->row_number) {
461       INGrowSeek(res, row_number);
462    }
463
464    return (*res->act_row->sqlvar[column_number].sqlind == -1) ? true : false;
465 }
466
467 int INGntuples(const INGresult *res)
468 {
469    return res->num_rows;
470 }
471
472 int INGnfields(const INGresult *res)
473 {
474    return res->num_fields;
475 }
476
477 char *INGfname(const INGresult *res, int column_number)
478 {
479    if ((column_number > res->num_fields) || (column_number < 0)) {
480       return NULL;
481    } else {
482       return res->fields[column_number].name;
483    }
484 }
485
486 short INGftype(const INGresult *res, int column_number)
487 {
488    return res->fields[column_number].type;
489 }
490
491 int INGexec(INGconn *conn, const char *query, bool transaction)
492 {
493    int check;
494    EXEC SQL BEGIN DECLARE SECTION;
495    int sess_id;
496    int rowcount;
497    char *stmt;
498    EXEC SQL END DECLARE SECTION;
499    
500    stmt = bstrdup(query);
501    rowcount = -1;
502
503    /*
504     * Switch to the correct default session for this thread.
505     */
506    sess_id = conn->session_id;
507    EXEC SQL SET_SQL (SESSION = :sess_id);
508    EXEC SQL EXECUTE IMMEDIATE :stmt;
509
510    free(stmt);
511
512    if ((check = INGcheck()) < 0) {
513       rowcount = check;
514       goto bail_out;
515    }
516
517    EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
518    if ((check = INGcheck()) < 0) {
519       rowcount = check;
520       goto bail_out;
521    }
522
523 bail_out:
524    /*
525     * If we are not in a transaction we commit our work now.
526     */
527    if (!transaction) {
528       EXEC SQL COMMIT WORK;
529    }
530    /*
531     * Switch to no default session for this thread.
532     */
533    EXEC SQL SET_SQL (SESSION = NONE);
534    return rowcount;
535 }
536
537 INGresult *INGquery(INGconn *conn, const char *query, bool transaction)
538 {
539    /*
540     * TODO: error handling
541     */
542    IISQLDA *desc = NULL;
543    INGresult *res = NULL;
544    int rows = -1;
545    int cols = INGgetCols(conn, query, transaction);
546    EXEC SQL BEGIN DECLARE SECTION;
547    int sess_id;
548    EXEC SQL END DECLARE SECTION;
549
550    /*
551     * Switch to the correct default session for this thread.
552     */
553    sess_id = conn->session_id;
554    EXEC SQL SET_SQL (SESSION = :sess_id);
555
556    desc = INGgetDescriptor(cols, query);
557    if (!desc) {
558       goto bail_out;
559    }
560
561    res = INGgetINGresult(desc);
562    if (!res) {
563       goto bail_out;
564    }
565
566    rows = INGfetchAll(query, res);
567
568    if (rows < 0) {
569       INGfreeDescriptor(desc);
570       INGfreeINGresult(res);
571       res = NULL;
572       goto bail_out;
573    }
574
575 bail_out:
576    /*
577     * If we are not in a transaction we commit our work now.
578     */
579    if (!transaction) {
580       EXEC SQL COMMIT WORK;
581    }
582    /*
583     * Switch to no default session for this thread.
584     */
585    EXEC SQL SET_SQL (SESSION = NONE);
586    return res;
587 }
588
589 void INGclear(INGresult *res)
590 {
591    if (res == NULL) {
592       return;
593    }
594
595    INGfreeDescriptor(res->sqlda);
596    INGfreeINGresult(res);
597 }
598
599 void INGcommit(const INGconn *conn)
600 {
601    EXEC SQL BEGIN DECLARE SECTION;
602    int sess_id;
603    EXEC SQL END DECLARE SECTION;
604
605    if (dbconn != NULL) {
606       sess_id = dbconn->session_id;
607       EXEC SQL DISCONNECT SESSION :sess_id;
608
609       /*
610        * Commit our work.
611        */
612       EXEC SQL COMMIT WORK;
613
614       /*
615        * Switch to no default session for this thread.
616        */
617       EXEC SQL SET_SQL (SESSION = NONE);
618    }
619 }
620
621 INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id)
622 {
623    INGconn *dbconn;
624
625    if (dbname == NULL || strlen(dbname) == 0) {
626       return NULL;
627    }
628
629    dbconn = (INGconn *)malloc(sizeof(INGconn));
630    memset(dbconn, 0, sizeof(INGconn));
631
632    EXEC SQL BEGIN DECLARE SECTION;
633    char ingdbname[24];
634    char ingdbuser[32];
635    char ingdbpasswd[32];
636    int sess_id;
637    EXEC SQL END DECLARE SECTION;
638
639    sess_id = session_id;
640    bstrncpy(ingdbname, dbname, sizeof(ingdbname));
641    
642    if (user != NULL) {
643       bstrncpy(ingdbuser, user, sizeof(ingdbuser));
644       if (passwd != NULL) {
645          bstrncpy(ingdbpasswd, passwd, sizeof(ingdbpasswd));
646       } else {
647          memset(ingdbpasswd, 0, sizeof(ingdbpasswd));
648       }
649       EXEC SQL CONNECT
650          :ingdbname
651          SESSION :sess_id
652          IDENTIFIED BY :ingdbuser
653          DBMS_PASSWORD = :ingdbpasswd;
654    } else {
655       EXEC SQL CONNECT
656          :ingdbname
657          SESSION :sess_id;
658    }   
659    if (INGcheck() < 0) {
660       return NULL;
661    }
662    
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);
669
670    /*
671     * Switch to no default session for this thread undo default settings from SQL CONNECT.
672     */
673    EXEC SQL SET_SQL (SESSION = NONE);
674
675    return dbconn;
676 }
677
678 void INGdisconnectDB(INGconn *dbconn)
679 {
680    EXEC SQL BEGIN DECLARE SECTION;
681    int sess_id;
682    EXEC SQL END DECLARE SECTION;
683
684    if (dbconn != NULL) {
685       sess_id = dbconn->session_id;
686       EXEC SQL DISCONNECT SESSION :sess_id;
687
688       free(dbconn->msg);
689       free(dbconn);
690    }
691 }
692
693 char *INGerrorMessage(const INGconn *conn)
694 {
695    EXEC SQL BEGIN DECLARE SECTION;
696    char errbuf[256];
697    EXEC SQL END DECLARE SECTION;
698
699    EXEC SQL INQUIRE_INGRES(:errbuf = ERRORTEXT);
700    memcpy(conn->msg, &errbuf, 256);
701    return conn->msg;
702 }
703
704 char *INGcmdTuples(INGresult *res)
705 {
706    return res->numrowstring;
707 }
708
709 /* TODO?
710 int INGputCopyEnd(INGconn *conn, const char *errormsg);
711 int INGputCopyData(INGconn *conn, const char *buffer, int nbytes);
712 */
713
714 #endif