]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/ingres.c
e984b799af50d1d04034a0ac8177fdead791823f
[bacula/bacula] / bacula / src / cats / ingres.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-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  *    based uopn work done 
34  *    by Dan Langille, December 2003 and
35  *    by Kern Sibbald, March 2000
36  *
37  */
38
39
40 /* The following is necessary so that we do not include
41  * the dummy external definition of DB.
42  */
43 #define __SQL_C                       /* indicate that this is sql.c */
44
45 #include "bacula.h"
46 #include "cats.h"
47
48 #ifdef HAVE_INGRES
49
50 #include "myingres.h"
51
52 /* -----------------------------------------------------------------------
53  *
54  *   Ingres dependent defines and subroutines
55  *
56  * -----------------------------------------------------------------------
57  */
58
59 /* List of open databases */  /* SRE: needed for ingres? */
60 static BQUEUE db_list = {&db_list, &db_list};
61
62 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
63
64 /*
65  * Retrieve database type
66  */
67 const char *
68 db_get_type(void)
69 {
70    return "Ingres";
71 }
72
73 /*
74  * Initialize database data structure. In principal this should
75  * never have errors, or it is really fatal.
76  */
77 B_DB *
78 db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password,
79                  const char *db_address, int db_port, const char *db_socket,
80                  int mult_db_connections)
81 {
82    B_DB *mdb;
83
84    if (!db_user) {
85       Jmsg(jcr, M_FATAL, 0, _("A user name for Ingres must be supplied.\n"));
86       return NULL;
87    }
88    P(mutex);                          /* lock DB queue */
89    if (!mult_db_connections) {
90       /* Look to see if DB already open */
91       for (mdb=NULL; (mdb=(B_DB *)qnext(&db_list, &mdb->bq)); ) {
92          if (bstrcmp(mdb->db_name, db_name) &&
93              bstrcmp(mdb->db_address, db_address) &&
94              mdb->db_port == db_port) {
95             Dmsg2(100, "DB REopen %d %s\n", mdb->ref_count, db_name);
96             mdb->ref_count++;
97             V(mutex);
98             return mdb;                  /* already open */
99          }
100       }
101    }
102    Dmsg0(100, "db_open first time\n");
103    mdb = (B_DB *)malloc(sizeof(B_DB));
104    memset(mdb, 0, sizeof(B_DB));
105    mdb->db_name = bstrdup(db_name);
106    mdb->db_user = bstrdup(db_user);
107    if (db_password) {
108       mdb->db_password = bstrdup(db_password);
109    }
110    if (db_address) {
111       mdb->db_address  = bstrdup(db_address);
112    }
113    if (db_socket) {
114       mdb->db_socket   = bstrdup(db_socket);
115    }
116    mdb->db_port        = db_port;
117    mdb->have_insert_id = TRUE;
118    mdb->errmsg         = get_pool_memory(PM_EMSG); /* get error message buffer */
119    *mdb->errmsg        = 0;
120    mdb->cmd            = get_pool_memory(PM_EMSG); /* get command buffer */
121    mdb->cached_path    = get_pool_memory(PM_FNAME);
122    mdb->cached_path_id = 0;
123    mdb->ref_count      = 1;
124    mdb->fname          = get_pool_memory(PM_FNAME);
125    mdb->path           = get_pool_memory(PM_FNAME);
126    mdb->esc_name       = get_pool_memory(PM_FNAME);
127    mdb->esc_path      = get_pool_memory(PM_FNAME);
128    mdb->allow_transactions = mult_db_connections;
129    mdb->limit_filter = new_bregexp("/LIMIT ([0-9]+)/FETCH FIRST $1 ROW ONLY/g");
130    qinsert(&db_list, &mdb->bq);            /* put db in list */
131    V(mutex);
132    return mdb;
133 }
134
135 /* Check that the database correspond to the encoding we want */
136 static bool check_database_encoding(JCR *jcr, B_DB *mdb)
137 {
138 /* SRE: TODO! Needed?
139  SQL_ROW row;
140    int ret=false;
141
142    if (!db_sql_query(mdb, "SELECT getdatabaseencoding()", NULL, NULL)) {
143       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
144       return false;
145    }
146
147    if ((row = sql_fetch_row(mdb)) == NULL) {
148       Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
149       Jmsg(jcr, M_ERROR, 0, "Can't check database encoding %s", mdb->errmsg);
150    } else {
151       ret = bstrcmp(row[0], "SQL_ASCII");
152       if (!ret) {
153          Mmsg(mdb->errmsg, 
154               _("Encoding error for database \"%s\". Wanted SQL_ASCII, got %s\n"),
155               mdb->db_name, row[0]);
156          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
157          Dmsg1(50, "%s", mdb->errmsg);
158       } 
159    }
160    return ret;
161 */
162     return true;
163 }
164
165 /*
166  * Check for errors in DBMS work
167  */
168 static int sql_check(B_DB *mdb)
169 {
170     int errorcode;
171
172     if ((errorcode = INGcheck()) < 0) {
173         /* TODO: fill mdb->errmsg */
174         Mmsg(mdb->errmsg, "Something went wrong - still searching!\n");
175     } else if (errorcode > 0) {
176         /* just a warning, proceed */
177     }
178     return errorcode;
179 }
180
181 /*
182  * Now actually open the database.  This can generate errors,
183  *   which are returned in the errmsg
184  *
185  * DO NOT close the database or free(mdb) here !!!!
186  */
187 int
188 db_open_database(JCR *jcr, B_DB *mdb)
189 {
190    int errstat;
191    char buf[10], *port;
192
193    P(mutex);
194    if (mdb->connected) {
195       V(mutex);
196       return 1;
197    }
198    mdb->connected = false;
199
200    if ((errstat=rwl_init(&mdb->lock)) != 0) {
201       berrno be;
202       Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"),
203             be.bstrerror(errstat));
204       V(mutex);
205       return 0;
206    }
207
208    if (mdb->db_port) {
209       bsnprintf(buf, sizeof(buf), "%d", mdb->db_port);
210       port = buf;
211    } else {
212       port = NULL;
213    }
214
215    mdb->db = INGconnectDB(mdb->db_name, mdb->db_user, mdb->db_password);
216
217    Dmsg0(50, "Ingres real CONNECT done\n");
218    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->db_user, mdb->db_name,
219             mdb->db_password==NULL?"(NULL)":mdb->db_password);
220
221    if (sql_check(mdb)) {
222       Mmsg2(&mdb->errmsg, _("Unable to connect to Ingres server.\n"
223             "Database=%s User=%s\n"
224             "It is probably not running or your password is incorrect.\n"),
225              mdb->db_name, mdb->db_user);
226       V(mutex);
227       return 0;
228    }
229
230    mdb->connected = true;
231
232    if (!check_tables_version(jcr, mdb)) {
233       V(mutex);
234       return 0;
235    }
236
237    //sql_query(mdb, "SET datestyle TO 'ISO, YMD'");
238    
239    /* check that encoding is SQL_ASCII */
240    check_database_encoding(jcr, mdb);
241
242    V(mutex);
243    return 1;
244 }
245
246 void
247 db_close_database(JCR *jcr, B_DB *mdb)
248 {
249    if (!mdb) {
250       return;
251    }
252    db_end_transaction(jcr, mdb);
253    P(mutex);
254    sql_free_result(mdb);
255    mdb->ref_count--;
256    if (mdb->ref_count == 0) {
257       qdchain(&mdb->bq);
258       if (mdb->connected && mdb->db) {
259          sql_close(mdb);
260       }
261       rwl_destroy(&mdb->lock);
262       free_pool_memory(mdb->errmsg);
263       free_pool_memory(mdb->cmd);
264       free_pool_memory(mdb->cached_path);
265       free_pool_memory(mdb->fname);
266       free_pool_memory(mdb->path);
267       free_pool_memory(mdb->esc_name);
268       free_pool_memory(mdb->esc_path);
269       free_bregexp(mdb->limit_filter);
270       if (mdb->db_name) {
271          free(mdb->db_name);
272       }
273       if (mdb->db_user) {
274          free(mdb->db_user);
275       }
276       if (mdb->db_password) {
277          free(mdb->db_password);
278       }
279       if (mdb->db_address) {
280          free(mdb->db_address);
281       }
282       if (mdb->db_socket) {
283          free(mdb->db_socket);
284       }
285       free(mdb);
286    }
287    V(mutex);
288 }
289
290 void db_check_backend_thread_safe()
291 { }
292
293
294
295 void db_thread_cleanup()
296 { }
297
298 /*
299  * Return the next unique index (auto-increment) for
300  * the given table.  Return NULL on error.
301  *
302  * For Ingres, NULL causes the auto-increment value     SRE: true?
303  *  to be updated.
304  */
305 int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
306 {
307    strcpy(index, "NULL");
308    return 1;
309 }
310
311
312 /*
313  * Escape strings so that Ingres is happy
314  *
315  *   NOTE! len is the length of the old string. Your new
316  *         string must be long enough (max 2*old+1) to hold
317  *         the escaped output.
318  */
319 void
320 db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
321 {
322    char *n, *o;
323
324    n = snew;
325    o = old;
326    while (len--) {
327       switch (*o) {
328       case '\'':
329          *n++ = '\'';
330          *n++ = '\'';
331          o++;
332          break;
333       case 0:
334          *n++ = '\\';
335          *n++ = 0;
336          o++;
337          break;
338       default:
339          *n++ = *o++;
340          break;
341       }
342    }
343    *n = 0;
344 }
345
346 /*
347  * Submit a general SQL command (cmd), and for each row returned,
348  *  the sqlite_handler is called with the ctx.
349  */
350 bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
351 {
352    SQL_ROW row;
353
354    Dmsg0(500, "db_sql_query started\n");
355
356    db_lock(mdb);
357    if (sql_query(mdb, query) != 0) {
358       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
359       db_unlock(mdb);
360       Dmsg0(500, "db_sql_query failed\n");
361       return false;
362    }
363    Dmsg0(500, "db_sql_query succeeded. checking handler\n");
364
365    if (result_handler != NULL) {
366       Dmsg0(500, "db_sql_query invoking handler\n");
367       if (mdb->result != NULL) {
368          int num_fields = sql_num_fields(mdb);
369
370          Dmsg0(500, "db_sql_query sql_store_result suceeded\n");
371          while ((row = sql_fetch_row(mdb)) != NULL) {
372
373             Dmsg0(500, "db_sql_query sql_fetch_row worked\n");
374             if (result_handler(ctx, num_fields, row))
375                break;
376          }
377
378         sql_free_result(mdb);
379       }
380    }
381    db_unlock(mdb);
382
383    Dmsg0(500, "db_sql_query finished\n");
384
385    return true;
386 }
387
388 /*
389  * Close database connection
390  */
391 void my_ingres_close(B_DB *mdb)
392 {
393     INGdisconnectDB(mdb->db);
394     //SRE: error handling? 
395 }
396
397 INGRES_ROW my_ingres_fetch_row(B_DB *mdb)
398 {
399    int j;
400    INGRES_ROW row = NULL; // by default, return NULL
401
402    if (!mdb->result) {
403       return row;
404    }
405    if (mdb->result->num_rows <= 0) {
406       return row;
407    }
408
409    Dmsg0(500, "my_ingres_fetch_row start\n");
410
411    if (!mdb->row || mdb->row_size < mdb->num_fields) {
412       int num_fields = mdb->num_fields;
413       Dmsg1(500, "we have need space of %d bytes\n", sizeof(char *) * mdb->num_fields);
414
415       if (mdb->row) {
416          Dmsg0(500, "my_ingres_fetch_row freeing space\n");
417          free(mdb->row);
418       }
419       num_fields += 20;                  /* add a bit extra */
420       mdb->row = (INGRES_ROW)malloc(sizeof(char *) * num_fields);
421       mdb->row_size = num_fields;
422
423       // now reset the row_number now that we have the space allocated
424       //mdb->row_number = 1;
425       mdb->row_number = 0;
426    }
427
428    // if still within the result set
429    //if (mdb->row_number <= mdb->num_rows) {
430    if (mdb->row_number < mdb->num_rows) {
431       Dmsg2(500, "my_ingres_fetch_row row number '%d' is acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
432       // get each value from this row
433       for (j = 0; j < mdb->num_fields; j++) {
434          mdb->row[j] = INGgetvalue(mdb->result, mdb->row_number, j);
435          Dmsg2(500, "my_ingres_fetch_row field '%d' has value '%s'\n", j, mdb->row[j]);
436       }
437       // increment the row number for the next call
438       mdb->row_number++;
439
440       row = mdb->row;
441    } else {
442       Dmsg2(500, "my_ingres_fetch_row row number '%d' is NOT acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
443    }
444
445    Dmsg1(500, "my_ingres_fetch_row finishes returning %p\n", row);
446
447    return row;
448 }
449
450
451 int my_ingres_max_length(B_DB *mdb, int field_num) {
452    //
453    // for a given column, find the max length
454    //
455    int max_length;
456    int i;
457    int this_length;
458
459    max_length = 0;
460    for (i = 0; i < mdb->num_rows; i++) {
461       if (INGgetisnull(mdb->result, i, field_num)) {
462           this_length = 4;        // "NULL"
463       } else {
464           this_length = cstrlen(INGgetvalue(mdb->result, i, field_num));
465       }
466
467       if (max_length < this_length) {
468           max_length = this_length;
469       }
470    }
471
472    return max_length;
473 }
474
475 INGRES_FIELD * my_ingres_fetch_field(B_DB *mdb)
476 {
477    int i;
478
479    Dmsg0(500, "my_ingres_fetch_field starts\n");
480
481    if (!mdb->fields || mdb->fields_size < mdb->num_fields) {
482       if (mdb->fields) {
483          free(mdb->fields);
484       }
485       Dmsg1(500, "allocating space for %d fields\n", mdb->num_fields);
486       mdb->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * mdb->num_fields);
487       mdb->fields_size = mdb->num_fields;
488
489       for (i = 0; i < mdb->num_fields; i++) {
490          Dmsg1(500, "filling field %d\n", i);
491          strcpy(mdb->fields[i].name,INGfname(mdb->result, i));
492          mdb->fields[i].max_length = my_ingres_max_length(mdb, i);
493          mdb->fields[i].type       = INGftype(mdb->result, i);
494          mdb->fields[i].flags      = 0;
495
496          Dmsg4(500, "my_ingres_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n",
497             mdb->fields[i].name, mdb->fields[i].max_length, mdb->fields[i].type,
498             mdb->fields[i].flags);
499       } // end for
500    } // end if
501
502    // increment field number for the next time around
503
504    Dmsg0(500, "my_ingres_fetch_field finishes\n");
505    return &mdb->fields[mdb->field_number++];
506 }
507
508 void my_ingres_data_seek(B_DB *mdb, int row)
509 {
510    // set the row number to be returned on the next call
511    // to my_ingres_fetch_row
512    mdb->row_number = row;
513 }
514
515 void my_ingres_field_seek(B_DB *mdb, int field)
516 {
517    mdb->field_number = field;
518 }
519
520 /*
521  * Note, if this routine returns 1 (failure), Bacula expects
522  *  that no result has been stored.
523  *
524  *  Returns:  0  on success
525  *            1  on failure
526  *
527  */
528 int my_ingres_query(B_DB *mdb, const char *query)
529 {
530    Dmsg0(500, "my_ingres_query started\n");
531    // We are starting a new query.  reset everything.
532    mdb->num_rows     = -1;
533    mdb->row_number   = -1;
534    mdb->field_number = -1;
535
536    int cols = -1;
537
538    if (mdb->result) {
539       INGclear(mdb->result);  /* hmm, someone forgot to free?? */
540       mdb->result = NULL;
541    }
542
543    Dmsg1(500, "my_ingres_query starts with '%s'\n", query);
544
545    /* TODO: differentiate between SELECTs and other queries */
546
547    if ((cols = INGgetCols(mdb, query)) <= 0) {
548       if (cols < 0 ) {
549          Dmsg0(500,"my_ingres_query: neg.columns: no DML stmt!\n");
550       }
551       Dmsg0(500,"my_ingres_query (non SELECT) starting...\n");
552       /* non SELECT */
553       mdb->num_rows = INGexec(mdb, mdb->db, query);
554       if (INGcheck()) {
555         Dmsg0(500,"my_ingres_query (non SELECT) went wrong\n");
556         mdb->status = 1;
557       } else {
558         Dmsg0(500,"my_ingres_query (non SELECT) seems ok\n");
559         mdb->status = 0;
560       }
561    } else {
562       /* SELECT */
563       Dmsg0(500,"my_ingres_query (SELECT) starting...\n");
564       mdb->result = INGquery(mdb, mdb->db, query);
565       if (mdb->result != NULL) {
566         Dmsg1(500, "we have a result\n", query);
567
568         // how many fields in the set?
569         mdb->num_fields = (int)INGnfields(mdb->result);
570         Dmsg1(500, "we have %d fields\n", mdb->num_fields);
571
572         mdb->num_rows = INGntuples(mdb->result);
573         Dmsg1(500, "we have %d rows\n", mdb->num_rows);
574
575         mdb->status = 0;                  /* succeed */
576       } else {
577         Dmsg0(500, "No resultset...\n");
578         mdb->status = 1; /* failed */
579       }
580    }
581
582    Dmsg0(500, "my_ingres_query finishing\n");
583    return mdb->status;
584 }
585
586 void my_ingres_free_result(B_DB *mdb)
587 {
588    
589    db_lock(mdb);
590    if (mdb->result) {
591       INGclear(mdb->result);
592       mdb->result = NULL;
593    }
594
595    if (mdb->row) {
596       free(mdb->row);
597       mdb->row = NULL;
598    }
599
600    if (mdb->fields) {
601       free(mdb->fields);
602       mdb->fields = NULL;
603    }
604    db_unlock(mdb);
605 }
606
607 int my_ingres_currval(B_DB *mdb, const char *table_name)
608 {
609    /*
610     * Obtain the current value of the sequence that
611     * provides the serial value for primary key of the table.
612     *
613     * currval is local to our session. It is not affected by
614     * other transactions.
615     *
616     * Determine the name of the sequence.
617     * As we name all sequences as <table>_seq this is easy.
618     */
619
620    char sequence[64];
621    char query[256];
622    INGresult *result;
623    int id = 0;
624
625    bstrncpy(sequence, table_name, sizeof(sequence));
626    bstrncat(sequence, "_seq", sizeof(sequence));
627
628    bsnprintf(query, sizeof(query), "SELECT %s.currval FROM %s", sequence, table_name);
629
630    Dmsg1(500, "my_ingres_currval invoked with '%s'\n", query);
631
632    result = INGquery(mdb, mdb->db, query);
633
634    if (!result) {
635       Dmsg1(50, "Query failed: %s\n", query);
636       goto bail_out;
637    }
638
639    Dmsg0(500, "exec done");
640
641    id = atoi(INGgetvalue(result, 0, 0));
642
643 bail_out:
644    INGclear(result);
645
646    return id;
647 }
648
649 #ifdef HAVE_BATCH_FILE_INSERT
650
651 int my_ingres_batch_start(JCR *jcr, B_DB *mdb)
652 {
653     //TODO!
654    return ING_ERROR;
655 }
656
657 /* set error to something to abort operation */
658 int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error)
659 {
660     //TODO!
661    return ING_ERROR;
662 }
663
664 int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
665 {
666     //TODO!
667    return ING_ERROR;
668 }
669
670 #endif /* HAVE_BATCH_FILE_INSERT */
671
672 /*
673  * Escape strings so that Ingres is happy on COPY
674  *
675  *   NOTE! len is the length of the old string. Your new
676  *         string must be long enough (max 2*old+1) to hold
677  *         the escaped output.
678  */
679 char *my_ingres_copy_escape(char *dest, char *src, size_t len)
680 {
681    /* we have to escape \t, \n, \r, \ */
682    char c = '\0' ;
683
684    while (len > 0 && *src) {
685       switch (*src) {
686       case '\n':
687          c = 'n';
688          break;
689       case '\\':
690          c = '\\';
691          break;
692       case '\t':
693          c = 't';
694          break;
695       case '\r':
696          c = 'r';
697          break;
698       default:
699          c = '\0' ;
700       }
701
702       if (c) {
703          *dest = '\\';
704          dest++;
705          *dest = c;
706       } else {
707          *dest = *src;
708       }
709
710       len--;
711       src++;
712       dest++;
713    }
714
715    *dest = '\0';
716    return dest;
717 }
718
719 #ifdef HAVE_BATCH_FILE_INSERT
720 const char *my_ingres_batch_lock_path_query = 
721    "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE";
722
723
724 const char *my_ingres_batch_lock_filename_query = 
725    "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE";
726
727 const char *my_ingres_batch_unlock_tables_query = "COMMIT";
728
729 const char *my_ingres_batch_fill_path_query = 
730    "INSERT INTO Path (Path) "
731     "SELECT a.Path FROM "
732      "(SELECT DISTINCT Path FROM batch) AS a "
733       "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) ";
734
735
736 const char *my_ingres_batch_fill_filename_query = 
737    "INSERT INTO Filename (Name) "
738     "SELECT a.Name FROM "
739      "(SELECT DISTINCT Name FROM batch) as a "
740       "WHERE NOT EXISTS "
741        "(SELECT Name FROM Filename WHERE Name = a.Name)";
742 #endif /* HAVE_BATCH_FILE_INSERT */
743
744 #endif /* HAVE_INGRES */