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