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