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