2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Catalog Database List records interface routines
22 * Written by Kern Sibbald, March 2000
28 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
32 /* -----------------------------------------------------------------------
34 * Generic Routines (or almost generic)
36 * -----------------------------------------------------------------------
39 #define append_filter(buf, sql) \
42 pm_strcat(buf, " AND ");\
44 pm_strcpy(buf, " WHERE ");\
46 pm_strcat(buf, sql); \
50 * Submit general SQL query
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)
56 if (!sql_query(query, QF_STORE_RESULT)) {
57 Mmsg(errmsg, _("Query failed: %s\n"), sql_strerror());
65 list_result(jcr,this, sendit, ctx, type);
71 void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr,
72 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
74 char esc[MAX_ESCAPE_NAME_LENGTH];
77 bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
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,ActionOnPurge,CacheRetention "
85 " FROM Pool WHERE Name='%s'", esc);
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,ActionOnPurge,CacheRetention "
91 " FROM Pool ORDER BY PoolId");
94 if (pdbr->Name[0] != 0) {
95 Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,MaxVolBytes,VolRetention,Enabled,PoolType,LabelFormat "
96 "FROM Pool WHERE Name='%s'", esc);
98 Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,MaxVolBytes,VolRetention,Enabled,PoolType,LabelFormat "
99 "FROM Pool ORDER BY PoolId");
103 if (!QueryDB(jcr, cmd)) {
108 list_result(jcr, this, sendit, ctx, type);
114 void BDB::bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
117 if (type == VERT_LIST) {
118 Mmsg(cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,"
120 "FROM Client ORDER BY ClientId");
122 Mmsg(cmd, "SELECT ClientId,Name,FileRetention,JobRetention "
123 "FROM Client ORDER BY ClientId");
126 if (!QueryDB(jcr, cmd)) {
131 list_result(jcr, this, sendit, ctx, type);
138 * List restore objects
140 * JobId | JobIds: List RestoreObjects for specific Job(s)
141 * It is possible to specify the ObjectType using FileType field.
143 void BDB::bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
149 if (rr->JobIds && is_a_number_list(rr->JobIds)) {
152 } else if (rr->JobId) {
153 jobid = edit_int64(rr->JobId, ed1);
159 if (rr->FileType > 0) {
160 Mmsg(filter, "AND ObjectType = %d ", rr->FileType);
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());
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());
178 if (!QueryDB(jcr, cmd)) {
183 list_result(jcr, this, sendit, ctx, type);
190 * If VolumeName is non-zero, list the record for that Volume
191 * otherwise, list the Volumes in the Pool specified by PoolId
193 void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
194 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
197 char esc[MAX_ESCAPE_NAME_LENGTH];
198 const char *expiresin = expires_in[bdb_get_type_index()];
201 bdb_escape_string(jcr, esc, mdbr->VolumeName, strlen(mdbr->VolumeName));
202 const char *join = "";
203 const char *where = "";
205 if (type == VERT_LIST) {
206 if (mdbr->VolumeName[0] != 0) {
207 Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
208 "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs,"
209 "VolFiles,VolBlocks,VolParts,VolCloudParts,Media.CacheRetention,VolMounts,VolBytes,"
210 "VolABytes,VolAPadding,"
211 "VolHoleBytes,VolHoles,LastPartBytes,VolErrors,VolWrites,"
212 "VolCapacityBytes,VolStatus,Media.Enabled,Media.Recycle,Media.VolRetention,"
213 "Media.VolUseDuration,Media.MaxVolJobs,Media.MaxVolFiles,Media.MaxVolBytes,InChanger,"
214 "EndFile,EndBlock,VolType,Media.LabelType,StorageId,DeviceId,"
215 "MediaAddressing,VolReadTime,VolWriteTime,"
216 "LocationId,RecycleCount,InitialWrite,Media.ScratchPoolId,Media.RecyclePoolId, "
217 "Media.ActionOnPurge,%s AS ExpiresIn, Comment"
218 " FROM Media %s WHERE Media.VolumeName='%s' %s",
225 Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
226 "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs,"
227 "VolFiles,VolBlocks,VolParts,VolCloudParts,Media.CacheRetention,VolMounts,VolBytes,"
228 "VolABytes,VolAPadding,"
229 "VolHoleBytes,VolHoles,LastPartBytes,VolErrors,VolWrites,"
230 "VolCapacityBytes,VolStatus,Media.Enabled,Media.Recycle,Media.VolRetention,"
231 "Media.VolUseDuration,Media.MaxVolJobs,Media.MaxVolFiles,Media.MaxVolBytes,InChanger,"
232 "EndFile,EndBlock,VolType,Media.LabelType,StorageId,DeviceId,"
233 "MediaAddressing,VolReadTime,VolWriteTime,"
234 "LocationId,RecycleCount,InitialWrite,Media.ScratchPoolId,Media.RecyclePoolId, "
235 "Media.ActionOnPurge,%s AS ExpiresIn, Comment"
236 " FROM Media %s WHERE Media.PoolId=%s %s ORDER BY MediaId",
239 edit_int64(mdbr->PoolId, ed1),
244 if (mdbr->VolumeName[0] != 0) {
245 Mmsg(cmd, "SELECT MediaId,VolumeName,VolStatus,Media.Enabled,"
246 "VolBytes,VolFiles,Media.VolRetention,Media.Recycle,Slot,InChanger,MediaType,VolType,"
247 "VolParts,%s AS ExpiresIn "
248 "FROM Media %s WHERE Media.VolumeName='%s' %s",
255 Mmsg(cmd, "SELECT MediaId,VolumeName,VolStatus,Media.Enabled,"
256 "VolBytes,VolFiles,Media.VolRetention,Media.Recycle,Slot,InChanger,MediaType,VolType,"
257 "VolParts,LastWritten,%s AS ExpiresIn "
258 "FROM Media %s WHERE Media.PoolId=%s %s ORDER BY MediaId",
261 edit_int64(mdbr->PoolId, ed1),
266 Dmsg1(DT_SQL|50, "q=%s\n", cmd);
267 if (!QueryDB(jcr, cmd)) {
272 list_result(jcr, this, sendit, ctx, type);
278 void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId,
279 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
284 const char *join = "";
285 const char *where = "";
287 if (type == VERT_LIST) {
288 if (JobId > 0) { /* do by JobId */
289 Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
290 "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
292 "FROM JobMedia JOIN Media USING (MediaId) %s "
293 "WHERE JobMedia.JobId=%s %s",
295 edit_int64(JobId, ed1),
298 Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
299 "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
301 "FROM JobMedia JOIN Media USING (MediaId) %s %s",
307 if (JobId > 0) { /* do by JobId */
308 Mmsg(cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
309 "FROM JobMedia JOIN Media USING (MediaId) %s WHERE "
310 "JobMedia.JobId=%s %s",
312 edit_int64(JobId, ed1),
315 Mmsg(cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
316 "FROM JobMedia JOIN Media USING (MediaId) %s %s",
321 Dmsg1(DT_SQL|50, "q=%s\n", cmd);
323 if (!QueryDB(jcr, cmd)) {
328 list_result(jcr, this, sendit, ctx, type);
335 void BDB::bdb_list_copies_records(JCR *jcr, uint32_t limit, char *JobIds,
336 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
338 POOL_MEM str_limit(PM_MESSAGE);
339 POOL_MEM str_jobids(PM_MESSAGE);
342 Mmsg(str_limit, " LIMIT %d", limit);
345 if (JobIds && JobIds[0]) {
346 Mmsg(str_jobids, " AND (Job.PriorJobId IN (%s) OR Job.JobId IN (%s)) ",
352 "SELECT DISTINCT Job.PriorJobId AS JobId, Job.Job, "
353 "Job.JobId AS CopyJobId, Media.MediaType "
355 "JOIN JobMedia USING (JobId) "
356 "JOIN Media USING (MediaId) "
357 "WHERE Job.Type = '%c' %s ORDER BY Job.PriorJobId DESC %s",
358 (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
360 if (!QueryDB(jcr, cmd)) {
364 if (sql_num_rows()) {
365 if (JobIds && JobIds[0]) {
366 sendit(ctx, _("These JobIds have copies as follows:\n"));
368 sendit(ctx, _("The catalog contains copies as follows:\n"));
371 list_result(jcr, this, sendit, ctx, type);
380 void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId,
381 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
389 if (type == VERT_LIST) {
390 Mmsg(cmd, "SELECT Time,LogText FROM Log "
391 "WHERE Log.JobId=%s ORDER BY LogId ASC", edit_int64(JobId, ed1));
393 Mmsg(cmd, "SELECT LogText FROM Log "
394 "WHERE Log.JobId=%s ORDER BY LogId ASC", edit_int64(JobId, ed1));
396 if (!QueryDB(jcr, cmd)) {
400 list_result(jcr, this, sendit, ctx, type);
410 * List Job record(s) that match JOB_DBR
412 * Currently, we return all jobs or if jr->JobId is set,
413 * only the job with the specified id.
415 alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
416 void *ctx, e_list_type type)
420 char esc[MAX_ESCAPE_NAME_LENGTH];
422 POOLMEM *where = get_pool_memory(PM_MESSAGE);
423 POOLMEM *tmp = get_pool_memory(PM_MESSAGE);
424 const char *order = "ASC";
428 if (jr->order == 1) {
432 snprintf(limit, sizeof(limit), " LIMIT %d", jr->limit);
437 bdb_escape_string(jcr, esc, jr->Name, strlen(jr->Name));
438 Mmsg(tmp, " Job.Name='%s' ", esc);
439 append_filter(where, tmp);
441 } else if (jr->JobId != 0) {
442 Mmsg(tmp, " Job.JobId=%s ", edit_int64(jr->JobId, ed1));
443 append_filter(where, tmp);
445 } else if (jr->Job[0] != 0) {
446 bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
447 Mmsg(tmp, " Job.Job='%s' ", esc);
448 append_filter(where, tmp);
451 if (type == INCOMPLETE_JOBS && jr->JobStatus == JS_FatalError) {
452 Mmsg(tmp, " Job.JobStatus IN ('E', 'f') ");
453 append_filter(where, tmp);
455 } else if (jr->JobStatus) {
456 Mmsg(tmp, " Job.JobStatus='%c' ", jr->JobStatus);
457 append_filter(where, tmp);
461 Mmsg(tmp, " Job.Type='%c' ", jr->JobType);
462 append_filter(where, tmp);
465 if (jr->JobErrors > 0) {
466 Mmsg(tmp, " Job.JobErrors > 0 ");
467 append_filter(where, tmp);
470 if (jr->ClientId > 0) {
471 Mmsg(tmp, " Job.ClientId=%s ", edit_int64(jr->ClientId, ed1));
472 append_filter(where, tmp);
478 "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
479 "Job.ClientId,Client.Name as ClientName,JobStatus,SchedTime,"
480 "StartTime,EndTime,RealEndTime,JobTDate,"
481 "VolSessionId,VolSessionTime,JobFiles,JobBytes,ReadBytes,JobErrors,"
482 "JobMissingFiles,Job.PoolId,Pool.Name as PoolName,PriorJobId,"
483 "Job.FileSetId,FileSet.FileSet,Job.HasBase,Job.HasCache,Job.Comment "
484 "FROM Job JOIN Client USING (ClientId) LEFT JOIN Pool USING (PoolId) "
485 "LEFT JOIN FileSet USING (FileSetId) %s "
486 "ORDER BY StartTime %s %s", where, order, limit);
490 "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
491 "FROM Job %s ORDER BY StartTime %s,JobId %s %s", where, order, order, limit);
493 case INCOMPLETE_JOBS:
495 "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
496 "FROM Job %s ORDER BY StartTime %s,JobId %s %s",
497 where, order, order, limit);
502 Dmsg1(100, "SQL: %s\n", cmd);
504 free_pool_memory(tmp);
505 free_pool_memory(where);
507 Dmsg1(000, "cmd: %s\n", cmd);
508 if (!QueryDB(jcr, cmd)) {
512 if (type == INCOMPLETE_JOBS) {
514 list = New(alist(10));
516 for (int i=0; (row=sql_fetch_row()) != NULL; i++) {
517 list->append(bstrdup(row[0]));
521 list_result(jcr, this, sendit, ctx, type);
531 void BDB::bdb_list_job_totals(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
536 Mmsg(cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
537 "AS Files,sum(JobBytes) AS Bytes,Name AS Job FROM Job GROUP BY Name");
539 if (!QueryDB(jcr, cmd)) {
544 list_result(jcr, this, sendit, ctx, HORZ_LIST);
549 Mmsg(cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
550 "AS Files,sum(JobBytes) As Bytes FROM Job");
552 if (!QueryDB(jcr, cmd)) {
557 list_result(jcr, this, sendit, ctx, HORZ_LIST);
563 /* List all file records from a job
564 * "deleted" values are described just below
566 void BDB::bdb_list_files_for_job(JCR *jcr, JobId_t jobid, int deleted, DB_LIST_HANDLER *sendit, void *ctx)
570 LIST_CTX lctx(jcr, this, sendit, ctx, HORZ_LIST);
573 case 0: /* Show only actual files */
574 opt = " AND FileIndex <> 0 ";
576 case 1: /* Show only deleted files */
577 opt = " AND FileIndex = 0 ";
579 default: /* Show everything */
587 * Stupid MySQL is NON-STANDARD !
589 if (bdb_get_type_index() == SQL_TYPE_MYSQL) {
590 Mmsg(cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
591 "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s %s "
593 "SELECT PathId, FilenameId "
594 "FROM BaseFiles JOIN File "
595 "ON (BaseFiles.FileId = File.FileId) "
596 "WHERE BaseFiles.JobId = %s"
597 ") AS F, Filename,Path "
598 "WHERE Filename.FilenameId=F.FilenameId "
599 "AND Path.PathId=F.PathId",
600 edit_int64(jobid, ed1), opt, ed1);
602 Mmsg(cmd, "SELECT Path.Path||Filename.Name AS Filename "
603 "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s %s "
605 "SELECT PathId, FilenameId "
606 "FROM BaseFiles JOIN File "
607 "ON (BaseFiles.FileId = File.FileId) "
608 "WHERE BaseFiles.JobId = %s"
609 ") AS F, Filename,Path "
610 "WHERE Filename.FilenameId=F.FilenameId "
611 "AND Path.PathId=F.PathId",
612 edit_int64(jobid, ed1), opt, ed1);
614 Dmsg1(100, "q=%s\n", cmd);
615 if (!bdb_big_sql_query(cmd, list_result, &lctx)) {
626 void BDB::bdb_list_base_files_for_job(JCR *jcr, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
629 LIST_CTX lctx(jcr, this, sendit, ctx, HORZ_LIST);
634 * Stupid MySQL is NON-STANDARD !
636 if (bdb_get_type_index() == SQL_TYPE_MYSQL) {
637 Mmsg(cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
638 "FROM BaseFiles, File, Filename, Path "
639 "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
640 "AND BaseFiles.FileId = File.FileId "
641 "AND Filename.FilenameId=File.FilenameId "
642 "AND Path.PathId=File.PathId",
643 edit_int64(jobid, ed1));
645 Mmsg(cmd, "SELECT Path.Path||Filename.Name AS Filename "
646 "FROM BaseFiles, File, Filename, Path "
647 "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
648 "AND BaseFiles.FileId = File.FileId "
649 "AND Filename.FilenameId=File.FilenameId "
650 "AND Path.PathId=File.PathId",
651 edit_int64(jobid, ed1));
654 if (!bdb_big_sql_query(cmd, list_result, &lctx)) {
665 void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
666 DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
668 POOLMEM *filter = get_pool_memory(PM_MESSAGE);
669 POOLMEM *tmp = get_pool_memory(PM_MESSAGE);
670 POOLMEM *esc = get_pool_memory(PM_MESSAGE);
677 bdb_escape_string(jcr, esc, sdbr->Name, strlen(sdbr->Name));
678 Mmsg(tmp, "Name='%s'", esc);
679 append_filter(filter, tmp);
681 if (sdbr->SnapshotId > 0) {
682 Mmsg(tmp, "Snapshot.SnapshotId=%d", sdbr->SnapshotId);
683 append_filter(filter, tmp);
685 if (sdbr->ClientId > 0) {
686 Mmsg(tmp, "Snapshot.ClientId=%d", sdbr->ClientId);
687 append_filter(filter, tmp);
689 if (sdbr->JobId > 0) {
690 Mmsg(tmp, "Snapshot.JobId=%d", sdbr->JobId);
691 append_filter(filter, tmp);
694 bdb_escape_string(jcr, esc, sdbr->Client, strlen(sdbr->Client));
695 Mmsg(tmp, "Client.Name='%s'", esc);
696 append_filter(filter, tmp);
698 if (sdbr->Device && *(sdbr->Device)) {
699 esc = check_pool_memory_size(esc, strlen(sdbr->Device) * 2 + 1);
700 bdb_escape_string(jcr, esc, sdbr->Device, strlen(sdbr->Device));
701 Mmsg(tmp, "Device='%s'", esc);
702 append_filter(filter, tmp);
705 bdb_escape_string(jcr, esc, sdbr->Type, strlen(sdbr->Type));
706 Mmsg(tmp, "Type='%s'", esc);
707 append_filter(filter, tmp);
709 if (*sdbr->created_before) {
710 bdb_escape_string(jcr, esc, sdbr->created_before, strlen(sdbr->created_before));
711 Mmsg(tmp, "CreateDate <= '%s'", esc);
712 append_filter(filter, tmp);
714 if (*sdbr->created_after) {
715 bdb_escape_string(jcr, esc, sdbr->created_after, strlen(sdbr->created_after));
716 Mmsg(tmp, "CreateDate >= '%s'", esc);
717 append_filter(filter, tmp);
720 Mmsg(tmp, "CreateTDate < (%s - Retention)", edit_int64(time(NULL), ed1));
721 append_filter(filter, tmp);
723 if (*sdbr->CreateDate) {
724 bdb_escape_string(jcr, esc, sdbr->CreateDate, strlen(sdbr->CreateDate));
725 Mmsg(tmp, "CreateDate = '%s'", esc);
726 append_filter(filter, tmp);
729 if (sdbr->sorted_client) {
730 pm_strcat(filter, " ORDER BY Client.Name, SnapshotId DESC");
733 pm_strcat(filter, " ORDER BY SnapshotId DESC");
736 if (type == VERT_LIST || type == ARG_LIST) {
737 Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, "
738 "FileSet.FileSet AS FileSet, JobId, Volume, Device, Type, Retention, Comment "
739 "FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) %s", filter);
741 } else if (type == HORZ_LIST) {
742 Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, "
744 "FROM Snapshot JOIN Client USING (ClientId) %s", filter);
747 if (!QueryDB(jcr, cmd)) {
751 list_result(jcr, this, sendit, ctx, type);
757 free_pool_memory(filter);
758 free_pool_memory(esc);
759 free_pool_memory(tmp);
762 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL */