]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/mysql.c
529e6c55c3707dc1967aff9a0f101f9814846eb9
[bacula/bacula] / bacula / src / cats / mysql.c
1 /* 
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
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.
8
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.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */ 
19 /* 
20  * Bacula Catalog Database routines specific to MySQL 
21  *   These are MySQL specific routines -- hopefully all 
22  *    other files are generic. 
23  * 
24  *    Written by Kern Sibbald, March 2000 
25  *
26  * Note: at one point, this file was changed to class based by a certain   
27  *  programmer, and other than "wrapping" in a class, which is a trivial  
28  *  change for a C++ programmer, nothing substantial was done, yet all the  
29  *  code was recommitted under this programmer's name.  Consequently, we  
30  *  undo those changes here.  
31  * 
32  */ 
33  
34 #include "bacula.h" 
35  
36 #ifdef HAVE_MYSQL 
37  
38 #include "cats.h" 
39 #include <mysql.h> 
40 #define  BDB_MYSQL_H_ 1 
41 #include "bdb_mysql.h" 
42  
43 /* ----------------------------------------------------------------------- 
44  * 
45  *   MySQL dependent defines and subroutines 
46  * 
47  * ----------------------------------------------------------------------- 
48  */ 
49  
50 /* List of open databases */ 
51 static dlist *db_list = NULL; 
52  
53 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
54  
55 BDB_MYSQL::BDB_MYSQL() 
56
57    BDB_MYSQL *mdb = this; 
58  
59    if (db_list == NULL) { 
60       db_list = New(dlist(this, &this->m_link)); 
61    } 
62    mdb->m_db_driver_type = SQL_DRIVER_TYPE_MYSQL; 
63    mdb->m_db_type = SQL_TYPE_MYSQL; 
64    mdb->m_db_driver = bstrdup("MySQL"); 
65    mdb->errmsg = get_pool_memory(PM_EMSG); /* get error message buffer */ 
66    mdb->errmsg[0] = 0; 
67    mdb->cmd = get_pool_memory(PM_EMSG);    /* get command buffer */ 
68    mdb->cached_path = get_pool_memory(PM_FNAME); 
69    mdb->cached_path_id = 0; 
70    mdb->m_ref_count = 1; 
71    mdb->fname = get_pool_memory(PM_FNAME); 
72    mdb->path = get_pool_memory(PM_FNAME); 
73    mdb->esc_name = get_pool_memory(PM_FNAME); 
74    mdb->esc_path = get_pool_memory(PM_FNAME); 
75    mdb->esc_obj = get_pool_memory(PM_FNAME); 
76    mdb->m_use_fatal_jmsg = true; 
77  
78    /* Initialize the private members. */ 
79    mdb->m_db_handle = NULL; 
80    mdb->m_result = NULL; 
81  
82    db_list->append(this); 
83
84  
85 BDB_MYSQL::~BDB_MYSQL() 
86
87
88  
89 /* 
90  * Initialize database data structure. In principal this should 
91  * never have errors, or it is really fatal. 
92  */ 
93 BDB *db_init_database(JCR *jcr, const char *db_driver, const char *db_name, const char *db_user, 
94                        const char *db_password, const char *db_address, int db_port, const char *db_socket, 
95                        const char *db_ssl_key, const char *db_ssl_cert, const char *db_ssl_ca,
96                        const char *db_ssl_capath, const char *db_ssl_cipher,
97                        bool mult_db_connections, bool disable_batch_insert) 
98
99    BDB_MYSQL *mdb = NULL; 
100  
101    if (!db_user) { 
102       Jmsg(jcr, M_FATAL, 0, _("A user name for MySQL must be supplied.\n")); 
103       return NULL; 
104    } 
105    P(mutex);                          /* lock DB queue */ 
106  
107    /* 
108     * Look to see if DB already open 
109     */ 
110    if (db_list && !mult_db_connections) { 
111       foreach_dlist(mdb, db_list) { 
112          if (mdb->bdb_match_database(db_driver, db_name, db_address, db_port)) { 
113             Dmsg1(100, "DB REopen %s\n", db_name); 
114             mdb->increment_refcount(); 
115             goto get_out; 
116          } 
117       } 
118    } 
119    Dmsg0(100, "db_init_database first time\n"); 
120    mdb = New(BDB_MYSQL()); 
121    if (!mdb) goto get_out; 
122  
123    /* 
124     * Initialize the parent class members. 
125     */ 
126    mdb->m_db_name = bstrdup(db_name); 
127    mdb->m_db_user = bstrdup(db_user); 
128    if (db_password) { 
129       mdb->m_db_password = bstrdup(db_password); 
130    } 
131    if (db_address) { 
132       mdb->m_db_address = bstrdup(db_address); 
133    } 
134    if (db_socket) {
135       mdb->m_db_socket = bstrdup(db_socket); 
136    } 
137    if (db_ssl_key) {
138       mdb->m_db_ssl_key = bstrdup(db_ssl_key);
139    }
140    if (db_ssl_cert) {
141       mdb->m_db_ssl_cert = bstrdup(db_ssl_cert);
142    }
143    if (db_ssl_ca) {
144       mdb->m_db_ssl_ca = bstrdup(db_ssl_ca);
145    }
146    if (db_ssl_capath) {
147       mdb->m_db_ssl_capath = bstrdup(db_ssl_capath);
148    }
149    if (db_ssl_cipher) {
150       mdb->m_db_ssl_cipher = bstrdup(db_ssl_cipher);
151    }
152    mdb->m_db_port = db_port; 
153  
154    if (disable_batch_insert) { 
155       mdb->m_disabled_batch_insert = true; 
156       mdb->m_have_batch_insert = false; 
157    } else { 
158       mdb->m_disabled_batch_insert = false; 
159 #ifdef USE_BATCH_FILE_INSERT 
160 #ifdef HAVE_MYSQL_THREAD_SAFE 
161       mdb->m_have_batch_insert = mysql_thread_safe(); 
162 #else 
163       mdb->m_have_batch_insert = false; 
164 #endif /* HAVE_MYSQL_THREAD_SAFE */ 
165 #else 
166       mdb->m_have_batch_insert = false; 
167 #endif /* USE_BATCH_FILE_INSERT */ 
168    } 
169  
170    mdb->m_allow_transactions = mult_db_connections; 
171  
172    /* At this time, when mult_db_connections == true, this is for 
173     * specific console command such as bvfs or batch mode, and we don't 
174     * want to share a batch mode or bvfs. In the future, we can change 
175     * the creation function to add this parameter. 
176     */ 
177    mdb->m_dedicated = mult_db_connections; 
178  
179 get_out: 
180    V(mutex); 
181    return mdb; 
182
183  
184  
185 /* 
186  * Now actually open the database.  This can generate errors, 
187  *  which are returned in the errmsg 
188  * 
189  * DO NOT close the database or delete mdb here !!!! 
190  */ 
191 bool BDB_MYSQL::bdb_open_database(JCR *jcr) 
192
193    BDB_MYSQL *mdb = this; 
194    bool retval = false; 
195    int errstat; 
196  
197    P(mutex); 
198    if (mdb->m_connected) { 
199       retval = true; 
200       goto get_out; 
201    } 
202  
203    if ((errstat=rwl_init(&mdb->m_lock)) != 0) { 
204       berrno be; 
205       Mmsg1(&mdb->errmsg, _("Unable to initialize DB lock. ERR=%s\n"), 
206             be.bstrerror(errstat)); 
207       goto get_out; 
208    } 
209  
210    /* 
211     * Connect to the database 
212     */ 
213 #ifdef xHAVE_EMBEDDED_MYSQL 
214 // mysql_server_init(0, NULL, NULL); 
215 #endif 
216    mysql_init(&mdb->m_instance); 
217  
218    Dmsg0(50, "mysql_init done\n"); 
219
220    /*
221    * Sets the appropriate certificate options for
222    * establishing secure connection using SSL to the database.
223    */
224    if (mdb->m_db_ssl_key) {
225       mysql_ssl_set(&(mdb->m_instance),
226                    mdb->m_db_ssl_key,
227                    mdb->m_db_ssl_cert,
228                    mdb->m_db_ssl_ca,
229                    mdb->m_db_ssl_capath,
230                    mdb->m_db_ssl_cipher);
231    }
232
233    /* 
234     * If connection fails, try at 5 sec intervals for 30 seconds. 
235     */ 
236    for (int retry=0; retry < 6; retry++) { 
237       mdb->m_db_handle = mysql_real_connect( 
238            &(mdb->m_instance),      /* db */ 
239            mdb->m_db_address,       /* default = localhost */ 
240            mdb->m_db_user,          /* login name */ 
241            mdb->m_db_password,      /* password */ 
242            mdb->m_db_name,          /* database name */ 
243            mdb->m_db_port,          /* default port */ 
244            mdb->m_db_socket,        /* default = socket */ 
245            CLIENT_FOUND_ROWS);      /* flags */ 
246  
247       /* 
248        * If no connect, try once more in case it is a timing problem 
249        */ 
250       if (mdb->m_db_handle != NULL) { 
251          break; 
252       } 
253       bmicrosleep(5,0); 
254    } 
255  
256    mdb->m_instance.reconnect = 1;             /* so connection does not timeout */ 
257    Dmsg0(50, "mysql_real_connect done\n"); 
258    Dmsg3(50, "db_user=%s db_name=%s db_password=%s\n", mdb->m_db_user, mdb->m_db_name, 
259         (mdb->m_db_password == NULL) ? "(NULL)" : mdb->m_db_password); 
260
261    if (mdb->m_db_handle == NULL) { 
262       Mmsg2(&mdb->errmsg, _("Unable to connect to MySQL server.\n" 
263 "Database=%s User=%s\n" 
264 "MySQL connect failed either server not running or your authorization is incorrect.\n"), 
265          mdb->m_db_name, mdb->m_db_user); 
266 #if MYSQL_VERSION_ID >= 40101 
267       Dmsg3(50, "Error %u (%s): %s\n", 
268             mysql_errno(&(mdb->m_instance)), mysql_sqlstate(&(mdb->m_instance)), 
269             mysql_error(&(mdb->m_instance))); 
270 #else 
271       Dmsg2(50, "Error %u: %s\n", 
272             mysql_errno(&(mdb->m_instance)), mysql_error(&(mdb->m_instance))); 
273 #endif 
274       goto get_out; 
275    } 
276  
277    /* get the current cipher used for SSL connection */
278    if (mdb->m_db_ssl_key) {
279       const char *cipher;
280       if (mdb->m_db_ssl_cipher) {
281          free(mdb->m_db_ssl_cipher);
282       }
283       cipher = (const char *)mysql_get_ssl_cipher(&(mdb->m_instance));
284       if (cipher) {
285          mdb->m_db_ssl_cipher = bstrdup(cipher);
286       }
287       Dmsg1(50, "db_ssl_ciper=%s\n", (mdb->m_db_ssl_cipher == NULL) ? "(NULL)" : mdb->m_db_ssl_cipher);
288    }
289
290    mdb->m_connected = true; 
291    if (!bdb_check_version(jcr)) { 
292       goto get_out; 
293    } 
294  
295    Dmsg3(100, "opendb ref=%d connected=%d db=%p\n", mdb->m_ref_count, mdb->m_connected, mdb->m_db_handle); 
296  
297    /* 
298     * Set connection timeout to 8 days specialy for batch mode 
299     */ 
300    sql_query("SET wait_timeout=691200"); 
301    sql_query("SET interactive_timeout=691200"); 
302  
303    retval = true; 
304  
305 get_out: 
306    V(mutex); 
307    return retval; 
308
309  
310 void BDB_MYSQL::bdb_close_database(JCR *jcr) 
311
312    BDB_MYSQL *mdb = this; 
313  
314    if (mdb->m_connected) { 
315       bdb_end_transaction(jcr); 
316    } 
317    P(mutex); 
318    mdb->m_ref_count--; 
319    Dmsg3(100, "closedb ref=%d connected=%d db=%p\n", mdb->m_ref_count, mdb->m_connected, mdb->m_db_handle); 
320    if (mdb->m_ref_count == 0) { 
321       if (mdb->m_connected) { 
322          sql_free_result(); 
323       } 
324       db_list->remove(mdb); 
325       if (mdb->m_connected) { 
326          Dmsg1(100, "close db=%p\n", mdb->m_db_handle); 
327          mysql_close(&mdb->m_instance); 
328       } 
329       if (is_rwl_valid(&mdb->m_lock)) { 
330          rwl_destroy(&mdb->m_lock); 
331       } 
332       free_pool_memory(mdb->errmsg); 
333       free_pool_memory(mdb->cmd); 
334       free_pool_memory(mdb->cached_path); 
335       free_pool_memory(mdb->fname); 
336       free_pool_memory(mdb->path); 
337       free_pool_memory(mdb->esc_name); 
338       free_pool_memory(mdb->esc_path); 
339       free_pool_memory(mdb->esc_obj); 
340       if (mdb->m_db_driver) { 
341          free(mdb->m_db_driver); 
342       } 
343       if (mdb->m_db_name) { 
344          free(mdb->m_db_name); 
345       } 
346       if (mdb->m_db_user) { 
347          free(mdb->m_db_user); 
348       } 
349       if (mdb->m_db_password) { 
350          free(mdb->m_db_password); 
351       } 
352       if (mdb->m_db_address) { 
353          free(mdb->m_db_address); 
354       } 
355       if (mdb->m_db_socket) { 
356          free(mdb->m_db_socket); 
357       }
358       if (mdb->m_db_ssl_key) {
359          free(mdb->m_db_ssl_key);
360       }
361       if (mdb->m_db_ssl_cert) {
362          free(mdb->m_db_ssl_cert);
363       }
364       if (mdb->m_db_ssl_ca) {
365          free(mdb->m_db_ssl_ca);
366       }
367       if (mdb->m_db_ssl_capath) {
368          free(mdb->m_db_ssl_capath);
369       }
370       if (mdb->m_db_ssl_cipher) {
371          free(mdb->m_db_ssl_cipher);
372       }
373       delete mdb; 
374       if (db_list->size() == 0) { 
375          delete db_list; 
376          db_list = NULL; 
377       } 
378    } 
379    V(mutex); 
380
381  
382 /* 
383  * This call is needed because the message channel thread 
384  *  opens a database on behalf of a jcr that was created in 
385  *  a different thread. MySQL then allocates thread specific 
386  *  data, which is NOT freed when the original jcr thread 
387  *  closes the database.  Thus the msgchan must call here 
388  *  to cleanup any thread specific data that it created. 
389  */ 
390 void BDB_MYSQL::bdb_thread_cleanup(void) 
391
392 #ifndef HAVE_WIN32 
393    mysql_thread_end();       /* Cleanup thread specific data */ 
394 #endif 
395
396  
397 /* 
398  * Escape strings so MySQL is happy 
399  * 
400  * len is the length of the old string. Your new 
401  *   string must be long enough (max 2*old+1) to hold 
402  *   the escaped output. 
403  */ 
404 void BDB_MYSQL::bdb_escape_string(JCR *jcr, char *snew, char *old, int len) 
405
406    BDB_MYSQL *mdb = this; 
407    mysql_real_escape_string(mdb->m_db_handle, snew, old, len); 
408
409  
410 /* 
411  * Escape binary object so that MySQL is happy 
412  * Memory is stored in BDB struct, no need to free it 
413  */ 
414 char *BDB_MYSQL::bdb_escape_object(JCR *jcr, char *old, int len) 
415
416    BDB_MYSQL *mdb = this; 
417    mdb->esc_obj = check_pool_memory_size(mdb->esc_obj, len*2+1); 
418    mysql_real_escape_string(mdb->m_db_handle, mdb->esc_obj, old, len); 
419    return mdb->esc_obj; 
420
421  
422 /* 
423  * Unescape binary object so that MySQL is happy 
424  */ 
425 void BDB_MYSQL::bdb_unescape_object(JCR *jcr, char *from, int32_t expected_len, 
426                                     POOLMEM **dest, int32_t *dest_len) 
427
428    if (!from) { 
429       *dest[0] = 0; 
430       *dest_len = 0; 
431       return; 
432    } 
433    *dest = check_pool_memory_size(*dest, expected_len+1); 
434    *dest_len = expected_len; 
435    memcpy(*dest, from, expected_len); 
436    (*dest)[expected_len]=0; 
437
438  
439 void BDB_MYSQL::bdb_start_transaction(JCR *jcr) 
440
441    if (!jcr->attr) { 
442       jcr->attr = get_pool_memory(PM_FNAME); 
443    } 
444    if (!jcr->ar) { 
445       jcr->ar = (ATTR_DBR *)malloc(sizeof(ATTR_DBR)); 
446       memset(jcr->ar, 0, sizeof(ATTR_DBR));
447    } 
448
449  
450 void BDB_MYSQL::bdb_end_transaction(JCR *jcr) 
451
452    if (jcr && jcr->cached_attribute) { 
453       Dmsg0(400, "Flush last cached attribute.\n"); 
454       if (!bdb_create_attributes_record(jcr, jcr->ar)) { 
455          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), jcr->db->bdb_strerror()); 
456       } 
457       jcr->cached_attribute = false; 
458    } 
459
460  
461 /* 
462  * Submit a general SQL command (cmd), and for each row returned, 
463  * the result_handler is called with the ctx. 
464  */ 
465 bool BDB_MYSQL::bdb_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx) 
466
467    int ret; 
468    SQL_ROW row; 
469    bool send = true; 
470    bool retval = false; 
471    BDB_MYSQL *mdb = this; 
472  
473    Dmsg1(500, "db_sql_query starts with %s\n", query); 
474  
475    bdb_lock(); 
476    errmsg[0] = 0; 
477    ret = mysql_query(m_db_handle, query); 
478    if (ret != 0) { 
479       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror()); 
480       Dmsg0(500, "db_sql_query failed\n"); 
481       goto get_out; 
482    } 
483  
484    Dmsg0(500, "db_sql_query succeeded. checking handler\n"); 
485  
486    if (result_handler) { 
487       if ((mdb->m_result = mysql_use_result(mdb->m_db_handle)) != NULL) { 
488          mdb->m_num_fields = mysql_num_fields(mdb->m_result); 
489  
490          /* 
491           * We *must* fetch all rows 
492           */ 
493          while ((row = mysql_fetch_row(m_result))) { 
494             if (send) { 
495                /* the result handler returns 1 when it has 
496                 *  seen all the data it wants.  However, we 
497                 *  loop to the end of the data. 
498                 */ 
499                if (result_handler(ctx, mdb->m_num_fields, row)) { 
500                   send = false; 
501                } 
502             } 
503          } 
504          sql_free_result(); 
505       } 
506    } 
507  
508    Dmsg0(500, "db_sql_query finished\n"); 
509    retval = true; 
510  
511 get_out: 
512    bdb_unlock(); 
513    return retval; 
514
515  
516 bool BDB_MYSQL::sql_query(const char *query, int flags) 
517
518    int ret; 
519    bool retval = true; 
520    BDB_MYSQL *mdb = this; 
521  
522    Dmsg1(500, "sql_query starts with '%s'\n", query); 
523    /* 
524     * We are starting a new query. reset everything. 
525     */ 
526    mdb->m_num_rows     = -1; 
527    mdb->m_row_number   = -1; 
528    mdb->m_field_number = -1; 
529  
530    if (mdb->m_result) { 
531       mysql_free_result(mdb->m_result); 
532       mdb->m_result = NULL; 
533    } 
534  
535    ret = mysql_query(mdb->m_db_handle, query); 
536    if (ret == 0) { 
537       Dmsg0(500, "we have a result\n"); 
538       if (flags & QF_STORE_RESULT) { 
539          mdb->m_result = mysql_store_result(mdb->m_db_handle); 
540          if (mdb->m_result != NULL) { 
541             mdb->m_num_fields = mysql_num_fields(mdb->m_result); 
542             Dmsg1(500, "we have %d fields\n", mdb->m_num_fields); 
543             mdb->m_num_rows = mysql_num_rows(mdb->m_result); 
544             Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
545          } else { 
546             mdb->m_num_fields = 0; 
547             mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
548             Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
549          } 
550       } else { 
551          mdb->m_num_fields = 0; 
552          mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
553          Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
554       } 
555    } else { 
556       Dmsg0(500, "we failed\n"); 
557       mdb->m_status = 1;                   /* failed */ 
558       retval = false; 
559    } 
560    return retval; 
561
562  
563 void BDB_MYSQL::sql_free_result(void) 
564
565    BDB_MYSQL *mdb = this; 
566    bdb_lock(); 
567    if (mdb->m_result) { 
568       mysql_free_result(mdb->m_result); 
569       mdb->m_result = NULL; 
570    } 
571    if (mdb->m_fields) { 
572       free(mdb->m_fields); 
573       mdb->m_fields = NULL; 
574    } 
575    mdb->m_num_rows = mdb->m_num_fields = 0; 
576    bdb_unlock(); 
577
578  
579 SQL_ROW BDB_MYSQL::sql_fetch_row(void) 
580
581    BDB_MYSQL *mdb = this; 
582    if (!mdb->m_result) { 
583       return NULL; 
584    } else { 
585       return mysql_fetch_row(mdb->m_result); 
586    } 
587
588  
589 const char *BDB_MYSQL::sql_strerror(void) 
590
591    BDB_MYSQL *mdb = this; 
592    return mysql_error(mdb->m_db_handle); 
593
594  
595 void BDB_MYSQL::sql_data_seek(int row) 
596
597    BDB_MYSQL *mdb = this; 
598    return mysql_data_seek(mdb->m_result, row); 
599
600  
601 int BDB_MYSQL::sql_affected_rows(void) 
602
603    BDB_MYSQL *mdb = this; 
604    return mysql_affected_rows(mdb->m_db_handle); 
605
606  
607 uint64_t BDB_MYSQL::sql_insert_autokey_record(const char *query, const char *table_name) 
608
609    BDB_MYSQL *mdb = this; 
610    /* 
611     * First execute the insert query and then retrieve the currval. 
612     */ 
613    if (mysql_query(mdb->m_db_handle, query) != 0) { 
614       return 0; 
615    } 
616  
617    mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
618    if (mdb->m_num_rows != 1) { 
619       return 0; 
620    } 
621  
622    mdb->changes++; 
623  
624    return mysql_insert_id(mdb->m_db_handle); 
625
626  
627 SQL_FIELD *BDB_MYSQL::sql_fetch_field(void) 
628
629    int i; 
630    MYSQL_FIELD *field; 
631    BDB_MYSQL *mdb = this; 
632  
633    if (!mdb->m_fields || mdb->m_fields_size < mdb->m_num_fields) { 
634       if (mdb->m_fields) { 
635          free(mdb->m_fields); 
636          mdb->m_fields = NULL; 
637       } 
638       Dmsg1(500, "allocating space for %d fields\n", mdb->m_num_fields); 
639       mdb->m_fields = (SQL_FIELD *)malloc(sizeof(SQL_FIELD) * mdb->m_num_fields); 
640       mdb->m_fields_size = mdb->m_num_fields; 
641  
642       for (i = 0; i < mdb->m_num_fields; i++) { 
643          Dmsg1(500, "filling field %d\n", i); 
644          if ((field = mysql_fetch_field(mdb->m_result)) != NULL) { 
645             mdb->m_fields[i].name = field->name; 
646             mdb->m_fields[i].max_length = field->max_length; 
647             mdb->m_fields[i].type = field->type; 
648             mdb->m_fields[i].flags = field->flags; 
649  
650             Dmsg4(500, "sql_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n", 
651                   mdb->m_fields[i].name, mdb->m_fields[i].max_length, mdb->m_fields[i].type, mdb->m_fields[i].flags); 
652          } 
653       } 
654    } 
655  
656    /* 
657     * Increment field number for the next time around 
658     */ 
659    return &mdb->m_fields[mdb->m_field_number++]; 
660
661  
662 bool BDB_MYSQL::sql_field_is_not_null(int field_type) 
663
664    return IS_NOT_NULL(field_type); 
665
666  
667 bool BDB_MYSQL::sql_field_is_numeric(int field_type) 
668
669    return IS_NUM(field_type); 
670
671  
672 /* 
673  * Returns true  if OK 
674  *         false if failed 
675  */ 
676 bool BDB_MYSQL::sql_batch_start(JCR *jcr) 
677
678    BDB_MYSQL *mdb = this; 
679    bool retval; 
680  
681    bdb_lock(); 
682    retval = sql_query("CREATE TEMPORARY TABLE batch (" 
683                       "FileIndex integer," 
684                       "JobId integer," 
685                       "Path blob," 
686                       "Name blob," 
687                       "LStat tinyblob," 
688                       "MD5 tinyblob," 
689                       "DeltaSeq integer)"); 
690    bdb_unlock(); 
691  
692    /* 
693     * Keep track of the number of changes in batch mode. 
694     */ 
695    mdb->changes = 0; 
696  
697    return retval; 
698
699  
700 /* set error to something to abort operation */ 
701 /* 
702  * Returns true  if OK 
703  *         false if failed 
704  */ 
705 bool BDB_MYSQL::sql_batch_end(JCR *jcr, const char *error) 
706
707    BDB_MYSQL *mdb = this; 
708  
709    mdb->m_status = 0; 
710  
711    /* 
712     * Flush any pending inserts. 
713     */ 
714    if (mdb->changes) { 
715       return sql_query(mdb->cmd); 
716    } 
717  
718    return true; 
719
720  
721 /* 
722  * Returns true  if OK 
723  *         false if failed 
724  */ 
725 bool BDB_MYSQL::sql_batch_insert(JCR *jcr, ATTR_DBR *ar) 
726
727    BDB_MYSQL *mdb = this; 
728    const char *digest; 
729    char ed1[50]; 
730  
731    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1); 
732    bdb_escape_string(jcr, mdb->esc_name, mdb->fname, mdb->fnl); 
733  
734    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1); 
735    bdb_escape_string(jcr, mdb->esc_path, mdb->path, mdb->pnl); 
736  
737    if (ar->Digest == NULL || ar->Digest[0] == 0) { 
738       digest = "0"; 
739    } else { 
740       digest = ar->Digest; 
741    } 
742  
743    /* 
744     * Try to batch up multiple inserts using multi-row inserts. 
745     */ 
746    if (mdb->changes == 0) { 
747       Mmsg(cmd, "INSERT INTO batch VALUES " 
748            "(%u,%s,'%s','%s','%s','%s',%u)", 
749            ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
750            mdb->esc_name, ar->attr, digest, ar->DeltaSeq); 
751       mdb->changes++; 
752    } else { 
753       /* 
754        * We use the esc_obj for temporary storage otherwise 
755        * we keep on copying data. 
756        */ 
757       Mmsg(mdb->esc_obj, ",(%u,%s,'%s','%s','%s','%s',%u)", 
758            ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
759            mdb->esc_name, ar->attr, digest, ar->DeltaSeq); 
760       pm_strcat(mdb->cmd, mdb->esc_obj); 
761       mdb->changes++; 
762    } 
763  
764    /* 
765     * See if we need to flush the query buffer filled 
766     * with multi-row inserts. 
767     */ 
768    if ((mdb->changes % MYSQL_CHANGES_PER_BATCH_INSERT) == 0) { 
769       if (!sql_query(mdb->cmd)) { 
770          mdb->changes = 0; 
771          return false; 
772       } else { 
773          mdb->changes = 0; 
774       } 
775    } 
776    return true; 
777
778  
779  
780 #endif /* HAVE_MYSQL */