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