]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_list.c
Change copyright as per agreement with FSFE
[bacula/bacula] / bacula / src / cats / sql_list.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  * Bacula Catalog Database List records interface routines
21  *
22  *    Written by Kern Sibbald, March 2000
23  *
24  */
25
26 #include  "bacula.h"
27
28 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
29
30 #include  "cats.h"
31
32 /* -----------------------------------------------------------------------
33  *
34  *   Generic Routines (or almost generic)
35  *
36  * -----------------------------------------------------------------------
37  */
38
39 /*
40  * Submit general SQL query
41  */
42 int BDB::bdb_list_sql_query(JCR *jcr, const char *query, DB_LIST_HANDLER *sendit,
43                       void *ctx, int verbose, e_list_type type)
44 {
45    bdb_lock();
46    if (!sql_query(query, QF_STORE_RESULT)) {
47       Mmsg(errmsg, _("Query failed: %s\n"), sql_strerror());
48       if (verbose) {
49          sendit(ctx, errmsg);
50       }
51       bdb_unlock();
52       return 0;
53    }
54
55    list_result(jcr,this, sendit, ctx, type);
56    sql_free_result();
57    bdb_unlock();
58    return 1;
59 }
60
61 void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr,
62                      DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
63 {
64    char esc[MAX_ESCAPE_NAME_LENGTH];
65
66    bdb_lock();
67    bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
68
69    if (type == VERT_LIST) {
70       if (pdbr->Name[0] != 0) {
71          Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,"
72             "AcceptAnyVolume,VolRetention,VolUseDuration,MaxVolJobs,MaxVolBytes,"
73             "AutoPrune,Recycle,PoolType,LabelFormat,Enabled,ScratchPoolId,"
74             "RecyclePoolId,LabelType "
75             " FROM Pool WHERE Name='%s'", esc);
76       } else {
77          Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,"
78             "AcceptAnyVolume,VolRetention,VolUseDuration,MaxVolJobs,MaxVolBytes,"
79             "AutoPrune,Recycle,PoolType,LabelFormat,Enabled,ScratchPoolId,"
80             "RecyclePoolId,LabelType "
81             " FROM Pool ORDER BY PoolId");
82       }
83    } else {
84       if (pdbr->Name[0] != 0) {
85          Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,PoolType,LabelFormat "
86            "FROM Pool WHERE Name='%s'", esc);
87       } else {
88          Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,PoolType,LabelFormat "
89            "FROM Pool ORDER BY PoolId");
90       }
91    }
92
93    if (!QueryDB(jcr, cmd)) {
94       bdb_unlock();
95       return;
96    }
97
98    list_result(jcr, this, sendit, ctx, type);
99
100    sql_free_result();
101    bdb_unlock();
102 }
103
104 void BDB::bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
105 {
106    bdb_lock();
107    if (type == VERT_LIST) {
108       Mmsg(cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,"
109          "JobRetention "
110          "FROM Client ORDER BY ClientId");
111    } else {
112       Mmsg(cmd, "SELECT ClientId,Name,FileRetention,JobRetention "
113          "FROM Client ORDER BY ClientId");
114    }
115
116    if (!QueryDB(jcr, cmd)) {
117       bdb_unlock();
118       return;
119    }
120
121    list_result(jcr, this, sendit, ctx, type);
122
123    sql_free_result();
124    bdb_unlock();
125 }
126
127 /*
128  * List restore objects
129  *
130  * JobId | JobIds: List RestoreObjects for specific Job(s)
131  * It is possible to specify the ObjectType using FileType field.
132  */
133 void BDB::bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
134 {
135    POOL_MEM filter;
136    char  ed1[50];
137    char *jobid;
138
139    if (rr->JobIds && is_a_number_list(rr->JobIds)) {
140       jobid = rr->JobIds;
141
142    } else if (rr->JobId) {
143       jobid = edit_int64(rr->JobId, ed1);
144
145    } else {
146       return;
147    }
148
149    if (rr->FileType > 0) {
150       Mmsg(filter, "AND ObjectType = %d ", rr->FileType);
151    }
152
153    bdb_lock();
154    if (type == VERT_LIST) {
155       Mmsg(cmd, "SELECT JobId, RestoreObjectId, ObjectName, "
156            "PluginName, ObjectType "
157            "FROM RestoreObject JOIN Job USING (JobId) WHERE JobId IN (%s) %s "
158            "ORDER BY JobTDate ASC, RestoreObjectId",
159            jobid, filter.c_str());
160    } else {
161       Mmsg(cmd, "SELECT JobId, RestoreObjectId, ObjectName, "
162            "PluginName, ObjectType, ObjectLength "
163            "FROM RestoreObject JOIN Job USING (JobId) WHERE JobId IN (%s) %s "
164            "ORDER BY JobTDate ASC, RestoreObjectId",
165            jobid, filter.c_str());
166    }
167
168    if (!QueryDB(jcr, cmd)) {
169       bdb_unlock();
170       return;
171    }
172
173    list_result(jcr, this, sendit, ctx, type);
174
175    sql_free_result();
176    bdb_unlock();
177 }
178
179 /*
180  * If VolumeName is non-zero, list the record for that Volume
181  *   otherwise, list the Volumes in the Pool specified by PoolId
182  */
183 void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
184                       DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
185 {
186    char ed1[50];
187    char esc[MAX_ESCAPE_NAME_LENGTH];
188
189    bdb_lock();
190    bdb_escape_string(jcr, esc, mdbr->VolumeName, strlen(mdbr->VolumeName));
191
192    if (type == VERT_LIST) {
193       if (mdbr->VolumeName[0] != 0) {
194          Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
195             "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs,"
196             "VolFiles,VolBlocks,VolMounts,VolBytes,VolABytes,VolAPadding,"
197             "VolHoleBytes,VolHoles,VolErrors,VolWrites,"
198             "VolCapacityBytes,VolStatus,Enabled,Recycle,VolRetention,"
199             "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger,"
200             "EndFile,EndBlock,VolParts,LabelType,StorageId,DeviceId,"
201             "MediaAddressing,VolReadTime,VolWriteTime,"
202             "LocationId,RecycleCount,InitialWrite,ScratchPoolId,RecyclePoolId, "
203             "ActionOnPurge,Comment"
204             " FROM Media WHERE Media.VolumeName='%s'", esc);
205       } else {
206          Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
207             "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs,"
208             "VolFiles,VolBlocks,VolMounts,VolBytes,VolABytes,VolAPadding,"
209             "VolHoleBytes,VolHoles,VolErrors,VolWrites,"
210             "VolCapacityBytes,VolStatus,Enabled,Recycle,VolRetention,"
211             "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger,"
212             "EndFile,EndBlock,VolParts,LabelType,StorageId,DeviceId,"
213             "MediaAddressing,VolReadTime,VolWriteTime,"
214             "LocationId,RecycleCount,InitialWrite,ScratchPoolId,RecyclePoolId, "
215             "ActionOnPurge,Comment"
216             " FROM Media WHERE Media.PoolId=%s ORDER BY MediaId",
217             edit_int64(mdbr->PoolId, ed1));
218       }
219    } else {
220       if (mdbr->VolumeName[0] != 0) {
221          Mmsg(cmd, "SELECT MediaId,VolumeName,VolStatus,Enabled,"
222             "VolBytes,VolFiles,VolRetention,Recycle,Slot,InChanger,MediaType,VolParts,LastWritten "
223             "FROM Media WHERE Media.VolumeName='%s'", esc);
224       } else {
225          Mmsg(cmd, "SELECT MediaId,VolumeName,VolStatus,Enabled,"
226             "VolBytes,VolFiles,VolRetention,Recycle,Slot,InChanger,MediaType,VolParts,LastWritten "
227             "FROM Media WHERE Media.PoolId=%s ORDER BY MediaId",
228             edit_int64(mdbr->PoolId, ed1));
229       }
230    }
231
232    if (!QueryDB(jcr, cmd)) {
233       bdb_unlock();
234       return;
235    }
236
237    list_result(jcr, this, sendit, ctx, type);
238
239    sql_free_result();
240    bdb_unlock();
241 }
242
243 void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId,
244                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
245 {
246    char ed1[50];
247
248    bdb_lock();
249    if (type == VERT_LIST) {
250       if (JobId > 0) {                   /* do by JobId */
251          Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
252             "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
253             "JobMedia.EndBlock "
254             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId "
255             "AND JobMedia.JobId=%s", edit_int64(JobId, ed1));
256       } else {
257          Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
258             "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
259             "JobMedia.EndBlock "
260             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId");
261       }
262
263    } else {
264       if (JobId > 0) {                   /* do by JobId */
265          Mmsg(cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
266             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId "
267             "AND JobMedia.JobId=%s", edit_int64(JobId, ed1));
268       } else {
269          Mmsg(cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
270             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId");
271       }
272    }
273    if (!QueryDB(jcr, cmd)) {
274       bdb_unlock();
275       return;
276    }
277
278    list_result(jcr, this, sendit, ctx, type);
279
280    sql_free_result();
281    bdb_unlock();
282 }
283
284
285 void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
286                             DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
287 {
288    POOL_MEM str_limit(PM_MESSAGE);
289    POOL_MEM str_jobids(PM_MESSAGE);
290
291    if (limit > 0) {
292       Mmsg(str_limit, " LIMIT %d", limit);
293    }
294
295    if (JobIds && JobIds[0]) {
296       Mmsg(str_jobids, " AND (Job.PriorJobId IN (%s) OR Job.JobId IN (%s)) ",
297            JobIds, JobIds);
298    }
299
300    bdb_lock();
301    Mmsg(cmd,
302    "SELECT DISTINCT Job.PriorJobId AS JobId, Job.Job, "
303                    "Job.JobId AS CopyJobId, Media.MediaType "
304      "FROM Job "
305      "JOIN JobMedia USING (JobId) "
306      "JOIN Media    USING (MediaId) "
307     "WHERE Job.Type = '%c' %s ORDER BY Job.PriorJobId DESC %s",
308         (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
309
310    if (!QueryDB(jcr, cmd)) {
311       goto bail_out;
312    }
313
314    if (sql_num_rows()) {
315       if (JobIds && JobIds[0]) {
316          sendit(ctx, _("These JobIds have copies as follows:\n"));
317       } else {
318          sendit(ctx, _("The catalog contains copies as follows:\n"));
319       }
320
321       list_result(jcr, this, sendit, ctx, type);
322    }
323
324    sql_free_result();
325
326 bail_out:
327    bdb_unlock();
328 }
329
330 void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId,
331                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
332 {
333    char ed1[50];
334  
335    if (JobId <= 0) {
336       return;
337    }
338    bdb_lock();
339    if (type == VERT_LIST) {
340       Mmsg(cmd, "SELECT Time,LogText FROM Log "
341            "WHERE Log.JobId=%s ORDER BY LogId ASC", edit_int64(JobId, ed1));
342    } else {
343       Mmsg(cmd, "SELECT LogText FROM Log "
344            "WHERE Log.JobId=%s ORDER BY LogId ASC", edit_int64(JobId, ed1));
345    }
346    if (!QueryDB(jcr, cmd)) {
347       goto bail_out;
348    }
349
350    list_result(jcr, this, sendit, ctx, type);
351
352    sql_free_result();
353
354 bail_out:
355    bdb_unlock();
356 }
357
358
359 /*
360  * List Job record(s) that match JOB_DBR
361  *
362  *  Currently, we return all jobs or if jr->JobId is set,
363  *  only the job with the specified id.
364  */
365 alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
366                     void *ctx, e_list_type type)
367 {
368    char ed1[50];
369    char limit[100];
370    char status[100];
371    char esc[MAX_ESCAPE_NAME_LENGTH];
372    alist *list = NULL;
373
374    bdb_lock();
375    if (jr->limit > 0) {
376       snprintf(limit, sizeof(limit), " LIMIT %d", jr->limit);
377    } else {
378       limit[0] = 0;
379    }
380    switch (type) {
381    case VERT_LIST:
382       if (jr->JobId == 0 && jr->Job[0] == 0) {
383          Mmsg(cmd,
384             "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
385             "Job.ClientId,Client.Name as ClientName,JobStatus,SchedTime,"
386             "StartTime,EndTime,RealEndTime,JobTDate,"
387             "VolSessionId,VolSessionTime,JobFiles,JobBytes,ReadBytes,JobErrors,"
388             "JobMissingFiles,Job.PoolId,Pool.Name as PooLname,PriorJobId,"
389             "Job.FileSetId,FileSet.FileSet,Job.HasCache "
390             "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool USING (PoolId) "
391             "LEFT JOIN FileSet USING (FileSetId) ",
392             "ORDER BY StartTime%s", limit);
393       } else {                           /* single record */
394          Mmsg(cmd,
395             "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
396             "Job.ClientId,Client.Name as ClientName,JobStatus,SchedTime,"
397             "StartTime,EndTime,RealEndTime,JobTDate,"
398             "VolSessionId,VolSessionTime,JobFiles,JobBytes,ReadBytes,JobErrors,"
399             "JobMissingFiles,Job.PoolId,Pool.Name as PooLname,PriorJobId,"
400             "Job.FileSetId,FileSet.FileSet,Job.HasCache "
401             "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool USING (PoolId) "
402             "LEFT JOIN FileSet USING (FileSetId) WHERE Job.JobId=%s ",
403             edit_int64(jr->JobId, ed1));
404       }
405       break;
406    case HORZ_LIST:
407       if (jr->Name[0] != 0) {
408          bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
409          Mmsg(cmd,
410            "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
411              "FROM Job WHERE Name='%s' ORDER BY StartTime,JobId ASC", esc);
412       } else if (jr->Job[0] != 0) {
413          bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
414          Mmsg(cmd,
415             "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
416             "FROM Job WHERE Job='%s' ORDER BY StartTime,JobId ASC", esc);
417       } else if (jr->JobId != 0) {
418          Mmsg(cmd,
419             "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
420             "FROM Job WHERE JobId=%s", edit_int64(jr->JobId, ed1));
421       } else {                           /* all records */
422          Mmsg(cmd,
423            "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
424            "FROM Job ORDER BY StartTime,JobId ASC%s", limit);
425       }
426       break;
427    case INCOMPLETE_JOBS:
428       if (jr->Name[0] != 0) {
429          bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
430          if (jr->JobStatus == JS_FatalError) {
431             snprintf(status, sizeof(status), "JobStatus IN ('E','f') AND");
432          } else if (jr->JobStatus != 0) {
433             snprintf(status, sizeof(status), "JobStatus='%c' AND", jr->JobStatus);
434          } else {
435             status[0] = 0;
436          }
437          Mmsg(cmd,
438            "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
439              "FROM Job WHERE %s Name='%s' ORDER BY StartTime,JobId ASC%s",
440               status, esc, limit);
441          Dmsg1(100, "SQL: %s\n", cmd);
442       } else {                           /* all records */
443          if (jr->JobStatus != 0) {
444             snprintf(status, sizeof(status), "WHERE JobStatus='%c'", jr->JobStatus);
445          } else {
446             status[0] = 0;
447          }
448          Mmsg(cmd,
449            "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
450            "FROM Job %s ORDER BY StartTime,JobId ASC%s", status, limit);
451          Dmsg1(100, "SQL: %s\n", cmd);
452       }
453       break;
454    default:
455       break;
456    }
457    if (!QueryDB(jcr, cmd)) {
458       bdb_unlock();
459       return NULL;
460    }
461    if (type == INCOMPLETE_JOBS) {
462       SQL_ROW row;
463       list = New(alist(10));
464       sql_data_seek(0);
465       for (int i=0; (row=sql_fetch_row()) != NULL; i++) {
466          list->append(bstrdup(row[0]));
467       }
468    }
469    sql_data_seek(0);
470    list_result(jcr, this, sendit, ctx, type);
471    sql_free_result();
472    bdb_unlock();
473    return list;
474 }
475
476 /*
477  * List Job totals
478  *
479  */
480 void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
481 {
482    bdb_lock();
483
484    /* List by Job */
485    Mmsg(cmd, "SELECT  count(*) AS Jobs,sum(JobFiles) "
486       "AS Files,sum(JobBytes) AS Bytes,Name AS Job FROM Job GROUP BY Name");
487
488    if (!QueryDB(jcr, cmd)) {
489       bdb_unlock();
490       return;
491    }
492
493    list_result(jcr, this, sendit, ctx, HORZ_LIST);
494
495    sql_free_result();
496
497    /* Do Grand Total */
498    Mmsg(cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
499         "AS Files,sum(JobBytes) As Bytes FROM Job");
500
501    if (!QueryDB(jcr, cmd)) {
502       bdb_unlock();
503       return;
504    }
505
506    list_result(jcr, this, sendit, ctx, HORZ_LIST);
507
508    sql_free_result();
509    bdb_unlock();
510 }
511
512 void BDB::bdb_list_files_for_job(JCR *jcr, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
513 {
514    char ed1[50];
515    LIST_CTX lctx(jcr, this, sendit, ctx, HORZ_LIST);
516
517    bdb_lock();
518
519    /*
520     * Stupid MySQL is NON-STANDARD !
521     */
522    if (bdb_get_type_index() == SQL_TYPE_MYSQL) {
523       Mmsg(cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
524            "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s "
525                   "UNION ALL "
526                  "SELECT PathId, FilenameId "
527                    "FROM BaseFiles JOIN File "
528                          "ON (BaseFiles.FileId = File.FileId) "
529                   "WHERE BaseFiles.JobId = %s"
530            ") AS F, Filename,Path "
531            "WHERE Filename.FilenameId=F.FilenameId "
532            "AND Path.PathId=F.PathId",
533            edit_int64(jobid, ed1), ed1);
534    } else {
535       Mmsg(cmd, "SELECT Path.Path||Filename.Name AS Filename "
536            "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s "
537                   "UNION ALL "
538                  "SELECT PathId, FilenameId "
539                    "FROM BaseFiles JOIN File "
540                          "ON (BaseFiles.FileId = File.FileId) "
541                   "WHERE BaseFiles.JobId = %s"
542            ") AS F, Filename,Path "
543            "WHERE Filename.FilenameId=F.FilenameId "
544            "AND Path.PathId=F.PathId",
545            edit_int64(jobid, ed1), ed1);
546    }
547
548    if (!bdb_big_sql_query(cmd, list_result, &lctx)) {
549        bdb_unlock();
550        return;
551    }
552
553    lctx.send_dashes();
554
555    sql_free_result();
556    bdb_unlock();
557 }
558
559 void BDB::bdb_list_base_files_for_job(JCR *jcr, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
560 {
561    char ed1[50];
562    LIST_CTX lctx(jcr, this, sendit, ctx, HORZ_LIST);
563
564    bdb_lock();
565
566    /*
567     * Stupid MySQL is NON-STANDARD !
568     */
569    if (bdb_get_type_index() == SQL_TYPE_MYSQL) {
570       Mmsg(cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
571            "FROM BaseFiles, File, Filename, Path "
572            "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
573            "AND BaseFiles.FileId = File.FileId "
574            "AND Filename.FilenameId=File.FilenameId "
575            "AND Path.PathId=File.PathId",
576          edit_int64(jobid, ed1));
577    } else {
578       Mmsg(cmd, "SELECT Path.Path||Filename.Name AS Filename "
579            "FROM BaseFiles, File, Filename, Path "
580            "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
581            "AND BaseFiles.FileId = File.FileId "
582            "AND Filename.FilenameId=File.FilenameId "
583            "AND Path.PathId=File.PathId",
584            edit_int64(jobid, ed1));
585    }
586
587    if (!bdb_big_sql_query(cmd, list_result, &lctx)) {
588        bdb_unlock();
589        return;
590    }
591
592    lctx.send_dashes();
593
594    sql_free_result();
595    bdb_unlock();
596 }
597
598 #define append_filter(buf, sql)  \
599    do {                          \
600       if (*buf) {                \
601          pm_strcat(buf, " AND ");\
602       } else {                   \
603          pm_strcpy(buf, " WHERE ");\
604       }                          \
605       pm_strcat(buf, sql);       \
606    } while (0)
607
608 void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
609               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
610 {
611    POOLMEM *filter = get_pool_memory(PM_MESSAGE);
612    POOLMEM *tmp    = get_pool_memory(PM_MESSAGE);
613    POOLMEM *esc    = get_pool_memory(PM_MESSAGE);
614    char ed1[50];
615
616    bdb_lock();
617    *filter = 0;
618
619    if (sdbr->Name[0]) {
620       bdb_escape_string(jcr, esc, sdbr->Name, strlen(sdbr->Name));
621       Mmsg(tmp, "Name='%s'", esc);
622       append_filter(filter, tmp);
623    }
624    if (sdbr->SnapshotId > 0) {
625       Mmsg(tmp, "Snapshot.SnapshotId=%d", sdbr->SnapshotId);
626       append_filter(filter, tmp);
627    }
628    if (sdbr->ClientId > 0) {
629       Mmsg(tmp, "Snapshot.ClientId=%d", sdbr->ClientId);
630       append_filter(filter, tmp);
631    }
632    if (sdbr->JobId > 0) {
633       Mmsg(tmp, "Snapshot.JobId=%d", sdbr->JobId);
634       append_filter(filter, tmp);
635    }
636    if (*sdbr->Client) {
637       bdb_escape_string(jcr, esc, sdbr->Client, strlen(sdbr->Client));
638       Mmsg(tmp, "Client.Name='%s'", esc);
639       append_filter(filter, tmp);
640    }
641    if (sdbr->Device && *(sdbr->Device)) {
642       esc = check_pool_memory_size(esc, strlen(sdbr->Device) * 2 + 1);
643       bdb_escape_string(jcr, esc, sdbr->Device, strlen(sdbr->Device));
644       Mmsg(tmp, "Device='%s'", esc);
645       append_filter(filter, tmp);
646    }
647    if (*sdbr->Type) {
648       bdb_escape_string(jcr, esc, sdbr->Type, strlen(sdbr->Type));
649       Mmsg(tmp, "Type='%s'", esc);
650       append_filter(filter, tmp);
651    }
652    if (*sdbr->created_before) {
653       bdb_escape_string(jcr, esc, sdbr->created_before, strlen(sdbr->created_before));
654       Mmsg(tmp, "CreateDate <= '%s'", esc);
655       append_filter(filter, tmp);
656    }
657    if (*sdbr->created_after) {
658       bdb_escape_string(jcr, esc, sdbr->created_after, strlen(sdbr->created_after));
659       Mmsg(tmp, "CreateDate >= '%s'", esc);
660       append_filter(filter, tmp);
661    }
662    if (sdbr->expired) {
663       Mmsg(tmp, "CreateTDate < (%s - Retention)", edit_int64(time(NULL), ed1));
664       append_filter(filter, tmp);
665    }
666    if (*sdbr->CreateDate) {
667       bdb_escape_string(jcr, esc, sdbr->CreateDate, strlen(sdbr->CreateDate));
668       Mmsg(tmp, "CreateDate = '%s'", esc);
669       append_filter(filter, tmp);
670    }
671
672    if (sdbr->sorted_client) {
673       pm_strcat(filter, " ORDER BY Client.Name, SnapshotId DESC");
674
675    } else {
676       pm_strcat(filter, " ORDER BY SnapshotId DESC");
677    }
678
679    if (type == VERT_LIST || type == ARG_LIST) {
680       Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, "
681            "FileSet.FileSet AS FileSet, JobId, Volume, Device, Type, Retention, Comment "
682            "FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) %s", filter);
683
684    } else if (type == HORZ_LIST) {
685       Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, "
686            "Device, Type "
687            "FROM Snapshot JOIN Client USING (ClientId) %s", filter);
688    }
689
690    if (!QueryDB(jcr, cmd)) {
691       goto bail_out;
692    }
693
694    list_result(jcr, this, sendit, ctx, type);
695
696 bail_out:
697    sql_free_result();
698    bdb_unlock();
699
700    free_pool_memory(filter);
701    free_pool_memory(esc);
702    free_pool_memory(tmp);
703 }
704
705 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */