2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Catalog Database interface routines
22 * Almost generic set of SQL database interface routines
23 * (with a little more work)
24 * SQL engine specific routines are in mysql.c, postgresql.c,
27 * Written by Kern Sibbald, March 2000
29 * Note: at one point, this file was changed to class based by a certain
30 * programmer, and other than "wrapping" in a class, which is a trivial
31 * change for a C++ programmer, nothing substantial was done, yet all the
32 * code was recommitted under this programmer's name. Consequently, we
33 * undo those changes here.
38 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
42 /* Forward referenced subroutines */
43 void print_dashes(BDB *mdb);
44 void print_result(BDB *mdb);
46 dbid_list::dbid_list()
48 memset(this, 0, sizeof(dbid_list));
50 DBId = (DBId_t *)malloc(max_ids * sizeof(DBId_t));
51 num_ids = num_seen = tot_ids = 0;
55 dbid_list::~dbid_list()
61 * Called here to retrieve an string list from the database
63 int db_string_list_handler(void *ctx, int num_fields, char **row)
65 alist **val = (alist **)ctx;
68 (*val)->append(bstrdup(row[0]));
75 * Called here to retrieve an integer from the database
77 int db_int_handler(void *ctx, int num_fields, char **row)
79 uint32_t *val = (uint32_t *)ctx;
81 Dmsg1(800, "int_handler starts with row pointing at %x\n", row);
84 Dmsg1(800, "int_handler finds '%s'\n", row[0]);
85 *val = str_to_int64(row[0]);
87 Dmsg0(800, "int_handler finds zero\n");
90 Dmsg0(800, "int_handler finishes\n");
95 * Called here to retrieve a 32/64 bit integer from the database.
96 * The returned integer will be extended to 64 bit.
98 int db_int64_handler(void *ctx, int num_fields, char **row)
100 db_int64_ctx *lctx = (db_int64_ctx *)ctx;
103 lctx->value = str_to_int64(row[0]);
110 * Called here to retrieve a btime from the database.
111 * The returned integer will be extended to 64 bit.
113 int db_strtime_handler(void *ctx, int num_fields, char **row)
115 db_int64_ctx *lctx = (db_int64_ctx *)ctx;
118 lctx->value = str_to_utime(row[0]);
125 * Use to build a comma separated list of values from a query. "10,20,30"
127 int db_list_handler(void *ctx, int num_fields, char **row)
129 db_list_ctx *lctx = (db_list_ctx *)ctx;
130 if (num_fields == 1 && row[0]) {
137 * specific context passed from bdb_check_max_connections to
138 * db_max_connections_handler.
140 struct max_connections_context {
142 uint32_t nr_connections;
146 * Called here to retrieve max_connections from db
148 static int db_max_connections_handler(void *ctx, int num_fields, char **row)
150 struct max_connections_context *context;
153 context = (struct max_connections_context *)ctx;
154 switch (context->db->bdb_get_type_index()) {
162 context->nr_connections = str_to_int64(row[index]);
164 Dmsg0(800, "int_handler finds zero\n");
165 context->nr_connections = 0;
173 acl_join = get_pool_memory(PM_MESSAGE);
174 acl_where = get_pool_memory(PM_MESSAGE);
180 free_pool_memory(acl_join);
181 free_pool_memory(acl_where);
184 /* Get the WHERE section of a query that permits to respect
187 * get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), true)
188 * -> WHERE Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
190 * get_acls(DB_ACL_BIT(DB_ACL_JOB) | DB_ACL_BIT(DB_ACL_CLIENT), false)
191 * -> AND Job.Name IN ('a', 'b', 'c') AND Client.Name IN ('d', 'e')
193 char *BDB::get_acls(int tables, bool where /* use WHERE or AND */)
196 pm_strcpy(acl_where, "");
198 for (int i=0 ; i < DB_ACL_LAST; i++) {
199 if (tables & DB_ACL_BIT(i)) {
200 pm_strcat(acl_where, get_acl((DB_ACL_t)i, where));
201 where = acl_where[0] == 0 && where;
207 /* Create the JOIN string that will help to filter queries results */
208 char *BDB::get_acl_join_filter(int tables)
211 pm_strcpy(acl_join, "");
213 if (tables & DB_ACL_BIT(DB_ACL_JOB)) {
214 Mmsg(tmp, " JOIN Job USING (JobId) ");
215 pm_strcat(acl_join, tmp);
217 if (tables & (DB_ACL_BIT(DB_ACL_CLIENT) | DB_ACL_BIT(DB_ACL_RCLIENT) | DB_ACL_BIT(DB_ACL_BCLIENT))) {
218 Mmsg(tmp, " JOIN Client USING (ClientId) ");
219 pm_strcat(acl_join, tmp);
221 if (tables & DB_ACL_BIT(DB_ACL_POOL)) {
222 Mmsg(tmp, " JOIN Pool USING (PoolId) ");
223 pm_strcat(acl_join, tmp);
225 if (tables & DB_ACL_BIT(DB_ACL_PATH)) {
226 Mmsg(tmp, " JOIN Path USING (PathId) ");
227 pm_strcat(acl_join, tmp);
229 if (tables & DB_ACL_BIT(DB_ACL_LOG)) {
230 Mmsg(tmp, " JOIN Log USING (JobId) ");
231 pm_strcat(acl_join, tmp);
233 if (tables & DB_ACL_BIT(DB_ACL_FILESET)) {
234 Mmsg(tmp, " LEFT JOIN FileSet USING (FileSetId) ");
235 pm_strcat(acl_join, tmp);
240 /* Intialize the ACL list */
243 for(int i=0; i < DB_ACL_LAST; i++) {
251 for(int i=0; i < DB_ACL_LAST; i++) {
252 free_and_null_pool_memory(acls[i]);
256 /* Get ACL for a given type */
257 const char *BDB::get_acl(DB_ACL_t type, bool where /* display WHERE or AND */)
262 strcpy(acls[type], where?" WHERE ":" AND ");
263 acls[type][7] = ' ' ; /* replace \0 by ' ' */
267 /* Keep UAContext ACLs in our structure for further SQL queries */
268 void BDB::set_acl(JCR *jcr, DB_ACL_t type, alist *list, alist *list2)
270 /* If the list is present, but we authorize everything */
271 if (list && list->size() == 1 && strcasecmp((char*)list->get(0), "*all*") == 0) {
275 /* If the list is present, but we authorize everything */
276 if (list2 && list2->size() == 1 && strcasecmp((char*)list2->get(0), "*all*") == 0) {
280 POOLMEM *tmp = get_pool_memory(PM_FNAME);
281 POOLMEM *where = get_pool_memory(PM_FNAME);
286 /* For clients, we can have up to 2 lists */
287 escape_acl_list(jcr, &tmp, list);
288 escape_acl_list(jcr, &tmp, list2);
292 Mmsg(where, " AND Job.Name IN (%s) ", tmp);
295 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
298 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
301 Mmsg(where, " AND Client.Name IN (%s) ", tmp);
304 Mmsg(where, " AND (FileSetId = 0 OR FileSet.FileSet IN (%s)) ", tmp);
307 Mmsg(where, " AND (PoolId = 0 OR Pool.Name IN (%s)) ", tmp);
313 free_pool_memory(tmp);
316 /* Convert a ACL list to a SQL IN() list */
317 char *BDB::escape_acl_list(JCR *jcr, POOLMEM **escaped_list, alist *lst)
324 return *escaped_list; /* TODO: check how we handle the empty list */
326 /* List is empty, reject everything */
327 } else if (lst->size() == 0) {
328 Mmsg(escaped_list, "''");
329 return *escaped_list;
332 foreach_alist(elt, lst) {
336 tmp.check_size(2 * len + 2 + 2);
340 bdb_escape_string(jcr, tmp.c_str() + 1 , elt, len);
344 if (*escaped_list[0]) {
345 pm_strcat(escaped_list, ",");
348 pm_strcat(escaped_list, tmp.c_str());
351 return *escaped_list;
355 * Check catalog max_connections setting
357 bool BDB::bdb_check_max_connections(JCR *jcr, uint32_t max_concurrent_jobs)
359 struct max_connections_context context;
361 /* Without Batch insert, no need to verify max_connections */
362 if (!batch_insert_available())
366 context.nr_connections = 0;
368 /* Check max_connections setting */
369 if (!bdb_sql_query(sql_get_max_connections[bdb_get_type_index()],
370 db_max_connections_handler, &context)) {
371 Jmsg(jcr, M_ERROR, 0, "Can't verify max_connections settings %s", errmsg);
374 if (context.nr_connections && max_concurrent_jobs && max_concurrent_jobs > context.nr_connections) {
376 _("Potential performance problem:\n"
377 "max_connections=%d set for %s database \"%s\" should be larger than Director's "
378 "MaxConcurrentJobs=%d\n"),
379 context.nr_connections, bdb_get_engine_name(), get_db_name(), max_concurrent_jobs);
380 Jmsg(jcr, M_WARNING, 0, "%s", errmsg);
387 /* NOTE!!! The following routines expect that the
388 * calling subroutine sets and clears the mutex
391 /* Check that the tables correspond to the version we want */
392 bool BDB::bdb_check_version(JCR *jcr)
394 uint32_t bacula_db_version = 0;
395 const char *query = "SELECT VersionId FROM Version";
397 bacula_db_version = 0;
398 if (!bdb_sql_query(query, db_int_handler, (void *)&bacula_db_version)) {
399 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
402 if (bacula_db_version != BDB_VERSION) {
403 Mmsg(errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
404 get_db_name(), BDB_VERSION, bacula_db_version);
405 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
412 * Utility routine for queries. The database MUST be locked before calling here.
413 * Returns: 0 on failure
416 bool BDB::QueryDB(JCR *jcr, char *cmd, const char *file, int line)
419 if (!sql_query(cmd, QF_STORE_RESULT)) {
420 m_msg(file, line, &errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror());
421 if (use_fatal_jmsg()) {
422 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
425 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
434 * Utility routine to do inserts
435 * Returns: 0 on failure
438 bool BDB::InsertDB(JCR *jcr, char *cmd, const char *file, int line)
440 if (!sql_query(cmd)) {
441 m_msg(file, line, &errmsg, _("insert %s failed:\n%s\n"), cmd, sql_strerror());
442 if (use_fatal_jmsg()) {
443 j_msg(file, line, jcr, M_FATAL, 0, "%s", errmsg);
446 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
450 int num_rows = sql_affected_rows();
453 m_msg(file, line, &errmsg, _("Insertion problem: affected_rows=%s\n"),
454 edit_uint64(num_rows, ed1));
456 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
464 /* Utility routine for updates.
465 * Returns: false on failure
468 * Some UPDATE queries must update record(s), other queries might not update
471 bool BDB::UpdateDB(JCR *jcr, char *cmd, bool can_be_empty,
472 const char *file, int line)
474 if (!sql_query(cmd)) {
475 m_msg(file, line, &errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror());
476 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
478 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
482 int num_rows = sql_affected_rows();
483 if ((num_rows == 0 && !can_be_empty) || num_rows < 0) {
485 m_msg(file, line, &errmsg, _("Update failed: affected_rows=%s for %s\n"),
486 edit_uint64(num_rows, ed1), cmd);
488 // j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
496 /* Utility routine for deletes
498 * Returns: -1 on error
499 * n number of rows affected
501 int BDB::DeleteDB(JCR *jcr, char *cmd, const char *file, int line)
504 if (!sql_query(cmd)) {
505 m_msg(file, line, &errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror());
506 j_msg(file, line, jcr, M_ERROR, 0, "%s", errmsg);
508 j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
513 return sql_affected_rows();
518 * Get record max. Query is already in mdb->cmd
521 * Returns: -1 on failure
524 int get_sql_record_max(JCR *jcr, BDB *mdb)
529 if (mdb->QueryDB(jcr, mdb->cmd)) {
530 if ((row = mdb->sql_fetch_row()) == NULL) {
531 Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror());
534 stat = str_to_int64(row[0]);
536 mdb->sql_free_result();
538 Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), mdb->sql_strerror());
545 * Given a full filename, split it into its path
546 * and filename parts. They are returned in pool memory
547 * in the mdb structure.
549 void split_path_and_file(JCR *jcr, BDB *mdb, const char *afname)
553 /* Find path without the filename.
554 * I.e. everything after the last / is a "filename".
555 * OK, maybe it is a directory name, but we treat it like
556 * a filename. If we don't find a / then the whole name
557 * must be a path name (e.g. c:).
559 for (p=f=afname; *p; p++) {
560 if (IsPathSeparator(*p)) {
561 f = p; /* set pos of last slash */
564 if (IsPathSeparator(*f)) { /* did we find a slash? */
565 f++; /* yes, point to filename */
566 } else { /* no, whole thing must be path name */
570 /* If filename doesn't exist (i.e. root directory), we
571 * simply create a blank name consisting of a single
572 * space. This makes handling zero length filenames
577 mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
578 memcpy(mdb->fname, f, mdb->fnl); /* copy filename */
579 mdb->fname[mdb->fnl] = 0;
585 mdb->pnl = f - afname;
587 mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
588 memcpy(mdb->path, afname, mdb->pnl);
589 mdb->path[mdb->pnl] = 0;
591 Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), afname);
592 Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
597 Dmsg3(500, "split fname=%s: path=%s file=%s\n", afname, mdb->path, mdb->fname);
601 * Set maximum field length to something reasonable
603 static int max_length(int max_length)
605 int max_len = max_length;
609 } else if (max_len > 100) {
616 * List dashes as part of header for listing SQL results in a table
619 list_dashes(BDB *mdb, DB_LIST_HANDLER *send, void *ctx)
625 mdb->sql_field_seek(0);
627 for (i = 0; i < mdb->sql_num_fields(); i++) {
628 field = mdb->sql_fetch_field();
632 len = max_length(field->max_length + 2);
633 for (j = 0; j < len; j++) {
641 /* Small handler to print the last line of a list xxx command */
642 static void last_line_handler(void *vctx, const char *str)
644 LIST_CTX *ctx = (LIST_CTX *)vctx;
645 bstrncat(ctx->line, str, sizeof(ctx->line));
648 int list_result(void *vctx, int nb_col, char **row)
651 int i, col_len, max_len = 0;
652 char buf[2000], ewc[30];
654 LIST_CTX *pctx = (LIST_CTX *)vctx;
655 DB_LIST_HANDLER *send = pctx->send;
656 e_list_type type = pctx->type;
657 BDB *mdb = pctx->mdb;
658 void *ctx = pctx->ctx;
659 JCR *jcr = pctx->jcr;
664 Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields());
665 /* determine column display widths */
666 mdb->sql_field_seek(0);
667 for (i = 0; i < mdb->sql_num_fields(); i++) {
668 Dmsg1(800, "list_result processing field %d\n", i);
669 field = mdb->sql_fetch_field();
673 col_len = cstrlen(field->name);
674 if (type == VERT_LIST) {
675 if (col_len > max_len) {
679 if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */
680 field->max_length += (field->max_length - 1) / 3;
682 if (col_len < (int)field->max_length) {
683 col_len = field->max_length;
685 if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) {
686 col_len = 4; /* 4 = length of the word "NULL" */
688 field->max_length = col_len; /* reset column info */
694 Dmsg0(800, "list_result finished first loop\n");
695 if (type == VERT_LIST) {
698 if (type == ARG_LIST) {
702 Dmsg1(800, "list_result starts second loop looking at %d fields\n",
703 mdb->sql_num_fields());
705 /* Keep the result to display the same line at the end of the table */
706 list_dashes(mdb, last_line_handler, pctx);
707 send(ctx, pctx->line);
710 mdb->sql_field_seek(0);
711 for (i = 0; i < mdb->sql_num_fields(); i++) {
712 Dmsg1(800, "list_result looking at field %d\n", i);
713 field = mdb->sql_fetch_field();
717 max_len = max_length(field->max_length);
718 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
722 list_dashes(mdb, send, ctx);
724 Dmsg1(800, "list_result starts third loop looking at %d fields\n",
725 mdb->sql_num_fields());
726 mdb->sql_field_seek(0);
728 for (i = 0; i < mdb->sql_num_fields(); i++) {
729 field = mdb->sql_fetch_field();
733 max_len = max_length(field->max_length);
734 if (row[i] == NULL) {
735 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
736 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
737 bsnprintf(buf, sizeof(buf), " %*s |", max_len,
738 add_commas(row[i], ewc));
740 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
749 Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields());
750 mdb->sql_field_seek(0);
751 for (i = 0; i < mdb->sql_num_fields(); i++) {
752 field = mdb->sql_fetch_field();
756 if (row[i] == NULL) {
757 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
758 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
759 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
760 add_commas(row[i], ewc));
762 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
770 Dmsg1(800, "list_result starts simple list at %d fields\n", mdb->sql_num_fields());
771 mdb->sql_field_seek(0);
772 for (i = 0; i < mdb->sql_num_fields(); i++) {
773 field = mdb->sql_fetch_field();
777 if (row[i] == NULL) {
778 bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name);
781 bsnprintf(buf, sizeof(buf), "%s%s=%s ", (i>0?" ":""), field->name, row[i]);
791 * If full_list is set, we list vertically, otherwise, we
792 * list on one line horizontally.
793 * Return number of rows
796 list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type)
800 int i, col_len, max_len = 0;
801 char buf[2000], ewc[30];
803 Dmsg0(800, "list_result starts\n");
804 if (mdb->sql_num_rows() == 0) {
805 send(ctx, _("No results to list.\n"));
806 return mdb->sql_num_rows();
809 Dmsg1(800, "list_result starts looking at %d fields\n", mdb->sql_num_fields());
810 /* determine column display widths */
811 mdb->sql_field_seek(0);
812 for (i = 0; i < mdb->sql_num_fields(); i++) {
813 Dmsg1(800, "list_result processing field %d\n", i);
814 field = mdb->sql_fetch_field();
818 col_len = cstrlen(field->name);
819 if (type == VERT_LIST) {
820 if (col_len > max_len) {
824 if (mdb->sql_field_is_numeric(field->type) && (int)field->max_length > 0) { /* fixup for commas */
825 field->max_length += (field->max_length - 1) / 3;
827 if (col_len < (int)field->max_length) {
828 col_len = field->max_length;
830 if (col_len < 4 && !mdb->sql_field_is_not_null(field->flags)) {
831 col_len = 4; /* 4 = length of the word "NULL" */
833 field->max_length = col_len; /* reset column info */
837 Dmsg0(800, "list_result finished first loop\n");
838 if (type == VERT_LIST) {
841 if (type == ARG_LIST) {
845 Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields());
846 list_dashes(mdb, send, ctx);
848 mdb->sql_field_seek(0);
849 for (i = 0; i < mdb->sql_num_fields(); i++) {
850 Dmsg1(800, "list_result looking at field %d\n", i);
851 field = mdb->sql_fetch_field();
855 max_len = max_length(field->max_length);
856 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name);
860 list_dashes(mdb, send, ctx);
862 Dmsg1(800, "list_result starts third loop looking at %d fields\n", mdb->sql_num_fields());
863 while ((row = mdb->sql_fetch_row()) != NULL) {
864 mdb->sql_field_seek(0);
866 for (i = 0; i < mdb->sql_num_fields(); i++) {
867 field = mdb->sql_fetch_field();
871 max_len = max_length(field->max_length);
872 if (row[i] == NULL) {
873 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL");
874 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
875 bsnprintf(buf, sizeof(buf), " %*s |", max_len,
876 add_commas(row[i], ewc));
878 strip_trailing_junk(row[i]);
879 bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]);
885 list_dashes(mdb, send, ctx);
886 return mdb->sql_num_rows();
890 Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields());
891 while ((row = mdb->sql_fetch_row()) != NULL) {
892 mdb->sql_field_seek(0);
893 for (i = 0; i < mdb->sql_num_fields(); i++) {
894 field = mdb->sql_fetch_field();
898 if (row[i] == NULL) {
899 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL");
900 } else if (mdb->sql_field_is_numeric(field->type) && !jcr->gui && is_an_integer(row[i])) {
901 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name,
902 add_commas(row[i], ewc));
904 strip_trailing_junk(row[i]);
905 bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]);
914 Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields());
915 while ((row = mdb->sql_fetch_row()) != NULL) {
916 mdb->sql_field_seek(0);
917 for (i = 0; i < mdb->sql_num_fields(); i++) {
918 field = mdb->sql_fetch_field();
922 if (row[i] == NULL) {
923 bsnprintf(buf, sizeof(buf), "%s%s=", (i>0?" ":""), field->name);
926 bsnprintf(buf, sizeof(buf), "%s%s=%s", (i>0?" ":""), field->name, row[i]);
932 return mdb->sql_num_rows();
936 * Open a new connexion to mdb catalog. This function is used
937 * by batch and accurate mode.
939 bool BDB::bdb_open_batch_connexion(JCR *jcr)
943 multi_db = batch_insert_available();
945 if (!jcr->db_batch) {
946 jcr->db_batch = bdb_clone_database_connection(jcr, multi_db);
947 if (!jcr->db_batch) {
948 Mmsg0(&errmsg, _("Could not init database batch connection\n"));
949 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
953 if (!jcr->db_batch->bdb_open_database(jcr)) {
954 Mmsg2(&errmsg, _("Could not open database \"%s\": ERR=%s\n"),
955 jcr->db_batch->get_db_name(), jcr->db_batch->bdb_strerror());
956 Jmsg(jcr, M_FATAL, 0, "%s", errmsg);
964 * !!! WARNING !!! Use this function only when bacula is stopped.
965 * ie, after a fatal signal and before exiting the program
966 * Print information about a BDB object.
968 void bdb_debug_print(JCR *jcr, FILE *fp)
976 fprintf(fp, "BDB=%p db_name=%s db_user=%s connected=%s\n",
977 mdb, NPRTB(mdb->get_db_name()), NPRTB(mdb->get_db_user()), mdb->is_connected() ? "true" : "false");
978 fprintf(fp, "\tcmd=\"%s\" changes=%i\n", NPRTB(mdb->cmd), mdb->changes);
979 mdb->print_lock_info(fp);
982 bool BDB::bdb_check_settings(JCR *jcr, int64_t *starttime, int val, int64_t val2)
987 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */