]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/myingres.sc
Add %D option to edit_job_code, simplify callbacks on director side
[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 with help of Marco van Wieringen April 2010
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 INGgetCols(INGconn *dbconn, const char *query, bool explicit_commit)
51 {
52    EXEC SQL BEGIN DECLARE SECTION;
53    int sess_id;
54    char *stmt;
55    EXEC SQL END DECLARE SECTION;
56    IISQLDA *sqlda;
57    int number = -1;
58
59    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + IISQDA_VAR_SIZE);
60    memset(sqlda, 0, (IISQDA_HEAD_SIZE + IISQDA_VAR_SIZE));
61    
62    sqlda->sqln = number;
63
64    stmt = bstrdup(query);
65
66    EXEC SQL WHENEVER SQLERROR GOTO bail_out;
67
68    /*
69     * Switch to the correct default session for this thread.
70     */
71    sess_id = dbconn->session_id;
72    EXEC SQL SET_SQL (SESSION = :sess_id);
73
74    EXEC SQL PREPARE s1 INTO :sqlda FROM :stmt;
75
76    EXEC SQL WHENEVER SQLERROR CONTINUE;
77      
78    number = sqlda->sqld;
79
80 bail_out:
81    /*
82     * If explicit_commit is set we commit our work now.
83     */
84    if (explicit_commit) {
85       EXEC SQL COMMIT WORK;
86    }
87
88    /*
89     * Switch to no default session for this thread.
90     */
91    EXEC SQL SET_SQL (SESSION = NONE);
92    free(stmt);
93    free(sqlda);
94    return number;
95 }
96
97 static inline IISQLDA *INGgetDescriptor(int numCols, const char *query)
98 {
99    EXEC SQL BEGIN DECLARE SECTION;
100    char *stmt;
101    EXEC SQL END DECLARE SECTION;
102    int i;
103    IISQLDA *sqlda;
104
105    sqlda = (IISQLDA *)malloc(IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE));
106    memset(sqlda, 0, (IISQDA_HEAD_SIZE + (numCols * IISQDA_VAR_SIZE)));
107    
108    sqlda->sqln = numCols;
109    
110    stmt = bstrdup(query);
111   
112    EXEC SQL PREPARE s2 INTO :sqlda FROM :stmt;
113
114    for (i = 0; i < sqlda->sqld; ++i) {
115       /*
116        * Negative type indicates nullable columns, so an indicator
117        * is allocated, otherwise it's null
118        */
119       if (sqlda->sqlvar[i].sqltype > 0) {
120          sqlda->sqlvar[i].sqlind = NULL;
121       } else {
122          sqlda->sqlvar[i].sqlind = (short *)malloc(sizeof(short));
123       }
124       /*
125        * Alloc space for variable like indicated in sqllen
126        * for date types sqllen is always 0 -> allocate by type
127        */
128       switch (abs(sqlda->sqlvar[i].sqltype)) {
129       case IISQ_TSW_TYPE:
130          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSW_LEN);
131          break;
132       case IISQ_TSWO_TYPE:
133          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSWO_LEN);
134          break;
135       case IISQ_TSTMP_TYPE:
136          sqlda->sqlvar[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN);
137          break;
138       default:
139          /*
140           * plus one to avoid zero mem allocs
141           */
142          sqlda->sqlvar[i].sqldata = (char *)malloc(sqlda->sqlvar[i].sqllen + 1);
143          break;
144       }
145    }
146    
147    free(stmt);
148    return sqlda;
149 }
150
151 static void INGfreeDescriptor(IISQLDA *sqlda)
152 {
153    int i;
154
155    if (!sqlda) {
156       return;
157    }
158
159    for (i = 0; i < sqlda->sqld; ++i) {
160       if (sqlda->sqlvar[i].sqldata) {
161          free(sqlda->sqlvar[i].sqldata);
162       }
163       if (sqlda->sqlvar[i].sqlind) {
164          free(sqlda->sqlvar[i].sqlind);
165       }
166    }
167    free(sqlda);
168 }
169
170 static inline int INGgetTypeSize(IISQLVAR *ingvar)
171 {
172    int inglength = 0;
173    
174    switch (ingvar->sqltype) {
175    case IISQ_TSWO_TYPE:
176       inglength = 20;
177       break;
178    case IISQ_TSW_TYPE:
179       inglength = 20;
180       break;
181    case IISQ_DTE_TYPE:
182       inglength = 25;
183       break;
184    case IISQ_MNY_TYPE:
185       inglength = 8;
186       break;
187    default:
188       inglength = ingvar->sqllen;
189       break;
190    }
191    
192    return inglength;
193 }
194
195 static inline INGresult *INGgetINGresult(int numCols, const char *query)
196 {
197    int i;
198    INGresult *ing_res;
199    
200    ing_res = (INGresult *)malloc(sizeof(INGresult));
201    memset(ing_res, 0, sizeof(INGresult));
202    
203    if ((ing_res->sqlda = INGgetDescriptor(numCols, query)) == NULL) {
204       return NULL;
205    }
206
207    ing_res->num_fields = ing_res->sqlda->sqld;
208    ing_res->num_rows = 0;
209    ing_res->first_row = NULL;
210    ing_res->status = ING_EMPTY_RESULT;
211    ing_res->act_row = NULL;
212    
213    if (ing_res->num_fields) {
214       ing_res->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * ing_res->num_fields);
215       memset(ing_res->fields, 0, sizeof(INGRES_FIELD) * ing_res->num_fields);
216
217       for (i = 0; i < ing_res->num_fields; ++i) {
218          ing_res->fields[i].name = (char *)malloc(ing_res->sqlda->sqlvar[i].sqlname.sqlnamel + 1);
219          bstrncpy(ing_res->fields[i].name, ing_res->sqlda->sqlvar[i].sqlname.sqlnamec, ing_res->sqlda->sqlvar[i].sqlname.sqlnamel + 1);
220          ing_res->fields[i].name[ing_res->sqlda->sqlvar[i].sqlname.sqlnamel] = '\0';
221          ing_res->fields[i].max_length = INGgetTypeSize(&ing_res->sqlda->sqlvar[i]);
222          ing_res->fields[i].type = abs(ing_res->sqlda->sqlvar[i].sqltype);
223          ing_res->fields[i].flags = (ing_res->sqlda->sqlvar[i].sqltype < 0) ? 1 : 0;
224       }
225    }
226
227    return ing_res;
228 }
229
230 static inline void INGfreeRowSpace(ING_ROW *row, IISQLDA *sqlda)
231 {
232    int i;
233
234    if (row == NULL || sqlda == NULL) {
235       return;
236    }
237
238    for (i = 0; i < sqlda->sqld; ++i) {
239       if (row->sqlvar[i].sqldata) {
240          free(row->sqlvar[i].sqldata);
241       }
242       if (row->sqlvar[i].sqlind) {
243          free(row->sqlvar[i].sqlind);
244       }
245    }
246    free(row->sqlvar);
247    free(row);
248 }
249
250 static void INGfreeINGresult(INGresult *ing_res)
251 {
252    int i;
253    int rows;
254    ING_ROW *rowtemp;
255
256    if (!ing_res) {
257       return;
258    }
259
260    /*
261     * Use of rows is a nasty workaround til I find the reason,
262     * why aggregates like max() don't work
263     */
264    rows = ing_res->num_rows;
265    ing_res->act_row = ing_res->first_row;
266    while (ing_res->act_row != NULL && rows > 0) {
267       rowtemp = ing_res->act_row->next;
268       INGfreeRowSpace(ing_res->act_row, ing_res->sqlda);
269       ing_res->act_row = rowtemp;
270       --rows;
271    }
272
273    if (ing_res->fields) {
274       for (i = 0; i < ing_res->num_fields; ++i) {
275          free(ing_res->fields[i].name);
276       }
277
278       free(ing_res->fields);
279    }
280
281    INGfreeDescriptor(ing_res->sqlda);
282
283    free(ing_res);
284 }
285
286 static inline ING_ROW *INGgetRowSpace(INGresult *ing_res)
287 {
288    int i;
289    unsigned short len; /* used for VARCHAR type length */
290    unsigned short th, tm, ts;
291    IISQLDA *sqlda;
292    ING_ROW *row = NULL;
293    ING_TIMESTAMP *tsp;
294    IISQLVAR *vars = NULL;
295
296    row = (ING_ROW *)malloc(sizeof(ING_ROW));
297    memset(row, 0, sizeof(ING_ROW));
298
299    sqlda = ing_res->sqlda;
300    vars = (IISQLVAR *)malloc(sizeof(IISQLVAR) * sqlda->sqld);
301    memset(vars, 0, sizeof(IISQLVAR) * sqlda->sqld);
302
303    row->sqlvar = vars;
304    row->next = NULL;
305
306    for (i = 0; i < sqlda->sqld; ++i) {
307       /*
308        * Make strings out of the data, then the space and assign 
309        * (why string? at least it seems that way, looking into the sources)
310        */
311       vars[i].sqlind = (short *)malloc(sizeof(short));
312       if (sqlda->sqlvar[i].sqlind) {
313          memcpy(vars[i].sqlind,sqlda->sqlvar[i].sqlind,sizeof(short));
314       } else {
315          *vars[i].sqlind = NULL;
316       }
317       /*
318        * if sqlind pointer exists AND points to -1 -> column is 'null'
319        */
320       if ( *vars[i].sqlind && (*vars[i].sqlind == -1)) {
321          vars[i].sqldata = NULL;
322       } else {
323          switch (ing_res->fields[i].type) {
324          case IISQ_VCH_TYPE:
325          case IISQ_LVCH_TYPE:
326          case IISQ_VBYTE_TYPE:
327          case IISQ_LBYTE_TYPE:
328          case IISQ_NVCHR_TYPE:
329          case IISQ_LNVCHR_TYPE:
330             len = ((ING_VARCHAR *)sqlda->sqlvar[i].sqldata)->len;
331             vars[i].sqldata = (char *)malloc(len + 1);
332             memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata + 2,len);
333             vars[i].sqldata[len] = '\0';
334             break;
335          case IISQ_CHA_TYPE:
336          case IISQ_BYTE_TYPE:
337          case IISQ_NCHR_TYPE:
338             vars[i].sqldata = (char *)malloc(ing_res->fields[i].max_length + 1);
339             memcpy(vars[i].sqldata,sqlda->sqlvar[i].sqldata,sqlda->sqlvar[i].sqllen);
340             vars[i].sqldata[ing_res->fields[i].max_length] = '\0';
341             break;
342          case IISQ_INT_TYPE:
343             switch (sqlda->sqlvar[i].sqllen) {
344             case 2:
345                vars[i].sqldata = (char *)malloc(6);
346                memset(vars[i].sqldata, 0, 6);
347                bsnprintf(vars[i].sqldata, 6, "%d",*(int16_t *)sqlda->sqlvar[i].sqldata);
348                break;
349             case 4:
350                vars[i].sqldata = (char *)malloc(11);
351                memset(vars[i].sqldata, 0, 11);
352                bsnprintf(vars[i].sqldata, 11, "%ld",*(int32_t *)sqlda->sqlvar[i].sqldata);
353                break;
354             case 8:
355                vars[i].sqldata = (char *)malloc(20);
356                memset(vars[i].sqldata, 0, 20);
357                bsnprintf(vars[i].sqldata, 20, "%lld",*(int64_t *)sqlda->sqlvar[i].sqldata);
358                break;
359             }
360             break;
361          case IISQ_TSTMP_TYPE:
362             vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN + 1);
363             vars[i].sqldata[IISQ_TSTMP_LEN] = '\0';
364             break;
365          case IISQ_TSWO_TYPE:
366             tsp = (ING_TIMESTAMP *)sqlda->sqlvar[i].sqldata;
367             th = tsp->secs / 3600; /* hours */
368             tm = tsp->secs % 3600; /* remaining seconds */
369             tm = tm / 60; /* minutes */
370             ts = tsp->secs - (th * 3600) - (tm * 60); /* seconds */
371             vars[i].sqldata = (char *)malloc(IISQ_TSTMP_LEN + 1);
372             bsnprintf(vars[i].sqldata, IISQ_TSWO_LEN + 1,
373                       "%04u-%02u-%02u %02u:%02u:%02u",
374                       tsp->year, tsp->month, tsp->day, th, tm, ts);
375             break;
376          case IISQ_TSW_TYPE:
377             tsp = (ING_TIMESTAMP *)sqlda->sqlvar[i].sqldata;
378             th = tsp->secs / 3600; /* hours */
379             tm = tsp->secs % 3600; /* remaining seconds */
380             tm = tm / 60; /* minutes */
381             ts = tsp->secs - (th * 3600) - (tm * 60); /* seconds */
382             vars[i].sqldata = (char *)malloc(IISQ_TSW_LEN + 1);
383             bsnprintf(vars[i].sqldata, IISQ_TSW_LEN + 1,
384                       "%04u-%02u-%02u %02u:%02u:%02u",
385                       tsp->year, tsp->month, tsp->day, th, tm, ts);
386             break;
387          default:
388             Jmsg(NULL, M_FATAL, 0,
389                  "INGgetRowSpace: encountered unhandled database datatype %d please report this as a bug\n",
390                  ing_res->fields[i].type);
391             break;
392          }
393       }
394    }
395    return row;
396 }
397
398 static inline int INGfetchAll(INGresult *ing_res)
399 {
400    ING_ROW *row;
401    IISQLDA *desc;
402    int linecount = -1;
403    
404    desc = ing_res->sqlda;
405    
406    EXEC SQL WHENEVER SQLERROR GOTO bail_out;
407
408    EXEC SQL DECLARE c2 CURSOR FOR s2;
409    EXEC SQL OPEN c2;
410       
411    EXEC SQL WHENEVER SQLERROR CONTINUE;
412
413    linecount = 0;
414    do {
415       EXEC SQL FETCH c2 USING DESCRIPTOR :desc;
416
417       if (sqlca.sqlcode == 0 || sqlca.sqlcode == -40202) {
418          /*
419           * Allocate space for fetched row
420           */
421          row = INGgetRowSpace(ing_res);
422             
423          /*
424           * Initialize list when encountered first time
425           */
426          if (ing_res->first_row == 0) {
427             ing_res->first_row = row; /* head of the list */
428             ing_res->first_row->next = NULL;
429             ing_res->act_row = ing_res->first_row;
430          }      
431
432          ing_res->act_row->next = row; /* append row to old act_row */
433          ing_res->act_row = row; /* set row as act_row */
434          row->row_number = linecount++;
435       }
436    } while ( (sqlca.sqlcode == 0) || (sqlca.sqlcode == -40202) );
437    
438    EXEC SQL CLOSE c2;
439    
440    ing_res->status = ING_COMMAND_OK;
441    ing_res->num_rows = linecount;
442
443 bail_out:
444    return linecount;
445 }
446
447 static inline ING_STATUS INGresultStatus(INGresult *ing_res)
448 {
449    if (ing_res == NULL) {
450       return ING_NO_RESULT;
451    } else {
452       return ing_res->status;
453    }
454 }
455
456 static void INGrowSeek(INGresult *ing_res, int row_number)
457 {
458    ING_ROW *trow = NULL;
459
460    if (ing_res->act_row->row_number == row_number) {
461       return;
462    }
463    
464    /*
465     * TODO: real error handling
466     */
467    if (row_number < 0 || row_number > ing_res->num_rows) {
468       return;
469    }
470
471    for (trow = ing_res->first_row; trow->row_number != row_number; trow = trow->next) ;
472    ing_res->act_row = trow;
473    /*
474     * Note - can be null - if row_number not found, right?
475     */
476 }
477
478 char *INGgetvalue(INGresult *ing_res, int row_number, int column_number)
479 {
480    if (row_number != ing_res->act_row->row_number) {
481       INGrowSeek(ing_res, row_number);
482    }
483
484    return ing_res->act_row->sqlvar[column_number].sqldata;
485 }
486
487 bool INGgetisnull(INGresult *ing_res, int row_number, int column_number)
488 {
489    if (row_number != ing_res->act_row->row_number) {
490       INGrowSeek(ing_res, row_number);
491    }
492
493    return (*ing_res->act_row->sqlvar[column_number].sqlind == -1) ? true : false;
494 }
495
496 int INGntuples(const INGresult *ing_res)
497 {
498    return ing_res->num_rows;
499 }
500
501 int INGnfields(const INGresult *ing_res)
502 {
503    return ing_res->num_fields;
504 }
505
506 char *INGfname(const INGresult *ing_res, int column_number)
507 {
508    if ((column_number > ing_res->num_fields) || (column_number < 0)) {
509       return NULL;
510    } else {
511       return ing_res->fields[column_number].name;
512    }
513 }
514
515 short INGftype(const INGresult *ing_res, int column_number)
516 {
517    return ing_res->fields[column_number].type;
518 }
519
520 int INGexec(INGconn *dbconn, const char *query, bool explicit_commit)
521 {
522    EXEC SQL BEGIN DECLARE SECTION;
523    int sess_id;
524    int rowcount;
525    int errors;
526    char *stmt;
527    EXEC SQL END DECLARE SECTION;
528    
529    rowcount = -1;
530    stmt = bstrdup(query);
531
532    EXEC SQL WHENEVER SQLERROR GOTO bail_out;
533
534    /*
535     * Switch to the correct default session for this thread.
536     */
537    sess_id = dbconn->session_id;
538    EXEC SQL SET_SQL (SESSION = :sess_id);
539
540    EXEC SQL EXECUTE IMMEDIATE :stmt;
541    EXEC SQL INQUIRE_INGRES(:rowcount = ROWCOUNT);
542
543    /*
544     * See if the negative rowcount is due to errors.
545     */
546    if (rowcount < 0) {
547       EXEC SQL INQUIRE_INGRES(:errors = DBMSERROR);
548
549       /*
550        * If the number of errors is 0 we got a negative rowcount
551        * because the statement we executed doesn't give a rowcount back.
552        * Lets pretend we have a rowcount of 1 then.
553        */
554       if (errors == 0) {
555          rowcount = 1;
556       }
557    }
558
559    EXEC SQL WHENEVER SQLERROR CONTINUE;
560
561 bail_out:
562    /*
563     * If explicit_commit is set we commit our work now.
564     */
565    if (explicit_commit) {
566       EXEC SQL COMMIT WORK;
567    }
568
569    /*
570     * Switch to no default session for this thread.
571     */
572    EXEC SQL SET_SQL (SESSION = NONE);
573    free(stmt);
574    return rowcount;
575 }
576
577 INGresult *INGquery(INGconn *dbconn, const char *query, bool explicit_commit)
578 {
579    /*
580     * TODO: error handling
581     */
582    INGresult *ing_res = NULL;
583    int rows;
584    int cols;
585    EXEC SQL BEGIN DECLARE SECTION;
586    int sess_id;
587    EXEC SQL END DECLARE SECTION;
588
589    cols = INGgetCols(dbconn, query, explicit_commit);
590
591    /*
592     * Switch to the correct default session for this thread.
593     */
594    sess_id = dbconn->session_id;
595    EXEC SQL SET_SQL (SESSION = :sess_id);
596
597    ing_res = INGgetINGresult(cols, query);
598    if (!ing_res) {
599       goto bail_out;
600    }
601
602    rows = INGfetchAll(ing_res);
603
604    if (rows < 0) {
605       INGfreeINGresult(ing_res);
606       ing_res = NULL;
607       goto bail_out;
608    }
609
610 bail_out:
611    /*
612     * If explicit_commit is set we commit our work now.
613     */
614    if (explicit_commit) {
615       EXEC SQL COMMIT WORK;
616    }
617
618    /*
619     * Switch to no default session for this thread.
620     */
621    EXEC SQL SET_SQL (SESSION = NONE);
622    return ing_res;
623 }
624
625 void INGclear(INGresult *ing_res)
626 {
627    if (ing_res == NULL) {
628       return;
629    }
630
631    INGfreeINGresult(ing_res);
632 }
633
634 void INGcommit(const INGconn *dbconn)
635 {
636    EXEC SQL BEGIN DECLARE SECTION;
637    int sess_id;
638    EXEC SQL END DECLARE SECTION;
639
640    if (dbconn != NULL) {
641       /*
642        * Switch to the correct default session for this thread.
643        */
644       sess_id = dbconn->session_id;
645       EXEC SQL SET_SQL (SESSION = :sess_id);
646
647       /*
648        * Commit our work.
649        */
650       EXEC SQL COMMIT WORK;
651
652       /*
653        * Switch to no default session for this thread.
654        */
655       EXEC SQL SET_SQL (SESSION = NONE);
656    }
657 }
658
659 INGconn *INGconnectDB(char *dbname, char *user, char *passwd, int session_id)
660 {
661    EXEC SQL BEGIN DECLARE SECTION;
662    char *ingdbname;
663    char *ingdbuser = NULL;
664    char *ingdbpasswd = NULL;
665    int sess_id;
666    EXEC SQL END DECLARE SECTION;
667    INGconn *dbconn = NULL;
668
669    if (dbname == NULL || strlen(dbname) == 0) {
670       return NULL;
671    }
672
673    sess_id = session_id;
674    ingdbname = dbname;
675    
676    EXEC SQL WHENEVER SQLERROR GOTO bail_out;
677
678    if (user != NULL) {
679       ingdbuser = user;
680       if (passwd != NULL) {
681          ingdbpasswd = passwd;
682          EXEC SQL CONNECT
683             :ingdbname
684             SESSION :sess_id
685             IDENTIFIED BY :ingdbuser
686             DBMS_PASSWORD = :ingdbpasswd;
687       } else {
688          EXEC SQL CONNECT
689             :ingdbname
690             SESSION :sess_id
691             IDENTIFIED BY :ingdbuser;
692       }
693    } else {
694       EXEC SQL CONNECT
695          :ingdbname
696          SESSION :sess_id;
697    }   
698    
699    EXEC SQL WHENEVER SQLERROR CONTINUE;
700
701    dbconn = (INGconn *)malloc(sizeof(INGconn));
702    memset(dbconn, 0, sizeof(INGconn));
703
704    dbconn->dbname = bstrdup(ingdbname);
705    if (user != NULL) {
706       dbconn->user = bstrdup(ingdbuser);
707       dbconn->password = bstrdup(ingdbpasswd);
708    }
709    dbconn->session_id = sess_id;
710    dbconn->msg = (char *)malloc(257);
711    memset(dbconn->msg, 0, 257);
712
713    /*
714     * Switch to no default session for this thread undo default settings from SQL CONNECT.
715     */
716    EXEC SQL SET_SQL (SESSION = NONE);
717
718 bail_out:
719    return dbconn;
720 }
721
722 void INGsetDefaultLockingMode(INGconn *dbconn)
723 {
724    /*
725     * Set the default Ingres session locking mode:
726     *
727     * SET LOCKMODE provides four different parameters to govern
728     * the nature of locking in an INGRES session:
729     *
730     * Level: This refers to the level of granularity desired when
731     * the table is accessed. You can specify any of the following
732     * locking levels:
733     *
734     * row     Specifies locking at the level of the row (subject to
735     *         escalation criteria; see below)
736     * page    Specifies locking at the level of the data page (subject to
737     *         escalation criteria; see below)
738     * table   Specifies table-level locking in the database
739     * session Specifies the current default for your INGRES session
740     * system  Specifies that INGRES will start with page-level locking,
741     *         unless it estimates that more than Maxlocks pages will be
742     *         referenced, in which case table-level locking will be used.
743     *
744     * Readlock: This refers to locking in situations where table access
745     *           is required for reading data only (as opposed to updating
746     *           data). You can specify any of the following Readlock modes:
747     *
748     *    nolock     Specifies no locking when reading data
749     *    shared     Specifies the default mode of locking when reading data
750     *    exclusive  Specifies exclusive locking when reading data (useful in
751     *               "select-for-update" processing within a multi-statement
752     *               transaction)
753     *    system     Specifies the general Readlock default for the INGRES system
754     *
755     * Maxlocks: This refers to an escalation factor, or number of locks on
756     *           data pages, at which locking escalates from page-level
757     *           to table-level. The number of locks available to you is
758     *           dependent upon your system configuration. You can specify the
759     *           following Maxlocks escalation factors:
760     *
761     *    n       A specific (integer) number of page locks to allow before
762     *            escalating to table-level locking. The default "n" is 10,
763     *            and "n" must be greater than 0.
764     *    session Specifies the current Maxlocks default for your INGRES
765     *            session
766     *    system  Specifies the general Maxlocks default for the INGRES system
767     *
768     * Note: If you specify page-level locking, and the number of locks granted
769     * during a query exceeds the system-wide lock limit, or if the operating
770     * system's locking resources are depleted, locking escalates to table-level.
771     * This escalation occurs automatically and is independent of the user.
772     *
773     * Timeout: This refers to a time limit, expressed in seconds, for which
774     * a lock request should remain pending. If INGRES cannot grant the lock
775     * request within the specified time, then the query that requested the
776     * lock aborts. You can specify the following timeout characteristics:
777     *
778     *    n       A specific (integer) number of seconds to wait for a lock
779     *            (setting "n" to 0 requires INGRES to wait indefinitely for
780     *            the lock)
781     *    session Specifies the current timeout default for your INGRES
782     *            session (which is also the INGRES default)
783     *    system  Specifies the general timeout default for the INGRES system
784     *
785     */
786    EXEC SQL BEGIN DECLARE SECTION;
787    int sess_id;
788    EXEC SQL END DECLARE SECTION;
789
790    if (dbconn != NULL) {
791       /*
792        * Switch to the correct default session for this thread.
793        */
794       sess_id = dbconn->session_id;
795       EXEC SQL SET_SQL (SESSION = :sess_id);
796
797       EXEC SQL SET LOCKMODE SESSION WHERE level = row, readlock = nolock;
798
799       /*
800        * Switch to no default session for this thread.
801        */
802       EXEC SQL SET_SQL (SESSION = NONE);
803    }
804 }
805
806 void INGdisconnectDB(INGconn *dbconn)
807 {
808    EXEC SQL BEGIN DECLARE SECTION;
809    int sess_id;
810    EXEC SQL END DECLARE SECTION;
811
812    if (dbconn != NULL) {
813       sess_id = dbconn->session_id;
814       EXEC SQL DISCONNECT SESSION :sess_id;
815
816       free(dbconn->dbname);
817       if (dbconn->user) {
818          free(dbconn->user);
819       }
820       if (dbconn->password) {
821          free(dbconn->password);
822       }
823       free(dbconn->msg);
824       free(dbconn);
825    }
826 }
827
828 char *INGerrorMessage(const INGconn *dbconn)
829 {
830    EXEC SQL BEGIN DECLARE SECTION;
831    int sess_id;
832    char errbuf[256];
833    EXEC SQL END DECLARE SECTION;
834
835    if (dbconn != NULL) {
836       /*
837        * Switch to the correct default session for this thread.
838        */
839       sess_id = dbconn->session_id;
840       EXEC SQL SET_SQL (SESSION = :sess_id);
841
842       EXEC SQL INQUIRE_INGRES (:errbuf = ERRORTEXT);
843       strncpy(dbconn->msg, errbuf, sizeof(dbconn->msg));
844
845       /*
846        * Switch to no default session for this thread.
847        */
848       EXEC SQL SET_SQL (SESSION = NONE);
849    }
850
851    return dbconn->msg;
852 }
853
854 #endif