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