]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_create.c
More db + sd error handling cleanup
[bacula/bacula] / bacula / src / cats / sql_create.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
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 two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
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.
17
18    You should have received a copy of the GNU 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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Bacula Catalog Database Create record interface routines
30  *
31  *    Kern Sibbald, March 2000
32  *
33  *    Version $Id$
34  */
35
36 /* The following is necessary so that we do not include
37  * the dummy external definition of DB.
38  */
39 #define __SQL_C                       /* indicate that this is sql.c */
40
41 #include "bacula.h"
42 #include "cats.h"
43
44 static const int dbglevel = 500;
45
46 #if    HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI
47
48 /* -----------------------------------------------------------------------
49  *
50  *   Generic Routines (or almost generic)
51  *
52  * -----------------------------------------------------------------------
53  */
54
55 /* Forward referenced subroutines */
56 #ifndef HAVE_BATCH_FILE_INSERT
57 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar);
58 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar);
59 #endif /* HAVE_BATCH_FILE_INSERT */
60
61
62 /* Create a new record for the Job
63  * Returns: false on failure
64  *          true  on success
65  */
66 bool
67 db_create_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
68 {
69    char dt[MAX_TIME_LENGTH];
70    time_t stime;
71    struct tm tm;
72    bool ok;
73    utime_t JobTDate;
74    char ed1[30],ed2[30];
75
76    db_lock(mdb);
77
78    stime = jr->SchedTime;
79    ASSERT(stime != 0);
80
81    (void)localtime_r(&stime, &tm);
82    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
83    JobTDate = (utime_t)stime;
84
85    /* Must create it */
86    Mmsg(mdb->cmd,
87 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,ClientId) "
88 "VALUES ('%s','%s','%c','%c','%c','%s',%s,%s)",
89            jr->Job, jr->Name, (char)(jr->JobType), (char)(jr->JobLevel),
90            (char)(jr->JobStatus), dt, edit_uint64(JobTDate, ed1),
91            edit_int64(jr->ClientId, ed2));
92
93    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
94       Mmsg2(&mdb->errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
95             mdb->cmd, sql_strerror(mdb));
96       jr->JobId = 0;
97       ok = false;
98    } else {
99       jr->JobId = sql_insert_id(mdb, NT_("Job"));
100       ok = true;
101    }
102    db_unlock(mdb);
103    return ok;
104 }
105
106
107 /* Create a JobMedia record for medium used this job
108  * Returns: false on failure
109  *          true  on success
110  */
111 bool
112 db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jm)
113 {
114    bool ok = true;
115    int count;
116    char ed1[50], ed2[50];
117
118    db_lock(mdb);
119
120    /* Now get count for VolIndex */
121    Mmsg(mdb->cmd, "SELECT count(*) from JobMedia WHERE JobId=%s",
122         edit_int64(jm->JobId, ed1));
123    count = get_sql_record_max(jcr, mdb);
124    if (count < 0) {
125       count = 0;
126    }
127    count++;
128
129    /* Note, jm->Strip is not used and is not likely to be used
130     * in the near future, so I have removed it from the insert
131     * to save space in the DB. KES June 2006.
132     */
133    Mmsg(mdb->cmd,
134         "INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,"
135         "StartFile,EndFile,StartBlock,EndBlock,VolIndex,Copy) "
136         "VALUES (%s,%s,%u,%u,%u,%u,%u,%u,%u,%u)",
137         edit_int64(jm->JobId, ed1),
138         edit_int64(jm->MediaId, ed2),
139         jm->FirstIndex, jm->LastIndex,
140         jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count,
141         jm->Copy);
142
143    Dmsg0(300, mdb->cmd);
144    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
145       Mmsg2(&mdb->errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), mdb->cmd,
146          sql_strerror(mdb));
147       ok = false;
148    } else {
149       /* Worked, now update the Media record with the EndFile and EndBlock */
150       Mmsg(mdb->cmd,
151            "UPDATE Media SET EndFile=%u, EndBlock=%u WHERE MediaId=%u",
152            jm->EndFile, jm->EndBlock, jm->MediaId);
153       if (!UPDATE_DB(jcr, mdb, mdb->cmd)) {
154          Mmsg2(&mdb->errmsg, _("Update Media record %s failed: ERR=%s\n"), mdb->cmd,
155               sql_strerror(mdb));
156          ok = false;
157       }
158    }
159    db_unlock(mdb);
160    Dmsg0(300, "Return from JobMedia\n");
161    return ok;
162 }
163
164 /* Create Unique Pool record
165  * Returns: false on failure
166  *          true  on success
167  */
168 bool
169 db_create_pool_record(JCR *jcr, B_DB *mdb, POOL_DBR *pr)
170 {
171    bool stat;        
172    char ed1[30], ed2[30], ed3[50], ed4[50], ed5[50];
173
174    Dmsg0(200, "In create pool\n");
175    db_lock(mdb);
176    Mmsg(mdb->cmd, "SELECT PoolId,Name FROM Pool WHERE Name='%s'", pr->Name);
177    Dmsg1(200, "selectpool: %s\n", mdb->cmd);
178
179    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
180       mdb->num_rows = sql_num_rows(mdb);
181       if (mdb->num_rows > 0) {
182          Mmsg1(&mdb->errmsg, _("pool record %s already exists\n"), pr->Name);
183          sql_free_result(mdb);
184          db_unlock(mdb);
185          return false;
186       }
187       sql_free_result(mdb);
188    }
189
190    /* Must create it */
191    Mmsg(mdb->cmd,
192 "INSERT INTO Pool (Name,NumVols,MaxVols,UseOnce,UseCatalog,"
193 "AcceptAnyVolume,AutoPrune,Recycle,VolRetention,VolUseDuration,"
194 "MaxVolJobs,MaxVolFiles,MaxVolBytes,PoolType,LabelType,LabelFormat,"
195 "RecyclePoolId,ScratchPoolId,ActionOnPurge) "
196 "VALUES ('%s',%u,%u,%d,%d,%d,%d,%d,%s,%s,%u,%u,%s,'%s',%d,'%s',%s,%s,%d)",
197                   pr->Name,
198                   pr->NumVols, pr->MaxVols,
199                   pr->UseOnce, pr->UseCatalog,
200                   pr->AcceptAnyVolume,
201                   pr->AutoPrune, pr->Recycle,
202                   edit_uint64(pr->VolRetention, ed1),
203                   edit_uint64(pr->VolUseDuration, ed2),
204                   pr->MaxVolJobs, pr->MaxVolFiles,
205                   edit_uint64(pr->MaxVolBytes, ed3),
206                   pr->PoolType, pr->LabelType, pr->LabelFormat,
207                   edit_int64(pr->RecyclePoolId,ed4),
208                   edit_int64(pr->ScratchPoolId,ed5),
209                   pr->ActionOnPurge
210       );
211    Dmsg1(200, "Create Pool: %s\n", mdb->cmd);
212    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
213       Mmsg2(&mdb->errmsg, _("Create db Pool record %s failed: ERR=%s\n"),
214             mdb->cmd, sql_strerror(mdb));
215       pr->PoolId = 0;
216       stat = false;
217    } else {
218       pr->PoolId = sql_insert_id(mdb, NT_("Pool"));
219       stat = true;
220    }
221    db_unlock(mdb);
222    return stat;
223 }
224
225 /*
226  * Create Unique Device record
227  * Returns: false on failure
228  *          true  on success
229  */
230 bool
231 db_create_device_record(JCR *jcr, B_DB *mdb, DEVICE_DBR *dr)
232 {
233    bool ok;
234    char ed1[30], ed2[30];
235
236    Dmsg0(200, "In create Device\n");
237    db_lock(mdb);
238    Mmsg(mdb->cmd, "SELECT DeviceId,Name FROM Device WHERE Name='%s'", dr->Name);
239    Dmsg1(200, "selectdevice: %s\n", mdb->cmd);
240
241    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
242       mdb->num_rows = sql_num_rows(mdb);
243       if (mdb->num_rows > 0) {
244          Mmsg1(&mdb->errmsg, _("Device record %s already exists\n"), dr->Name);
245          sql_free_result(mdb);
246          db_unlock(mdb);
247          return false;
248       }
249       sql_free_result(mdb);
250    }
251
252    /* Must create it */
253    Mmsg(mdb->cmd,
254 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
255                   dr->Name,
256                   edit_uint64(dr->MediaTypeId, ed1),
257                   edit_int64(dr->StorageId, ed2));
258    Dmsg1(200, "Create Device: %s\n", mdb->cmd);
259    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
260       Mmsg2(&mdb->errmsg, _("Create db Device record %s failed: ERR=%s\n"),
261             mdb->cmd, sql_strerror(mdb));
262       dr->DeviceId = 0;
263       ok = false;
264    } else {
265       dr->DeviceId = sql_insert_id(mdb, NT_("Device"));
266       ok = true;
267    }
268    db_unlock(mdb);
269    return ok;
270 }
271
272
273
274 /*
275  * Create a Unique record for Storage -- no duplicates
276  * Returns: false on failure
277  *          true  on success with id in sr->StorageId
278  */
279 bool db_create_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr)
280 {
281    SQL_ROW row;
282    bool ok;
283
284    db_lock(mdb);
285    Mmsg(mdb->cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'", sr->Name);
286
287    sr->StorageId = 0;
288    sr->created = false;
289    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
290       mdb->num_rows = sql_num_rows(mdb);
291       /* If more than one, report error, but return first row */
292       if (mdb->num_rows > 1) {
293          Mmsg1(&mdb->errmsg, _("More than one Storage record!: %d\n"), (int)(mdb->num_rows));
294          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
295       }
296       if (mdb->num_rows >= 1) {
297          if ((row = sql_fetch_row(mdb)) == NULL) {
298             Mmsg1(&mdb->errmsg, _("error fetching Storage row: %s\n"), sql_strerror(mdb));
299             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
300             sql_free_result(mdb);
301             db_unlock(mdb);
302             return false;
303          }
304          sr->StorageId = str_to_int64(row[0]);
305          sr->AutoChanger = atoi(row[1]);   /* bool */
306          sql_free_result(mdb);
307          db_unlock(mdb);
308          return true;
309       }
310       sql_free_result(mdb);
311    }
312
313    /* Must create it */
314    Mmsg(mdb->cmd, "INSERT INTO Storage (Name,AutoChanger)"
315         " VALUES ('%s',%d)", sr->Name, sr->AutoChanger);
316
317    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
318       Mmsg2(&mdb->errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
319             mdb->cmd, sql_strerror(mdb));
320       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
321       ok = false;
322    } else {
323       sr->StorageId = sql_insert_id(mdb, NT_("Storage"));
324       sr->created = true;
325       ok = true;
326    }
327    db_unlock(mdb);
328    return ok;
329 }
330
331
332 /*
333  * Create Unique MediaType record
334  * Returns: false on failure
335  *          true  on success
336  */
337 bool
338 db_create_mediatype_record(JCR *jcr, B_DB *mdb, MEDIATYPE_DBR *mr)
339 {
340    bool stat;        
341
342    Dmsg0(200, "In create mediatype\n");
343    db_lock(mdb);
344    Mmsg(mdb->cmd, "SELECT MediaTypeId,MediaType FROM MediaType WHERE MediaType='%s'", mr->MediaType);
345    Dmsg1(200, "selectmediatype: %s\n", mdb->cmd);
346
347    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
348       mdb->num_rows = sql_num_rows(mdb);
349       if (mdb->num_rows > 0) {
350          Mmsg1(&mdb->errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
351          sql_free_result(mdb);
352          db_unlock(mdb);
353          return false;
354       }
355       sql_free_result(mdb);
356    }
357
358    /* Must create it */
359    Mmsg(mdb->cmd,
360 "INSERT INTO MediaType (MediaType,ReadOnly) "
361 "VALUES ('%s',%d)",
362                   mr->MediaType,
363                   mr->ReadOnly);
364    Dmsg1(200, "Create mediatype: %s\n", mdb->cmd);
365    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
366       Mmsg2(&mdb->errmsg, _("Create db mediatype record %s failed: ERR=%s\n"),
367             mdb->cmd, sql_strerror(mdb));
368       mr->MediaTypeId = 0;
369       stat = false;
370    } else {
371       mr->MediaTypeId = sql_insert_id(mdb, NT_("MediaType"));
372       stat = true;
373    }
374    db_unlock(mdb);
375    return stat;
376 }
377
378
379 /*
380  * Create Media record. VolumeName and non-zero Slot must be unique
381  *
382  * Returns: 0 on failure
383  *          1 on success
384  */
385 int
386 db_create_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
387 {
388    int stat;
389    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
390    char ed9[50], ed10[50], ed11[50], ed12[50];
391    struct tm tm;
392
393    db_lock(mdb);
394    Mmsg(mdb->cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'",
395            mr->VolumeName);
396    Dmsg1(500, "selectpool: %s\n", mdb->cmd);
397
398    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
399       mdb->num_rows = sql_num_rows(mdb);
400       if (mdb->num_rows > 0) {
401          Mmsg1(&mdb->errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
402          sql_free_result(mdb);
403          db_unlock(mdb);
404          return 0;
405       }
406       sql_free_result(mdb);
407    }
408
409    /* Must create it */
410    Mmsg(mdb->cmd,
411 "INSERT INTO Media (VolumeName,MediaType,MediaTypeId,PoolId,MaxVolBytes,"
412 "VolCapacityBytes,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
413 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolParts,"
414 "EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
415 "ScratchPoolId,RecyclePoolId,Enabled,ActionOnPurge)"
416 "VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,0,0,%d,%s,"
417 "%s,%s,%s,%s,%d,%d)",
418           mr->VolumeName,
419           mr->MediaType, mr->PoolId,
420           edit_uint64(mr->MaxVolBytes,ed1),
421           edit_uint64(mr->VolCapacityBytes, ed2),
422           mr->Recycle,
423           edit_uint64(mr->VolRetention, ed3),
424           edit_uint64(mr->VolUseDuration, ed4),
425           mr->MaxVolJobs,
426           mr->MaxVolFiles,
427           mr->VolStatus,
428           mr->Slot,
429           edit_uint64(mr->VolBytes, ed5),
430           mr->InChanger,
431           edit_int64(mr->VolReadTime, ed6),
432           edit_int64(mr->VolWriteTime, ed7),
433           mr->VolParts,
434           mr->LabelType,
435           edit_int64(mr->StorageId, ed8), 
436           edit_int64(mr->DeviceId, ed9), 
437           edit_int64(mr->LocationId, ed10), 
438           edit_int64(mr->ScratchPoolId, ed11), 
439           edit_int64(mr->RecyclePoolId, ed12), 
440           mr->Enabled, mr->ActionOnPurge
441           );
442
443
444    Dmsg1(500, "Create Volume: %s\n", mdb->cmd);
445    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
446       Mmsg2(&mdb->errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
447             mdb->cmd, sql_strerror(mdb));
448       stat = 0;
449    } else {
450       mr->MediaId = sql_insert_id(mdb, NT_("Media"));
451       stat = 1;
452       if (mr->set_label_date) {
453          char dt[MAX_TIME_LENGTH];
454          if (mr->LabelDate == 0) {
455             mr->LabelDate = time(NULL);
456          }
457          (void)localtime_r(&mr->LabelDate, &tm);
458          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
459          Mmsg(mdb->cmd, "UPDATE Media SET LabelDate='%s' "
460               "WHERE MediaId=%d", dt, mr->MediaId);
461          stat = UPDATE_DB(jcr, mdb, mdb->cmd);
462       }
463       /*
464        * Make sure that if InChanger is non-zero any other identical slot
465        *   has InChanger zero.
466        */
467       db_make_inchanger_unique(jcr, mdb, mr);
468    }
469
470    db_unlock(mdb);
471    return stat;
472 }
473
474 /*
475  * Create a Unique record for the client -- no duplicates
476  * Returns: 0 on failure
477  *          1 on success with id in cr->ClientId
478  */
479 int db_create_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr)
480 {
481    SQL_ROW row;
482    int stat;
483    char ed1[50], ed2[50];
484
485    db_lock(mdb);
486    Mmsg(mdb->cmd, "SELECT ClientId,Uname FROM Client WHERE Name='%s'", cr->Name);
487
488    cr->ClientId = 0;
489    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
490       mdb->num_rows = sql_num_rows(mdb);
491       /* If more than one, report error, but return first row */
492       if (mdb->num_rows > 1) {
493          Mmsg1(&mdb->errmsg, _("More than one Client!: %d\n"), (int)(mdb->num_rows));
494          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
495       }
496       if (mdb->num_rows >= 1) {
497          if ((row = sql_fetch_row(mdb)) == NULL) {
498             Mmsg1(&mdb->errmsg, _("error fetching Client row: %s\n"), sql_strerror(mdb));
499             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
500             sql_free_result(mdb);
501             db_unlock(mdb);
502             return 0;
503          }
504          cr->ClientId = str_to_int64(row[0]);
505          if (row[1]) {
506             bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
507          } else {
508             cr->Uname[0] = 0;         /* no name */
509          }
510          sql_free_result(mdb);
511          db_unlock(mdb);
512          return 1;
513       }
514       sql_free_result(mdb);
515    }
516
517    /* Must create it */
518    Mmsg(mdb->cmd, "INSERT INTO Client (Name,Uname,AutoPrune,"
519 "FileRetention,JobRetention) VALUES "
520 "('%s','%s',%d,%s,%s)", cr->Name, cr->Uname, cr->AutoPrune,
521       edit_uint64(cr->FileRetention, ed1),
522       edit_uint64(cr->JobRetention, ed2));
523
524    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
525       Mmsg2(&mdb->errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
526             mdb->cmd, sql_strerror(mdb));
527       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
528       cr->ClientId = 0;
529       stat = 0;
530    } else {
531       cr->ClientId = sql_insert_id(mdb, NT_("Client"));
532       stat = 1;
533    }
534    db_unlock(mdb);
535    return stat;
536 }
537
538
539 /* Create a Unique record for the Path -- no duplicates */
540 int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
541 {
542    SQL_ROW row;
543    int stat;
544
545    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
546    db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
547
548    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
549        strcmp(mdb->cached_path, mdb->path) == 0) {
550       ar->PathId = mdb->cached_path_id;
551       return 1;
552    }
553
554    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
555
556    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
557       mdb->num_rows = sql_num_rows(mdb);
558       if (mdb->num_rows > 1) {
559          char ed1[30];
560          Mmsg2(&mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
561             edit_uint64(mdb->num_rows, ed1), mdb->path);
562          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
563       }
564       /* Even if there are multiple paths, take the first one */
565       if (mdb->num_rows >= 1) {
566          if ((row = sql_fetch_row(mdb)) == NULL) {
567             Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
568             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
569             sql_free_result(mdb);
570             ar->PathId = 0;
571             ASSERT(ar->PathId);
572             return 0;
573          }
574          ar->PathId = str_to_int64(row[0]);
575          sql_free_result(mdb);
576          /* Cache path */
577          if (ar->PathId != mdb->cached_path_id) {
578             mdb->cached_path_id = ar->PathId;
579             mdb->cached_path_len = mdb->pnl;
580             pm_strcpy(mdb->cached_path, mdb->path);
581          }
582          ASSERT(ar->PathId);
583          return 1;
584       }
585       sql_free_result(mdb);
586    }
587
588    Mmsg(mdb->cmd, "INSERT INTO Path (Path) VALUES ('%s')", mdb->esc_name);
589
590    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
591       Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
592          mdb->cmd, sql_strerror(mdb));
593       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
594       ar->PathId = 0;
595       stat = 0;
596    } else {
597       ar->PathId = sql_insert_id(mdb, NT_("Path"));
598       stat = 1;
599    }
600
601    /* Cache path */
602    if (stat && ar->PathId != mdb->cached_path_id) {
603       mdb->cached_path_id = ar->PathId;
604       mdb->cached_path_len = mdb->pnl;
605       pm_strcpy(mdb->cached_path, mdb->path);
606    }
607    return stat;
608 }
609
610 /*
611  * Create a Unique record for the counter -- no duplicates
612  * Returns: 0 on failure
613  *          1 on success with counter filled in
614  */
615 int db_create_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
616 {
617    COUNTER_DBR mcr;
618    int stat;
619
620    db_lock(mdb);
621    memset(&mcr, 0, sizeof(mcr));
622    bstrncpy(mcr.Counter, cr->Counter, sizeof(mcr.Counter));
623    if (db_get_counter_record(jcr, mdb, &mcr)) {
624       memcpy(cr, &mcr, sizeof(COUNTER_DBR));
625       db_unlock(mdb);
626       return 1;
627    }
628
629    /* Must create it */
630    Mmsg(mdb->cmd, "INSERT INTO Counters (Counter,MinValue,MaxValue,CurrentValue,"
631       "WrapCounter) VALUES ('%s','%d','%d','%d','%s')",
632       cr->Counter, cr->MinValue, cr->MaxValue, cr->CurrentValue,
633       cr->WrapCounter);
634
635    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
636       Mmsg2(&mdb->errmsg, _("Create DB Counters record %s failed. ERR=%s\n"),
637             mdb->cmd, sql_strerror(mdb));
638       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
639       stat = 0;
640    } else {
641       stat = 1;
642    }
643    db_unlock(mdb);
644    return stat;
645 }
646
647
648 /*
649  * Create a FileSet record. This record is unique in the
650  *  name and the MD5 signature of the include/exclude sets.
651  *  Returns: 0 on failure
652  *           1 on success with FileSetId in record
653  */
654 bool db_create_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
655 {
656    SQL_ROW row;
657    bool stat;
658    struct tm tm;
659
660    db_lock(mdb);
661    fsr->created = false;
662    Mmsg(mdb->cmd, "SELECT FileSetId,CreateTime FROM FileSet WHERE "
663 "FileSet='%s' AND MD5='%s'", fsr->FileSet, fsr->MD5);
664
665    fsr->FileSetId = 0;
666    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
667       mdb->num_rows = sql_num_rows(mdb);
668       if (mdb->num_rows > 1) {
669          Mmsg1(&mdb->errmsg, _("More than one FileSet!: %d\n"), (int)(mdb->num_rows));
670          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
671       }
672       if (mdb->num_rows >= 1) {
673          if ((row = sql_fetch_row(mdb)) == NULL) {
674             Mmsg1(&mdb->errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror(mdb));
675             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
676             sql_free_result(mdb);
677             db_unlock(mdb);
678             return false;
679          }
680          fsr->FileSetId = str_to_int64(row[0]);
681          if (row[1] == NULL) {
682             fsr->cCreateTime[0] = 0;
683          } else {
684             bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
685          }
686          sql_free_result(mdb);
687          db_unlock(mdb);
688          return true;
689       }
690       sql_free_result(mdb);
691    }
692
693    if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
694       fsr->CreateTime = time(NULL);
695    }
696    (void)localtime_r(&fsr->CreateTime, &tm);
697    strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
698
699    /* Must create it */
700       Mmsg(mdb->cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
701 "VALUES ('%s','%s','%s')", fsr->FileSet, fsr->MD5, fsr->cCreateTime);
702
703    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
704       Mmsg2(&mdb->errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
705             mdb->cmd, sql_strerror(mdb));
706       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
707       fsr->FileSetId = 0;
708       stat = false;
709    } else {
710       fsr->FileSetId = sql_insert_id(mdb, NT_("FileSet"));
711       fsr->created = true;
712       stat = true;
713    }
714
715    db_unlock(mdb);
716    return stat;
717 }
718
719
720 /*
721  *  struct stat
722  *  {
723  *      dev_t         st_dev;       * device *
724  *      ino_t         st_ino;       * inode *
725  *      mode_t        st_mode;      * protection *
726  *      nlink_t       st_nlink;     * number of hard links *
727  *      uid_t         st_uid;       * user ID of owner *
728  *      gid_t         st_gid;       * group ID of owner *
729  *      dev_t         st_rdev;      * device type (if inode device) *
730  *      off_t         st_size;      * total size, in bytes *
731  *      unsigned long st_blksize;   * blocksize for filesystem I/O *
732  *      unsigned long st_blocks;    * number of blocks allocated *
733  *      time_t        st_atime;     * time of last access *
734  *      time_t        st_mtime;     * time of last modification *
735  *      time_t        st_ctime;     * time of last inode change *
736  *  };
737  */
738
739 #ifdef HAVE_BATCH_FILE_INSERT
740
741 /*  All sql_batch_* functions are used to do bulk batch insert in File/Filename/Path
742  *  tables. This code can be activated by adding "#define HAVE_BATCH_FILE_INSERT 1"
743  *  in baconfig.h
744  *  
745  *  To sum up :
746  *   - bulk load a temp table
747  *   - insert missing filenames into filename with a single query (lock filenames 
748  *   - table before that to avoid possible duplicate inserts with concurrent update)
749  *   - insert missing paths into path with another single query
750  *   - then insert the join between the temp, filename and path tables into file.
751  */
752
753 /* 
754  * Returns 1 if OK
755  *         0 if failed
756  */
757 bool my_batch_start(JCR *jcr, B_DB *mdb)
758 {
759    bool ok;
760
761    db_lock(mdb);
762    ok =  db_sql_query(mdb,
763              "CREATE TEMPORARY TABLE batch ("
764                 "FileIndex integer,"
765                 "JobId integer,"
766                 "Path blob,"
767                 "Name blob,"
768                 "LStat tinyblob,"
769                 "MD5 tinyblob)",NULL, NULL);
770    db_unlock(mdb);
771    return ok;
772 }
773
774 /* 
775  * Returns 1 if OK
776  *         0 if failed
777  */
778 bool my_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
779 {
780    size_t len;
781    const char *digest;
782    char ed1[50];
783
784    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
785    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
786
787    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1);
788    db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
789
790    if (ar->Digest == NULL || ar->Digest[0] == 0) {
791       digest = "0";
792    } else {
793       digest = ar->Digest;
794    }
795
796    len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
797               ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
798               mdb->esc_name, ar->attr, digest);
799
800    return INSERT_DB(jcr, mdb, mdb->cmd);
801 }
802
803 /* set error to something to abort operation */
804 /* 
805  * Returns 1 if OK
806  *         0 if failed
807  */
808 bool my_batch_end(JCR *jcr, B_DB *mdb, const char *error)
809 {
810    
811    Dmsg0(50, "sql_batch_end started\n");
812
813    if (mdb) {
814 #ifdef HAVE_DBI 
815       mdb->status = (dbi_error_flag)0;
816 #else
817       mdb->status = 0;
818 #endif
819    }
820    return true;
821 }
822
823 /* 
824  * Returns 1 if OK
825  *         0 if failed
826  */
827 bool db_write_batch_file_records(JCR *jcr)
828 {
829    int JobStatus = jcr->JobStatus;
830
831    if (!jcr->batch_started) {         /* no files to backup ? */
832       Dmsg0(50,"db_create_file_record : no files\n");
833       return true;
834    }
835    if (job_canceled(jcr)) {
836       return false;
837    }
838
839    Dmsg1(50,"db_create_file_record changes=%u\n",jcr->db_batch->changes);
840
841    jcr->JobStatus = JS_AttrInserting;
842    if (!sql_batch_end(jcr, jcr->db_batch, NULL)) {
843       Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
844       return false;
845    }
846    if (job_canceled(jcr)) {
847       return false;
848    }
849
850
851    /* we have to lock tables */
852    if (!db_sql_query(jcr->db_batch, sql_batch_lock_path_query, NULL, NULL)) {
853       Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
854       return false;
855    }
856
857    if (!db_sql_query(jcr->db_batch, sql_batch_fill_path_query, NULL, NULL)) {
858       Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
859       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
860       return false;
861    }
862    
863    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
864       Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
865       return false;      
866    }
867
868    /* we have to lock tables */
869    if (!db_sql_query(jcr->db_batch,sql_batch_lock_filename_query,NULL, NULL)) {
870       Jmsg1(jcr, M_FATAL, 0, "Lock Filename table %s\n", jcr->db_batch->errmsg);
871       return false;
872    }
873    
874    if (!db_sql_query(jcr->db_batch,sql_batch_fill_filename_query, NULL,NULL)) {
875       Jmsg1(jcr,M_FATAL,0,"Fill Filename table %s\n",jcr->db_batch->errmsg);
876       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
877       return false;            
878    }
879
880    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
881       Jmsg1(jcr, M_FATAL, 0, "Unlock Filename table %s\n", jcr->db_batch->errmsg);
882       return false;
883    }
884    
885    if (!db_sql_query(jcr->db_batch, 
886        "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5)"
887          "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
888                 "Filename.FilenameId,batch.LStat, batch.MD5 "
889            "FROM batch "
890            "JOIN Path ON (batch.Path = Path.Path) "
891            "JOIN Filename ON (batch.Name = Filename.Name)",
892                      NULL,NULL))
893    {
894       Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
895       return false;
896    }
897
898    db_sql_query(jcr->db_batch, "DROP TABLE batch", NULL,NULL);
899
900    jcr->JobStatus = JobStatus;         /* reset entry status */
901    return true;
902 }
903
904 /*
905  * Create File record in B_DB
906  *
907  *  In order to reduce database size, we store the File attributes,
908  *  the FileName, and the Path separately.  In principle, there
909  *  is a single FileName record and a single Path record, no matter
910  *  how many times it occurs.  This is this subroutine, we separate
911  *  the file and the path and fill temporary tables with this three records.
912  *
913  *  Note: all routines that call this expect to be able to call
914  *    db_strerror(mdb) to get the error message, so the error message
915  *    MUST be edited into mdb->errmsg before returning an error status.
916  */
917 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
918 {
919    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
920    Dmsg0(dbglevel, "put_file_into_catalog\n");
921
922    /* Open the dedicated connexion */
923    if (!jcr->batch_started) {
924       if (!db_open_batch_connexion(jcr, mdb)) {
925          return false;     /* error already printed */
926       }
927       if (!sql_batch_start(jcr, jcr->db_batch)) {
928          Mmsg1(&mdb->errmsg, 
929               "Can't start batch mode: ERR=%s", db_strerror(jcr->db_batch));
930          Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
931          return false;
932       }
933       jcr->batch_started = true;
934    }
935    B_DB *bdb = jcr->db_batch;
936
937    /*
938     * Make sure we have an acceptable attributes record.
939     */
940    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
941          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
942       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
943          ar->Stream);
944       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
945       return false;
946    }
947
948    split_path_and_file(jcr, bdb, ar->fname);
949
950
951 /*
952  * if (bdb->changes > 100000) {
953  *    db_write_batch_file_records(jcr);
954  *    bdb->changes = 0;
955  *     sql_batch_start(jcr, bdb);
956  * }
957  */
958
959    return sql_batch_insert(jcr, bdb, ar);
960 }
961
962 #else  /* ! HAVE_BATCH_FILE_INSERT */
963
964 /*
965  * Create File record in B_DB
966  *
967  *  In order to reduce database size, we store the File attributes,
968  *  the FileName, and the Path separately.  In principle, there
969  *  is a single FileName record and a single Path record, no matter
970  *  how many times it occurs.  This is this subroutine, we separate
971  *  the file and the path and create three database records.
972  */
973 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
974 {
975    db_lock(mdb);
976    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
977    Dmsg0(dbglevel, "put_file_into_catalog\n");
978    /*
979     * Make sure we have an acceptable attributes record.
980     */
981    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
982          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
983       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
984          ar->Stream);
985       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
986       goto bail_out;
987    }
988
989
990    split_path_and_file(jcr, mdb, ar->fname);
991
992    if (!db_create_filename_record(jcr, mdb, ar)) {
993       goto bail_out;
994    }
995    Dmsg1(dbglevel, "db_create_filename_record: %s\n", mdb->esc_name);
996
997
998    if (!db_create_path_record(jcr, mdb, ar)) {
999       goto bail_out;
1000    }
1001    Dmsg1(dbglevel, "db_create_path_record: %s\n", mdb->esc_name);
1002
1003    /* Now create master File record */
1004    if (!db_create_file_record(jcr, mdb, ar)) {
1005       goto bail_out;
1006    }
1007    Dmsg0(dbglevel, "db_create_file_record OK\n");
1008
1009    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", mdb->path, mdb->fname, ar->FilenameId);
1010    db_unlock(mdb);
1011    return true;
1012
1013 bail_out:
1014    db_unlock(mdb);
1015    return false;
1016 }
1017
1018
1019 /*
1020  * This is the master File entry containing the attributes.
1021  *  The filename and path records have already been created.
1022  */
1023 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1024 {
1025    int stat;
1026    static const char *no_digest = "0";
1027    const char *digest;
1028
1029    ASSERT(ar->JobId);
1030    ASSERT(ar->PathId);
1031    ASSERT(ar->FilenameId);
1032
1033    if (ar->Digest == NULL || ar->Digest[0] == 0) {
1034       digest = no_digest;
1035    } else {
1036       digest = ar->Digest;
1037    }
1038
1039    /* Must create it */
1040    Mmsg(mdb->cmd,
1041         "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
1042         "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')",
1043         ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
1044         ar->attr, digest);
1045
1046    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1047       Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
1048          mdb->cmd, sql_strerror(mdb));
1049       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1050       ar->FileId = 0;
1051       stat = 0;
1052    } else {
1053       ar->FileId = sql_insert_id(mdb, NT_("File"));
1054       stat = 1;
1055    }
1056    return stat;
1057 }
1058
1059 /* Create a Unique record for the filename -- no duplicates */
1060 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1061 {
1062    SQL_ROW row;
1063
1064    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1065    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1066    
1067    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
1068
1069    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1070       mdb->num_rows = sql_num_rows(mdb);
1071       if (mdb->num_rows > 1) {
1072          char ed1[30];
1073          Mmsg2(&mdb->errmsg, _("More than one Filename! %s for file: %s\n"),
1074             edit_uint64(mdb->num_rows, ed1), mdb->fname);
1075          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1076       }
1077       if (mdb->num_rows >= 1) {
1078          if ((row = sql_fetch_row(mdb)) == NULL) {
1079             Mmsg2(&mdb->errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
1080                 mdb->fname, sql_strerror(mdb));
1081             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1082             ar->FilenameId = 0;
1083          } else {
1084             ar->FilenameId = str_to_int64(row[0]);
1085          }
1086          sql_free_result(mdb);
1087          return ar->FilenameId > 0;
1088       }
1089       sql_free_result(mdb);
1090    }
1091
1092    Mmsg(mdb->cmd, "INSERT INTO Filename (Name) VALUES ('%s')", mdb->esc_name);
1093
1094    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1095       Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1096             mdb->cmd, sql_strerror(mdb));
1097       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1098       ar->FilenameId = 0;
1099    } else {
1100       ar->FilenameId = sql_insert_id(mdb, NT_("Filename"));
1101    }
1102    return ar->FilenameId > 0;
1103 }
1104
1105 bool db_write_batch_file_records(JCR *jcr)
1106 {
1107    return true;
1108 }
1109
1110 #endif /* ! HAVE_BATCH_FILE_INSERT */
1111
1112 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */