]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_create.c
Add Joao's sql_create change
[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 John Walker.
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
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 #ifdef HAVE_BATCH_FILE_INSERT
670
671 /*  All sql_batch_* functions are used to do bulk batch insert in File/Filename/Path
672  *  tables. This code can be activated by adding "#define HAVE_BATCH_FILE_INSERT 1"
673  *  in baconfig.h
674  *  
675  *  To sum up :
676  *   - bulk load a temp table
677  *   - insert missing filenames into filename with a single query (lock filenames 
678  *   - table before that to avoid possible duplicate inserts with concurrent update)
679  *   - insert missing paths into path with another single query
680  *   - then insert the join between the temp, filename and path tables into file.
681  */
682
683 /* 
684  * Returns 1 if OK
685  *         0 if failed
686  */
687 bool my_batch_start(JCR *jcr, B_DB *mdb)
688 {
689    bool ok;
690
691    db_lock(mdb);
692    ok =  db_sql_query(mdb,
693              "CREATE TEMPORARY TABLE batch ("
694                 "FileIndex integer,"
695                 "JobId integer,"
696                 "Path blob,"
697                 "Name blob,"
698                 "LStat tinyblob,"
699                 "MD5 tinyblob)",NULL, NULL);
700    db_unlock(mdb);
701    return ok;
702 }
703
704 /* 
705  * Returns 1 if OK
706  *         0 if failed
707  */
708 bool my_batch_insert(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
709 {
710    size_t len;
711    const char *digest;
712    char ed1[50];
713
714    mdb->esc_name = check_pool_memory_size(mdb->esc_name, mdb->fnl*2+1);
715    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
716
717    mdb->esc_path = check_pool_memory_size(mdb->esc_path, mdb->pnl*2+1);
718    db_escape_string(jcr, mdb, mdb->esc_path, mdb->path, mdb->pnl);
719
720    if (ar->Digest == NULL || ar->Digest[0] == 0) {
721       digest = "0";
722    } else {
723       digest = ar->Digest;
724    }
725
726    len = Mmsg(mdb->cmd, "INSERT INTO batch VALUES (%u,%s,'%s','%s','%s','%s')",
727               ar->FileIndex, edit_int64(ar->JobId,ed1), mdb->esc_path, 
728               mdb->esc_name, ar->attr, digest);
729
730    return INSERT_DB(jcr, mdb, mdb->cmd);
731 }
732
733 /* set error to something to abort operation */
734 /* 
735  * Returns 1 if OK
736  *         0 if failed
737  */
738 bool my_batch_end(JCR *jcr, B_DB *mdb, const char *error)
739 {
740    
741    Dmsg0(50, "sql_batch_end started\n");
742
743    if (mdb) {
744 #ifdef HAVE_DBI 
745       mdb->status = (dbi_error_flag)0;
746 #else
747       mdb->status = 0;
748 #endif
749    }
750    return true;
751 }
752
753 /* 
754  * Returns 1 if OK
755  *         0 if failed
756  */
757 bool db_write_batch_file_records(JCR *jcr)
758 {
759    int JobStatus = jcr->JobStatus;
760
761    if (!jcr->db_batch) {         /* no files to backup ? */
762       Dmsg0(50,"db_create_file_record : no files\n");
763       return true;
764    }
765    if (job_canceled(jcr)) {
766       return false;
767    }
768
769    Dmsg1(50,"db_create_file_record changes=%u\n",jcr->db_batch->changes);
770
771    jcr->JobStatus = JS_AttrInserting;
772    if (!sql_batch_end(jcr, jcr->db_batch, NULL)) {
773       Jmsg1(jcr, M_FATAL, 0, "Batch end %s\n", jcr->db_batch->errmsg);
774       return false;
775    }
776    if (job_canceled(jcr)) {
777       return false;
778    }
779
780
781    /* we have to lock tables */
782    if (!db_sql_query(jcr->db_batch, sql_batch_lock_path_query, NULL, NULL)) {
783       Jmsg1(jcr, M_FATAL, 0, "Lock Path table %s\n", jcr->db_batch->errmsg);
784       return false;
785    }
786
787    if (!db_sql_query(jcr->db_batch, sql_batch_fill_path_query, NULL, NULL)) {
788       Jmsg1(jcr, M_FATAL, 0, "Fill Path table %s\n",jcr->db_batch->errmsg);
789       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
790       return false;
791    }
792    
793    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
794       Jmsg1(jcr, M_FATAL, 0, "Unlock Path table %s\n", jcr->db_batch->errmsg);
795       return false;      
796    }
797
798    /* we have to lock tables */
799    if (!db_sql_query(jcr->db_batch,sql_batch_lock_filename_query,NULL, NULL)) {
800       Jmsg1(jcr, M_FATAL, 0, "Lock Filename table %s\n", jcr->db_batch->errmsg);
801       return false;
802    }
803    
804    if (!db_sql_query(jcr->db_batch,sql_batch_fill_filename_query, NULL,NULL)) {
805       Jmsg1(jcr,M_FATAL,0,"Fill Filename table %s\n",jcr->db_batch->errmsg);
806       db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query, NULL, NULL);
807       return false;            
808    }
809
810    if (!db_sql_query(jcr->db_batch, sql_batch_unlock_tables_query,NULL,NULL)) {
811       Jmsg1(jcr, M_FATAL, 0, "Unlock Filename table %s\n", jcr->db_batch->errmsg);
812       return false;
813    }
814    
815    if (!db_sql_query(jcr->db_batch, 
816        "INSERT INTO File (FileIndex, JobId, PathId, FilenameId, LStat, MD5)"
817          "SELECT batch.FileIndex, batch.JobId, Path.PathId, "
818                 "Filename.FilenameId,batch.LStat, batch.MD5 "
819            "FROM batch "
820            "JOIN Path ON (batch.Path = Path.Path) "
821            "JOIN Filename ON (batch.Name = Filename.Name)",
822                      NULL,NULL))
823    {
824       Jmsg1(jcr, M_FATAL, 0, "Fill File table %s\n", jcr->db_batch->errmsg);
825       return false;
826    }
827
828    db_sql_query(jcr->db_batch, "DROP TABLE batch", NULL,NULL);
829
830    jcr->JobStatus = JobStatus;         /* reset entry status */
831    return true;
832 }
833
834 /*
835  * Create File record in B_DB
836  *
837  *  In order to reduce database size, we store the File attributes,
838  *  the FileName, and the Path separately.  In principle, there
839  *  is a single FileName record and a single Path record, no matter
840  *  how many times it occurs.  This is this subroutine, we separate
841  *  the file and the path and fill temporary tables with this three records.
842  */
843 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
844 {
845    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
846    Dmsg0(dbglevel, "put_file_into_catalog\n");
847
848    if (!jcr->db_batch) {
849       Dmsg2(100, "Opendb attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
850       jcr->db_batch = db_init_database(jcr, 
851                                       mdb->db_name, 
852                                       mdb->db_user,
853                                       mdb->db_password, 
854                                       mdb->db_address,
855                                       mdb->db_port,
856                                       mdb->db_socket,
857                                       1 /* multi_db = true */);
858       if (!jcr->db_batch) {
859          Mmsg1(&mdb->errmsg, _("Could not init batch database: \"%s\".\n"),
860                         jcr->db->db_name);
861          Jmsg1(jcr, M_FATAL, 0, "%s", mdb->errmsg);
862          return false;
863       }
864
865       if (!db_open_database(jcr, jcr->db_batch)) {
866          Mmsg2(&mdb->errmsg,  _("Could not open database \"%s\": ERR=%s\n"),
867               jcr->db->db_name, db_strerror(jcr->db_batch));
868          Jmsg1(jcr, M_FATAL, 0, "%s", mdb->errmsg);
869          return false;
870       }      
871       
872       if (!sql_batch_start(jcr, jcr->db_batch)) {
873          Mmsg1(&mdb->errmsg, 
874               "Can't start batch mode: ERR=%s", db_strerror(jcr->db_batch));
875          Jmsg1(jcr, M_FATAL, 0, "%s", mdb->errmsg);
876          return false;
877       }
878       Dmsg3(100, "initdb ref=%d connected=%d db=%p\n", jcr->db_batch->ref_count,
879             jcr->db_batch->connected, jcr->db_batch->db);
880    }
881    B_DB *bdb = jcr->db_batch;
882
883    /*
884     * Make sure we have an acceptable attributes record.
885     */
886    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
887          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
888       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
889          ar->Stream);
890       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
891       return false;
892    }
893
894    split_path_and_file(jcr, bdb, ar->fname);
895
896
897 /*
898    if (bdb->changes > 100000) {
899       db_write_batch_file_records(jcr);
900       bdb->changes = 0;
901       sql_batch_start(jcr, bdb);
902    }
903 */
904
905    return sql_batch_insert(jcr, bdb, ar);
906 }
907
908 #else  /* ! HAVE_BATCH_FILE_INSERT */
909
910 /*
911  * Create File record in B_DB
912  *
913  *  In order to reduce database size, we store the File attributes,
914  *  the FileName, and the Path separately.  In principle, there
915  *  is a single FileName record and a single Path record, no matter
916  *  how many times it occurs.  This is this subroutine, we separate
917  *  the file and the path and create three database records.
918  */
919 bool db_create_file_attributes_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
920 {
921    db_lock(mdb);
922    Dmsg1(dbglevel, "Fname=%s\n", ar->fname);
923    Dmsg0(dbglevel, "put_file_into_catalog\n");
924    /*
925     * Make sure we have an acceptable attributes record.
926     */
927    if (!(ar->Stream == STREAM_UNIX_ATTRIBUTES ||
928          ar->Stream == STREAM_UNIX_ATTRIBUTES_EX)) {
929       Mmsg1(&mdb->errmsg, _("Attempt to put non-attributes into catalog. Stream=%d\n"),
930          ar->Stream);
931       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
932       goto bail_out;
933    }
934
935
936    split_path_and_file(jcr, mdb, ar->fname);
937
938    if (!db_create_filename_record(jcr, mdb, ar)) {
939       goto bail_out;
940    }
941    Dmsg1(dbglevel, "db_create_filename_record: %s\n", mdb->esc_name);
942
943
944    if (!db_create_path_record(jcr, mdb, ar)) {
945       goto bail_out;
946    }
947    Dmsg1(dbglevel, "db_create_path_record: %s\n", mdb->esc_name);
948
949    /* Now create master File record */
950    if (!db_create_file_record(jcr, mdb, ar)) {
951       goto bail_out;
952    }
953    Dmsg0(dbglevel, "db_create_file_record OK\n");
954
955    Dmsg3(dbglevel, "CreateAttributes Path=%s File=%s FilenameId=%d\n", mdb->path, mdb->fname, ar->FilenameId);
956    db_unlock(mdb);
957    return true;
958
959 bail_out:
960    db_unlock(mdb);
961    return false;
962 }
963
964
965 /*
966  * This is the master File entry containing the attributes.
967  *  The filename and path records have already been created.
968  */
969 static int db_create_file_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
970 {
971    int stat;
972    static const char *no_digest = "0";
973    const char *digest;
974
975    ASSERT(ar->JobId);
976    ASSERT(ar->PathId);
977    ASSERT(ar->FilenameId);
978
979    if (ar->Digest == NULL || ar->Digest[0] == 0) {
980       digest = no_digest;
981    } else {
982       digest = ar->Digest;
983    }
984
985    /* Must create it */
986    Mmsg(mdb->cmd,
987         "INSERT INTO File (FileIndex,JobId,PathId,FilenameId,"
988         "LStat,MD5) VALUES (%u,%u,%u,%u,'%s','%s')",
989         ar->FileIndex, ar->JobId, ar->PathId, ar->FilenameId,
990         ar->attr, digest);
991
992    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
993       Mmsg2(&mdb->errmsg, _("Create db File record %s failed. ERR=%s"),
994          mdb->cmd, sql_strerror(mdb));
995       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
996       ar->FileId = 0;
997       stat = 0;
998    } else {
999       ar->FileId = sql_insert_id(mdb, NT_("File"));
1000       stat = 1;
1001    }
1002    return stat;
1003 }
1004
1005 /* Create a Unique record for the Path -- no duplicates */
1006 static int db_create_path_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1007 {
1008    SQL_ROW row;
1009    int stat;
1010
1011    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->pnl+2);
1012    db_escape_string(jcr, mdb, mdb->esc_name, mdb->path, mdb->pnl);
1013
1014    if (mdb->cached_path_id != 0 && mdb->cached_path_len == mdb->pnl &&
1015        strcmp(mdb->cached_path, mdb->path) == 0) {
1016       ar->PathId = mdb->cached_path_id;
1017       return 1;
1018    }
1019
1020    Mmsg(mdb->cmd, "SELECT PathId FROM Path WHERE Path='%s'", mdb->esc_name);
1021
1022    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1023       mdb->num_rows = sql_num_rows(mdb);
1024       if (mdb->num_rows > 1) {
1025          char ed1[30];
1026          Mmsg2(&mdb->errmsg, _("More than one Path!: %s for path: %s\n"),
1027             edit_uint64(mdb->num_rows, ed1), mdb->path);
1028          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1029       }
1030       /* Even if there are multiple paths, take the first one */
1031       if (mdb->num_rows >= 1) {
1032          if ((row = sql_fetch_row(mdb)) == NULL) {
1033             Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
1034             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1035             sql_free_result(mdb);
1036             ar->PathId = 0;
1037             ASSERT(ar->PathId);
1038             return 0;
1039          }
1040          ar->PathId = str_to_int64(row[0]);
1041          sql_free_result(mdb);
1042          /* Cache path */
1043          if (ar->PathId != mdb->cached_path_id) {
1044             mdb->cached_path_id = ar->PathId;
1045             mdb->cached_path_len = mdb->pnl;
1046             pm_strcpy(mdb->cached_path, mdb->path);
1047          }
1048          ASSERT(ar->PathId);
1049          return 1;
1050       }
1051       sql_free_result(mdb);
1052    }
1053
1054    Mmsg(mdb->cmd, "INSERT INTO Path (Path) VALUES ('%s')", mdb->esc_name);
1055
1056    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1057       Mmsg2(&mdb->errmsg, _("Create db Path record %s failed. ERR=%s\n"),
1058          mdb->cmd, sql_strerror(mdb));
1059       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1060       ar->PathId = 0;
1061       stat = 0;
1062    } else {
1063       ar->PathId = sql_insert_id(mdb, NT_("Path"));
1064       stat = 1;
1065    }
1066
1067    /* Cache path */
1068    if (stat && ar->PathId != mdb->cached_path_id) {
1069       mdb->cached_path_id = ar->PathId;
1070       mdb->cached_path_len = mdb->pnl;
1071       pm_strcpy(mdb->cached_path, mdb->path);
1072    }
1073    return stat;
1074 }
1075
1076 /* Create a Unique record for the filename -- no duplicates */
1077 static int db_create_filename_record(JCR *jcr, B_DB *mdb, ATTR_DBR *ar)
1078 {
1079    SQL_ROW row;
1080
1081    mdb->esc_name = check_pool_memory_size(mdb->esc_name, 2*mdb->fnl+2);
1082    db_escape_string(jcr, mdb, mdb->esc_name, mdb->fname, mdb->fnl);
1083    
1084    Mmsg(mdb->cmd, "SELECT FilenameId FROM Filename WHERE Name='%s'", mdb->esc_name);
1085
1086    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
1087       mdb->num_rows = sql_num_rows(mdb);
1088       if (mdb->num_rows > 1) {
1089          char ed1[30];
1090          Mmsg2(&mdb->errmsg, _("More than one Filename! %s for file: %s\n"),
1091             edit_uint64(mdb->num_rows, ed1), mdb->fname);
1092          Jmsg(jcr, M_WARNING, 0, "%s", mdb->errmsg);
1093       }
1094       if (mdb->num_rows >= 1) {
1095          if ((row = sql_fetch_row(mdb)) == NULL) {
1096             Mmsg2(&mdb->errmsg, _("Error fetching row for file=%s: ERR=%s\n"),
1097                 mdb->fname, sql_strerror(mdb));
1098             Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
1099             ar->FilenameId = 0;
1100          } else {
1101             ar->FilenameId = str_to_int64(row[0]);
1102          }
1103          sql_free_result(mdb);
1104          return ar->FilenameId > 0;
1105       }
1106       sql_free_result(mdb);
1107    }
1108
1109    Mmsg(mdb->cmd, "INSERT INTO Filename (Name) VALUES ('%s')", mdb->esc_name);
1110
1111    if (!INSERT_DB(jcr, mdb, mdb->cmd)) {
1112       Mmsg2(&mdb->errmsg, _("Create db Filename record %s failed. ERR=%s\n"),
1113             mdb->cmd, sql_strerror(mdb));
1114       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
1115       ar->FilenameId = 0;
1116    } else {
1117       ar->FilenameId = sql_insert_id(mdb, NT_("Filename"));
1118    }
1119    return ar->FilenameId > 0;
1120 }
1121
1122 bool db_write_batch_file_records(JCR *jcr)
1123 {
1124    return true;
1125 }
1126
1127 #endif /* ! HAVE_BATCH_FILE_INSERT */
1128
1129 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_SQLITE || HAVE_POSTGRESQL || HAVE_DBI */