]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/ingres.c
Fixed typo
[bacula/bacula] / bacula / src / cats / ingres.c
1
2 /*
3    Bacula® - The Network Backup Solution
4
5    Copyright (C) 2003-2010 Free Software Foundation Europe e.V.
6
7    The main author of Bacula is Kern Sibbald, with contributions from
8    many others, a complete list can be found in the file AUTHORS.
9    This program is Free Software; you can redistribute it and/or
10    modify it under the terms of version two of the GNU General Public
11    License as published by the Free Software Foundation and included
12    in the file LICENSE.
13
14    This program is distributed in the hope that it will be useful, but
15    WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17    General Public License for more details.
18
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22    02110-1301, USA.
23
24    Bacula® is a registered trademark of Kern Sibbald.
25    The licensor of Bacula is the Free Software Foundation Europe
26    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
27    Switzerland, email:ftf@fsfeurope.org.
28 */
29 /*
30  * Bacula Catalog Database routines specific to Ingres
31  *   These are Ingres specific routines
32  *
33  *    Stefan Reddig, June 2009
34  *    based uopn work done 
35  *    by Dan Langille, December 2003 and
36  *    by Kern Sibbald, March 2000
37  *
38  */
39
40
41 /* The following is necessary so that we do not include
42  * the dummy external definition of DB.
43  */
44 #define __SQL_C                       /* indicate that this is sql.c */
45
46 #include "bacula.h"
47 #include "cats.h"
48
49 #ifdef HAVE_INGRES
50
51 #include "myingres.h"
52
53 /* -----------------------------------------------------------------------
54  *
55  *   Ingres dependent defines and subroutines
56  *
57  * -----------------------------------------------------------------------
58  */
59
60 /* List of open databases */  /* SRE: needed for ingres? */
61 static BQUEUE db_list = {&db_list, &db_list};
62
63 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
64
65 /*
66  * Retrieve database type
67  */
68 const char *
69 db_get_type(void)
70 {
71    return "Ingres";
72 }
73
74 /*
75  * Initialize database data structure. In principal this should
76  * never have errors, or it is really fatal.
77  */
78 B_DB *
79 db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password,
80                  const char *db_address, int db_port, const char *db_socket,
81                  int mult_db_connections)
82 {
83    B_DB *mdb;
84
85    if (!db_user) {
86       Jmsg(jcr, M_FATAL, 0, _("A user name for Ingres must be supplied.\n"));
87       return NULL;
88    }
89    P(mutex);                          /* lock DB queue */
90    if (!mult_db_connections) {
91       /* Look to see if DB already open */
92       for (mdb=NULL; (mdb=(B_DB *)qnext(&db_list, &mdb->bq)); ) {
93          if (bstrcmp(mdb->db_name, db_name) &&
94              bstrcmp(mdb->db_address, db_address) &&
95              mdb->db_port == db_port) {
96             Dmsg2(100, "DB REopen %d %s\n", mdb->ref_count, db_name);
97             mdb->ref_count++;
98             V(mutex);
99             return mdb;                  /* already open */
100          }
101       }
102    }
103    Dmsg0(100, "db_open first time\n");
104    mdb = (B_DB *)malloc(sizeof(B_DB));
105    memset(mdb, 0, sizeof(B_DB));
106    mdb->db_name = bstrdup(db_name);
107    mdb->db_user = bstrdup(db_user);
108    if (db_password) {
109       mdb->db_password = bstrdup(db_password);
110    }
111    if (db_address) {
112       mdb->db_address  = bstrdup(db_address);
113    }
114    if (db_socket) {
115       mdb->db_socket   = bstrdup(db_socket);
116    }
117    mdb->db_port        = db_port;
118    mdb->have_insert_id = TRUE;
119    mdb->errmsg         = get_pool_memory(PM_EMSG); /* get error message buffer */
120    *mdb->errmsg        = 0;
121    mdb->cmd            = get_pool_memory(PM_EMSG); /* get command buffer */
122    mdb->cached_path    = get_pool_memory(PM_FNAME);
123    mdb->cached_path_id = 0;
124    mdb->ref_count      = 1;
125    mdb->fname          = get_pool_memory(PM_FNAME);
126    mdb->path           = get_pool_memory(PM_FNAME);
127    mdb->esc_name       = get_pool_memory(PM_FNAME);
128    mdb->esc_path      = get_pool_memory(PM_FNAME);
129    mdb->allow_transactions = mult_db_connections;
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       if (mdb->db_name) {
270          free(mdb->db_name);
271       }
272       if (mdb->db_user) {
273          free(mdb->db_user);
274       }
275       if (mdb->db_password) {
276          free(mdb->db_password);
277       }
278       if (mdb->db_address) {
279          free(mdb->db_address);
280       }
281       if (mdb->db_socket) {
282          free(mdb->db_socket);
283       }
284       free(mdb);
285    }
286    V(mutex);
287 }
288
289 void db_check_backend_thread_safe()
290 { }
291
292
293
294 void db_thread_cleanup()
295 { }
296
297 /*
298  * Return the next unique index (auto-increment) for
299  * the given table.  Return NULL on error.
300  *
301  * For Ingres, NULL causes the auto-increment value     SRE: true?
302  *  to be updated.
303  */
304 int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
305 {
306    strcpy(index, "NULL");
307    return 1;
308 }
309
310
311 /*
312  * Escape strings so that Ingres is happy
313  *
314  *   NOTE! len is the length of the old string. Your new
315  *         string must be long enough (max 2*old+1) to hold
316  *         the escaped output.
317  * SRE: TODO! 
318  */
319 void
320 db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
321 {
322 /*
323    int error;
324   
325    PQescapeStringConn(mdb->db, snew, old, len, &error);
326    if (error) {
327       Jmsg(jcr, M_FATAL, 0, _("PQescapeStringConn returned non-zero.\n"));*/
328       /* error on encoding, probably invalid multibyte encoding in the source string
329         see PQescapeStringConn documentation for details. */
330 /*      Dmsg0(500, "PQescapeStringConn failed\n");
331    }*/
332 }
333
334 /*
335  * Submit a general SQL command (cmd), and for each row returned,
336  *  the sqlite_handler is called with the ctx.
337  */
338 bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
339 {
340    SQL_ROW row;
341
342    Dmsg0(500, "db_sql_query started\n");
343
344    db_lock(mdb);
345    if (sql_query(mdb, query) != 0) {
346       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
347       db_unlock(mdb);
348       Dmsg0(500, "db_sql_query failed\n");
349       return false;
350    }
351    Dmsg0(500, "db_sql_query succeeded. checking handler\n");
352
353    if (result_handler != NULL) {
354       Dmsg0(500, "db_sql_query invoking handler\n");
355       if ((mdb->result = sql_store_result(mdb)) != NULL) {
356          int num_fields = sql_num_fields(mdb);
357
358          Dmsg0(500, "db_sql_query sql_store_result suceeded\n");
359          while ((row = sql_fetch_row(mdb)) != NULL) {
360
361             Dmsg0(500, "db_sql_query sql_fetch_row worked\n");
362             if (result_handler(ctx, num_fields, row))
363                break;
364          }
365
366         sql_free_result(mdb);
367       }
368    }
369    db_unlock(mdb);
370
371    Dmsg0(500, "db_sql_query finished\n");
372
373    return true;
374 }
375
376 /*
377  * Close database connection
378  */
379 void my_ingres_close(B_DB *mdb)
380 {
381     INGdisconnectDB(mdb->db);
382     //SRE: error handling? 
383 }
384
385 INGRES_ROW my_ingres_fetch_row(B_DB *mdb)
386 {
387    int j;
388    INGRES_ROW row = NULL; // by default, return NULL
389
390    Dmsg0(500, "my_ingres_fetch_row start\n");
391
392    if (!mdb->row || mdb->row_size < mdb->num_fields) {
393       int num_fields = mdb->num_fields;
394       Dmsg1(500, "we have need space of %d bytes\n", sizeof(char *) * mdb->num_fields);
395
396       if (mdb->row) {
397          Dmsg0(500, "my_ingres_fetch_row freeing space\n");
398          free(mdb->row);
399       }
400       num_fields += 20;                  /* add a bit extra */
401       mdb->row = (INGRES_ROW)malloc(sizeof(char *) * num_fields);
402       mdb->row_size = num_fields;
403
404       // now reset the row_number now that we have the space allocated
405       mdb->row_number = 0;
406    }
407
408    // if still within the result set
409    if (mdb->row_number < mdb->num_rows) {
410       Dmsg2(500, "my_ingres_fetch_row row number '%d' is acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
411       // get each value from this row
412       for (j = 0; j < mdb->num_fields; j++) {
413          mdb->row[j] = INGgetvalue(mdb->result, mdb->row_number, j);
414          Dmsg2(500, "my_ingres_fetch_row field '%d' has value '%s'\n", j, mdb->row[j]);
415       }
416       // increment the row number for the next call
417       mdb->row_number++;
418
419       row = mdb->row;
420    } else {
421       Dmsg2(500, "my_ingres_fetch_row row number '%d' is NOT acceptable (0..%d)\n", mdb->row_number, mdb->num_rows);
422    }
423
424    Dmsg1(500, "my_ingres_fetch_row finishes returning %p\n", row);
425
426    return row;
427 }
428
429
430 int my_ingres_max_length(B_DB *mdb, int field_num) {
431    //
432    // for a given column, find the max length
433    //
434    int max_length;
435    int i;
436    int this_length;
437
438    max_length = 0;
439    for (i = 0; i < mdb->num_rows; i++) {
440       if (INGgetisnull(mdb->result, i, field_num)) {
441           this_length = 4;        // "NULL"
442       } else {
443           this_length = cstrlen(INGgetvalue(mdb->result, i, field_num));
444       }
445
446       if (max_length < this_length) {
447           max_length = this_length;
448       }
449    }
450
451    return max_length;
452 }
453
454 INGRES_FIELD * my_ingres_fetch_field(B_DB *mdb)
455 {
456    int i;
457
458    Dmsg0(500, "my_ingres_fetch_field starts\n");
459
460    if (!mdb->fields || mdb->fields_size < mdb->num_fields) {
461       if (mdb->fields) {
462          free(mdb->fields);
463       }
464       Dmsg1(500, "allocating space for %d fields\n", mdb->num_fields);
465       mdb->fields = (INGRES_FIELD *)malloc(sizeof(INGRES_FIELD) * mdb->num_fields);
466       mdb->fields_size = mdb->num_fields;
467
468       for (i = 0; i < mdb->num_fields; i++) {
469          Dmsg1(500, "filling field %d\n", i);
470          strcpy(mdb->fields[i].name,INGfname(mdb->result, i));
471          mdb->fields[i].max_length = my_ingres_max_length(mdb, i);
472          mdb->fields[i].type       = INGftype(mdb->result, i);
473          mdb->fields[i].flags      = 0;
474
475          Dmsg4(500, "my_ingres_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n",
476             mdb->fields[i].name, mdb->fields[i].max_length, mdb->fields[i].type,
477             mdb->fields[i].flags);
478       } // end for
479    } // end if
480
481    // increment field number for the next time around
482
483    Dmsg0(500, "my_ingres_fetch_field finishes\n");
484    return &mdb->fields[mdb->field_number++];
485 }
486
487 void my_ingres_data_seek(B_DB *mdb, int row)
488 {
489    // set the row number to be returned on the next call
490    // to my_ingres_fetch_row
491    mdb->row_number = row;
492 }
493
494 void my_ingres_field_seek(B_DB *mdb, int field)
495 {
496    mdb->field_number = field;
497 }
498
499 /*
500  * Note, if this routine returns 1 (failure), Bacula expects
501  *  that no result has been stored.
502  *
503  *  Returns:  0  on success
504  *            1  on failure
505  *
506  */
507 int my_ingres_query(B_DB *mdb, const char *query)
508 {
509    Dmsg0(500, "my_ingres_query started\n");
510    // We are starting a new query.  reset everything.
511    mdb->num_rows     = -1;
512    mdb->row_number   = -1;
513    mdb->field_number = -1;
514
515    int cols = -1;
516
517    if (mdb->result) {
518       INGclear(mdb->result);  /* hmm, someone forgot to free?? */
519       mdb->result = NULL;
520    }
521
522    Dmsg1(500, "my_ingres_query starts with '%s'\n", query);
523
524    /* TODO: differentiate between SELECTs and other queries */
525
526    if ((cols = INGgetCols(query)) <= 0) {
527       if (cols < 0 ) {
528          Dmsg0(500,"my_ingres_query: neg.columns: no DML stmt!\n");
529       }
530       Dmsg0(500,"my_ingres_query (non SELECT) starting...\n");
531       /* non SELECT */
532       mdb->num_rows = INGexec(mdb->db, query);
533       if (INGcheck()) {
534         Dmsg0(500,"my_ingres_query (non SELECT) went wrong\n");
535         mdb->status = 1;
536       } else {
537         Dmsg0(500,"my_ingres_query (non SELECT) seems ok\n");
538         mdb->status = 0;
539       }
540    } else {
541       /* SELECT */
542       Dmsg0(500,"my_ingres_query (SELECT) starting...\n");
543       mdb->result = INGquery(mdb->db, query);
544       if ( mdb->result != NULL ) {
545         Dmsg1(500, "we have a result\n", query);
546
547         // how many fields in the set?
548         mdb->num_fields = (int)INGnfields(mdb->result);
549         Dmsg1(500, "we have %d fields\n", mdb->num_fields);
550
551         mdb->num_rows = INGntuples(mdb->result);
552         Dmsg1(500, "we have %d rows\n", mdb->num_rows);
553
554         mdb->status = 0;                  /* succeed */
555       } else {
556         Dmsg0(500, "No resultset...\n");
557         mdb->status = 1; /* failed */
558       }
559    }
560
561    Dmsg0(500, "my_ingres_query finishing\n");
562    return mdb->status;
563 }
564
565 void my_ingres_free_result(B_DB *mdb)
566 {
567    
568    db_lock(mdb);
569    if (mdb->result) {
570       INGclear(mdb->result);
571       mdb->result = NULL;
572    }
573
574    if (mdb->row) {
575       free(mdb->row);
576       mdb->row = NULL;
577    }
578
579    if (mdb->fields) {
580       free(mdb->fields);
581       mdb->fields = NULL;
582    }
583    db_unlock(mdb);
584 }
585
586 int my_ingres_currval(B_DB *mdb, const char *table_name)
587 {
588    // TODO!
589    return -1;
590 }
591
592 #ifdef HAVE_BATCH_FILE_INSERT
593
594 int my_ingres_batch_start(JCR *jcr, B_DB *mdb)
595 {
596     //TODO!
597    return ING_ERROR;
598 }
599
600 /* set error to something to abort operation */
601 int my_ingres_batch_end(JCR *jcr, B_DB *mdb, const char *error)
602 {
603     //TODO!
604    return ING_ERROR;
605 }
606
607 int my_ingres_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
608 {
609     //TODO!
610    return ING_ERROR;
611 }
612
613 #endif /* HAVE_BATCH_FILE_INSERT */
614
615 /*
616  * Escape strings so that Ingres is happy on COPY
617  *
618  *   NOTE! len is the length of the old string. Your new
619  *         string must be long enough (max 2*old+1) to hold
620  *         the escaped output.
621  */
622 char *my_ingres_copy_escape(char *dest, char *src, size_t len)
623 {
624    /* we have to escape \t, \n, \r, \ */
625    char c = '\0' ;
626
627    while (len > 0 && *src) {
628       switch (*src) {
629       case '\n':
630          c = 'n';
631          break;
632       case '\\':
633          c = '\\';
634          break;
635       case '\t':
636          c = 't';
637          break;
638       case '\r':
639          c = 'r';
640          break;
641       default:
642          c = '\0' ;
643       }
644
645       if (c) {
646          *dest = '\\';
647          dest++;
648          *dest = c;
649       } else {
650          *dest = *src;
651       }
652
653       len--;
654       src++;
655       dest++;
656    }
657
658    *dest = '\0';
659    return dest;
660 }
661
662 #ifdef HAVE_BATCH_FILE_INSERT
663 const char *my_ingres_batch_lock_path_query = 
664    "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE";
665
666
667 const char *my_ingres_batch_lock_filename_query = 
668    "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE";
669
670 const char *my_ingres_batch_unlock_tables_query = "COMMIT";
671
672 const char *my_ingres_batch_fill_path_query = 
673    "INSERT INTO Path (Path) "
674     "SELECT a.Path FROM "
675      "(SELECT DISTINCT Path FROM batch) AS a "
676       "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) ";
677
678
679 const char *my_ingres_batch_fill_filename_query = 
680    "INSERT INTO Filename (Name) "
681     "SELECT a.Name FROM "
682      "(SELECT DISTINCT Name FROM batch) as a "
683       "WHERE NOT EXISTS "
684        "(SELECT Name FROM Filename WHERE Name = a.Name)";
685 #endif /* HAVE_BATCH_FILE_INSERT */
686
687 #endif /* HAVE_INGRES */