/*
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2009 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.
*
* Kern Sibbald, March 2000
*
- * Version $Id$
+ * Version $Id: sql.c 8034 2008-11-11 14:33:46Z ricozz $
*/
/* The following is necessary so that we do not include
#include "bacula.h"
#include "cats.h"
-#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI
+#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
uint32_t bacula_db_version = 0;
db_type = SQL_TYPE_SQLITE;
} else if (strcasecmp(p, "sqlite3") == 0) {
db_type = SQL_TYPE_SQLITE3;
+ } else if (strcasecmp(p, "ingres") == 0) {
+ db_type = SQL_TYPE_INGRES;
} else {
Jmsg1(jcr, M_ABORT, 0, _("Unknown database type: %s\n"), p);
}
db_type = SQL_TYPE_MYSQL;
#elif HAVE_POSTGRESQL
db_type = SQL_TYPE_POSTGRESQL;
+#elif HAVE_INGRES
+ db_type = SQL_TYPE_INGRES;
#elif HAVE_SQLITE
db_type = SQL_TYPE_SQLITE;
#elif HAVE_SQLITE3
free(DBId);
}
-
/*
* Called here to retrieve an integer from the database
*/
-static int int_handler(void *ctx, int num_fields, char **row)
+int db_int_handler(void *ctx, int num_fields, char **row)
{
uint32_t *val = (uint32_t *)ctx;
return 0;
}
+/*
+ * Use to build a comma separated list of values from a query. "10,20,30"
+ */
+int db_list_handler(void *ctx, int num_fields, char **row)
+{
+ db_list_ctx *lctx = (db_list_ctx *)ctx;
+ if (num_fields == 1 && row[0]) {
+ if (lctx->list[0]) {
+ pm_strcat(lctx->list, ",");
+ }
+ pm_strcat(lctx->list, row[0]);
+ lctx->count++;
+ }
+ return 0;
+}
+
+/*
+ * Called here to retrieve an integer from the database
+ */
+static int db_max_connections_handler(void *ctx, int num_fields, char **row)
+{
+ uint32_t *val = (uint32_t *)ctx;
+ uint32_t index = sql_get_max_connections_index[db_type];
+ if (row[index]) {
+ *val = str_to_int64(row[index]);
+ } else {
+ Dmsg0(800, "int_handler finds zero\n");
+ *val = 0;
+ }
+ return 0;
+}
+
+/*
+ * Check catalog max_connections setting
+ */
+bool db_check_max_connections(JCR *jcr, B_DB *mdb, uint32_t max_concurrent_jobs)
+{
+ uint32_t max_conn=0;
+ int ret=true;
+
+ /* Without Batch insert, no need to verify max_connections */
+#ifndef HAVE_BATCH_FILE_INSERT
+ return ret;
+#endif
+
+ /* Check max_connections setting */
+ if (!db_sql_query(mdb, sql_get_max_connections[db_type],
+ db_max_connections_handler, &max_conn)) {
+ Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", mdb->errmsg);
+ return ret;
+ }
+ if (max_conn && max_concurrent_jobs && max_concurrent_jobs > max_conn) {
+ Mmsg(mdb->errmsg,
+ _("Potential performance problem:\n"
+ "max_connections=%d set for %s database \"%s\" should be larger than Director's "
+ "MaxConcurrentJobs=%d\n"),
+ max_conn, db_get_type(), mdb->db_name, max_concurrent_jobs);
+ Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
+ ret = false;
+ }
+
+ return ret;
+}
/* NOTE!!! The following routines expect that the
* calling subroutine sets and clears the mutex
const char *query = "SELECT VersionId FROM Version";
bacula_db_version = 0;
- if (!db_sql_query(mdb, query, int_handler, (void *)&bacula_db_version)) {
+ if (!db_sql_query(mdb, query, db_int_handler, (void *)&bacula_db_version)) {
Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
return false;
}
return mdb->errmsg;
}
-static void update_lock_dbg(B_DB *mdb)
-{
-#ifdef xxx
- if (mdb->allow_transactions) { /* batch connection */
- return;
- }
- if (_db_lock_recurse_count && !pthread_equal(_db_lock_threadid, pthread_self())) {
- Dmsg2(1, "ERROR: not the same threadif %p != %p\n", _db_lock_threadid, pthread_self());
- }
- _db_lock_recurse_count++;
- _db_lock_time = (utime_t) time(NULL);
- _db_lock_threadid = pthread_self();
-#endif
-}
-
-static void update_unlock_dbg(B_DB *mdb)
-{
-#ifdef xxx
- if (mdb->allow_transactions) { /* batch connection */
- return;
- }
- if (!pthread_equal(_db_lock_threadid, pthread_self())) {
- Dmsg2(1, "ERROR: not the same threadid %p != %p", _db_lock_threadid, pthread_self());
- }
- _db_lock_recurse_count--;
- if (!_db_lock_recurse_count) {
- memset(&_db_lock_threadid, 0, sizeof(_db_lock_threadid));
- }
-#endif
-}
-
/*
* Lock database, this can be called multiple times by the same
* thread without blocking, but must be unlocked the number of
void _db_lock(const char *file, int line, B_DB *mdb)
{
int errstat;
- if ((errstat=rwl_writelock(&mdb->lock)) != 0) {
+ if ((errstat=rwl_writelock_p(&mdb->lock, file, line)) != 0) {
berrno be;
e_msg(file, line, M_FATAL, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
errstat, be.bstrerror(errstat));
}
- update_lock_dbg(mdb);
}
/*
void _db_unlock(const char *file, int line, B_DB *mdb)
{
int errstat;
- update_unlock_dbg(mdb);
if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) {
berrno be;
e_msg(file, line, M_FATAL, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
db_unlock(mdb);
#endif
+#ifdef HAVE_INGRES
+ if (!mdb->allow_transactions) {
+ return;
+ }
+ db_lock(mdb);
+ /* Allow only 25,000 changes per transaction */
+ if (mdb->transaction && mdb->changes > 25000) {
+ db_end_transaction(jcr, mdb);
+ }
+ if (!mdb->transaction) {
+ db_sql_query(mdb, "BEGIN", NULL, NULL); /* begin transaction */
+ Dmsg0(400, "Start Ingres transaction\n");
+ mdb->transaction = 1;
+ }
+ db_unlock(mdb);
+#endif
+
#ifdef HAVE_DBI
if (db_type == SQL_TYPE_SQLITE) {
if (!mdb->allow_transactions) {
if (jcr && jcr->cached_attribute) {
Dmsg0(400, "Flush last cached attribute.\n");
- if (!db_create_file_attributes_record(jcr, mdb, jcr->ar)) {
+ if (!db_create_attributes_record(jcr, mdb, jcr->ar)) {
Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
}
jcr->cached_attribute = false;
db_unlock(mdb);
#endif
+
+
+#ifdef HAVE_INGRES
+ if (!mdb->allow_transactions) {
+ return;
+ }
+ db_lock(mdb);
+ if (mdb->transaction) {
+ db_sql_query(mdb, "COMMIT", NULL, NULL); /* end transaction */
+ mdb->transaction = 0;
+ Dmsg1(400, "End Ingres transaction changes=%d\n", mdb->changes);
+ }
+ mdb->changes = 0;
+ db_unlock(mdb);
+#endif
+
+
#ifdef HAVE_POSTGRESQL
if (!mdb->allow_transactions) {
return;
Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname);
}
+/*
+ * Set maximum field length to something reasonable
+ */
+static int max_length(int max_length)
+{
+ int max_len = max_length;
+ /* Sanity check */
+ if (max_len < 0) {
+ max_len = 2;
+ } else if (max_len > 100) {
+ max_len = 100;
+ }
+ return max_len;
+}
+
/*
* List dashes as part of header for listing SQL results in a table
*/
{
SQL_FIELD *field;
int i, j;
+ int len;
sql_field_seek(mdb, 0);
send(ctx, "+");
if (!field) {
break;
}
- for (j = 0; j < (int)field->max_length + 2; j++) {
+ len = max_length(field->max_length + 2);
+ for (j = 0; j < len; j++) {
send(ctx, "-");
}
send(ctx, "+");
if (!field) {
break;
}
- bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, field->name);
+ max_len = max_length(field->max_length);
+ bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
send(ctx, buf);
}
send(ctx, "\n");
if (!field) {
break;
}
+ max_len = max_length(field->max_length);
if (row[i] == NULL) {
- bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, "NULL");
+ bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
} else if (IS_NUM(field->type) && !jcr->gui && is_an_integer(row[i])) {
- bsnprintf(buf, sizeof(buf), " %*s |", (int)field->max_length,
+ bsnprintf(buf, sizeof(buf), " %*s |", max_len,
add_commas(row[i], ewc));
} else {
- bsnprintf(buf, sizeof(buf), " %-*s |", (int)field->max_length, row[i]);
+ bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
}
send(ctx, buf);
}
*/
bool db_open_batch_connexion(JCR *jcr, B_DB *mdb)
{
- int multi_db=false;
-
#ifdef HAVE_BATCH_FILE_INSERT
- multi_db=true; /* we force a new connexion only if batch insert is enabled */
+ const int multi_db = true; /* we force a new connection only if batch insert is enabled */
+#else
+ const int multi_db = false;
#endif
if (!jcr->db_batch) {
mdb->db_socket,
multi_db /* multi_db = true when using batch mode */);
if (!jcr->db_batch) {
- Jmsg0(jcr, M_FATAL, 0, "Could not init batch connexion");
+ Mmsg0(&mdb->errmsg, _("Could not init database batch connection"));
+ Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
return false;
}
if (!db_open_database(jcr, jcr->db_batch)) {
- Mmsg2(&jcr->db_batch->errmsg, _("Could not open database \"%s\": ERR=%s\n"),
+ Mmsg2(&mdb->errmsg, _("Could not open database \"%s\": ERR=%s\n"),
jcr->db_batch->db_name, db_strerror(jcr->db_batch));
- Jmsg1(jcr, M_FATAL, 0, "%s", jcr->db_batch->errmsg);
+ Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
return false;
}
Dmsg3(100, "initdb ref=%d connected=%d db=%p\n", jcr->db_batch->ref_count,
return true;
}
-#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL*/
+/*
+ * !!! WARNING !!! Use this function only when bacula is stopped.
+ * ie, after a fatal signal and before exiting the program
+ * Print information about a B_DB object.
+ */
+void db_debug_print(JCR *jcr, FILE *fp)
+{
+ B_DB *mdb = jcr->db;
+
+ if (!mdb) {
+ return;
+ }
+
+ fprintf(fp, "B_DB=%p db_name=%s db_user=%s connected=%i\n",
+ mdb, NPRTB(mdb->db_name), NPRTB(mdb->db_user), mdb->connected);
+ fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
+ if (mdb->lock.valid == RWLOCK_VALID) {
+ fprintf(fp, "\tRWLOCK=%p w_active=%i w_wait=%i\n", &mdb->lock, mdb->lock.w_active, mdb->lock.w_wait);
+ }
+}
+
+#endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_INGRES*/