]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/cats/sqlite.c
bvfs: Restore should be ok with MySQL
[bacula/bacula] / bacula / src / cats / sqlite.c
index a03691fed1c46bacecb19f576a64fecf400f1c2d..bc2458a731f167b54595849bed63f973f2c11d69 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
 
    The main author of Bacula is Kern Sibbald, with contributions from
    many others, a complete list can be found in the file AUTHORS.
    This program is Free Software; you can redistribute it and/or
-   modify it under the terms of version two of the GNU General Public
+   modify it under the terms of version three of the GNU Affero General Public
    License as published by the Free Software Foundation and included
    in the file LICENSE.
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    General Public License for more details.
 
-   You should have received a copy of the GNU General Public License
+   You should have received a copy of the GNU Affero General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   Bacula® is a registered trademark of John Walker.
+   Bacula® is a registered trademark of Kern Sibbald.
    The licensor of Bacula is the Free Software Foundation Europe
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
@@ -30,7 +30,6 @@
  *
  *    Kern Sibbald, January 2002
  *
- *    Version $Id$
  */
 
 
  */
 
 /* List of open databases */
-static BQUEUE db_list = {&db_list, &db_list};
+static dlist *db_list = NULL;
 
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
-int QueryDB(const char *file, int line, JCR *jcr, B_DB *db, char *select_cmd);
-
-
 /*
  * Retrieve database type
  */
 const char *
 db_get_type(void)
 {
+#ifdef HAVE_SQLITE3
+   return "SQLite3";
+#else
    return "SQLite";
+#endif
 }
 
 /*
@@ -98,12 +98,15 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
                  const char *db_address, int db_port, const char *db_socket,
                  int mult_db_connections)
 {
-   B_DB *mdb;
+   B_DB *mdb = NULL;
 
    P(mutex);                          /* lock DB queue */
+   if (db_list == NULL) {
+      db_list = New(dlist(mdb, &mdb->link));
+   }
    /* Look to see if DB already open */
    if (!mult_db_connections) {
-      for (mdb=NULL; (mdb=(B_DB *)qnext(&db_list, &mdb->bq)); ) {
+      foreach_dlist(mdb, db_list) {
          if (bstrcmp(mdb->db_name, db_name) &&
              bstrcmp(mdb->db_address, db_address) &&
              mdb->db_port == db_port) {
@@ -115,10 +118,9 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
       }
    }
    Dmsg0(300, "db_open first time\n");
-   mdb = (B_DB *) malloc(sizeof(B_DB));
+   mdb = (B_DB *)malloc(sizeof(B_DB));
    memset(mdb, 0, sizeof(B_DB));
    mdb->db_name = bstrdup(db_name);
-   mdb->have_insert_id = TRUE;
    mdb->errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */
    *mdb->errmsg = 0;
    mdb->cmd = get_pool_memory(PM_EMSG);    /* get command buffer */
@@ -129,8 +131,9 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
    mdb->path = get_pool_memory(PM_FNAME);
    mdb->esc_name = get_pool_memory(PM_FNAME);
    mdb->esc_path = get_pool_memory(PM_FNAME);
+   mdb->esc_obj  = get_pool_memory(PM_FNAME);
    mdb->allow_transactions = mult_db_connections;
-   qinsert(&db_list, &mdb->bq);            /* put db in list */
+   db_list->append(mdb);
    V(mutex);
    return mdb;
 }
@@ -244,7 +247,7 @@ db_close_database(JCR *jcr, B_DB *mdb)
    sql_free_result(mdb);
    mdb->ref_count--;
    if (mdb->ref_count == 0) {
-      qdchain(&mdb->bq);
+      db_list->remove(mdb);
       if (mdb->connected && mdb->db) {
          sqlite_close(mdb->db);
       }
@@ -256,14 +259,31 @@ db_close_database(JCR *jcr, B_DB *mdb)
       free_pool_memory(mdb->path);
       free_pool_memory(mdb->esc_name);
       free_pool_memory(mdb->esc_path);
+      free_pool_memory(mdb->esc_obj);
       if (mdb->db_name) {
          free(mdb->db_name);
       }
       free(mdb);
+      if (db_list->size() == 0) {
+         delete db_list;
+         db_list = NULL;
+      }
    }
    V(mutex);
 }
 
+void db_check_backend_thread_safe()
+{
+#ifdef HAVE_BATCH_FILE_INSERT
+# ifdef HAVE_SQLITE3_THREADSAFE
+   if (!sqlite3_threadsafe()) {
+      Emsg0(M_ABORT, 0, _("SQLite3 client library must be thread-safe "
+                          "when using BatchMode.\n"));
+   }
+# endif
+#endif
+}
+
 void db_thread_cleanup()
 {
 #ifdef HAVE_SQLITE3
@@ -281,6 +301,60 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
    return 1;
 }
 
+/*
+ * Escape binary object so that SQLite is happy
+ * Memory is stored in B_DB struct, no need to free it
+ *
+ * TODO: this should be implemented  (escape \0)
+ */
+char *
+db_escape_object(JCR *jcr, B_DB *mdb, char *old, int len)
+{
+   char *n, *o;
+
+   n = mdb->esc_obj = check_pool_memory_size(mdb->esc_obj, len*2+1);
+   o = old;
+   while (len--) {
+      switch (*o) {
+      case '\'':
+         *n++ = '\'';
+         *n++ = '\'';
+         o++;
+         break;
+      case 0:
+         *n++ = '\\';
+         *n++ = 0;
+         o++;
+         break;
+      default:
+         *n++ = *o++;
+         break;
+      }
+   }
+   *n = 0;
+   return mdb->esc_obj;
+}
+
+/*
+ * Unescape binary object so that SQLIte is happy
+ *
+ * TODO: need to be implemented (escape \0)
+ */
+void
+db_unescape_object(JCR *jcr, B_DB *mdb, 
+                   char *from, int32_t expected_len, 
+                   POOLMEM **dest, int32_t *dest_len)
+{
+   if (!from) {
+      *dest[0] = 0;
+      *dest_len = 0;
+      return;
+   }
+   *dest = check_pool_memory_size(*dest, expected_len+1);
+   *dest_len = expected_len;
+   memcpy(*dest, from, expected_len);
+   (*dest)[expected_len]=0;
+}
 
 /*
  * Escape strings so that SQLite is happy
@@ -290,7 +364,7 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
  *         the escaped output.
  */
 void
-db_escape_string(JCR *jcr, B_DB *db, char *snew, char *old, int len)
+db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
 {
    char *n, *o;
 
@@ -355,7 +429,7 @@ bool db_sql_query(B_DB *mdb, const char *query, DB_RESULT_HANDLER *result_handle
    rh_data.result_handler = result_handler;
    rh_data.ctx = ctx;
    stat = sqlite_exec(mdb->db, query, sqlite_result, (void *)&rh_data, &mdb->sqlite_errmsg);
-   if (stat != 0) {
+   if (stat != SQLITE_OK) {
       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror(mdb));
       db_unlock(mdb);
       return false;
@@ -382,14 +456,17 @@ int my_sqlite_query(B_DB *mdb, const char *cmd)
    }
    stat = sqlite_get_table(mdb->db, (char *)cmd, &mdb->result, &mdb->nrow, &mdb->ncolumn,
             &mdb->sqlite_errmsg);
-   mdb->row = 0;                      /* row fetched */
+   mdb->row = 0;                      /* no row fetched yet */
+   if (stat != 0) {                   /* something went wrong */
+      mdb->nrow = mdb->ncolumn = 0;
+   }
    return stat;
 }
 
 /* Fetch one row at a time */
 SQL_ROW my_sqlite_fetch_row(B_DB *mdb)
 {
-   if (mdb->row >= mdb->nrow) {
+   if (!mdb->result || (mdb->row >= mdb->nrow)) {
       return NULL;
    }
    mdb->row++;
@@ -402,9 +479,15 @@ void my_sqlite_free_table(B_DB *mdb)
 
    if (mdb->fields_defined) {
       for (i=0; i < sql_num_fields(mdb); i++) {
-         free(mdb->fields[i]);
+         if (mdb->fields[i]) {
+            free(mdb->fields[i]);
+            mdb->fields[i] = NULL;
+         }
+      }
+      if (mdb->fields) {
+         free(mdb->fields);
+         mdb->fields = NULL;
       }
-      free(mdb->fields);
       mdb->fields_defined = false;
    }
    if (mdb->result) {
@@ -418,6 +501,7 @@ void my_sqlite_field_seek(B_DB *mdb, int field)
 {
    int i, j;
    if (mdb->result == NULL) {
+      mdb->field = 0;
       return;
    }
    /* On first call, set up the fields */
@@ -425,6 +509,16 @@ void my_sqlite_field_seek(B_DB *mdb, int field)
       mdb->fields = (SQL_FIELD **)malloc(sizeof(SQL_FIELD) * mdb->ncolumn);
       for (i=0; i < sql_num_fields(mdb); i++) {
          mdb->fields[i] = (SQL_FIELD *)malloc(sizeof(SQL_FIELD));
+         /* ***FIXME***  it seems to me that this is wrong
+          *   fields has lots of items
+          */
+         if (mdb->result[i] == NULL) {
+            mdb->fields_defined = false;
+            free(mdb->fields);
+            mdb->fields = NULL;
+            mdb->field = 0;
+            return;
+         }
          mdb->fields[i]->name = mdb->result[i];
          mdb->fields[i]->length = cstrlen(mdb->fields[i]->name);
          mdb->fields[i]->max_length = mdb->fields[i]->length;
@@ -442,18 +536,47 @@ void my_sqlite_field_seek(B_DB *mdb, int field)
          mdb->fields[i]->type = 0;
          mdb->fields[i]->flags = 1;        /* not null */
       }
-      mdb->fields_defined = TRUE;
+      mdb->fields_defined = true;
    }
-   if (field > sql_num_fields(mdb)) {
-      field = sql_num_fields(mdb);
+   if (sql_num_fields(mdb) <= 0) {
+      field = 0;
+   } else if (field > sql_num_fields(mdb) - 1) {
+      field = sql_num_fields(mdb) - 1;
     }
     mdb->field = field;
-
 }
 
 SQL_FIELD *my_sqlite_fetch_field(B_DB *mdb)
 {
-   return mdb->fields[mdb->field++];
+   if (mdb->fields_defined && mdb->field < sql_num_fields(mdb)) {
+      return mdb->fields[mdb->field++];
+   } else {
+      mdb->field = 0;
+      return NULL;
+   }
+}
+
+uint64_t my_sqlite_insert_autokey_record(B_DB *mdb, const char *query, const char *table_name)
+{
+   /*
+    * First execute the insert query and then retrieve the currval.
+    */
+   if (my_sqlite_query(mdb, query)) {
+      return 0;
+   }
+
+   mdb->num_rows = sql_affected_rows(mdb);
+   if (mdb->num_rows != 1) {
+      return 0;
+   }
+
+   mdb->changes++;
+
+#ifdef HAVE_SQLITE3
+   return sqlite3_last_insert_rowid(mdb->db);
+#else
+   return sqlite_last_insert_rowid(mdb->db);
+#endif
 }
 
 #ifdef HAVE_BATCH_FILE_INSERT