]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql_list.c
Implement db_big_sql_query() that uses cursor on PostgreSQL and limit memory usage...
[bacula/bacula] / bacula / src / cats / sql_list.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 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 three of the GNU Affero 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 Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  * Bacula Catalog Database List records interface routines
30  *
31  *    Kern Sibbald, March 2000
32  *
33  *    Version $Id: sql_list.c 8508 2009-03-07 20:59:46Z kerns $
34  */
35
36 #include "bacula.h"
37 #include "cats.h"
38
39 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
40
41 #include "cats.h"
42 #include "bdb_priv.h"
43 #include "sql_glue.h"
44
45 /* -----------------------------------------------------------------------
46  *
47  *   Generic Routines (or almost generic)
48  *
49  * -----------------------------------------------------------------------
50  */
51
52 /*
53  * Submit general SQL query
54  */
55 int db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit,
56                       void *ctx, int verbose, e_list_type type)
57 {
58    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
59
60    db_lock(mdb);
61
62    if (!db_big_sql_query(mdb, query, list_result, &lctx)) {
63       Mmsg(mdb->errmsg, _("Query failed: %s\n"), sql_strerror(mdb));
64       if (verbose) {
65          sendit(ctx, mdb->errmsg);
66       }
67       db_unlock(mdb);
68       return 0;
69    }
70
71    lctx.send_dashes();
72
73    sql_free_result(mdb);
74    db_unlock(mdb);
75    return 1;
76 }
77
78 void
79 db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr,
80                      DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
81 {
82    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
83
84    db_lock(mdb);
85    if (type == VERT_LIST) {
86       if (pdbr->Name[0] != 0) {
87          Mmsg(mdb->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 WHERE Name='%s'", pdbr->Name);
92       } else {
93          Mmsg(mdb->cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,"
94             "AcceptAnyVolume,VolRetention,VolUseDuration,MaxVolJobs,MaxVolBytes,"
95             "AutoPrune,Recycle,PoolType,LabelFormat,Enabled,ScratchPoolId,"
96             "RecyclePoolId,LabelType "
97             " FROM Pool ORDER BY PoolId");
98       }
99    } else {
100       if (pdbr->Name[0] != 0) {
101          Mmsg(mdb->cmd, "SELECT PoolId,Name,NumVols,MaxVols,PoolType,LabelFormat "
102            "FROM Pool WHERE Name='%s'", pdbr->Name);
103       } else {
104          Mmsg(mdb->cmd, "SELECT PoolId,Name,NumVols,MaxVols,PoolType,LabelFormat "
105            "FROM Pool ORDER BY PoolId");
106       }
107    }
108
109    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
110       db_unlock(mdb);
111       return;
112    }
113
114    lctx.send_dashes();
115
116    sql_free_result(mdb);
117    db_unlock(mdb);
118 }
119
120 void
121 db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
122 {
123    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
124
125    db_lock(mdb);
126    if (type == VERT_LIST) {
127       Mmsg(mdb->cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,"
128          "JobRetention "
129          "FROM Client ORDER BY ClientId");
130    } else {
131       Mmsg(mdb->cmd, "SELECT ClientId,Name,FileRetention,JobRetention "
132          "FROM Client ORDER BY ClientId");
133    }
134
135    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
136       db_unlock(mdb);
137       return;
138    }
139    lctx.send_dashes();
140
141    sql_free_result(mdb);
142    db_unlock(mdb);
143 }
144
145
146 /*
147  * If VolumeName is non-zero, list the record for that Volume
148  *   otherwise, list the Volumes in the Pool specified by PoolId
149  */
150 void
151 db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr,
152                       DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
153 {
154    char ed1[50];
155    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
156
157    db_lock(mdb);
158    if (type == VERT_LIST) {
159       if (mdbr->VolumeName[0] != 0) {
160          Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
161             "MediaType,FirstWritten,LastWritten,LabelDate,VolJobs,"
162             "VolFiles,VolBlocks,VolMounts,VolBytes,VolErrors,VolWrites,"
163             "VolCapacityBytes,VolStatus,Enabled,Recycle,VolRetention,"
164             "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger,"
165             "EndFile,EndBlock,VolParts,LabelType,StorageId,DeviceId,"
166             "LocationId,RecycleCount,InitialWrite,ScratchPoolId,RecyclePoolId, "
167             "Comment"
168             " FROM Media WHERE Media.VolumeName='%s'", mdbr->VolumeName);
169       } else {
170          Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
171             "MediaType,FirstWritten,LastWritten,LabelDate,VolJobs,"
172             "VolFiles,VolBlocks,VolMounts,VolBytes,VolErrors,VolWrites,"
173             "VolCapacityBytes,VolStatus,Enabled,Recycle,VolRetention,"
174             "VolUseDuration,MaxVolJobs,MaxVolFiles,MaxVolBytes,InChanger,"
175             "EndFile,EndBlock,VolParts,LabelType,StorageId,DeviceId,"
176             "LocationId,RecycleCount,InitialWrite,ScratchPoolId,RecyclePoolId, "
177             "Comment"
178             " FROM Media WHERE Media.PoolId=%s ORDER BY MediaId",
179             edit_int64(mdbr->PoolId, ed1));
180       }
181    } else {
182       if (mdbr->VolumeName[0] != 0) {
183          Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolStatus,Enabled,"
184             "VolBytes,VolFiles,VolRetention,Recycle,Slot,InChanger,MediaType,LastWritten "
185             "FROM Media WHERE Media.VolumeName='%s'", mdbr->VolumeName);
186       } else {
187          Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolStatus,Enabled,"
188             "VolBytes,VolFiles,VolRetention,Recycle,Slot,InChanger,MediaType,LastWritten "
189             "FROM Media WHERE Media.PoolId=%s ORDER BY MediaId",
190             edit_int64(mdbr->PoolId, ed1));
191       }
192    }
193
194    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
195       db_unlock(mdb);
196       return;
197    }
198
199    lctx.send_dashes();
200
201    sql_free_result(mdb);
202    db_unlock(mdb);
203 }
204
205 void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
206                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
207 {
208    char ed1[50];
209    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
210
211    db_lock(mdb);
212    if (type == VERT_LIST) {
213       if (JobId > 0) {                   /* do by JobId */
214          Mmsg(mdb->cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
215             "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
216             "JobMedia.EndBlock "
217             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId "
218             "AND JobMedia.JobId=%s", edit_int64(JobId, ed1));
219       } else {
220          Mmsg(mdb->cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
221             "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
222             "JobMedia.EndBlock "
223             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId");
224       }
225
226    } else {
227       if (JobId > 0) {                   /* do by JobId */
228          Mmsg(mdb->cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
229             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId "
230             "AND JobMedia.JobId=%s", edit_int64(JobId, ed1));
231       } else {
232          Mmsg(mdb->cmd, "SELECT JobId,Media.VolumeName,FirstIndex,LastIndex "
233             "FROM JobMedia,Media WHERE Media.MediaId=JobMedia.MediaId");
234       }
235    }
236
237    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
238       db_unlock(mdb);
239       return;
240    }
241
242    lctx.send_dashes();
243
244    sql_free_result(mdb);
245    db_unlock(mdb);
246 }
247
248
249 void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds,
250                             DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
251 {
252    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
253    POOL_MEM str_limit(PM_MESSAGE);
254    POOL_MEM str_jobids(PM_MESSAGE);
255
256    if (limit > 0) {
257       Mmsg(str_limit, " LIMIT %d", limit);
258    }
259
260    if (JobIds && JobIds[0]) {
261       Mmsg(str_jobids, " AND (Job.PriorJobId IN (%s) OR Job.JobId IN (%s)) ", 
262            JobIds, JobIds);      
263    }
264
265    db_lock(mdb);
266    Mmsg(mdb->cmd, 
267    "SELECT DISTINCT Job.PriorJobId AS JobId, Job.Job, "
268                    "Job.JobId AS CopyJobId, Media.MediaType "
269      "FROM Job " 
270      "JOIN JobMedia USING (JobId) "
271      "JOIN Media    USING (MediaId) "
272     "WHERE Job.Type = '%c' %s ORDER BY Job.PriorJobId DESC %s",
273         (char) JT_JOB_COPY, str_jobids.c_str(), str_limit.c_str());
274
275    if (JobIds && JobIds[0]) {
276       sendit(ctx, _("These JobIds have copies as follows:\n"));
277    } else {
278       sendit(ctx, _("The catalog contains copies as follows:\n"));
279    }
280
281    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
282       goto bail_out;
283    }
284
285    lctx.send_dashes();
286
287    sql_free_result(mdb);
288
289 bail_out:
290    db_unlock(mdb);
291 }
292
293 void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId,
294                               DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
295 {
296    char ed1[50];
297    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
298
299    if (JobId <= 0) {
300       return;
301    }
302    db_lock(mdb);
303    if (type == VERT_LIST) {
304       Mmsg(mdb->cmd, "SELECT Time,LogText FROM Log "
305            "WHERE Log.JobId=%s", edit_int64(JobId, ed1));
306    } else {
307       Mmsg(mdb->cmd, "SELECT LogText FROM Log "
308            "WHERE Log.JobId=%s", edit_int64(JobId, ed1));
309    }
310
311    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
312       goto bail_out;
313    }
314
315    lctx.send_dashes();
316
317    sql_free_result(mdb);
318
319 bail_out:
320    db_unlock(mdb);
321 }
322
323
324 /*
325  * List Job record(s) that match JOB_DBR
326  *
327  *  Currently, we return all jobs or if jr->JobId is set,
328  *  only the job with the specified id.
329  */
330 void
331 db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
332                     void *ctx, e_list_type type)
333 {
334    char ed1[50];
335    char limit[100];
336    LIST_CTX lctx(jcr, mdb, sendit, ctx, type);
337
338    db_lock(mdb);
339    if (jr->limit > 0) {
340       snprintf(limit, sizeof(limit), " LIMIT %d", jr->limit);
341    } else {
342       limit[0] = 0;
343    }
344    if (type == VERT_LIST) {
345       if (jr->JobId == 0 && jr->Job[0] == 0) {
346          Mmsg(mdb->cmd,
347             "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
348             "Job.ClientId,Client.Name as ClientName,JobStatus,SchedTime,"
349             "StartTime,EndTime,RealEndTime,JobTDate,"
350             "VolSessionId,VolSessionTime,JobFiles,JobErrors,"
351             "JobMissingFiles,Job.PoolId,Pool.Name as PooLname,PriorJobId,"
352             "Job.FileSetId,FileSet.FileSet "
353             "FROM Job,Client,Pool,FileSet WHERE "
354             "Client.ClientId=Job.ClientId AND Pool.PoolId=Job.PoolId "
355             "AND FileSet.FileSetId=Job.FileSetId  ORDER BY StartTime%s", limit);
356       } else {                           /* single record */
357          Mmsg(mdb->cmd,
358             "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
359             "Job.ClientId,Client.Name,JobStatus,SchedTime,"
360             "StartTime,EndTime,RealEndTime,JobTDate,"
361             "VolSessionId,VolSessionTime,JobFiles,JobErrors,"
362             "JobMissingFiles,Job.PoolId,Pool.Name as PooLname,PriorJobId,"
363             "Job.FileSetId,FileSet.FileSet "
364             "FROM Job,Client,Pool,FileSet WHERE Job.JobId=%s AND "
365             "Client.ClientId=Job.ClientId AND Pool.PoolId=Job.PoolId "
366             "AND FileSet.FileSetId=Job.FileSetId",
367             edit_int64(jr->JobId, ed1));
368       }
369    } else {
370       if (jr->Name[0] != 0) {
371          Mmsg(mdb->cmd,
372             "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
373             "FROM Job WHERE Name='%s' ORDER BY StartTime,JobId ASC", jr->Name);
374       } else if (jr->Job[0] != 0) {
375          Mmsg(mdb->cmd,
376             "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
377             "FROM Job WHERE Job='%s' ORDER BY StartTime,JobId ASC", jr->Job);
378       } else if (jr->JobId != 0) {
379          Mmsg(mdb->cmd,
380             "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
381             "FROM Job WHERE JobId=%s", edit_int64(jr->JobId, ed1));
382       } else {                           /* all records */
383          Mmsg(mdb->cmd,
384            "SELECT JobId,Name,StartTime,Type,Level,JobFiles,JobBytes,JobStatus "
385            "FROM Job ORDER BY StartTime,JobId ASC%s", limit);
386       }
387    }
388
389    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
390       db_unlock(mdb);
391       return;
392    }
393    lctx.send_dashes();
394
395    sql_free_result(mdb);
396    db_unlock(mdb);
397 }
398
399 /*
400  * List Job totals
401  *
402  */
403 void
404 db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx)
405 {
406    LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
407
408    db_lock(mdb);
409
410    /* List by Job */
411    Mmsg(mdb->cmd, "SELECT  count(*) AS Jobs,sum(JobFiles) "
412       "AS Files,sum(JobBytes) AS Bytes,Name AS Job FROM Job GROUP BY Name");
413
414    if (!db_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
415       db_unlock(mdb);
416       return;
417    }
418    lctx.send_dashes();
419
420    sql_free_result(mdb);
421
422    /* Do Grand Total */
423    Mmsg(mdb->cmd, "SELECT count(*) AS Jobs,sum(JobFiles) "
424         "AS Files,sum(JobBytes) As Bytes FROM Job");
425
426    lctx.empty();
427    if (!db_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
428       db_unlock(mdb);
429       return;
430    }
431
432    lctx.send_dashes();
433
434    sql_free_result(mdb);
435    db_unlock(mdb);
436 }
437
438 void
439 db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
440 {
441    LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
442    char ed1[50];
443
444    db_lock(mdb);
445
446    /*
447     * Stupid MySQL is NON-STANDARD !
448     */
449    if (db_get_type_index(mdb) == SQL_TYPE_MYSQL) {
450       Mmsg(mdb->cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
451            "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s "
452                   "UNION ALL "
453                  "SELECT PathId, FilenameId "
454                    "FROM BaseFiles JOIN File "
455                          "ON (BaseFiles.FileId = File.FileId) "
456                   "WHERE BaseFiles.JobId = %s"
457            ") AS F, Filename,Path "
458            "WHERE Filename.FilenameId=F.FilenameId "
459            "AND Path.PathId=F.PathId",
460            edit_int64(jobid, ed1), ed1);
461    } else {
462       Mmsg(mdb->cmd, "SELECT Path.Path||Filename.Name AS Filename "
463            "FROM (SELECT PathId, FilenameId FROM File WHERE JobId=%s "
464                   "UNION ALL "
465                  "SELECT PathId, FilenameId "
466                    "FROM BaseFiles JOIN File "
467                          "ON (BaseFiles.FileId = File.FileId) "
468                   "WHERE BaseFiles.JobId = %s"
469            ") AS F, Filename,Path "
470            "WHERE Filename.FilenameId=F.FilenameId "
471            "AND Path.PathId=F.PathId",
472            edit_int64(jobid, ed1), ed1);
473    }
474
475    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
476       db_unlock(mdb);
477       return;
478    }
479    
480    lctx.send_dashes();
481
482    sql_free_result(mdb);
483    db_unlock(mdb);
484 }
485
486 void
487 db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx)
488 {
489    LIST_CTX lctx(jcr, mdb, sendit, ctx, HORZ_LIST);
490    char ed1[50];
491
492    db_lock(mdb);
493
494    /*
495     * Stupid MySQL is NON-STANDARD !
496     */
497    if (db_get_type_index(mdb) == SQL_TYPE_MYSQL) {
498       Mmsg(mdb->cmd, "SELECT CONCAT(Path.Path,Filename.Name) AS Filename "
499            "FROM BaseFiles, File, Filename, Path "
500            "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
501            "AND BaseFiles.FileId = File.FileId "
502            "AND Filename.FilenameId=File.FilenameId "
503            "AND Path.PathId=File.PathId",
504          edit_int64(jobid, ed1));
505    } else {
506       Mmsg(mdb->cmd, "SELECT Path.Path||Filename.Name AS Filename "
507            "FROM BaseFiles, File, Filename, Path "
508            "WHERE BaseFiles.JobId=%s AND BaseFiles.BaseJobId = File.JobId "
509            "AND BaseFiles.FileId = File.FileId "
510            "AND Filename.FilenameId=File.FilenameId "
511            "AND Path.PathId=File.PathId",
512            edit_int64(jobid, ed1));
513    }
514
515    if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) {
516       db_unlock(mdb);
517       return;
518    }
519
520    lctx.send_dashes();
521
522    sql_free_result(mdb);
523    db_unlock(mdb);
524 }
525
526 #endif /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI */