]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_create.c
kes Apply Eric's scratch patch that moves a purged Volume to
[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];
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) "
196 "VALUES ('%s',%u,%u,%d,%d,%d,%d,%d,%s,%s,%u,%u,%s,'%s',%d,'%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    Dmsg1(200, "Create Pool: %s\n", mdb->cmd);
208    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
209       Mmsg2(&mdb->errmsg, _("Create db Pool record %s failed: ERR=%s\n"),
210             mdb->cmd, sql_strerror(mdb));
211       pr->PoolId = 0;
212       stat = false;
213    } else {
214       pr->PoolId = sql_insert_id(mdb, NT_("Pool"));
215       stat = true;
216    }
217    db_unlock(mdb);
218    return stat;
219 }
220
221 /*
222  * Create Unique Device record
223  * Returns: false on failure
224  *          true  on success
225  */
226 bool
227 db_create_device_record(JCR *jcr, B_DB *mdb, DEVICE_DBR *dr)
228 {
229    bool ok;
230    char ed1[30], ed2[30];
231
232    Dmsg0(200, "In create Device\n");
233    db_lock(mdb);
234    Mmsg(mdb->cmd, "SELECT DeviceId,Name FROM Device WHERE Name='%s'", dr->Name);
235    Dmsg1(200, "selectdevice: %s\n", mdb->cmd);
236
237    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
238       mdb->num_rows = sql_num_rows(mdb);
239       if (mdb->num_rows > 0) {
240          Mmsg1(&mdb->errmsg, _("Device record %s already exists\n"), dr->Name);
241          sql_free_result(mdb);
242          db_unlock(mdb);
243          return false;
244       }
245       sql_free_result(mdb);
246    }
247
248    /* Must create it */
249    Mmsg(mdb->cmd,
250 "INSERT INTO Device (Name,MediaTypeId,StorageId) VALUES ('%s',%s,%s)",
251                   dr->Name,
252                   edit_uint64(dr->MediaTypeId, ed1),
253                   edit_int64(dr->StorageId, ed2));
254    Dmsg1(200, "Create Device: %s\n", mdb->cmd);
255    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
256       Mmsg2(&mdb->errmsg, _("Create db Device record %s failed: ERR=%s\n"),
257             mdb->cmd, sql_strerror(mdb));
258       dr->DeviceId = 0;
259       ok = false;
260    } else {
261       dr->DeviceId = sql_insert_id(mdb, NT_("Device"));
262       ok = true;
263    }
264    db_unlock(mdb);
265    return ok;
266 }
267
268
269
270 /*
271  * Create a Unique record for Storage -- no duplicates
272  * Returns: false on failure
273  *          true  on success with id in sr->StorageId
274  */
275 bool db_create_storage_record(JCR *jcr, B_DB *mdb, STORAGE_DBR *sr)
276 {
277    SQL_ROW row;
278    bool ok;
279
280    db_lock(mdb);
281    Mmsg(mdb->cmd, "SELECT StorageId,AutoChanger FROM Storage WHERE Name='%s'", sr->Name);
282
283    sr->StorageId = 0;
284    sr->created = false;
285    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
286       mdb->num_rows = sql_num_rows(mdb);
287       /* If more than one, report error, but return first row */
288       if (mdb->num_rows > 1) {
289          Mmsg1(&mdb->errmsg, _("More than one Storage record!: %d\n"), (int)(mdb->num_rows));
290          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
291       }
292       if (mdb->num_rows >= 1) {
293          if ((row = sql_fetch_row(mdb)) == NULL) {
294             Mmsg1(&mdb->errmsg, _("error fetching Storage row: %s\n"), sql_strerror(mdb));
295             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
296             sql_free_result(mdb);
297             db_unlock(mdb);
298             return false;
299          }
300          sr->StorageId = str_to_int64(row[0]);
301          sr->AutoChanger = atoi(row[1]);   /* bool */
302          sql_free_result(mdb);
303          db_unlock(mdb);
304          return true;
305       }
306       sql_free_result(mdb);
307    }
308
309    /* Must create it */
310    Mmsg(mdb->cmd, "INSERT INTO Storage (Name,AutoChanger)"
311         " VALUES ('%s',%d)", sr->Name, sr->AutoChanger);
312
313    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
314       Mmsg2(&mdb->errmsg, _("Create DB Storage record %s failed. ERR=%s\n"),
315             mdb->cmd, sql_strerror(mdb));
316       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
317       ok = false;
318    } else {
319       sr->StorageId = sql_insert_id(mdb, NT_("Storage"));
320       sr->created = true;
321       ok = true;
322    }
323    db_unlock(mdb);
324    return ok;
325 }
326
327
328 /*
329  * Create Unique MediaType record
330  * Returns: false on failure
331  *          true  on success
332  */
333 bool
334 db_create_mediatype_record(JCR *jcr, B_DB *mdb, MEDIATYPE_DBR *mr)
335 {
336    bool stat;        
337
338    Dmsg0(200, "In create mediatype\n");
339    db_lock(mdb);
340    Mmsg(mdb->cmd, "SELECT MediaTypeId,MediaType FROM MediaType WHERE MediaType='%s'", mr->MediaType);
341    Dmsg1(200, "selectmediatype: %s\n", mdb->cmd);
342
343    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
344       mdb->num_rows = sql_num_rows(mdb);
345       if (mdb->num_rows > 0) {
346          Mmsg1(&mdb->errmsg, _("mediatype record %s already exists\n"), mr->MediaType);
347          sql_free_result(mdb);
348          db_unlock(mdb);
349          return false;
350       }
351       sql_free_result(mdb);
352    }
353
354    /* Must create it */
355    Mmsg(mdb->cmd,
356 "INSERT INTO MediaType (MediaType,ReadOnly) "
357 "VALUES ('%s',%d)",
358                   mr->MediaType,
359                   mr->ReadOnly);
360    Dmsg1(200, "Create mediatype: %s\n", mdb->cmd);
361    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
362       Mmsg2(&mdb->errmsg, _("Create db mediatype record %s failed: ERR=%s\n"),
363             mdb->cmd, sql_strerror(mdb));
364       mr->MediaTypeId = 0;
365       stat = false;
366    } else {
367       mr->MediaTypeId = sql_insert_id(mdb, NT_("MediaType"));
368       stat = true;
369    }
370    db_unlock(mdb);
371    return stat;
372 }
373
374
375 /*
376  * Create Media record. VolumeName and non-zero Slot must be unique
377  *
378  * Returns: 0 on failure
379  *          1 on success
380  */
381 int
382 db_create_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr)
383 {
384    int stat;
385    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
386    char ed9[50], ed10[50], ed11[50], ed12[50];
387    struct tm tm;
388
389    db_lock(mdb);
390    Mmsg(mdb->cmd, "SELECT MediaId FROM Media WHERE VolumeName='%s'",
391            mr->VolumeName);
392    Dmsg1(500, "selectpool: %s\n", mdb->cmd);
393
394    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
395       mdb->num_rows = sql_num_rows(mdb);
396       if (mdb->num_rows > 0) {
397          Mmsg1(&mdb->errmsg, _("Volume \"%s\" already exists.\n"), mr->VolumeName);
398          sql_free_result(mdb);
399          db_unlock(mdb);
400          return 0;
401       }
402       sql_free_result(mdb);
403    }
404
405    /* Must create it */
406    Mmsg(mdb->cmd,
407 "INSERT INTO Media (VolumeName,MediaType,MediaTypeId,PoolId,MaxVolBytes,"
408 "VolCapacityBytes,Recycle,VolRetention,VolUseDuration,MaxVolJobs,MaxVolFiles,"
409 "VolStatus,Slot,VolBytes,InChanger,VolReadTime,VolWriteTime,VolParts,"
410 "EndFile,EndBlock,LabelType,StorageId,DeviceId,LocationId,"
411 "ScratchPoolId,RecyclePoolId,Enabled)"
412 "VALUES ('%s','%s',0,%u,%s,%s,%d,%s,%s,%u,%u,'%s',%d,%s,%d,%s,%s,%d,0,0,%d,%s,"
413 "%s,%s,%s,%s,%d)",
414           mr->VolumeName,
415           mr->MediaType, mr->PoolId,
416           edit_uint64(mr->MaxVolBytes,ed1),
417           edit_uint64(mr->VolCapacityBytes, ed2),
418           mr->Recycle,
419           edit_uint64(mr->VolRetention, ed3),
420           edit_uint64(mr->VolUseDuration, ed4),
421           mr->MaxVolJobs,
422           mr->MaxVolFiles,
423           mr->VolStatus,
424           mr->Slot,
425           edit_uint64(mr->VolBytes, ed5),
426           mr->InChanger,
427           edit_uint64(mr->VolReadTime, ed6),
428           edit_uint64(mr->VolWriteTime, ed7),
429           mr->VolParts,
430           mr->LabelType,
431           edit_int64(mr->StorageId, ed8), 
432           edit_int64(mr->DeviceId, ed9), 
433           edit_int64(mr->LocationId, ed10), 
434           edit_int64(mr->ScratchPoolId, ed11), 
435           edit_int64(mr->RecyclePoolId, ed12), 
436           mr->Enabled
437           );
438
439
440    Dmsg1(500, "Create Volume: %s\n", mdb->cmd);
441    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
442       Mmsg2(&mdb->errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
443             mdb->cmd, sql_strerror(mdb));
444       stat = 0;
445    } else {
446       mr->MediaId = sql_insert_id(mdb, NT_("Media"));
447       stat = 1;
448       if (mr->set_label_date) {
449          char dt[MAX_TIME_LENGTH];
450          if (mr->LabelDate == 0) {
451             mr->LabelDate = time(NULL);
452          }
453          (void)localtime_r(&mr->LabelDate, &tm);
454          strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
455          Mmsg(mdb->cmd, "UPDATE Media SET LabelDate='%s' "
456               "WHERE MediaId=%d", dt, mr->MediaId);
457          stat = UPDATE_DB(jcr, mdb, mdb->cmd);
458       }
459    }
460
461    /*
462     * Make sure that if InChanger is non-zero any other identical slot
463     *   has InChanger zero.
464     */
465    db_make_inchanger_unique(jcr, mdb, mr);
466
467    db_unlock(mdb);
468    return stat;
469 }
470
471 /*
472  * Create a Unique record for the client -- no duplicates
473  * Returns: 0 on failure
474  *          1 on success with id in cr->ClientId
475  */
476 int db_create_client_record(JCR *jcr, B_DB *mdb, CLIENT_DBR *cr)
477 {
478    SQL_ROW row;
479    int stat;
480    char ed1[50], ed2[50];
481
482    db_lock(mdb);
483    Mmsg(mdb->cmd, "SELECT ClientId,Uname FROM Client WHERE Name='%s'", cr->Name);
484
485    cr->ClientId = 0;
486    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
487       mdb->num_rows = sql_num_rows(mdb);
488       /* If more than one, report error, but return first row */
489       if (mdb->num_rows > 1) {
490          Mmsg1(&mdb->errmsg, _("More than one Client!: %d\n"), (int)(mdb->num_rows));
491          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
492       }
493       if (mdb->num_rows >= 1) {
494          if ((row = sql_fetch_row(mdb)) == NULL) {
495             Mmsg1(&mdb->errmsg, _("error fetching Client row: %s\n"), sql_strerror(mdb));
496             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
497             sql_free_result(mdb);
498             db_unlock(mdb);
499             return 0;
500          }
501          cr->ClientId = str_to_int64(row[0]);
502          if (row[1]) {
503             bstrncpy(cr->Uname, row[1], sizeof(cr->Uname));
504          } else {
505             cr->Uname[0] = 0;         /* no name */
506          }
507          sql_free_result(mdb);
508          db_unlock(mdb);
509          return 1;
510       }
511       sql_free_result(mdb);
512    }
513
514    /* Must create it */
515    Mmsg(mdb->cmd, "INSERT INTO Client (Name,Uname,AutoPrune,"
516 "FileRetention,JobRetention) VALUES "
517 "('%s','%s',%d,%s,%s)", cr->Name, cr->Uname, cr->AutoPrune,
518       edit_uint64(cr->FileRetention, ed1),
519       edit_uint64(cr->JobRetention, ed2));
520
521    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
522       Mmsg2(&mdb->errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
523             mdb->cmd, sql_strerror(mdb));
524       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
525       cr->ClientId = 0;
526       stat = 0;
527    } else {
528       cr->ClientId = sql_insert_id(mdb, NT_("Client"));
529       stat = 1;
530    }
531    db_unlock(mdb);
532    return stat;
533 }
534
535
536
537
538
539 /*
540  * Create a Unique record for the counter -- no duplicates
541  * Returns: 0 on failure
542  *          1 on success with counter filled in
543  */
544 int db_create_counter_record(JCR *jcr, B_DB *mdb, COUNTER_DBR *cr)
545 {
546    COUNTER_DBR mcr;
547    int stat;
548
549    db_lock(mdb);
550    memset(&mcr, 0, sizeof(mcr));
551    bstrncpy(mcr.Counter, cr->Counter, sizeof(mcr.Counter));
552    if (db_get_counter_record(jcr, mdb, &mcr)) {
553       memcpy(cr, &mcr, sizeof(COUNTER_DBR));
554       db_unlock(mdb);
555       return 1;
556    }
557
558    /* Must create it */
559    Mmsg(mdb->cmd, "INSERT INTO Counters (Counter,MinValue,MaxValue,CurrentValue,"
560       "WrapCounter) VALUES ('%s','%d','%d','%d','%s')",
561       cr->Counter, cr->MinValue, cr->MaxValue, cr->CurrentValue,
562       cr->WrapCounter);
563
564    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
565       Mmsg2(&mdb->errmsg, _("Create DB Counters record %s failed. ERR=%s\n"),
566             mdb->cmd, sql_strerror(mdb));
567       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
568       stat = 0;
569    } else {
570       stat = 1;
571    }
572    db_unlock(mdb);
573    return stat;
574 }
575
576
577 /*
578  * Create a FileSet record. This record is unique in the
579  *  name and the MD5 signature of the include/exclude sets.
580  *  Returns: 0 on failure
581  *           1 on success with FileSetId in record
582  */
583 bool db_create_fileset_record(JCR *jcr, B_DB *mdb, FILESET_DBR *fsr)
584 {
585    SQL_ROW row;
586    bool stat;
587    struct tm tm;
588
589    db_lock(mdb);
590    fsr->created = false;
591    Mmsg(mdb->cmd, "SELECT FileSetId,CreateTime FROM FileSet WHERE "
592 "FileSet='%s' AND MD5='%s'", fsr->FileSet, fsr->MD5);
593
594    fsr->FileSetId = 0;
595    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
596       mdb->num_rows = sql_num_rows(mdb);
597       if (mdb->num_rows > 1) {
598          Mmsg1(&mdb->errmsg, _("More than one FileSet!: %d\n"), (int)(mdb->num_rows));
599          Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
600       }
601       if (mdb->num_rows >= 1) {
602          if ((row = sql_fetch_row(mdb)) == NULL) {
603             Mmsg1(&mdb->errmsg, _("error fetching FileSet row: ERR=%s\n"), sql_strerror(mdb));
604             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
605             sql_free_result(mdb);
606             db_unlock(mdb);
607             return false;
608          }
609          fsr->FileSetId = str_to_int64(row[0]);
610          if (row[1] == NULL) {
611             fsr->cCreateTime[0] = 0;
612          } else {
613             bstrncpy(fsr->cCreateTime, row[1], sizeof(fsr->cCreateTime));
614          }
615          sql_free_result(mdb);
616          db_unlock(mdb);
617          return true;
618       }
619       sql_free_result(mdb);
620    }
621
622    if (fsr->CreateTime == 0 && fsr->cCreateTime[0] == 0) {
623       fsr->CreateTime = time(NULL);
624    }
625    (void)localtime_r(&fsr->CreateTime, &tm);
626    strftime(fsr->cCreateTime, sizeof(fsr->cCreateTime), "%Y-%m-%d %H:%M:%S", &tm);
627
628    /* Must create it */
629       Mmsg(mdb->cmd, "INSERT INTO FileSet (FileSet,MD5,CreateTime) "
630 "VALUES ('%s','%s','%s')", fsr->FileSet, fsr->MD5, fsr->cCreateTime);
631
632    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
633       Mmsg2(&mdb->errmsg, _("Create DB FileSet record %s failed. ERR=%s\n"),
634             mdb->cmd, sql_strerror(mdb));
635       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
636       fsr->FileSetId = 0;
637       stat = false;
638    } else {
639       fsr->FileSetId = sql_insert_id(mdb, NT_("FileSet"));
640       fsr->created = true;
641       stat = true;
642    }
643
644    db_unlock(mdb);
645    return stat;
646 }
647
648
649 /*
650  *  struct stat
651  *  {
652  *      dev_t         st_dev;       * device *
653  *      ino_t         st_ino;       * inode *
654  *      mode_t        st_mode;      * protection *
655  *      nlink_t       st_nlink;     * number of hard links *
656  *      uid_t         st_uid;       * user ID of owner *
657  *      gid_t         st_gid;       * group ID of owner *
658  *      dev_t         st_rdev;      * device type (if inode device) *
659  *      off_t         st_size;      * total size, in bytes *
660  *      unsigned long st_blksize;   * blocksize for filesystem I/O *
661  *      unsigned long st_blocks;    * number of blocks allocated *
662  *      time_t        st_atime;     * time of last access *
663  *      time_t        st_mtime;     * time of last modification *
664  *      time_t        st_ctime;     * time of last inode change *
665  *  };
666  */
667
668 /*  All db_batch_* functions are used to do bulk batch insert in File/Filename/Path
669  *  tables. This code can be activated by adding "#define HAVE_BATCH_FILE_INSERT 1"
670  *  in baconfig.h
671  *  
672  *  To sum up :
673  *   - bulk load a temp table
674  *   - insert missing filenames into filename with a single query (lock filenames 
675  *   - table before that to avoid possible duplicate inserts with concurrent update)
676  *   - insert missing paths into path with another single query
677  *   - then insert the join between the temp, filename and path tables into file.
678  */
679
680 int db_batch_start(B_DB *mdb)
681 {
682    return sql_query(mdb,
683              " CREATE TEMPORARY TABLE batch "
684              "        (fileindex integer,   "
685              "        jobid integer,        "
686              "        path blob,            "
687              "        name blob,            "
688              "        lstat tinyblob,       "
689              "        md5 tinyblob)         ");
690 }
691
692 int db_batch_insert(B_DB *mdb, ATTR_DBR *ar)
693 {
694    size_t len;
695    char *digest;
696    char ed1[50];
697
698    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
699    db_escape_string(mdb->esc_name, mdb->fname, mdb->fnl);
700
701    mdb->esc_name2 = check_pool_memory_size(mdb->esc_name2, mdb->pnl*2+1);
702    db_escape_string(mdb->esc_name2, mdb->path, mdb->pnl);
703
704    if (ar->Digest == NULL || ar->Digest[0] == 0) {
705       digest = "0";
706    } else {
707       digest = ar->Digest;
708    }
709
710    len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
711               ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->path, 
712               mdb->fname, ar->attr, digest);
713
714    sql_query(mdb, mdb->cmd);
715
716    return mdb->status;
717 }
718
719 /* set error to something to abort operation */
720 int db_batch_end(B_DB *mdb, const char *error)
721 {
722    
723    Dmsg0(50, "db_batch_end started");
724
725    if (mdb) {
726       mdb->status = 0;
727       return mdb->status;
728    }
729    return 0;
730 }
731
732 int db_create_batch_file_record(JCR *jcr)
733 {
734    Dmsg0(50,"db_create_file_record : no files");
735
736    if (!jcr->db_batch) {         /* no files to backup ? */
737       Dmsg0(50,"db_create_file_record : no files\n");
738       return 0;
739    }
740
741    if (sql_batch_end(jcr->db_batch, NULL)) {
742       Jmsg(jcr, M_FATAL, 0, "Bad batch end %s\n", jcr->db_batch->errmsg);
743       return 1;
744    }
745
746    /* we have to lock tables */
747    if (sql_query(jcr->db_batch, sql_batch_lock_path_query))
748    {
749       Jmsg(jcr, M_FATAL, 0, "Can't lock Path table %s\n", jcr->db_batch->errmsg);
750       return 1;
751    }
752
753    if (sql_query(jcr->db_batch, sql_batch_fill_path_query))
754    {
755       Jmsg(jcr, M_FATAL, 0, "Can't fill Path table %s\n",jcr->db_batch->errmsg);
756       sql_query(jcr->db_batch, sql_batch_unlock_tables_query);
757       return 1;
758    }
759    
760    if (sql_query(jcr->db_batch, sql_batch_unlock_tables_query))
761    {
762       Jmsg(jcr, M_FATAL, 0, "Can't unlock Path table %s\n", jcr->db_batch->errmsg);
763       return 1;      
764    }
765
766    /* we have to lock tables */
767    if (sql_query(jcr->db_batch, sql_batch_lock_filename_query))
768    {
769       Jmsg(jcr, M_FATAL, 0, "Can't lock Filename table %s\n", jcr->db_batch->errmsg);
770       return 1;
771    }
772    
773    if (sql_query(jcr->db_batch, sql_batch_fill_filename_query))
774    {
775       Jmsg(jcr,M_FATAL,0,"Can't fill Filename table %s\n",jcr->db_batch->errmsg);
776       sql_query(jcr->db_batch, sql_batch_unlock_tables_query);
777       return 1;            
778    }
779
780    if (sql_query(jcr->db_batch, sql_batch_unlock_tables_query)) {
781       Jmsg(jcr, M_FATAL, 0, "Can't unlock Filename table %s\n", jcr->db_batch->errmsg);
782       return 1;
783    }
784    
785    if (sql_query(jcr->db_batch, 
786        " INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5)"
787        "  SELECT batch.FileIndex, batch.JobId, Path.PathId,               " 
788        "         Filename.FilenameId,batch.LStat, batch.MD5               "
789        "  FROM batch                                                      "
790        "    JOIN Path ON (batch.Path = Path.Path)                         "
791        "    JOIN Filename ON (batch.Name = Filename.Name)                 "))
792    {
793       Jmsg(jcr, M_FATAL, 0, "Can't fill File table %s\n", jcr->db_batch->errmsg);
794       return 1;
795    }
796
797    sql_query(jcr->db_batch, "DROP TABLE batch");
798
799    return 0;
800 }
801
802 #ifdef HAVE_BATCH_FILE_INSERT
803 /*
804  * Create File record in B_DB
805  *
806  *  In order to reduce database size, we store the File attributes,
807  *  the FileName, and the Path separately.  In principle, there
808  *  is a single FileName record and a single Path record, no matter
809  *  how many times it occurs.  This is this subroutine, we separate
810  *  the file and the path and fill temporary tables with this three records.
811  */
812 int db_create_file_attributes_record(JCR *jcr, B_DB *_mdb, ATTR_DBR *ar)
813 {
814
815    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
816    Dmsg0(dbglevel, "put_file_into_catalog\n");
817
818    if (!jcr->db_batch) {
819       jcr->db_batch = db_init_database(jcr, 
820                                       jcr->db->db_name, 
821                                       jcr->db->db_user,
822                                       jcr->db->db_password, 
823                                       jcr->db->db_address,
824                                       jcr->db->db_port,
825                                       jcr->db->db_socket,
826                                       1 /* multi_db = true */);
827
828       if (!jcr->db_batch || !db_open_database(jcr, jcr->db_batch)) {
829          Jmsg(jcr, M_FATAL, 0, _("Could not open database \"%s\".\n"),
830               jcr->db->db_name);
831          if (jcr->db_batch) {
832             Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db_batch));
833          }
834          return 0;
835       }      
836       
837       sql_batch_start(jcr->db_batch);
838    }
839
840    B_DB *mdb = jcr->db_batch;
841
842    /*
843     * Make sure we have an acceptable attributes record.
844     */
845    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
846          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
847       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
848          ar->Stream);
849       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
850       return 0;
851    }
852
853    split_path_and_file(jcr, mdb, ar->fname);
854
855
856 /*
857    if (jcr->changes > 100000) {
858       sql_batch_end(mdb, NULL);
859       sql_batch_start(mdb);
860       jcr->changes = 0;
861    }
862 */
863
864    return (sql_batch_insert(mdb, ar) == 0);
865 }
866
867 #else  /* ! HAVE_BATCH_FILE_INSERT */
868
869 /*
870  * Create File record in B_DB
871  *
872  *  In order to reduce database size, we store the File attributes,
873  *  the FileName, and the Path separately.  In principle, there
874  *  is a single FileName record and a single Path record, no matter
875  *  how many times it occurs.  This is this subroutine, we separate
876  *  the file and the path and create three database records.
877  */
878 int db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
879 {
880
881    db_lock(mdb);
882    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
883    Dmsg0(dbglevel, "put_file_into_catalog\n");
884    /*
885     * Make sure we have an acceptable attributes record.
886     */
887    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
888          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
889       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
890          ar->Stream);
891       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
892       goto bail_out;
893    }
894
895
896    split_path_and_file(jcr, mdb, ar->fname);
897
898    if (!db_create_filename_record(jcr, mdb, ar)) {
899       goto bail_out;
900    }
901    Dmsg1(dbglevel, "db_create_filename_record: %s\n", mdb->esc_name);
902
903
904    if (!db_create_path_record(jcr, mdb, ar)) {
905       goto bail_out;
906    }
907    Dmsg1(dbglevel, "db_create_path_record: %s\n", mdb->esc_name);
908
909    /* Now create master File record */
910    if (!db_create_file_record(jcr, mdb, ar)) {
911       goto bail_out;
912    }
913    Dmsg0(dbglevel, "db_create_file_record OK\n");
914
915    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", mdb->path, mdb->fname, ar->FilenameId);
916    db_unlock(mdb);
917    return 1;
918
919 bail_out:
920    db_unlock(mdb);
921    return 0;
922 }
923
924 #endif /* ! HAVE_BATCH_FILE_INSERT */
925
926 /*
927  * This is the master File entry containing the attributes.
928  *  The filename and path records have already been created.
929  */
930 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
931 {
932    int stat;
933    static char *no_digest = "0";
934    char *digest;
935
936    ASSERT(ar->JobId);
937    ASSERT(ar->PathId);
938    ASSERT(ar->FilenameId);
939
940    if (ar->Digest == NULL || ar->Digest[0] == 0) {
941       digest = no_digest;
942    } else {
943       digest = ar->Digest;
944    }
945
946    /* Must create it */
947    Mmsg(mdb->cmd,
948         "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
949         "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')",
950         ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
951         ar->attr, digest);
952
953    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
954       Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
955          mdb->cmd, sql_strerror(mdb));
956       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
957       ar->FileId = 0;
958       stat = 0;
959    } else {
960       ar->FileId = sql_insert_id(mdb, NT_("File"));
961       stat = 1;
962    }
963    return stat;
964 }
965
966 /* Create a Unique record for the Path -- no duplicates */
967 static int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
968 {
969    SQL_ROW row;
970    int stat;
971
972    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
973    db_escape_string(mdb->esc_name, mdb->path, mdb->pnl);
974
975    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
976        strcmp(mdb->cached_path, mdb->path) == 0) {
977       ar->PathId = mdb->cached_path_id;
978       return 1;
979    }
980
981    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
982
983    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
984       mdb->num_rows = sql_num_rows(mdb);
985       if (mdb->num_rows > 1) {
986          char ed1[30];
987          Mmsg2(&mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
988             edit_uint64(mdb->num_rows, ed1), mdb->path);
989          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
990       }
991       /* Even if there are multiple paths, take the first one */
992       if (mdb->num_rows >= 1) {
993          if ((row = sql_fetch_row(mdb)) == NULL) {
994             Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
995             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
996             sql_free_result(mdb);
997             ar->PathId = 0;
998             ASSERT(ar->PathId);
999             return 0;
1000          }
1001          ar->PathId = str_to_int64(row[0]);
1002          sql_free_result(mdb);
1003          /* Cache path */
1004          if (ar->PathId != mdb->cached_path_id) {
1005             mdb->cached_path_id = ar->PathId;
1006             mdb->cached_path_len = mdb->pnl;
1007             pm_strcpy(mdb->cached_path, mdb->path);
1008          }
1009          ASSERT(ar->PathId);
1010          return 1;
1011       }
1012       sql_free_result(mdb);
1013    }
1014
1015    Mmsg(mdb->cmd, "INSERT INTO Path (Path) VALUES ('%s')", mdb->esc_name);
1016
1017    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1018       Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
1019          mdb->cmd, sql_strerror(mdb));
1020       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1021       ar->PathId = 0;
1022       stat = 0;
1023    } else {
1024       ar->PathId = sql_insert_id(mdb, NT_("Path"));
1025       stat = 1;
1026    }
1027
1028    /* Cache path */
1029    if (stat && ar->PathId != mdb->cached_path_id) {
1030       mdb->cached_path_id = ar->PathId;
1031       mdb->cached_path_len = mdb->pnl;
1032       pm_strcpy(mdb->cached_path, mdb->path);
1033    }
1034    return stat;
1035 }
1036
1037 /* Create a Unique record for the filename -- no duplicates */
1038 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1039 {
1040    SQL_ROW row;
1041
1042    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1043    db_escape_string(mdb->esc_name, mdb->fname, mdb->fnl);
1044
1045    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
1046
1047    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1048       mdb->num_rows = sql_num_rows(mdb);
1049       if (mdb->num_rows > 1) {
1050          char ed1[30];
1051          Mmsg2(&mdb->errmsg, _("More than one Filename! %s for file: %s\n"),
1052             edit_uint64(mdb->num_rows, ed1), mdb->fname);
1053          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1054       }
1055       if (mdb->num_rows >= 1) {
1056          if ((row = sql_fetch_row(mdb)) == NULL) {
1057             Mmsg2(&mdb->errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
1058                 mdb->fname, sql_strerror(mdb));
1059             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1060             ar->FilenameId = 0;
1061          } else {
1062             ar->FilenameId = str_to_int64(row[0]);
1063          }
1064          sql_free_result(mdb);
1065          return ar->FilenameId > 0;
1066       }
1067       sql_free_result(mdb);
1068    }
1069
1070    Mmsg(mdb->cmd, "INSERT INTO Filename (Name) VALUES ('%s')", mdb->esc_name);
1071
1072    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1073       Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1074             mdb->cmd, sql_strerror(mdb));
1075       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1076       ar->FilenameId = 0;
1077    } else {
1078       ar->FilenameId = sql_insert_id(mdb, NT_("Filename"));
1079    }
1080    return ar->FilenameId > 0;
1081 }
1082
1083 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL */