2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Bacula Catalog Database routines specific to MySQL
30 * These are MySQL specific routines -- hopefully all
31 * other files are generic.
33 * Kern Sibbald, March 2000
35 * Major rewrite by Marco van Wieringen, January 2010 for catalog refactoring.
45 #include <bdb_mysql.h>
47 /* -----------------------------------------------------------------------
49 * MySQL dependent defines and subroutines
51 * -----------------------------------------------------------------------
55 * List of open databases
57 static dlist *db_list = NULL;
59 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
61 B_DB_MYSQL::B_DB_MYSQL(JCR *jcr,
62 const char *db_driver,
65 const char *db_password,
66 const char *db_address,
68 const char *db_socket,
69 bool mult_db_connections,
70 bool disable_batch_insert)
73 * Initialize the parent class members.
75 m_db_interface_type = SQL_INTERFACE_TYPE_MYSQL;
76 m_db_type = SQL_TYPE_MYSQL;
77 m_db_driver = bstrdup("MySQL");
78 m_db_name = bstrdup(db_name);
79 m_db_user = bstrdup(db_user);
81 m_db_password = bstrdup(db_password);
84 m_db_address = bstrdup(db_address);
87 m_db_socket = bstrdup(db_socket);
91 if (disable_batch_insert) {
92 m_disabled_batch_insert = true;
93 m_have_batch_insert = false;
95 m_disabled_batch_insert = false;
96 #if defined(USE_BATCH_FILE_INSERT)
97 # if defined(HAVE_MYSQL_THREAD_SAFE)
98 m_have_batch_insert = mysql_thread_safe();
100 m_have_batch_insert = false;
101 # endif /* HAVE_MYSQL_THREAD_SAFE */
103 m_have_batch_insert = false;
104 #endif /* USE_BATCH_FILE_INSERT */
106 errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */
108 cmd = get_pool_memory(PM_EMSG); /* get command buffer */
109 cached_path = get_pool_memory(PM_FNAME);
112 fname = get_pool_memory(PM_FNAME);
113 path = get_pool_memory(PM_FNAME);
114 esc_name = get_pool_memory(PM_FNAME);
115 esc_path = get_pool_memory(PM_FNAME);
116 esc_obj = get_pool_memory(PM_FNAME);
117 m_allow_transactions = mult_db_connections;
120 * Initialize the private members.
126 * Put the db in the list.
128 if (db_list == NULL) {
129 db_list = New(dlist(this, &this->m_link));
131 db_list->append(this);
134 B_DB_MYSQL::~B_DB_MYSQL()
139 * Now actually open the database. This can generate errors,
140 * which are returned in the errmsg
142 * DO NOT close the database or delete mdb here !!!!
144 bool B_DB_MYSQL::db_open_database(JCR *jcr)
155 if ((errstat=rwl_init(&m_lock)) != 0) {
157 Mmsg1(&errmsg, _("Unable to initialize DB lock. ERR=%s\n"),
158 be.bstrerror(errstat));
163 * Connect to the database
165 #ifdef xHAVE_EMBEDDED_MYSQL
166 // mysql_server_init(0, NULL, NULL);
168 mysql_init(&m_instance);
170 Dmsg0(50, "mysql_init done\n");
172 * If connection fails, try at 5 sec intervals for 30 seconds.
174 for (int retry=0; retry < 6; retry++) {
175 m_db_handle = mysql_real_connect(
176 &(m_instance), /* db */
177 m_db_address, /* default = localhost */
178 m_db_user, /* login name */
179 m_db_password, /* password */
180 m_db_name, /* database name */
181 m_db_port, /* default port */
182 m_db_socket, /* default = socket */
183 CLIENT_FOUND_ROWS); /* flags */
186 * If no connect, try once more in case it is a timing problem
188 if (m_db_handle != NULL) {
194 m_instance.reconnect = 1; /* so connection does not timeout */
195 Dmsg0(50, "mysql_real_connect done\n");
196 Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", m_db_user, m_db_name,
197 (m_db_password == NULL) ? "(NULL)" : m_db_password);
199 if (m_db_handle == NULL) {
200 Mmsg2(&errmsg, _("Unable to connect to MySQL server.\n"
201 "Database=%s User=%s\n"
202 "MySQL connect failed either server not running or your authorization is incorrect.\n"),
203 m_db_name, m_db_user);
204 #if MYSQL_VERSION_ID >= 40101
205 Dmsg3(50, "Error %u (%s): %s\n",
206 mysql_errno(&(m_instance)), mysql_sqlstate(&(m_instance)),
207 mysql_error(&(m_instance)));
209 Dmsg2(50, "Error %u: %s\n",
210 mysql_errno(&(m_instance)), mysql_error(&(m_instance)));
216 if (!check_tables_version(jcr, this)) {
220 Dmsg3(100, "opendb ref=%d connected=%d db=%p\n", m_ref_count, m_connected, m_db_handle);
223 * Set connection timeout to 8 days specialy for batch mode
225 sql_query("SET wait_timeout=691200");
226 sql_query("SET interactive_timeout=691200");
235 void B_DB_MYSQL::db_close_database(JCR *jcr)
237 db_end_transaction(jcr);
241 Dmsg3(100, "closedb ref=%d connected=%d db=%p\n", m_ref_count, m_connected, m_db_handle);
242 if (m_ref_count == 0) {
243 db_list->remove(this);
245 Dmsg1(100, "close db=%p\n", m_db_handle);
246 mysql_close(&m_instance);
248 #ifdef xHAVE_EMBEDDED_MYSQL
249 // mysql_server_end();
252 rwl_destroy(&m_lock);
253 free_pool_memory(errmsg);
254 free_pool_memory(cmd);
255 free_pool_memory(cached_path);
256 free_pool_memory(fname);
257 free_pool_memory(path);
258 free_pool_memory(esc_name);
259 free_pool_memory(esc_path);
260 free_pool_memory(esc_obj);
280 if (db_list->size() == 0) {
289 * This call is needed because the message channel thread
290 * opens a database on behalf of a jcr that was created in
291 * a different thread. MySQL then allocates thread specific
292 * data, which is NOT freed when the original jcr thread
293 * closes the database. Thus the msgchan must call here
294 * to cleanup any thread specific data that it created.
296 void B_DB_MYSQL::db_thread_cleanup(void)
304 * Escape strings so that MySQL is happy
306 * NOTE! len is the length of the old string. Your new
307 * string must be long enough (max 2*old+1) to hold
308 * the escaped output.
310 void B_DB_MYSQL::db_escape_string(JCR *jcr, char *snew, char *old, int len)
312 mysql_real_escape_string(m_db_handle, snew, old, len);
316 * Escape binary object so that MySQL is happy
317 * Memory is stored in B_DB struct, no need to free it
319 char *B_DB_MYSQL::db_escape_object(JCR *jcr, char *old, int len)
321 esc_obj = check_pool_memory_size(esc_obj, len*2+1);
322 mysql_real_escape_string(m_db_handle, esc_obj, old, len);
327 * Unescape binary object so that MySQL is happy
329 void B_DB_MYSQL::db_unescape_object(JCR *jcr, char *from, int32_t expected_len,
330 POOLMEM **dest, int32_t *dest_len)
337 *dest = check_pool_memory_size(*dest, expected_len+1);
338 *dest_len = expected_len;
339 memcpy(*dest, from, expected_len);
340 (*dest)[expected_len]=0;
343 void B_DB_MYSQL::db_start_transaction(JCR *jcr)
346 jcr->attr = get_pool_memory(PM_FNAME);
349 jcr->ar = (ATTR_DBR *)malloc(sizeof(ATTR_DBR));
353 void B_DB_MYSQL::db_end_transaction(JCR *jcr)
355 if (jcr && jcr->cached_attribute) {
356 Dmsg0(400, "Flush last cached attribute.\n");
357 if (!db_create_attributes_record(jcr, this, jcr->ar)) {
358 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
360 jcr->cached_attribute = false;
365 * Submit a general SQL command (cmd), and for each row returned,
366 * the result_handler is called with the ctx.
368 bool B_DB_MYSQL::db_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx)
375 Dmsg1(500, "db_sql_query starts with %s\n", query);
378 ret = mysql_query(m_db_handle, query);
380 Mmsg(errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror());
381 Dmsg0(500, "db_sql_query failed\n");
385 Dmsg0(500, "db_sql_query succeeded. checking handler\n");
387 if (result_handler != NULL) {
388 if ((m_result = mysql_use_result(m_db_handle)) != NULL) {
389 m_num_fields = mysql_num_fields(m_result);
392 * We *must* fetch all rows
394 while ((row = mysql_fetch_row(m_result)) != NULL) {
396 /* the result handler returns 1 when it has
397 * seen all the data it wants. However, we
398 * loop to the end of the data.
400 if (result_handler(ctx, m_num_fields, row)) {
409 Dmsg0(500, "db_sql_query finished\n");
417 bool B_DB_MYSQL::sql_query(const char *query, int flags)
422 Dmsg1(500, "sql_query starts with '%s'\n", query);
424 * We are starting a new query. reset everything.
431 mysql_free_result(m_result);
435 ret = mysql_query(m_db_handle, query);
437 Dmsg0(500, "we have a result\n");
438 if (flags & QF_STORE_RESULT) {
439 m_result = mysql_store_result(m_db_handle);
440 if (m_result != NULL) {
441 m_num_fields = mysql_num_fields(m_result);
442 Dmsg1(500, "we have %d fields\n", m_num_fields);
443 m_num_rows = mysql_num_rows(m_result);
444 Dmsg1(500, "we have %d rows\n", m_num_rows);
447 m_num_rows = mysql_affected_rows(m_db_handle);
448 Dmsg1(500, "we have %d rows\n", m_num_rows);
452 m_num_rows = mysql_affected_rows(m_db_handle);
453 Dmsg1(500, "we have %d rows\n", m_num_rows);
456 Dmsg0(500, "we failed\n");
457 m_status = 1; /* failed */
463 void B_DB_MYSQL::sql_free_result(void)
467 mysql_free_result(m_result);
474 m_num_rows = m_num_fields = 0;
478 SQL_ROW B_DB_MYSQL::sql_fetch_row(void)
483 return mysql_fetch_row(m_result);
487 const char *B_DB_MYSQL::sql_strerror(void)
489 return mysql_error(m_db_handle);
492 void B_DB_MYSQL::sql_data_seek(int row)
494 return mysql_data_seek(m_result, row);
497 int B_DB_MYSQL::sql_affected_rows(void)
499 return mysql_affected_rows(m_db_handle);
502 uint64_t B_DB_MYSQL::sql_insert_autokey_record(const char *query, const char *table_name)
505 * First execute the insert query and then retrieve the currval.
507 if (mysql_query(m_db_handle, query) != 0) {
511 m_num_rows = mysql_affected_rows(m_db_handle);
512 if (m_num_rows != 1) {
518 return mysql_insert_id(m_db_handle);
521 SQL_FIELD *B_DB_MYSQL::sql_fetch_field(void)
526 if (!m_fields || m_fields_size < m_num_fields) {
531 Dmsg1(500, "allocating space for %d fields\n", m_num_fields);
532 m_fields = (SQL_FIELD *)malloc(sizeof(SQL_FIELD) * m_num_fields);
533 m_fields_size = m_num_fields;
535 for (i = 0; i < m_num_fields; i++) {
536 Dmsg1(500, "filling field %d\n", i);
537 if ((field = mysql_fetch_field(m_result)) != NULL) {
538 m_fields[i].name = field->name;
539 m_fields[i].max_length = field->max_length;
540 m_fields[i].type = field->type;
541 m_fields[i].flags = field->flags;
543 Dmsg4(500, "sql_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n",
544 m_fields[i].name, m_fields[i].max_length, m_fields[i].type, m_fields[i].flags);
550 * Increment field number for the next time around
552 return &m_fields[m_field_number++];
555 bool B_DB_MYSQL::sql_field_is_not_null(int field_type)
557 return IS_NOT_NULL(field_type);
560 bool B_DB_MYSQL::sql_field_is_numeric(int field_type)
562 return IS_NUM(field_type);
569 bool B_DB_MYSQL::sql_batch_start(JCR *jcr)
574 retval = sql_query("CREATE TEMPORARY TABLE batch ("
581 "DeltaSeq integer)");
587 /* set error to something to abort operation */
592 bool B_DB_MYSQL::sql_batch_end(JCR *jcr, const char *error)
603 bool B_DB_MYSQL::sql_batch_insert(JCR *jcr, ATTR_DBR *ar)
609 esc_name = check_pool_memory_size(esc_name, fnl*2+1);
610 db_escape_string(jcr, esc_name, fname, fnl);
612 esc_path = check_pool_memory_size(esc_path, pnl*2+1);
613 db_escape_string(jcr, esc_path, path, pnl);
615 if (ar->Digest == NULL || ar->Digest[0] == 0) {
621 len = Mmsg(cmd, "INSERT INTO batch VALUES "
622 "(%u,%s,'%s','%s','%s','%s',%u)",
623 ar->FileIndex, edit_int64(ar->JobId,ed1), esc_path,
624 esc_name, ar->attr, digest, ar->DeltaSeq);
626 return sql_query(cmd);
630 * Initialize database data structure. In principal this should
631 * never have errors, or it is really fatal.
633 B_DB *db_init_database(JCR *jcr, const char *db_driver, const char *db_name, const char *db_user,
634 const char *db_password, const char *db_address, int db_port, const char *db_socket,
635 bool mult_db_connections, bool disable_batch_insert)
637 B_DB_MYSQL *mdb = NULL;
640 Jmsg(jcr, M_FATAL, 0, _("A user name for MySQL must be supplied.\n"));
643 P(mutex); /* lock DB queue */
646 * Look to see if DB already open
648 if (db_list && !mult_db_connections) {
649 foreach_dlist(mdb, db_list) {
650 if (mdb->db_match_database(db_driver, db_name, db_address, db_port)) {
651 Dmsg1(100, "DB REopen %s\n", db_name);
652 mdb->increment_refcount();
657 Dmsg0(100, "db_init_database first time\n");
658 mdb = New(B_DB_MYSQL(jcr, db_driver, db_name, db_user, db_password, db_address,
659 db_port, db_socket, mult_db_connections, disable_batch_insert));
666 #endif /* HAVE_MYSQL */