]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/ingres.c
Tweak postgresql update script to handle previous Bweb install
[bacula/bacula] / bacula / src / cats / ingres.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2003-2007 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    qinsert(&db_list, &mdb->bq);            /* put db in list */
130    V(mutex);
131    return mdb;
132 }
133
134 /* Check that the database correspond to the encoding we want */
135 static bool check_database_encoding(JCR *jcr, B_DB *mdb)
136 {
137 /* SRE: TODO! Needed?
138  SQL_ROW row;
139    int ret=false;
140
141    if (!db_sql_query(mdb, "SELECT getdatabaseencoding()", NULL, NULL)) {
142       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
143       return false;
144    }
145
146    if ((row = sql_fetch_row(mdb)) == NULL) {
147       Mmsg1(mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
148       Jmsg(jcr, M_ERROR, 0, "Can't check database encoding %s", mdb->errmsg);
149    } else {
150       ret = bstrcmp(row[0], "SQL_ASCII");
151       if (!ret) {
152          Mmsg(mdb->errmsg, 
153               _("Encoding error for database \"%s\". Wanted SQL_ASCII, got %s\n"),
154               mdb->db_name, row[0]);
155          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
156          Dmsg1(50, "%s", mdb->errmsg);
157       } 
158    }
159    return ret;
160 */
161     return true;
162 }
163
164 /*
165  * Check for errors in DBMS work
166  */
167 static int sql_check(B_DB *mdb)
168 {
169     return INGcheck();
170 }
171
172 /*
173  * Now actually open the database.  This can generate errors,
174  *   which are returned in the errmsg
175  *
176  * DO NOT close the database or free(mdb) here !!!!
177  */
178 int
179 db_open_database(JCR *jcr, B_DB *mdb)
180 {
181    int errstat;
182    char buf[10], *port;
183
184    P(mutex);
185    if (mdb->connected) {
186       V(mutex);
187       return 1;
188    }
189    mdb->connected = false;
190
191    if ((errstat=rwl_init(&mdb->lock)) != 0) {
192       berrno be;
193       Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"),
194             be.bstrerror(errstat));
195       V(mutex);
196       return 0;
197    }
198
199    if (mdb->db_port) {
200       bsnprintf(buf, sizeof(buf), "%d", mdb->db_port);
201       port = buf;
202    } else {
203       port = NULL;
204    }
205
206    mdb->db = INGconnectDB(mdb->db_name, mdb->db_user, mdb->db_password);
207
208    Dmsg0(50, "Ingres real CONNECT done\n");
209    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->db_user, mdb->db_name,
210             mdb->db_password==NULL?"(NULL)":mdb->db_password);
211
212    if (sql_check(mdb)) {
213       Mmsg2(&mdb->errmsg, _("Unable to connect to Ingres server.\n"
214             "Database=%s User=%s\n"
215             "It is probably not running or your password is incorrect.\n"),
216              mdb->db_name, mdb->db_user);
217       V(mutex);
218       return 0;
219    }
220
221    mdb->connected = true;
222
223    if (!check_tables_version(jcr, mdb)) {
224       V(mutex);
225       return 0;
226    }
227
228    //sql_query(mdb, "SET datestyle TO 'ISO, YMD'");
229    
230    /* check that encoding is SQL_ASCII */
231    check_database_encoding(jcr, mdb);
232
233    V(mutex);
234    return 1;
235 }
236
237 void
238 db_close_database(JCR *jcr, B_DB *mdb)
239 {
240    if (!mdb) {
241       return;
242    }
243    db_end_transaction(jcr, mdb);
244    P(mutex);
245    sql_free_result(mdb);
246    mdb->ref_count--;
247    if (mdb->ref_count == 0) {
248       qdchain(&mdb->bq);
249       if (mdb->connected && mdb->db) {
250          sql_close(mdb);
251       }
252       rwl_destroy(&mdb->lock);
253       free_pool_memory(mdb->errmsg);
254       free_pool_memory(mdb->cmd);
255       free_pool_memory(mdb->cached_path);
256       free_pool_memory(mdb->fname);
257       free_pool_memory(mdb->path);
258       free_pool_memory(mdb->esc_name);
259       free_pool_memory(mdb->esc_path);
260       if (mdb->db_name) {
261          free(mdb->db_name);
262       }
263       if (mdb->db_user) {
264          free(mdb->db_user);
265       }
266       if (mdb->db_password) {
267          free(mdb->db_password);
268       }
269       if (mdb->db_address) {
270          free(mdb->db_address);
271       }
272       if (mdb->db_socket) {
273          free(mdb->db_socket);
274       }
275       free(mdb);
276    }
277    V(mutex);
278 }
279
280 void db_thread_cleanup()
281 { }
282
283 /*
284  * Return the next unique index (auto-increment) for
285  * the given table.  Return NULL on error.
286  *
287  * For Ingres, NULL causes the auto-increment value     SRE: true?
288  *  to be updated.
289  */
290 int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
291 {
292    strcpy(index, "NULL");
293    return 1;
294 }
295
296
297 /*
298  * Escape strings so that Ingres is happy
299  *
300  *   NOTE! len is the length of the old string. Your new
301  *         string must be long enough (max 2*old+1) to hold
302  *         the escaped output.
303  * SRE: TODO! 
304  */
305 void
306 db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
307 {
308 /*
309    int error;
310   
311    PQescapeStringConn(mdb->db, snew, old, len, &error);
312    if (error) {
313       Jmsg(jcr, M_FATAL, 0, _("PQescapeStringConn returned non-zero.\n"));*/
314       /* error on encoding, probably invalid multibyte encoding in the source string
315         see PQescapeStringConn documentation for details. */
316 /*      Dmsg0(500, "PQescapeStringConn failed\n");
317    }*/
318 }
319
320 /*
321  * Submit a general SQL command (cmd), and for each row returned,
322  *  the sqlite_handler is called with the ctx.
323  */
324 bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
325 {
326    SQL_ROW row;
327
328    Dmsg0(500, "db_sql_query started\n");
329
330    db_lock(mdb);
331    if (sql_query(mdb, query) != 0) {
332       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
333       db_unlock(mdb);
334       Dmsg0(500, "db_sql_query failed\n");
335       return false;
336    }
337    Dmsg0(500, "db_sql_query succeeded. checking handler\n");
338
339    if (result_handler != NULL) {
340       Dmsg0(500, "db_sql_query invoking handler\n");
341       if ((mdb->result = sql_store_result(mdb)) != NULL) {
342          int num_fields = sql_num_fields(mdb);
343
344          Dmsg0(500, "db_sql_query sql_store_result suceeded\n");
345          while ((row = sql_fetch_row(mdb)) != NULL) {
346
347             Dmsg0(500, "db_sql_query sql_fetch_row worked\n");
348             if (result_handler(ctx, num_fields, row))
349                break;
350          }
351
352         sql_free_result(mdb);
353       }
354    }
355    db_unlock(mdb);
356
357    Dmsg0(500, "db_sql_query finished\n");
358
359    return true;
360 }
361
362 /*
363  * Close database connection
364  */
365 void my_ingres_close(B_DB *mdb)
366 {
367     INGdisconnectDB(mdb->db);
368     //SRE: error handling? 
369 }
370
371 INGRES_ROW my_ingres_fetch_row(B_DB *mdb)
372 {
373    int j;
374    INGRES_ROW row = NULL; // by default, return NULL
375
376    Dmsg0(500, "my_ingres_fetch_row start\n");
377
378    if (!mdb->row || mdb->row_size < mdb->num_fields) {
379       int num_fields = mdb->num_fields;
380       Dmsg1(500, "we have need space of %d bytes\n", sizeof(char *) * mdb->num_fields);
381
382       if (mdb->row) {
383          Dmsg0(500, "my_ingres_fetch_row freeing space\n");
384          free(mdb->row);
385       }
386       num_fields += 20;                  /* add a bit extra */
387       mdb->row = (INGRES_ROW)malloc(sizeof(char *) * num_fields);
388       mdb->row_size = num_fields;
389
390       // now reset the row_number now that we have the space allocated
391       mdb->row_number = 0;
392    }
393
394    // if still within the result set
395    if (mdb->row_number < mdb->num_rows) {
396       Dmsg2(500, "my_ingres_fetch_row row number '%d' is acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
397       // get each value from this row
398       for (j = 0; j < mdb->num_fields; j++) {
399          mdb->row[j] = INGgetvalue(mdb->result, mdb->row_number, j);
400          Dmsg2(500, "my_ingres_fetch_row field '%d' has value '%s'\n", j, mdb->row[j]);
401       }
402       // increment the row number for the next call
403       mdb->row_number++;
404
405       row = mdb->row;
406    } else {
407       Dmsg2(500, "my_ingres_fetch_row row number '%d' is NOT acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
408    }
409
410    Dmsg1(500, "my_ingres_fetch_row finishes returning %p\n", row);
411
412    return row;
413 }
414
415
416 int my_ingres_max_length(B_DB *mdb, int field_num) {
417    //
418    // for a given column, find the max length
419    //
420    int max_length;
421    int i;
422    int this_length;
423
424    max_length = 0;
425    for (i = 0; i < mdb->num_rows; i++) {
426       if (INGgetisnull(mdb->result, i, field_num)) {
427           this_length = 4;        // "NULL"
428       } else {
429           this_length = cstrlen(INGgetvalue(mdb->result, i, field_num));
430       }
431
432       if (max_length < this_length) {
433           max_length = this_length;
434       }
435    }
436
437    return max_length;
438 }
439
440 INGRES_FIELD * my_ingres_fetch_field(B_DB *mdb)
441 {
442    int     i;
443
444    Dmsg0(500, "my_ingres_fetch_field starts\n");
445
446    if (!mdb->fields || mdb->fields_size < mdb->num_fields) {
447       if (mdb->fields) {
448          free(mdb->fields);
449       }
450       Dmsg1(500, "allocating space for %d fields\n", mdb->num_fields);
451       mdb->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * mdb->num_fields);
452       mdb->fields_size = mdb->num_fields;
453
454       for (i = 0; i < mdb->num_fields; i++) {
455          Dmsg1(500, "filling field %d\n", i);
456          strcpy(mdb->fields[i].name,INGfname(mdb->result, i));
457          mdb->fields[i].max_length = my_ingres_max_length(mdb, i);
458          mdb->fields[i].type       = INGftype(mdb->result, i);
459          mdb->fields[i].flags      = 0;
460
461          Dmsg4(500, "my_ingres_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n",
462             mdb->fields[i].name, mdb->fields[i].max_length, mdb->fields[i].type,
463             mdb->fields[i].flags);
464       } // end for
465    } // end if
466
467    // increment field number for the next time around
468
469    Dmsg0(500, "my_ingres_fetch_field finishes\n");
470    return &mdb->fields[mdb->field_number++];
471 }
472
473 void my_ingres_data_seek(B_DB *mdb, int row)
474 {
475    // set the row number to be returned on the next call
476    // to my_ingres_fetch_row
477    mdb->row_number = row;
478 }
479
480 void my_ingres_field_seek(B_DB *mdb, int field)
481 {
482    mdb->field_number = field;
483 }
484
485 /*
486  * Note, if this routine returns 1 (failure), Bacula expects
487  *  that no result has been stored.
488  * This is where QUERY_DB comes with Ingres.   SRE: true?
489  *
490  *  Returns:  0  on success
491  *            1  on failure
492  *
493  */
494 int my_ingres_query(B_DB *mdb, const char *query)
495 {
496    Dmsg0(500, "my_ingres_query started\n");
497    // We are starting a new query.  reset everything.
498    mdb->num_rows     = -1;
499    mdb->row_number   = -1;
500    mdb->field_number = -1;
501
502    if (mdb->result) {
503       INGclear(mdb->result);  /* hmm, someone forgot to free?? */
504       mdb->result = NULL;
505    }
506
507    Dmsg1(500, "my_ingres_query starts with '%s'\n", query);
508    mdb->result = INGexec(mdb->db, query);
509    if (!mdb->result) {
510       Dmsg1(50, "Query failed: %s\n", query);
511       goto bail_out;
512    }
513
514    mdb->status = INGresultStatus(mdb->result);
515    if (mdb->status == ING_COMMAND_OK) {
516       Dmsg1(500, "we have a result\n", query);
517
518       // how many fields in the set?
519       mdb->num_fields = (int)INGnfields(mdb->result);
520       Dmsg1(500, "we have %d fields\n", mdb->num_fields);
521
522       mdb->num_rows = INGntuples(mdb->result);
523       Dmsg1(500, "we have %d rows\n", mdb->num_rows);
524
525       mdb->status = 0;                  /* succeed */
526    } else {
527       Dmsg1(50, "Result status failed: %s\n", query);
528       goto bail_out;
529    }
530
531    Dmsg0(500, "my_ingres_query finishing\n");
532    return mdb->status;
533
534 bail_out:
535    Dmsg1(500, "we failed\n", query);
536    INGclear(mdb->result);
537    mdb->result = NULL;
538    mdb->status = 1;                   /* failed */
539    return mdb->status;
540 }
541
542 void my_ingres_free_result(B_DB *mdb)
543 {
544    
545    db_lock(mdb);
546    if (mdb->result) {
547       INGclear(mdb->result);
548       mdb->result = NULL;
549    }
550
551    if (mdb->row) {
552       free(mdb->row);
553       mdb->row = NULL;
554    }
555
556    if (mdb->fields) {
557       free(mdb->fields);
558       mdb->fields = NULL;
559    }
560    db_unlock(mdb);
561 }
562
563 int my_ingres_currval(B_DB *mdb, const char *table_name)
564 {
565    // TODO!
566    return -1;
567 }
568
569 #ifdef HAVE_BATCH_FILE_INSERT
570
571 int my_ingres_batch_start(JCR *jcr, B_DB *mdb)
572 {
573     //TODO!
574    return ING_ERROR;
575 }
576
577 /* set error to something to abort operation */
578 int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error)
579 {
580     //TODO!
581    return ING_ERROR;
582 }
583
584 int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
585 {
586     //TODO!
587    return ING_ERROR;
588 }
589
590 #endif /* HAVE_BATCH_FILE_INSERT */
591
592 /*
593  * Escape strings so that Ingres is happy on COPY
594  *
595  *   NOTE! len is the length of the old string. Your new
596  *         string must be long enough (max 2*old+1) to hold
597  *         the escaped output.
598  */
599 char *my_ingres_copy_escape(char *dest, char *src, size_t len)
600 {
601    /* we have to escape \t, \n, \r, \ */
602    char c = '\0' ;
603
604    while (len > 0 && *src) {
605       switch (*src) {
606       case '\n':
607          c = 'n';
608          break;
609       case '\\':
610          c = '\\';
611          break;
612       case '\t':
613          c = 't';
614          break;
615       case '\r':
616          c = 'r';
617          break;
618       default:
619          c = '\0' ;
620       }
621
622       if (c) {
623          *dest = '\\';
624          dest++;
625          *dest = c;
626       } else {
627          *dest = *src;
628       }
629
630       len--;
631       src++;
632       dest++;
633    }
634
635    *dest = '\0';
636    return dest;
637 }
638
639 #ifdef HAVE_BATCH_FILE_INSERT
640 const char *my_ingres_batch_lock_path_query = 
641    "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE";
642
643
644 const char *my_ingres_batch_lock_filename_query = 
645    "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE";
646
647 const char *my_ingres_batch_unlock_tables_query = "COMMIT";
648
649 const char *my_ingres_batch_fill_path_query = 
650    "INSERT INTO Path (Path) "
651     "SELECT a.Path FROM "
652      "(SELECT DISTINCT Path FROM batch) AS a "
653       "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) ";
654
655
656 const char *my_ingres_batch_fill_filename_query = 
657    "INSERT INTO Filename (Name) "
658     "SELECT a.Name FROM "
659      "(SELECT DISTINCT Name FROM batch) as a "
660       "WHERE NOT EXISTS "
661        "(SELECT Name FROM Filename WHERE Name = a.Name)";
662 #endif /* HAVE_BATCH_FILE_INSERT */
663
664 #endif /* HAVE_INGRES */