]> git.sur5r.net Git - bacula/bacula/blobdiff - bacula/src/cats/dbi.c
bat: Use BVFS on bRestore view
[bacula/bacula] / bacula / src / cats / dbi.c
index 6cdaf88b2bce8b5f96e9a259fad3befddce5b65b..53f0ea576077a0059c96b0f4b2856287ff2a4205 100644 (file)
@@ -1,12 +1,12 @@
 /*
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2003-2008 Free Software Foundation Europe e.V.
+   Copyright (C) 2003-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.
  *    based upon work done by Dan Langille, December 2003 and
  *    by Kern Sibbald, March 2000
  *
- *    Version $Id$
+ */
+/*
+ * This code only compiles against a recent version of libdbi. The current
+ * release found on the libdbi website (0.8.3) won't work for this code.
+ *
+ * You find the libdbi library on http://sourceforge.net/projects/libdbi
+ *
+ * A fairly recent version of libdbi from CVS works, so either make sure
+ * your distribution has a fairly recent version of libdbi installed or
+ * clone the CVS repositories from sourceforge and compile that code and
+ * install it.
+ *
+ * You need:
+ * cvs co :pserver:anonymous@libdbi.cvs.sourceforge.net:/cvsroot/libdbi
+ * cvs co :pserver:anonymous@libdbi-drivers.cvs.sourceforge.net:/cvsroot/libdbi-drivers
  */
 
 
  */
 
 /* List of open databases */
-static BQUEUE db_list = {&db_list, &db_list};
+static dlist *db_list = NULL;
 
 /* Control allocated fields by my_dbi_getvalue */
-static BQUEUE dbi_getvalue_list = {&dbi_getvalue_list, &dbi_getvalue_list};
+static dlist *dbi_getvalue_list = NULL;
 
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -77,10 +91,11 @@ db_get_type(void)
  */
 B_DB *
 db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char *db_password,
-                 const char *db_address, int db_port, const char *db_socket, 
+                 const char *db_address, int db_port, const char *db_socket,
                  int mult_db_connections)
 {
-   B_DB *mdb;
+   B_DB *mdb = NULL;
+   DBI_FIELD_GET *field;
    char db_driver[10];
    char db_driverdir[256];
 
@@ -89,9 +104,9 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
       Jmsg(jcr, M_FATAL, 0, _("A dbi driver for DBI must be supplied.\n"));
       return NULL;
    }
-   
-   /* Do the correct selection of driver. 
-    * Can be one of the varius supported by libdbi 
+
+   /* Do the correct selection of driver.
+    * Can be one of the varius supported by libdbi
     */
    switch (db_type) {
    case SQL_TYPE_MYSQL:
@@ -101,26 +116,33 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
       bstrncpy(db_driver,"pgsql", sizeof(db_driver));
       break;
    case SQL_TYPE_SQLITE:
-      bstrncpy(db_driver,"pgsql", sizeof(db_driver));
+      bstrncpy(db_driver,"sqlite", sizeof(db_driver));
+      break;
+   case SQL_TYPE_SQLITE3:
+      bstrncpy(db_driver,"sqlite3", sizeof(db_driver));
       break;
    }
-   
+
    /* Set db_driverdir whereis is the libdbi drivers */
    bstrncpy(db_driverdir, DBI_DRIVER_DIR, 255);
-   
+
    if (!db_user) {
       Jmsg(jcr, M_FATAL, 0, _("A user name for DBI must be supplied.\n"));
       return NULL;
    }
    P(mutex);                          /* lock DB queue */
+   if (db_list == NULL) {
+      db_list = New(dlist(mdb, &mdb->link));
+      dbi_getvalue_list = New(dlist(field, &field->link));
+   }
    if (!mult_db_connections) {
       /* Look to see if DB already open */
-      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) &&
              bstrcmp(mdb->db_driver, db_driver) &&
-             mdb->db_port == db_port) { 
-            Dmsg4(100, "DB REopen %d %s %s erro: %d\n", mdb->ref_count, db_driver, db_name, 
+             mdb->db_port == db_port) {
+            Dmsg4(100, "DB REopen %d %s %s erro: %d\n", mdb->ref_count, db_driver, db_name,
                   dbi_conn_error(mdb->db, NULL));
             mdb->ref_count++;
             V(mutex);
@@ -150,7 +172,6 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
    }
    mdb->db_type        = db_type;
    mdb->db_port        = db_port;
-   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 */
@@ -162,7 +183,7 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
    mdb->esc_name       = get_pool_memory(PM_FNAME);
    mdb->esc_path      = get_pool_memory(PM_FNAME);
    mdb->allow_transactions = mult_db_connections;
-   qinsert(&db_list, &mdb->bq);            /* put db in list */
+   db_list->append(mdb);                   /* put db in list */
    V(mutex);
    return mdb;
 }
@@ -175,13 +196,16 @@ db_init_database(JCR *jcr, const char *db_name, const char *db_user, const char
  */
 int
 db_open_database(JCR *jcr, B_DB *mdb)
-{   
+{
    int errstat;
    int dbstat;
+   uint8_t len;
    const char *errmsg;
    char buf[10], *port;
-   int numdrivers; 
-   
+   int numdrivers;
+   char *db_name = NULL;
+   char *db_dir = NULL;
+
    P(mutex);
    if (mdb->connected) {
       V(mutex);
@@ -203,7 +227,7 @@ db_open_database(JCR *jcr, B_DB *mdb)
    } else {
       port = NULL;
    }
-   
+
    numdrivers = dbi_initialize_r(mdb->db_driverdir, &(mdb->instance));
    if (numdrivers < 0) {
       Mmsg2(&mdb->errmsg, _("Unable to locate the DBD drivers to DBI interface in: \n"
@@ -213,36 +237,72 @@ db_open_database(JCR *jcr, B_DB *mdb)
       return 0;
    }
    mdb->db = (void **)dbi_conn_new_r(mdb->db_driver, mdb->instance);
-   dbi_conn_set_option(mdb->db, "host", mdb->db_address); /* default = localhost */
-   dbi_conn_set_option(mdb->db, "port", port);            /* default port */
-   dbi_conn_set_option(mdb->db, "username", mdb->db_user);     /* login name */
-   dbi_conn_set_option(mdb->db, "password", mdb->db_password); /* password */
-   dbi_conn_set_option(mdb->db, "dbname", mdb->db_name);       /* database name */
-      
+   /* Can be many types of databases */
+   switch (mdb->db_type) {
+   case SQL_TYPE_MYSQL:
+      dbi_conn_set_option(mdb->db, "host", mdb->db_address); /* default = localhost */
+      dbi_conn_set_option(mdb->db, "port", port);            /* default port */
+      dbi_conn_set_option(mdb->db, "username", mdb->db_user);     /* login name */
+      dbi_conn_set_option(mdb->db, "password", mdb->db_password); /* password */
+      dbi_conn_set_option(mdb->db, "dbname", mdb->db_name);       /* database name */
+      break;
+   case SQL_TYPE_POSTGRESQL:
+      dbi_conn_set_option(mdb->db, "host", mdb->db_address);
+      dbi_conn_set_option(mdb->db, "port", port);
+      dbi_conn_set_option(mdb->db, "username", mdb->db_user);
+      dbi_conn_set_option(mdb->db, "password", mdb->db_password);
+      dbi_conn_set_option(mdb->db, "dbname", mdb->db_name);
+      break;
+   case SQL_TYPE_SQLITE:
+      len = strlen(working_directory) + 5;
+      db_dir = (char *)malloc(len);
+      strcpy(db_dir, working_directory);
+      strcat(db_dir, "/");
+      len = strlen(mdb->db_name) + 5;
+      db_name = (char *)malloc(len);
+      strcpy(db_name, mdb->db_name);
+      strcat(db_name, ".db");
+      dbi_conn_set_option(mdb->db, "sqlite_dbdir", db_dir);
+      dbi_conn_set_option(mdb->db, "dbname", db_name);
+      break;
+   case SQL_TYPE_SQLITE3:
+      len = strlen(working_directory) + 5;
+      db_dir = (char *)malloc(len);
+      strcpy(db_dir, working_directory);
+      strcat(db_dir, "/");
+      len = strlen(mdb->db_name) + 5;
+      db_name = (char *)malloc(len);
+      strcpy(db_name, mdb->db_name);
+      strcat(db_name, ".db");
+      dbi_conn_set_option(mdb->db, "sqlite3_dbdir", db_dir);
+      dbi_conn_set_option(mdb->db, "dbname", db_name);
+      Dmsg2(500, "SQLITE: %s %s\n", db_dir, db_name);
+      break;
+   }
+
    /* If connection fails, try at 5 sec intervals for 30 seconds. */
    for (int retry=0; retry < 6; retry++) {
-         
-      dbstat = dbi_conn_connect(mdb->db); 
+
+      dbstat = dbi_conn_connect(mdb->db);
       if ( dbstat == 0) {
          break;
       }
-         
+
       dbi_conn_error(mdb->db, &errmsg);
       Dmsg1(50, "dbi error: %s\n", errmsg);
-                   
+
       bmicrosleep(5, 0);
-         
+
    }
-   
+
    if ( dbstat != 0 ) {
-      Mmsg3(&mdb->errmsg, _("Unable to connect to DBI interface.\n"
-                       "Type=%s Database=%s User=%s\n"
-                       "It is probably not running or your password is incorrect.\n"),
-                        mdb->db_driver, mdb->db_name, mdb->db_user);
+      Mmsg3(&mdb->errmsg, _("Unable to connect to DBI interface. Type=%s Database=%s User=%s\n"
+         "Possible causes: SQL server not running; password incorrect; max_connections exceeded.\n"),
+         mdb->db_driver, mdb->db_name, mdb->db_user);
       V(mutex);
-      return 0;           
-   } 
-   
+      return 0;
+   }
+
    Dmsg0(50, "dbi_real_connect done\n");
    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n",
                     mdb->db_user, mdb->db_name,
@@ -254,7 +314,7 @@ db_open_database(JCR *jcr, B_DB *mdb)
       V(mutex);
       return 0;
    }
-   
+
    switch (mdb->db_type) {
    case SQL_TYPE_MYSQL:
       /* Set connection timeout to 8 days specialy for batch mode */
@@ -269,10 +329,15 @@ db_open_database(JCR *jcr, B_DB *mdb)
       sql_query(mdb, "SET datestyle TO 'ISO, YMD'");
       sql_query(mdb, "set standard_conforming_strings=on");
       break;
-   case SQL_TYPE_SQLITE:
-      break;
    }
-   
+
+   if(db_dir) {
+      free(db_dir);
+   }
+   if(db_name) {
+      free(db_name);
+   }
+
    V(mutex);
    return 1;
 }
@@ -288,7 +353,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) {
          //sql_close(mdb);
          dbi_shutdown_r(mdb->instance);
@@ -324,11 +389,18 @@ db_close_database(JCR *jcr, B_DB *mdb)
       if (mdb->db_driver) {
           free(mdb->db_driver);
       }
-      free(mdb);            
-   }   
+      free(mdb);
+      if (db_list->size() == 0) {
+         delete db_list;
+         db_list = NULL;
+      }
+   }
    V(mutex);
 }
 
+void db_check_backend_thread_safe()
+{ }
+
 void db_thread_cleanup()
 { }
 
@@ -350,9 +422,9 @@ int db_next_index(JCR *jcr, B_DB *mdb, char *table, char *index)
  *   NOTE! len is the length of the old string. Your new
  *         string must be long enough (max 2*old+1) to hold
  *         the escaped output.
- * 
+ *
  * dbi_conn_quote_string_copy receives a pointer to pointer.
- * We need copy the value of pointer to snew because libdbi change the 
+ * We need copy the value of pointer to snew because libdbi change the
  * pointer
  */
 void
@@ -360,9 +432,9 @@ db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
 {
    char *inew;
    char *pnew;
-   
+
    if (len == 0) {
-      snew[0] = 0; 
+      snew[0] = 0;
    } else {
       /* correct the size of old basead in len
        * and copy new string to inew
@@ -373,11 +445,11 @@ db_escape_string(JCR *jcr, B_DB *mdb, char *snew, char *old, int len)
       dbi_conn_escape_string_copy(mdb->db, inew, &pnew);
       free(inew);
       /* copy the escaped string to snew */
-      bstrncpy(snew, pnew, 2 * len + 1);   
+      bstrncpy(snew, pnew, 2 * len + 1);
    }
-   
+
    Dmsg2(500, "dbi_conn_escape_string_copy %p %s\n",snew,snew);
-   
+
 }
 
 /*
@@ -430,9 +502,7 @@ DBI_ROW my_dbi_fetch_row(B_DB *mdb)
    DBI_ROW row = NULL; // by default, return NULL
 
    Dmsg0(500, "my_dbi_fetch_row start\n");
-   
-
-   if (!mdb->row || mdb->row_size < mdb->num_fields) {   
+   if ((!mdb->row || mdb->row_size < mdb->num_fields) && mdb->num_rows > 0) {
       int num_fields = mdb->num_fields;
       Dmsg1(500, "we have need space of %d bytes\n", sizeof(char *) * mdb->num_fields);
 
@@ -441,11 +511,11 @@ DBI_ROW my_dbi_fetch_row(B_DB *mdb)
          Dmsg2(500, "my_dbi_free_row row: '%p' num_fields: '%d'\n", mdb->row, mdb->num_fields);
          if (mdb->num_rows != 0) {
             for(j = 0; j < mdb->num_fields; j++) {
-               Dmsg2(500, "my_dbi_free_row row '%p' '%d'\n", mdb->row[j], j);               
+               Dmsg2(500, "my_dbi_free_row row '%p' '%d'\n", mdb->row[j], j);
                   if(mdb->row[j]) {
                      free(mdb->row[j]);
-                  }                  
-            } 
+                  }
+            }
          }
          free(mdb->row);
       }
@@ -458,19 +528,19 @@ DBI_ROW my_dbi_fetch_row(B_DB *mdb)
    }
 
    // if still within the result set
-   if (mdb->row_number <= mdb->num_rows) {
+   if (mdb->row_number <= mdb->num_rows && mdb->row_number != DBI_ERROR_BADPTR) {
       Dmsg2(500, "my_dbi_fetch_row row number '%d' is acceptable (1..%d)\n", mdb->row_number, mdb->num_rows);
       // get each value from this row
       for (j = 0; j < mdb->num_fields; j++) {
-         mdb->row[j] = my_dbi_getvalue(mdb->result, mdb->row_number, j);         
-         // allocate space to queue row 
+         mdb->row[j] = my_dbi_getvalue(mdb->result, mdb->row_number, j);
+         // allocate space to queue row
          mdb->field_get = (DBI_FIELD_GET *)malloc(sizeof(DBI_FIELD_GET));
          // store the pointer in queue
-         mdb->field_get->value = mdb->row[j];  
+         mdb->field_get->value = mdb->row[j];
          Dmsg4(500, "my_dbi_fetch_row row[%d] field: '%p' in queue: '%p' has value: '%s'\n",
                j, mdb->row[j], mdb->field_get->value, mdb->row[j]);
          // insert in queue to future free
-         qinsert(&dbi_getvalue_list, &mdb->field_get->bq);         
+         dbi_getvalue_list->append(mdb->field_get);
       }
       // increment the row number for the next call
       mdb->row_number++;
@@ -527,7 +597,7 @@ DBI_FIELD * my_dbi_fetch_field(B_DB *mdb)
       Dmsg1(500, "allocating space for %d fields\n", mdb->num_fields);
       mdb->fields = (DBI_FIELD *)malloc(sizeof(DBI_FIELD) * mdb->num_fields);
       mdb->fields_size = mdb->num_fields;
-      
+
       for (i = 0; i < mdb->num_fields; i++) {
          // num_fileds is starting at 1, increment i by 1
          dbi_index = i + 1;
@@ -582,16 +652,16 @@ int my_dbi_query(B_DB *mdb, const char *query)
       dbi_result_free(mdb->result);  /* hmm, someone forgot to free?? */
       mdb->result = NULL;
    }
-         
+
    mdb->result = (void **)dbi_conn_query(mdb->db, query);
-      
+
    if (!mdb->result) {
-      Dmsg2(50, "Query failed: %s %p\n", query, mdb->result);      
+      Dmsg2(50, "Query failed: %s %p\n", query, mdb->result);
       goto bail_out;
    }
-   
+
    mdb->status = (dbi_error_flag) dbi_conn_error(mdb->db, &errmsg);
-   
+
    if (mdb->status == DBI_ERROR_NONE) {
       Dmsg1(500, "we have a result\n", query);
 
@@ -624,52 +694,51 @@ bail_out:
 }
 
 void my_dbi_free_result(B_DB *mdb)
-{ 
-   
+{
+
    DBI_FIELD_GET *f;
-   db_lock(mdb);  
-   int i = 0;
+   db_lock(mdb);
    if (mdb->result) {
       Dmsg1(500, "my_dbi_free_result result '%p'\n", mdb->result);
       dbi_result_free(mdb->result);
    }
 
    mdb->result = NULL;
-   
+
    if (mdb->row) {
       free(mdb->row);
    }
-   
+
    /* now is time to free all value return by my_dbi_get_value
     * this is necessary because libdbi don't free memory return by yours results
     * and Bacula has some routine wich call more than once time my_dbi_fetch_row
-    * 
+    *
     * Using a queue to store all pointer allocate is a good way to free all things
     * when necessary
     */
-   while((f=(DBI_FIELD_GET *)qremove(&dbi_getvalue_list))) {
+   foreach_dlist(f, dbi_getvalue_list) {
       Dmsg2(500, "my_dbi_free_result field value: '%p' in queue: '%p'\n", f->value, f);
       free(f->value);
       free(f);
    }
-      
+
    mdb->row = NULL;
-   
+
    if (mdb->fields) {
       free(mdb->fields);
       mdb->fields = NULL;
    }
    db_unlock(mdb);
    Dmsg0(500, "my_dbi_free_result finish\n");
-   
+
 }
 
-const char *my_dbi_strerror(B_DB *mdb) 
+const char *my_dbi_strerror(B_DB *mdb)
 {
    const char *errmsg;
-   
+
    dbi_conn_error(mdb->db, &errmsg);
-        
+
    return errmsg;
 }
 
@@ -677,16 +746,16 @@ const char *my_dbi_strerror(B_DB *mdb)
 
 /*
  * This can be a bit strang but is the one way to do
- * 
+ *
  * Returns 1 if OK
  *         0 if failed
  */
 int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
 {
-   char *query = "COPY batch FROM STDIN";
-   
+   const char *query = "COPY batch FROM STDIN";
+
    Dmsg0(500, "my_dbi_batch_start started\n");
-   
+
    switch (mdb->db_type) {
    case SQL_TYPE_MYSQL:
       db_lock(mdb);
@@ -707,11 +776,8 @@ int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
       return 1;
       break;
    case SQL_TYPE_POSTGRESQL:
-      
-      //query = "COPY batch FROM STDIN";
 
-      if (my_dbi_query(mdb,
-                              "CREATE TEMPORARY TABLE batch ("
+      if (my_dbi_query(mdb, "CREATE TEMPORARY TABLE batch ("
                                   "fileindex int,"
                                   "jobid int,"
                                   "path varchar,"
@@ -722,7 +788,7 @@ int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
          Dmsg0(500, "my_dbi_batch_start failed\n");
          return 1;
       }
-      
+
       // We are starting a new query.  reset everything.
       mdb->num_rows     = -1;
       mdb->row_number   = -1;
@@ -744,7 +810,7 @@ int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
 
       mdb->status = (dbi_error_flag)dbi_conn_error(mdb->db, NULL);
       //mdb->status = DBI_ERROR_NONE;
-         
+
       if (mdb->status == DBI_ERROR_NONE) {
          // how many fields in the set?
          mdb->num_fields = dbi_result_get_numfields(mdb->result);
@@ -761,8 +827,24 @@ int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
       break;
    case SQL_TYPE_SQLITE:
       db_lock(mdb);
-      if (my_dbi_query(mdb,
-                              "CREATE TEMPORARY TABLE batch ("
+      if (my_dbi_query(mdb, "CREATE TEMPORARY TABLE batch ("
+                                  "FileIndex integer,"
+                                  "JobId integer,"
+                                  "Path blob,"
+                                  "Name blob,"
+                                  "LStat tinyblob,"
+                                  "MD5 tinyblob)") == 1)
+      {
+         Dmsg0(500, "my_dbi_batch_start failed\n");
+         goto bail_out;
+      }
+      db_unlock(mdb);
+      Dmsg0(500, "my_dbi_batch_start finishing\n");
+      return 1;
+      break;
+   case SQL_TYPE_SQLITE3:
+      db_lock(mdb);
+      if (my_dbi_query(mdb, "CREATE TEMPORARY TABLE batch ("
                                   "FileIndex integer,"
                                   "JobId integer,"
                                   "Path blob,"
@@ -778,7 +860,7 @@ int my_dbi_batch_start(JCR *jcr, B_DB *mdb)
       return 1;
       break;
    }
-   
+
 bail_out:
    Mmsg1(&mdb->errmsg, _("error starting batch mode: %s"), my_dbi_strerror(mdb));
    mdb->status = (dbi_error_flag) 0;
@@ -792,9 +874,9 @@ int my_dbi_batch_end(JCR *jcr, B_DB *mdb, const char *error)
 {
    int res = 0;
    int count = 30;
-   int (*custom_function)(void*, const char*) = NULL;  
+   int (*custom_function)(void*, const char*) = NULL;
    dbi_conn_t *myconn = (dbi_conn_t *)(mdb->db);
-   
+
    Dmsg0(500, "my_dbi_batch_end started\n");
 
    if (!mdb) {                  /* no files ? */
@@ -810,16 +892,16 @@ int my_dbi_batch_end(JCR *jcr, B_DB *mdb, const char *error)
    case SQL_TYPE_POSTGRESQL:
       custom_function = (custom_function_end_t)dbi_driver_specific_function(dbi_conn_get_driver(mdb->db), "PQputCopyEnd");
 
-                  
-      do { 
-         res = (*custom_function)(myconn->connection, error);         
+
+      do {
+         res = (*custom_function)(myconn->connection, error);
       } while (res == 0 && --count > 0);
 
       if (res == 1) {
          Dmsg0(500, "ok\n");
          mdb->status = (dbi_error_flag) 1;
       }
-         
+
       if (res <= 0) {
          Dmsg0(500, "we failed\n");
          mdb->status = (dbi_error_flag) 0;
@@ -831,23 +913,28 @@ int my_dbi_batch_end(JCR *jcr, B_DB *mdb, const char *error)
          mdb->status = (dbi_error_flag) 0;
       }
       break;
+   case SQL_TYPE_SQLITE3:
+      if(mdb) {
+         mdb->status = (dbi_error_flag) 0;
+      }
+      break;
    }
 
    Dmsg0(500, "my_dbi_batch_end finishing\n");
 
-   return true;      
+   return true;
 }
 
-/* 
- * This function is big and use a big switch.  
+/*
+ * This function is big and use a big switch.
  * In near future is better split in small functions
  * and refactory.
- * 
+ *
  */
 int my_dbi_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
 {
    int res;
-   int count=30;   
+   int count=30;
    dbi_conn_t *myconn = (dbi_conn_t *)(mdb->db);
    int (*custom_function)(void*, const char*, int) = NULL;
    char* (*custom_function_error)(void*) = NULL;
@@ -857,11 +944,11 @@ int my_dbi_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
 
    Dmsg0(500, "my_dbi_batch_insert started \n");
 
-   mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);    
+   mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1);
 
    if (ar->Digest == NULL || ar->Digest[0] == 0) {
-      digest = "0";
+      *digest = '\0';
    } else {
       digest = ar->Digest;
    }
@@ -871,33 +958,33 @@ int my_dbi_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
       db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
       db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
       len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
-                      ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
+                      ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path,
                       mdb->esc_name, ar->attr, digest);
-      
+
       if (my_dbi_query(mdb,mdb->cmd) == 1)
       {
          Dmsg0(500, "my_dbi_batch_insert failed\n");
          goto bail_out;
       }
-      
+
       Dmsg0(500, "my_dbi_batch_insert finishing\n");
-      
+
       return 1;
       break;
    case SQL_TYPE_POSTGRESQL:
       my_postgresql_copy_escape(mdb->esc_name, mdb->fname, mdb->fnl);
       my_postgresql_copy_escape(mdb->esc_path, mdb->path, mdb->pnl);
-      len = Mmsg(mdb->cmd, "%u\t%s\t%s\t%s\t%s\t%s\n", 
-                     ar->FileIndex, edit_int64(ar->JobId, ed1), mdb->esc_path, 
+      len = Mmsg(mdb->cmd, "%u\t%s\t%s\t%s\t%s\t%s\n",
+                     ar->FileIndex, edit_int64(ar->JobId, ed1), mdb->esc_path,
                      mdb->esc_name, ar->attr, digest);
-      
+
       /* libdbi don't support CopyData and we need call a postgresql
        * specific function to do this work
        */
       Dmsg2(500, "my_dbi_batch_insert :\n %s \ncmd_size: %d",mdb->cmd, len);
       if ((custom_function = (custom_function_insert_t)dbi_driver_specific_function(dbi_conn_get_driver(mdb->db),
             "PQputCopyData")) != NULL) {
-         do { 
+         do {
             res = (*custom_function)(myconn->connection, mdb->cmd, len);
          } while (res == 0 && --count > 0);
 
@@ -915,7 +1002,7 @@ int my_dbi_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
          Dmsg0(500, "my_dbi_batch_insert finishing\n");
          return mdb->status;
       } else {
-         // ensure to detect a PQerror 
+         // ensure to detect a PQerror
          custom_function_error = (custom_function_error_t)dbi_driver_specific_function(dbi_conn_get_driver(mdb->db), "PQerrorMessage");
          Dmsg1(500, "my_dbi_batch_insert failed\n PQerrorMessage: %s", (*custom_function_error)(myconn->connection));
          goto bail_out;
@@ -925,24 +1012,40 @@ int my_dbi_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
       db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
       db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
       len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
-                      ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
+                      ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path,
                       mdb->esc_name, ar->attr, digest);
       if (my_dbi_query(mdb,mdb->cmd) == 1)
       {
          Dmsg0(500, "my_dbi_batch_insert failed\n");
          goto bail_out;
       }
-      
+
       Dmsg0(500, "my_dbi_batch_insert finishing\n");
-      
+
+      return 1;
+      break;
+   case SQL_TYPE_SQLITE3:
+      db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
+      db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
+      len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
+                      ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path,
+                      mdb->esc_name, ar->attr, digest);
+      if (my_dbi_query(mdb,mdb->cmd) == 1)
+      {
+         Dmsg0(500, "my_dbi_batch_insert failed\n");
+         goto bail_out;
+      }
+
+      Dmsg0(500, "my_dbi_batch_insert finishing\n");
+
       return 1;
       break;
    }
-    
+
 bail_out:
   Mmsg1(&mdb->errmsg, _("error inserting batch mode: %s"), my_dbi_strerror(mdb));
   mdb->status = (dbi_error_flag) 0;
-  my_dbi_free_result(mdb);  
+  my_dbi_free_result(mdb);
   return mdb->status;
 }
 
@@ -1000,7 +1103,7 @@ char *my_postgresql_copy_escape(char *dest, char *src, size_t len)
  * int PQgetisnull(const PGresult *res,
  *              int row_number,
  *               int column_number);
- * 
+ *
  *  use dbi_result_seek_row to search in result set
  */
 int my_dbi_getisnull(dbi_result *result, int row_number, int column_number) {
@@ -1009,21 +1112,21 @@ int my_dbi_getisnull(dbi_result *result, int row_number, int column_number) {
    if(row_number == 0) {
       row_number++;
    }
-   
+
    column_number++;
-   
+
    if(dbi_result_seek_row(result, row_number)) {
 
       i = dbi_result_field_is_null_idx(result,column_number);
 
       return i;
    } else {
-           
+
       return 0;
    }
-                
+
 }
-/* my_dbi_getvalue 
+/* my_dbi_getvalue
  * like PQgetvalue;
  * char *PQgetvalue(const PGresult *res,
  *                int row_number,
@@ -1036,54 +1139,54 @@ char *my_dbi_getvalue(dbi_result *result, int row_number, unsigned int column_nu
 
    char *buf = NULL;
    const char *errmsg;
-   const char *field_name;     
+   const char *field_name;
    unsigned short dbitype;
    size_t field_length;
    int64_t num;
-        
+
    /* correct the index for dbi interface
     * dbi index begins 1
     * I prefer do not change others functions
     */
-   Dmsg3(600, "my_dbi_getvalue pre-starting result '%p' row number '%d' column number '%d'\n", 
+   Dmsg3(600, "my_dbi_getvalue pre-starting result '%p' row number '%d' column number '%d'\n",
                                 result, row_number, column_number);
-        
+
    column_number++;
 
    if(row_number == 0) {
      row_number++;
    }
-      
-   Dmsg3(600, "my_dbi_getvalue starting result '%p' row number '%d' column number '%d'\n", 
+
+   Dmsg3(600, "my_dbi_getvalue starting result '%p' row number '%d' column number '%d'\n",
                         result, row_number, column_number);
-   
+
    if(dbi_result_seek_row(result, row_number)) {
 
       field_name = dbi_result_get_field_name(result, column_number);
       field_length = dbi_result_get_field_length(result, field_name);
       dbitype = dbi_result_get_field_type_idx(result,column_number);
-      
+
       Dmsg3(500, "my_dbi_getvalue start: type: '%d' "
-            "field_length bytes: '%d' fieldname: '%s'\n", 
+            "field_length bytes: '%d' fieldname: '%s'\n",
             dbitype, field_length, field_name);
-      
+
       if(field_length) {
          //buf = (char *)malloc(sizeof(char *) * field_length + 1);
          buf = (char *)malloc(field_length + 1);
       } else {
          /* if numbers */
          buf = (char *)malloc(sizeof(char *) * 50);
-      }           
-      
+      }
+
       switch (dbitype) {
       case DBI_TYPE_INTEGER:
-         num = dbi_result_get_longlong(result, field_name);         
+         num = dbi_result_get_longlong(result, field_name);
          edit_int64(num, buf);
          field_length = strlen(buf);
          break;
       case DBI_TYPE_STRING:
          if(field_length) {
-            field_length = bsnprintf(buf, field_length + 1, "%s", 
+            field_length = bsnprintf(buf, field_length + 1, "%s",
             dbi_result_get_string(result, field_name));
          } else {
             buf[0] = 0;
@@ -1094,7 +1197,7 @@ char *my_dbi_getvalue(dbi_result *result, int row_number, unsigned int column_nu
          * following, change this to what Bacula espected
          */
          if(field_length) {
-            field_length = bsnprintf(buf, field_length + 1, "%s", 
+            field_length = bsnprintf(buf, field_length + 1, "%s",
                   dbi_result_get_binary(result, field_name));
          } else {
             buf[0] = 0;
@@ -1103,11 +1206,11 @@ char *my_dbi_getvalue(dbi_result *result, int row_number, unsigned int column_nu
       case DBI_TYPE_DATETIME:
          time_t last;
          struct tm tm;
-         
+
          last = dbi_result_get_datetime(result, field_name);
-         
+
          if(last == -1) {
-                field_length = bsnprintf(buf, 20, "0000-00-00 00:00:00"); 
+                field_length = bsnprintf(buf, 20, "0000-00-00 00:00:00");
          } else {
             (void)localtime_r(&last, &tm);
             field_length = bsnprintf(buf, 20, "%04d-%02d-%02d %02d:%02d:%02d",
@@ -1121,15 +1224,15 @@ char *my_dbi_getvalue(dbi_result *result, int row_number, unsigned int column_nu
       dbi_conn_error(dbi_result_get_conn(result), &errmsg);
       Dmsg1(500, "my_dbi_getvalue error: %s\n", errmsg);
    }
-                
-   Dmsg3(500, "my_dbi_getvalue finish buffer: '%p' num bytes: '%d' data: '%s'\n", 
+
+   Dmsg3(500, "my_dbi_getvalue finish buffer: '%p' num bytes: '%d' data: '%s'\n",
       buf, field_length, buf);
-      
+
    // don't worry about this buf
    return buf;
 }
 
-int my_dbi_sql_insert_id(B_DB *mdb, char *table_name)
+static uint64_t my_dbi_sequence_last(B_DB *mdb, const char *table_name)
 {
    /*
     Obtain the current value of the sequence that
@@ -1148,12 +1251,12 @@ int my_dbi_sql_insert_id(B_DB *mdb, char *table_name)
 
     everything else can use the PostgreSQL formula.
    */
-   
-   char      sequence[30];  
-   uint64_t    id = 0;
+
+   char sequence[30];
+   uint64_t id = 0;
 
    if (mdb->db_type == SQL_TYPE_POSTGRESQL) {
-           
+
       if (strcasecmp(table_name, "basefiles") == 0) {
          bstrncpy(sequence, "basefiles_baseid", sizeof(sequence));
       } else {
@@ -1168,39 +1271,73 @@ int my_dbi_sql_insert_id(B_DB *mdb, char *table_name)
    } else {
       id = dbi_conn_sequence_last(mdb->db, NT_(table_name));
    }
-   
+
    return id;
 }
 
+uint64_t my_dbi_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_dbi_query(mdb, query)) {
+      return 0;
+   }
+
+   mdb->num_rows = sql_affected_rows(mdb);
+   if (mdb->num_rows != 1) {
+      return 0;
+   }
+
+   mdb->changes++;
+
+   return my_dbi_sequence_last(mdb, table_name);
+}
+
 #ifdef HAVE_BATCH_FILE_INSERT
-const char *my_dbi_batch_lock_path_query[3] = {
+const char *my_dbi_batch_lock_path_query[5] = {
    /* Mysql */
    "LOCK TABLES Path write, batch write, Path as p write",
    /* Postgresql */
    "BEGIN; LOCK TABLE Path IN SHARE ROW EXCLUSIVE MODE",
    /* SQLite */
-   "BEGIN"};  
-
-const char *my_dbi_batch_lock_filename_query[3] = {
+   "BEGIN",
+   /* SQLite3 */
+   "BEGIN",
+   /* Ingres */
+   "BEGIN"
+};
+
+const char *my_dbi_batch_lock_filename_query[5] = {
    /* Mysql */
    "LOCK TABLES Filename write, batch write, Filename as f write",
    /* Postgresql */
    "BEGIN; LOCK TABLE Filename IN SHARE ROW EXCLUSIVE MODE",
    /* SQLite */
-   "BEGIN"};
-
-const char *my_dbi_batch_unlock_tables_query[3] = {
+   "BEGIN",
+   /* SQLite3 */
+   "BEGIN",
+   /* Ingres */
+   "BEGIN"
+};
+
+const char *my_dbi_batch_unlock_tables_query[5] = {
    /* Mysql */
    "UNLOCK TABLES",
    /* Postgresql */
    "COMMIT",
    /* SQLite */
-   "COMMIT"};
+   "COMMIT",
+   /* SQLite3 */
+   "COMMIT",
+   /* Ingres */
+   "COMMIT"
+};
 
-const char *my_dbi_batch_fill_path_query[3] = {
+const char *my_dbi_batch_fill_path_query[5] = {
    /* Mysql */
    "INSERT INTO Path (Path) "
-   "SELECT a.Path FROM " 
+   "SELECT a.Path FROM "
    "(SELECT DISTINCT Path FROM batch) AS a WHERE NOT EXISTS "
    "(SELECT Path FROM Path AS p WHERE p.Path = a.Path)",
    /* Postgresql */
@@ -1209,14 +1346,24 @@ const char *my_dbi_batch_fill_path_query[3] = {
    "(SELECT DISTINCT Path FROM batch) AS a "
    "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) ",
    /* SQLite */
-   "INSERT INTO Path (Path)" 
+   "INSERT INTO Path (Path)"
+   " SELECT DISTINCT Path FROM batch"
+   " EXCEPT SELECT Path FROM Path",
+   /* SQLite3 */
+   "INSERT INTO Path (Path)"
    " SELECT DISTINCT Path FROM batch"
-   " EXCEPT SELECT Path FROM Path"};
+   " EXCEPT SELECT Path FROM Path",
+   /* Ingres */
+   "INSERT INTO Path (Path) "
+   "SELECT a.Path FROM "
+   "(SELECT DISTINCT Path FROM batch) AS a "
+   "WHERE NOT EXISTS (SELECT Path FROM Path WHERE Path = a.Path) "
+};
 
-const char *my_dbi_batch_fill_filename_query[3] = {
+const char *my_dbi_batch_fill_filename_query[5] = {
    /* Mysql */
    "INSERT INTO Filename (Name) "
-   "SELECT a.Name FROM " 
+   "SELECT a.Name FROM "
    "(SELECT DISTINCT Name FROM batch) AS a WHERE NOT EXISTS "
    "(SELECT Name FROM Filename AS f WHERE f.Name = a.Name)",
    /* Postgresql */
@@ -1228,7 +1375,32 @@ const char *my_dbi_batch_fill_filename_query[3] = {
    /* SQLite */
    "INSERT INTO Filename (Name)"
    " SELECT DISTINCT Name FROM batch "
-   " EXCEPT SELECT Name FROM Filename"};
+   " EXCEPT SELECT Name FROM Filename",
+   /* SQLite3 */
+   "INSERT INTO Filename (Name)"
+   " SELECT DISTINCT Name FROM batch "
+   " EXCEPT SELECT Name FROM Filename",
+   /* Ingres */
+   "INSERT INTO Filename (Name) "
+   "SELECT a.Name FROM "
+   "(SELECT DISTINCT Name FROM batch) as a "
+   "WHERE NOT EXISTS "
+   "(SELECT Name FROM Filename WHERE Name = a.Name)"
+};
+
 #endif /* HAVE_BATCH_FILE_INSERT */
 
+const char *my_dbi_match[5] = {
+   /* Mysql */
+   "MATCH",
+   /* Postgresql */
+   "~",
+   /* SQLite */
+   "MATCH",
+   /* SQLite3 */
+   "MATCH",
+   /* Ingres */
+   "~"
+};
+
 #endif /* HAVE_DBI */