]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/ingres.c
Revert "Fix typo"
[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    char *new_query;
531
532    if (strstr(query, "LIMIT") != NULL) {
533       new_query = mdb->limit_filter->replace(query);
534    } else {
535       new_query = query;
536    }
537
538    Dmsg0(500, "my_ingres_query started\n");
539    // We are starting a new query.  reset everything.
540    mdb->num_rows     = -1;
541    mdb->row_number   = -1;
542    mdb->field_number = -1;
543
544    int cols = -1;
545
546    if (mdb->result) {
547       INGclear(mdb->result);  /* hmm, someone forgot to free?? */
548       mdb->result = NULL;
549    }
550
551    Dmsg1(500, "my_ingres_query starts with '%s'\n", new_query);
552
553    /* TODO: differentiate between SELECTs and other queries */
554
555    if ((cols = INGgetCols(new_query)) <= 0) {
556       if (cols < 0 ) {
557          Dmsg0(500,"my_ingres_query: neg.columns: no DML stmt!\n");
558       }
559       Dmsg0(500,"my_ingres_query (non SELECT) starting...\n");
560       /* non SELECT */
561       mdb->num_rows = INGexec(mdb->db, new_query);
562       if (INGcheck()) {
563         Dmsg0(500,"my_ingres_query (non SELECT) went wrong\n");
564         mdb->status = 1;
565       } else {
566         Dmsg0(500,"my_ingres_query (non SELECT) seems ok\n");
567         mdb->status = 0;
568       }
569    } else {
570       /* SELECT */
571       Dmsg0(500,"my_ingres_query (SELECT) starting...\n");
572       mdb->result = INGquery(mdb->db, new_query);
573       if (mdb->result != NULL) {
574         Dmsg1(500, "we have a result\n", new_query);
575
576         // how many fields in the set?
577         mdb->num_fields = (int)INGnfields(mdb->result);
578         Dmsg1(500, "we have %d fields\n", mdb->num_fields);
579
580         mdb->num_rows = INGntuples(mdb->result);
581         Dmsg1(500, "we have %d rows\n", mdb->num_rows);
582
583         mdb->status = 0;                  /* succeed */
584       } else {
585         Dmsg0(500, "No resultset...\n");
586         mdb->status = 1; /* failed */
587       }
588    }
589
590    Dmsg0(500, "my_ingres_query finishing\n");
591    return mdb->status;
592 }
593
594 void my_ingres_free_result(B_DB *mdb)
595 {
596    
597    db_lock(mdb);
598    if (mdb->result) {
599       INGclear(mdb->result);
600       mdb->result = NULL;
601    }
602
603    if (mdb->row) {
604       free(mdb->row);
605       mdb->row = NULL;
606    }
607
608    if (mdb->fields) {
609       free(mdb->fields);
610       mdb->fields = NULL;
611    }
612    db_unlock(mdb);
613 }
614
615 int my_ingres_currval(B_DB *mdb, const char *table_name)
616 {
617    /*
618     * Obtain the current value of the sequence that
619     * provides the serial value for primary key of the table.
620     *
621     * currval is local to our session. It is not affected by
622     * other transactions.
623     *
624     * Determine the name of the sequence.
625     * As we name all sequences as <table>_seq this is easy.
626     */
627
628    char sequence[64];
629    char query[256];
630    INGresult *result;
631    int id = 0;
632
633    bstrncpy(sequence, table_name, sizeof(sequence));
634    bstrncat(sequence, "_seq", sizeof(sequence));
635
636    bsnprintf(query, sizeof(query), "SELECT %s.currval FROM %s", sequence, table_name);
637
638    Dmsg1(500, "my_ingres_currval invoked with '%s'\n", query);
639
640    result = INGquery(mdb->db, query);
641
642    if (!result) {
643       Dmsg1(50, "Query failed: %s\n", query);
644       goto bail_out;
645    }
646
647    Dmsg0(500, "exec done");
648
649    id = atoi(INGgetvalue(result, 0, 0));
650
651 bail_out:
652    INGclear(result);
653
654    return id;
655 }
656
657 #ifdef HAVE_BATCH_FILE_INSERT
658
659 int my_ingres_batch_start(JCR *jcr, B_DB *mdb)
660 {
661     //TODO!
662    return ING_ERROR;
663 }
664
665 /* set error to something to abort operation */
666 int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error)
667 {
668     //TODO!
669    return ING_ERROR;
670 }
671
672 int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
673 {
674     //TODO!
675    return ING_ERROR;
676 }
677
678 #endif /* HAVE_BATCH_FILE_INSERT */
679
680 /*
681  * Escape strings so that Ingres is happy on COPY
682  *
683  *   NOTE! len is the length of the old string. Your new
684  *         string must be long enough (max 2*old+1) to hold
685  *         the escaped output.
686  */
687 char *my_ingres_copy_escape(char *dest, char *src, size_t len)
688 {
689    /* we have to escape \t, \n, \r, \ */
690    char c = '\0' ;
691
692    while (len > 0 && *src) {
693       switch (*src) {
694       case '\n':
695          c = 'n';
696          break;
697       case '\\':
698          c = '\\';
699          break;
700       case '\t':
701          c = 't';
702          break;
703       case '\r':
704          c = 'r';
705          break;
706       default:
707          c = '\0' ;
708       }
709
710       if (c) {
711          *dest = '\\';
712          dest++;
713          *dest = c;
714       } else {
715          *dest = *src;
716       }
717
718       len--;
719       src++;
720       dest++;
721    }
722
723    *dest = '\0';
724    return dest;
725 }
726
727 #ifdef HAVE_BATCH_FILE_INSERT
728 const char *my_ingres_batch_lock_path_query = 
729    "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE";
730
731
732 const char *my_ingres_batch_lock_filename_query = 
733    "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE";
734
735 const char *my_ingres_batch_unlock_tables_query = "COMMIT";
736
737 const char *my_ingres_batch_fill_path_query = 
738    "INSERT INTO Path (Path) "
739     "SELECT a.Path FROM "
740      "(SELECT DISTINCT Path FROM batch) AS a "
741       "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) ";
742
743
744 const char *my_ingres_batch_fill_filename_query = 
745    "INSERT INTO Filename (Name) "
746     "SELECT a.Name FROM "
747      "(SELECT DISTINCT Name FROM batch) as a "
748       "WHERE NOT EXISTS "
749        "(SELECT Name FROM Filename WHERE Name = a.Name)";
750 #endif /* HAVE_BATCH_FILE_INSERT */
751
752 #endif /* HAVE_INGRES */