]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_create.c
ebl Fix #1175 About update slots that don't reset InChanger flag when
[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 static int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar);
60 #endif /* HAVE_BATCH_FILE_INSERT */
61
62
63 /* Create a new record for the Job
64  * Returns: false on failure
65  *          true  on success
66  */
67 bool
68 db_create_job_record(JCR *jcr, B_DB *mdb, JOB_DBR *jr)
69 {
70    char dt[MAX_TIME_LENGTH];
71    time_t stime;
72    struct tm tm;
73    bool ok;
74    utime_t JobTDate;
75    char ed1[30],ed2[30];
76
77    db_lock(mdb);
78
79    stime = jr->SchedTime;
80    ASSERT(stime != 0);
81
82    (void)localtime_r(&stime, &tm);
83    strftime(dt, sizeof(dt), "%Y-%m-%d %H:%M:%S", &tm);
84    JobTDate = (utime_t)stime;
85
86    /* Must create it */
87    Mmsg(mdb->cmd,
88 "INSERT INTO Job (Job,Name,Type,Level,JobStatus,SchedTime,JobTDate,ClientId) "
89 "VALUES ('%s','%s','%c','%c','%c','%s',%s,%s)",
90            jr->Job, jr->Name, (char)(jr->JobType), (char)(jr->JobLevel),
91            (char)(jr->JobStatus), dt, edit_uint64(JobTDate, ed1),
92            edit_int64(jr->ClientId, ed2));
93
94    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
95       Mmsg2(&mdb->errmsg, _("Create DB Job record %s failed. ERR=%s\n"),
96             mdb->cmd, sql_strerror(mdb));
97       jr->JobId = 0;
98       ok = false;
99    } else {
100       jr->JobId = sql_insert_id(mdb, NT_("Job"));
101       ok = true;
102    }
103    db_unlock(mdb);
104    return ok;
105 }
106
107
108 /* Create a JobMedia record for medium used this job
109  * Returns: false on failure
110  *          true  on success
111  */
112 bool
113 db_create_jobmedia_record(JCR *jcr, B_DB *mdb, JOBMEDIA_DBR *jm)
114 {
115    bool ok = true;
116    int count;
117    char ed1[50], ed2[50];
118
119    db_lock(mdb);
120
121    /* Now get count for VolIndex */
122    Mmsg(mdb->cmd, "SELECT count(*) from JobMedia WHERE JobId=%s",
123         edit_int64(jm->JobId, ed1));
124    count = get_sql_record_max(jcr, mdb);
125    if (count < 0) {
126       count = 0;
127    }
128    count++;
129
130    /* Note, jm->Strip is not used and is not likely to be used
131     * in the near future, so I have removed it from the insert
132     * to save space in the DB. KES June 2006.
133     */
134    Mmsg(mdb->cmd,
135         "INSERT INTO JobMedia (JobId,MediaId,FirstIndex,LastIndex,"
136         "StartFile,EndFile,StartBlock,EndBlock,VolIndex,Copy) "
137         "VALUES (%s,%s,%u,%u,%u,%u,%u,%u,%u,%u)",
138         edit_int64(jm->JobId, ed1),
139         edit_int64(jm->MediaId, ed2),
140         jm->FirstIndex, jm->LastIndex,
141         jm->StartFile, jm->EndFile, jm->StartBlock, jm->EndBlock,count,
142         jm->Copy);
143
144    Dmsg0(300, mdb->cmd);
145    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
146       Mmsg2(&mdb->errmsg, _("Create JobMedia record %s failed: ERR=%s\n"), mdb->cmd,
147          sql_strerror(mdb));
148       ok = false;
149    } else {
150       /* Worked, now update the Media record with the EndFile and EndBlock */
151       Mmsg(mdb->cmd,
152            "UPDATE Media SET EndFile=%u, EndBlock=%u WHERE MediaId=%u",
153            jm->EndFile, jm->EndBlock, jm->MediaId);
154       if (!UPDATE_DB(jcr, mdb, mdb->cmd)) {
155          Mmsg2(&mdb->errmsg, _("Update Media record %s failed: ERR=%s\n"), mdb->cmd,
156               sql_strerror(mdb));
157          ok = false;
158       }
159    }
160    db_unlock(mdb);
161    Dmsg0(300, "Return from JobMedia\n");
162    return ok;
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        * Make sure that if InChanger is non-zero any other identical slot
462        *   has InChanger zero.
463        */
464       db_make_inchanger_unique(jcr, mdb, mr);
465    }
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 #ifdef HAVE_BATCH_FILE_INSERT
669
670 /*  All sql_batch_* functions are used to do bulk batch insert in File/Filename/Path
671  *  tables. This code can be activated by adding "#define HAVE_BATCH_FILE_INSERT 1"
672  *  in baconfig.h
673  *  
674  *  To sum up :
675  *   - bulk load a temp table
676  *   - insert missing filenames into filename with a single query (lock filenames 
677  *   - table before that to avoid possible duplicate inserts with concurrent update)
678  *   - insert missing paths into path with another single query
679  *   - then insert the join between the temp, filename and path tables into file.
680  */
681
682 /* 
683  * Returns 1 if OK
684  *         0 if failed
685  */
686 bool my_batch_start(JCR *jcr, B_DB *mdb)
687 {
688    bool ok;
689
690    db_lock(mdb);
691    ok =  db_sql_query(mdb,
692              "CREATE TEMPORARY TABLE batch ("
693                 "FileIndex integer,"
694                 "JobId integer,"
695                 "Path blob,"
696                 "Name blob,"
697                 "LStat tinyblob,"
698                 "MD5 tinyblob)",NULL, NULL);
699    db_unlock(mdb);
700    return ok;
701 }
702
703 /* 
704  * Returns 1 if OK
705  *         0 if failed
706  */
707 bool my_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
708 {
709    size_t len;
710    const char *digest;
711    char ed1[50];
712
713    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
714    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
715
716    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1);
717    db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
718
719    if (ar->Digest == NULL || ar->Digest[0] == 0) {
720       digest = "0";
721    } else {
722       digest = ar->Digest;
723    }
724
725    len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
726               ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
727               mdb->esc_name, ar->attr, digest);
728
729    return INSERT_DB(jcr, mdb, mdb->cmd);
730 }
731
732 /* set error to something to abort operation */
733 /* 
734  * Returns 1 if OK
735  *         0 if failed
736  */
737 bool my_batch_end(JCR *jcr, B_DB *mdb, const char *error)
738 {
739    
740    Dmsg0(50, "sql_batch_end started\n");
741
742    if (mdb) {
743 #ifdef HAVE_DBI 
744       mdb->status = (dbi_error_flag)0;
745 #else
746       mdb->status = 0;
747 #endif
748    }
749    return true;
750 }
751
752 /* 
753  * Returns 1 if OK
754  *         0 if failed
755  */
756 bool db_write_batch_file_records(JCR *jcr)
757 {
758    int JobStatus = jcr->JobStatus;
759
760    if (!jcr->batch_started) {         /* no files to backup ? */
761       Dmsg0(50,"db_create_file_record : no files\n");
762       return true;
763    }
764    if (job_canceled(jcr)) {
765       return false;
766    }
767
768    Dmsg1(50,"db_create_file_record changes=%u\n",jcr->db_batch->changes);
769
770    jcr->JobStatus = JS_AttrInserting;
771    if (!sql_batch_end(jcr, jcr->db_batch, NULL)) {
772       Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
773       return false;
774    }
775    if (job_canceled(jcr)) {
776       return false;
777    }
778
779
780    /* we have to lock tables */
781    if (!db_sql_query(jcr->db_batch, sql_batch_lock_path_query, NULL, NULL)) {
782       Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
783       return false;
784    }
785
786    if (!db_sql_query(jcr->db_batch, sql_batch_fill_path_query, NULL, NULL)) {
787       Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
788       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
789       return false;
790    }
791    
792    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
793       Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
794       return false;      
795    }
796
797    /* we have to lock tables */
798    if (!db_sql_query(jcr->db_batch,sql_batch_lock_filename_query,NULL, NULL)) {
799       Jmsg1(jcr, M_FATAL, 0, "Lock Filename table %s\n", jcr->db_batch->errmsg);
800       return false;
801    }
802    
803    if (!db_sql_query(jcr->db_batch,sql_batch_fill_filename_query, NULL,NULL)) {
804       Jmsg1(jcr,M_FATAL,0,"Fill Filename table %s\n",jcr->db_batch->errmsg);
805       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
806       return false;            
807    }
808
809    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
810       Jmsg1(jcr, M_FATAL, 0, "Unlock Filename table %s\n", jcr->db_batch->errmsg);
811       return false;
812    }
813    
814    if (!db_sql_query(jcr->db_batch, 
815        "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5)"
816          "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
817                 "Filename.FilenameId,batch.LStat, batch.MD5 "
818            "FROM batch "
819            "JOIN Path ON (batch.Path = Path.Path) "
820            "JOIN Filename ON (batch.Name = Filename.Name)",
821                      NULL,NULL))
822    {
823       Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
824       return false;
825    }
826
827    db_sql_query(jcr->db_batch, "DROP TABLE batch", NULL,NULL);
828
829    jcr->JobStatus = JobStatus;         /* reset entry status */
830    return true;
831 }
832
833 /*
834  * Create File record in B_DB
835  *
836  *  In order to reduce database size, we store the File attributes,
837  *  the FileName, and the Path separately.  In principle, there
838  *  is a single FileName record and a single Path record, no matter
839  *  how many times it occurs.  This is this subroutine, we separate
840  *  the file and the path and fill temporary tables with this three records.
841  */
842 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
843 {
844    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
845    Dmsg0(dbglevel, "put_file_into_catalog\n");
846
847    /* Open the dedicated connexion */
848    if (!jcr->batch_started) {
849
850       if (!db_open_batch_connexion(jcr, mdb)) {
851          return false;
852       }
853       if (!sql_batch_start(jcr, jcr->db_batch)) {
854          Mmsg1(&mdb->errmsg, 
855               "Can't start batch mode: ERR=%s", db_strerror(jcr->db_batch));
856          Jmsg1(jcr, M_FATAL, 0, "%s", mdb->errmsg);
857          return false;
858       }
859       jcr->batch_started = true;
860    }
861    B_DB *bdb = jcr->db_batch;
862
863    /*
864     * Make sure we have an acceptable attributes record.
865     */
866    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
867          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
868       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
869          ar->Stream);
870       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
871       return false;
872    }
873
874    split_path_and_file(jcr, bdb, ar->fname);
875
876
877 /*
878  * if (bdb->changes > 100000) {
879  *    db_write_batch_file_records(jcr);
880  *    bdb->changes = 0;
881  *     sql_batch_start(jcr, bdb);
882  * }
883  */
884
885    return sql_batch_insert(jcr, bdb, ar);
886 }
887
888 #else  /* ! HAVE_BATCH_FILE_INSERT */
889
890 /*
891  * Create File record in B_DB
892  *
893  *  In order to reduce database size, we store the File attributes,
894  *  the FileName, and the Path separately.  In principle, there
895  *  is a single FileName record and a single Path record, no matter
896  *  how many times it occurs.  This is this subroutine, we separate
897  *  the file and the path and create three database records.
898  */
899 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
900 {
901    db_lock(mdb);
902    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
903    Dmsg0(dbglevel, "put_file_into_catalog\n");
904    /*
905     * Make sure we have an acceptable attributes record.
906     */
907    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
908          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
909       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
910          ar->Stream);
911       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
912       goto bail_out;
913    }
914
915
916    split_path_and_file(jcr, mdb, ar->fname);
917
918    if (!db_create_filename_record(jcr, mdb, ar)) {
919       goto bail_out;
920    }
921    Dmsg1(dbglevel, "db_create_filename_record: %s\n", mdb->esc_name);
922
923
924    if (!db_create_path_record(jcr, mdb, ar)) {
925       goto bail_out;
926    }
927    Dmsg1(dbglevel, "db_create_path_record: %s\n", mdb->esc_name);
928
929    /* Now create master File record */
930    if (!db_create_file_record(jcr, mdb, ar)) {
931       goto bail_out;
932    }
933    Dmsg0(dbglevel, "db_create_file_record OK\n");
934
935    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", mdb->path, mdb->fname, ar->FilenameId);
936    db_unlock(mdb);
937    return true;
938
939 bail_out:
940    db_unlock(mdb);
941    return false;
942 }
943
944
945 /*
946  * This is the master File entry containing the attributes.
947  *  The filename and path records have already been created.
948  */
949 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
950 {
951    int stat;
952    static const char *no_digest = "0";
953    const char *digest;
954
955    ASSERT(ar->JobId);
956    ASSERT(ar->PathId);
957    ASSERT(ar->FilenameId);
958
959    if (ar->Digest == NULL || ar->Digest[0] == 0) {
960       digest = no_digest;
961    } else {
962       digest = ar->Digest;
963    }
964
965    /* Must create it */
966    Mmsg(mdb->cmd,
967         "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
968         "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')",
969         ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
970         ar->attr, digest);
971
972    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
973       Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
974          mdb->cmd, sql_strerror(mdb));
975       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
976       ar->FileId = 0;
977       stat = 0;
978    } else {
979       ar->FileId = sql_insert_id(mdb, NT_("File"));
980       stat = 1;
981    }
982    return stat;
983 }
984
985 /* Create a Unique record for the Path -- no duplicates */
986 static int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
987 {
988    SQL_ROW row;
989    int stat;
990
991    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
992    db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
993
994    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
995        strcmp(mdb->cached_path, mdb->path) == 0) {
996       ar->PathId = mdb->cached_path_id;
997       return 1;
998    }
999
1000    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
1001
1002    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1003       mdb->num_rows = sql_num_rows(mdb);
1004       if (mdb->num_rows > 1) {
1005          char ed1[30];
1006          Mmsg2(&mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
1007             edit_uint64(mdb->num_rows, ed1), mdb->path);
1008          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1009       }
1010       /* Even if there are multiple paths, take the first one */
1011       if (mdb->num_rows >= 1) {
1012          if ((row = sql_fetch_row(mdb)) == NULL) {
1013             Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1014             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1015             sql_free_result(mdb);
1016             ar->PathId = 0;
1017             ASSERT(ar->PathId);
1018             return 0;
1019          }
1020          ar->PathId = str_to_int64(row[0]);
1021          sql_free_result(mdb);
1022          /* Cache path */
1023          if (ar->PathId != mdb->cached_path_id) {
1024             mdb->cached_path_id = ar->PathId;
1025             mdb->cached_path_len = mdb->pnl;
1026             pm_strcpy(mdb->cached_path, mdb->path);
1027          }
1028          ASSERT(ar->PathId);
1029          return 1;
1030       }
1031       sql_free_result(mdb);
1032    }
1033
1034    Mmsg(mdb->cmd, "INSERT INTO Path (Path) VALUES ('%s')", mdb->esc_name);
1035
1036    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1037       Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
1038          mdb->cmd, sql_strerror(mdb));
1039       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1040       ar->PathId = 0;
1041       stat = 0;
1042    } else {
1043       ar->PathId = sql_insert_id(mdb, NT_("Path"));
1044       stat = 1;
1045    }
1046
1047    /* Cache path */
1048    if (stat && ar->PathId != mdb->cached_path_id) {
1049       mdb->cached_path_id = ar->PathId;
1050       mdb->cached_path_len = mdb->pnl;
1051       pm_strcpy(mdb->cached_path, mdb->path);
1052    }
1053    return stat;
1054 }
1055
1056 /* Create a Unique record for the filename -- no duplicates */
1057 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1058 {
1059    SQL_ROW row;
1060
1061    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1062    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1063    
1064    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
1065
1066    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1067       mdb->num_rows = sql_num_rows(mdb);
1068       if (mdb->num_rows > 1) {
1069          char ed1[30];
1070          Mmsg2(&mdb->errmsg, _("More than one Filename! %s for file: %s\n"),
1071             edit_uint64(mdb->num_rows, ed1), mdb->fname);
1072          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1073       }
1074       if (mdb->num_rows >= 1) {
1075          if ((row = sql_fetch_row(mdb)) == NULL) {
1076             Mmsg2(&mdb->errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
1077                 mdb->fname, sql_strerror(mdb));
1078             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1079             ar->FilenameId = 0;
1080          } else {
1081             ar->FilenameId = str_to_int64(row[0]);
1082          }
1083          sql_free_result(mdb);
1084          return ar->FilenameId > 0;
1085       }
1086       sql_free_result(mdb);
1087    }
1088
1089    Mmsg(mdb->cmd, "INSERT INTO Filename (Name) VALUES ('%s')", mdb->esc_name);
1090
1091    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1092       Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1093             mdb->cmd, sql_strerror(mdb));
1094       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1095       ar->FilenameId = 0;
1096    } else {
1097       ar->FilenameId = sql_insert_id(mdb, NT_("Filename"));
1098    }
1099    return ar->FilenameId > 0;
1100 }
1101
1102 bool db_write_batch_file_records(JCR *jcr)
1103 {
1104    return true;
1105 }
1106
1107 #endif /* ! HAVE_BATCH_FILE_INSERT */
1108
1109 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */