]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/mysql.c
Change copyright as per agreement with FSFE
[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    } 
447
448  
449 void BDB_MYSQL::bdb_end_transaction(JCR *jcr) 
450
451    if (jcr && jcr->cached_attribute) { 
452       Dmsg0(400, "Flush last cached attribute.\n"); 
453       if (!bdb_create_attributes_record(jcr, jcr->ar)) { 
454          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), jcr->db->bdb_strerror()); 
455       } 
456       jcr->cached_attribute = false; 
457    } 
458
459  
460 /* 
461  * Submit a general SQL command (cmd), and for each row returned, 
462  * the result_handler is called with the ctx. 
463  */ 
464 bool BDB_MYSQL::bdb_sql_query(const char *query, DB_RESULT_HANDLER *result_handler, void *ctx) 
465
466    int ret; 
467    SQL_ROW row; 
468    bool send = true; 
469    bool retval = false; 
470    BDB_MYSQL *mdb = this; 
471  
472    Dmsg1(500, "db_sql_query starts with %s\n", query); 
473  
474    bdb_lock(); 
475    errmsg[0] = 0; 
476    ret = mysql_query(m_db_handle, query); 
477    if (ret != 0) { 
478       Mmsg(mdb->errmsg, _("Query failed: %s: ERR=%s\n"), query, sql_strerror()); 
479       Dmsg0(500, "db_sql_query failed\n"); 
480       goto get_out; 
481    } 
482  
483    Dmsg0(500, "db_sql_query succeeded. checking handler\n"); 
484  
485    if (result_handler) { 
486       if ((mdb->m_result = mysql_use_result(mdb->m_db_handle)) != NULL) { 
487          mdb->m_num_fields = mysql_num_fields(mdb->m_result); 
488  
489          /* 
490           * We *must* fetch all rows 
491           */ 
492          while ((row = mysql_fetch_row(m_result))) { 
493             if (send) { 
494                /* the result handler returns 1 when it has 
495                 *  seen all the data it wants.  However, we 
496                 *  loop to the end of the data. 
497                 */ 
498                if (result_handler(ctx, mdb->m_num_fields, row)) { 
499                   send = false; 
500                } 
501             } 
502          } 
503          sql_free_result(); 
504       } 
505    } 
506  
507    Dmsg0(500, "db_sql_query finished\n"); 
508    retval = true; 
509  
510 get_out: 
511    bdb_unlock(); 
512    return retval; 
513
514  
515 bool BDB_MYSQL::sql_query(const char *query, int flags) 
516
517    int ret; 
518    bool retval = true; 
519    BDB_MYSQL *mdb = this; 
520  
521    Dmsg1(500, "sql_query starts with '%s'\n", query); 
522    /* 
523     * We are starting a new query. reset everything. 
524     */ 
525    mdb->m_num_rows     = -1; 
526    mdb->m_row_number   = -1; 
527    mdb->m_field_number = -1; 
528  
529    if (mdb->m_result) { 
530       mysql_free_result(mdb->m_result); 
531       mdb->m_result = NULL; 
532    } 
533  
534    ret = mysql_query(mdb->m_db_handle, query); 
535    if (ret == 0) { 
536       Dmsg0(500, "we have a result\n"); 
537       if (flags & QF_STORE_RESULT) { 
538          mdb->m_result = mysql_store_result(mdb->m_db_handle); 
539          if (mdb->m_result != NULL) { 
540             mdb->m_num_fields = mysql_num_fields(mdb->m_result); 
541             Dmsg1(500, "we have %d fields\n", mdb->m_num_fields); 
542             mdb->m_num_rows = mysql_num_rows(mdb->m_result); 
543             Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
544          } else { 
545             mdb->m_num_fields = 0; 
546             mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
547             Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
548          } 
549       } else { 
550          mdb->m_num_fields = 0; 
551          mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
552          Dmsg1(500, "we have %d rows\n", mdb->m_num_rows); 
553       } 
554    } else { 
555       Dmsg0(500, "we failed\n"); 
556       mdb->m_status = 1;                   /* failed */ 
557       retval = false; 
558    } 
559    return retval; 
560
561  
562 void BDB_MYSQL::sql_free_result(void) 
563
564    BDB_MYSQL *mdb = this; 
565    bdb_lock(); 
566    if (mdb->m_result) { 
567       mysql_free_result(mdb->m_result); 
568       mdb->m_result = NULL; 
569    } 
570    if (mdb->m_fields) { 
571       free(mdb->m_fields); 
572       mdb->m_fields = NULL; 
573    } 
574    mdb->m_num_rows = mdb->m_num_fields = 0; 
575    bdb_unlock(); 
576
577  
578 SQL_ROW BDB_MYSQL::sql_fetch_row(void) 
579
580    BDB_MYSQL *mdb = this; 
581    if (!mdb->m_result) { 
582       return NULL; 
583    } else { 
584       return mysql_fetch_row(mdb->m_result); 
585    } 
586
587  
588 const char *BDB_MYSQL::sql_strerror(void) 
589
590    BDB_MYSQL *mdb = this; 
591    return mysql_error(mdb->m_db_handle); 
592
593  
594 void BDB_MYSQL::sql_data_seek(int row) 
595
596    BDB_MYSQL *mdb = this; 
597    return mysql_data_seek(mdb->m_result, row); 
598
599  
600 int BDB_MYSQL::sql_affected_rows(void) 
601
602    BDB_MYSQL *mdb = this; 
603    return mysql_affected_rows(mdb->m_db_handle); 
604
605  
606 uint64_t BDB_MYSQL::sql_insert_autokey_record(const char *query, const char *table_name) 
607
608    BDB_MYSQL *mdb = this; 
609    /* 
610     * First execute the insert query and then retrieve the currval. 
611     */ 
612    if (mysql_query(mdb->m_db_handle, query) != 0) { 
613       return 0; 
614    } 
615  
616    mdb->m_num_rows = mysql_affected_rows(mdb->m_db_handle); 
617    if (mdb->m_num_rows != 1) { 
618       return 0; 
619    } 
620  
621    mdb->changes++; 
622  
623    return mysql_insert_id(mdb->m_db_handle); 
624
625  
626 SQL_FIELD *BDB_MYSQL::sql_fetch_field(void) 
627
628    int i; 
629    MYSQL_FIELD *field; 
630    BDB_MYSQL *mdb = this; 
631  
632    if (!mdb->m_fields || mdb->m_fields_size < mdb->m_num_fields) { 
633       if (mdb->m_fields) { 
634          free(mdb->m_fields); 
635          mdb->m_fields = NULL; 
636       } 
637       Dmsg1(500, "allocating space for %d fields\n", mdb->m_num_fields); 
638       mdb->m_fields = (SQL_FIELD *)malloc(sizeof(SQL_FIELD) * mdb->m_num_fields); 
639       mdb->m_fields_size = mdb->m_num_fields; 
640  
641       for (i = 0; i < mdb->m_num_fields; i++) { 
642          Dmsg1(500, "filling field %d\n", i); 
643          if ((field = mysql_fetch_field(mdb->m_result)) != NULL) { 
644             mdb->m_fields[i].name = field->name; 
645             mdb->m_fields[i].max_length = field->max_length; 
646             mdb->m_fields[i].type = field->type; 
647             mdb->m_fields[i].flags = field->flags; 
648  
649             Dmsg4(500, "sql_fetch_field finds field '%s' has length='%d' type='%d' and IsNull=%d\n", 
650                   mdb->m_fields[i].name, mdb->m_fields[i].max_length, mdb->m_fields[i].type, mdb->m_fields[i].flags); 
651          } 
652       } 
653    } 
654  
655    /* 
656     * Increment field number for the next time around 
657     */ 
658    return &mdb->m_fields[mdb->m_field_number++]; 
659
660  
661 bool BDB_MYSQL::sql_field_is_not_null(int field_type) 
662
663    return IS_NOT_NULL(field_type); 
664
665  
666 bool BDB_MYSQL::sql_field_is_numeric(int field_type) 
667
668    return IS_NUM(field_type); 
669
670  
671 /* 
672  * Returns true  if OK 
673  *         false if failed 
674  */ 
675 bool BDB_MYSQL::sql_batch_start(JCR *jcr) 
676
677    BDB_MYSQL *mdb = this; 
678    bool retval; 
679  
680    bdb_lock(); 
681    retval = sql_query("CREATE TEMPORARY TABLE batch (" 
682                       "FileIndex integer," 
683                       "JobId integer," 
684                       "Path blob," 
685                       "Name blob," 
686                       "LStat tinyblob," 
687                       "MD5 tinyblob," 
688                       "DeltaSeq integer)"); 
689    bdb_unlock(); 
690  
691    /* 
692     * Keep track of the number of changes in batch mode. 
693     */ 
694    mdb->changes = 0; 
695  
696    return retval; 
697
698  
699 /* set error to something to abort operation */ 
700 /* 
701  * Returns true  if OK 
702  *         false if failed 
703  */ 
704 bool BDB_MYSQL::sql_batch_end(JCR *jcr, const char *error) 
705
706    BDB_MYSQL *mdb = this; 
707  
708    mdb->m_status = 0; 
709  
710    /* 
711     * Flush any pending inserts. 
712     */ 
713    if (mdb->changes) { 
714       return sql_query(mdb->cmd); 
715    } 
716  
717    return true; 
718
719  
720 /* 
721  * Returns true  if OK 
722  *         false if failed 
723  */ 
724 bool BDB_MYSQL::sql_batch_insert(JCR *jcr, ATTR_DBR *ar) 
725
726    BDB_MYSQL *mdb = this; 
727    const char *digest; 
728    char ed1[50]; 
729  
730    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1); 
731    bdb_escape_string(jcr, mdb->esc_name, mdb->fname, mdb->fnl); 
732  
733    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1); 
734    bdb_escape_string(jcr, mdb->esc_path, mdb->path, mdb->pnl); 
735  
736    if (ar->Digest == NULL || ar->Digest[0] == 0) { 
737       digest = "0"; 
738    } else { 
739       digest = ar->Digest; 
740    } 
741  
742    /* 
743     * Try to batch up multiple inserts using multi-row inserts. 
744     */ 
745    if (mdb->changes == 0) { 
746       Mmsg(cmd, "INSERT INTO batch VALUES " 
747            "(%u,%s,'%s','%s','%s','%s',%u)", 
748            ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
749            mdb->esc_name, ar->attr, digest, ar->DeltaSeq); 
750       mdb->changes++; 
751    } else { 
752       /* 
753        * We use the esc_obj for temporary storage otherwise 
754        * we keep on copying data. 
755        */ 
756       Mmsg(mdb->esc_obj, ",(%u,%s,'%s','%s','%s','%s',%u)", 
757            ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
758            mdb->esc_name, ar->attr, digest, ar->DeltaSeq); 
759       pm_strcat(mdb->cmd, mdb->esc_obj); 
760       mdb->changes++; 
761    } 
762  
763    /* 
764     * See if we need to flush the query buffer filled 
765     * with multi-row inserts. 
766     */ 
767    if ((mdb->changes % MYSQL_CHANGES_PER_BATCH_INSERT) == 0) { 
768       if (!sql_query(mdb->cmd)) { 
769          mdb->changes = 0; 
770          return false; 
771       } else { 
772          mdb->changes = 0; 
773       } 
774    } 
775    return true; 
776
777  
778  
779 #endif /* HAVE_MYSQL */