/*
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.
*
* 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;
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) {
}
}
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 */
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;
}
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);
}
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
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
* 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;
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;
}
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++;
mdb->fields[i] = NULL;
}
}
- if (mbd->fields) {
+ if (mdb->fields) {
free(mdb->fields);
mdb->fields = NULL;
}
{
int i, j;
if (mdb->result == NULL) {
+ mdb->field = 0;
return;
}
/* On first call, set up the fields */
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;
}
mdb->fields_defined = true;
}
- if (field > sql_num_fields(mdb) - 1) {
+ 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;
}
}
+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
const char *my_sqlite_batch_lock_query = "BEGIN";
const char *my_sqlite_batch_unlock_query = "COMMIT";