1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 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  *  Catalog DB header file
21  *
22  *  Written by Kern E. Sibbald
23  *
24  *  Anyone who accesses the database will need to include
25  *   this file.
26  */
27 
28 #ifndef __CATS_H_
29 #define __CATS_H_ 1
30 
31 /*
32    Here is how database versions work.
33 
34    While I am working on a new release with database changes, the
35    update scripts are in the src/cats directory under the names
36    update_xxx_tables.in.  Most of the time, I make database updates
37    in one go and immediately update the version, but not always.  If
38    there are going to be several updates as is the case with version
39    1.37, then I will often forgo changing the version until the last
40    update otherwise I will end up with too many versions and a lot
41    of confusion.
42 
43    When I am pretty sure there will be no more updates, I will
44    change the version from 8 to 9 (in the present case), and when I
45    am 100% sure there will be no more changes, the update script
46    will be copied to the updatedb directory with the correct name
47    (in the present case 8 to 9).
48  */
49 
50 /* Current database version number for all drivers */
51 #define BDB_VERSION 16
52 
53 typedef void (DB_LIST_HANDLER)(void *, const char *);
54 typedef int (DB_RESULT_HANDLER)(void *, int, char **);
55 
56 /* What kind of database we have */
57 typedef enum {
58    SQL_TYPE_MYSQL      = 0,
59    SQL_TYPE_POSTGRESQL = 1,
60    SQL_TYPE_SQLITE3    = 2,
61    SQL_TYPE_UNKNOWN    = 99
62 } SQL_DBTYPE;
63 
64 /* What kind of driver we have */
65 typedef enum {
66    SQL_DRIVER_TYPE_MYSQL      = 0,
67    SQL_DRIVER_TYPE_POSTGRESQL = 1,
68    SQL_DRIVER_TYPE_SQLITE3    = 2
69 } SQL_DRIVER;
70 
71 
72 /* ==============================================================
73  *
74  *  What follows are definitions that are used "globally" for all
75  *   the different SQL engines and both inside and external to the
76  *   cats directory.
77  */
78 
79 #define faddr_t long
80 
81 /*
82  * Generic definition of a sql_row.
83  */
84 typedef char **SQL_ROW;
85 
86 /*
87  * Generic definition of a a sql_field.
88  */
89 typedef struct sql_field {
90    char *name;                        /* name of column */
91    int max_length;                    /* max length */
92    uint32_t type;                     /* type */
93    uint32_t flags;                    /* flags */
94 } SQL_FIELD;
95 
96 
97 /*
98  * Structure used when calling db_get_query_ids()
99  *  allows the subroutine to return a list of ids.
100  */
101 class dbid_list : public SMARTALLOC {
102 public:
103    DBId_t *DBId;                      /* array of DBIds */
104    char *PurgedFiles;                 /* Array of PurgedFile flags */
105    int num_ids;                       /* num of ids actually stored */
106    int max_ids;                       /* size of id array */
107    int num_seen;                      /* number of ids processed */
108    int tot_ids;                       /* total to process */
109 
110    dbid_list();                       /* in sql.c */
111    ~dbid_list();                      /* in sql.c */
112 };
113 
114 /* Job information passed to create job record and update
115  * job record at end of job. Note, although this record
116  * contains all the fields found in the Job database record,
117  * it also contains fields found in the JobMedia record.
118  */
119 /* Job record */
120 struct JOB_DBR {
121    JobId_t JobId;
122    char Job[MAX_NAME_LENGTH];         /* Job unique name */
123    char Name[MAX_NAME_LENGTH];        /* Job base name */
124    char PriorJob[MAX_NAME_LENGTH];    /* PriorJob name if any */
125    int JobType;                       /* actually char(1) */
126    int JobLevel;                      /* actually char(1) */
127    int JobStatus;                     /* actually char(1) */
128    DBId_t ClientId;                   /* Id of client */
129    DBId_t PoolId;                     /* Id of pool */
130    DBId_t FileSetId;                  /* Id of FileSet */
131    DBId_t PriorJobId;                 /* Id of migrated (prior) job */
132    time_t SchedTime;                  /* Time job scheduled */
133    time_t StartTime;                  /* Job start time */
134    time_t EndTime;                    /* Job termination time of orig job */
135    time_t RealEndTime;                /* Job termination time of this job */
136    utime_t JobTDate;                  /* Backup time/date in seconds */
137    uint32_t VolSessionId;
138    uint32_t VolSessionTime;
139    uint32_t JobFiles;
140    uint32_t JobErrors;
141    uint32_t JobMissingFiles;
142    uint64_t JobBytes;
143    uint64_t ReadBytes;
144    int PurgedFiles;
145    int HasBase;
146 
147    /* Note, FirstIndex, LastIndex, Start/End File and Block
148     * are only used in the JobMedia record.
149     */
150    uint32_t FirstIndex;               /* First index this Volume */
151    uint32_t LastIndex;                /* Last index this Volume */
152    uint32_t StartFile;
153    uint32_t EndFile;
154    uint32_t StartBlock;
155    uint32_t EndBlock;
156 
157    char cSchedTime[MAX_TIME_LENGTH];
158    char cStartTime[MAX_TIME_LENGTH];
159    char cEndTime[MAX_TIME_LENGTH];
160    char cRealEndTime[MAX_TIME_LENGTH];
161    /* Extra stuff not in DB */
162    int     order;                     /* 0 ASC, 1 DESC */
163    int     limit;                     /* limit records to display */
164    faddr_t rec_addr;
165    int32_t FileIndex;                 /* added during Verify */
166 
167    int     CorrNbJob;                 /* used by dbd_get_job_statistics() */
168    int     CorrJobBytes;              /* used by dbd_get_job_statistics() */
169    int     CorrJobFiles;              /* used by dbd_get_job_statistics() */
170 };
171 
172 /* Job Media information used to create the media records
173  * for each Volume used for the job.
174  */
175 /* JobMedia record */
176 struct JOBMEDIA_DBR {
177    DBId_t JobMediaId;                 /* record id */
178    JobId_t  JobId;                    /* JobId */
179    DBId_t MediaId;                    /* MediaId */
180    uint32_t FirstIndex;               /* First index this Volume */
181    uint32_t LastIndex;                /* Last index this Volume */
182    uint32_t StartFile;                /* File for start of data */
183    uint32_t EndFile;                  /* End file on Volume */
184    uint32_t StartBlock;               /* start block on tape */
185    uint32_t EndBlock;                 /* last block */
186    uint32_t VolIndex;                 /* Volume seqence no. */
187 };
188 
189 
190 /* Volume Parameter structure */
191 struct VOL_PARAMS {
192    char VolumeName[MAX_NAME_LENGTH];  /* Volume name */
193    char MediaType[MAX_NAME_LENGTH];   /* Media Type */
194    char Storage[MAX_NAME_LENGTH];     /* Storage name */
195    uint32_t VolIndex;                 /* Volume seqence no. */
196    uint32_t FirstIndex;               /* First index this Volume */
197    uint32_t LastIndex;                /* Last index this Volume */
198    int32_t Slot;                      /* Slot */
199    uint64_t StartAddr;                /* Start address */
200    uint64_t EndAddr;                  /* End address */
201    int32_t InChanger;                 /* InChanger flag */
202 };
203 
204 
205 /* Attributes record -- NOT same as in database because
206  *  in general, this "record" creates multiple database
207  *  records (e.g. pathname, filename, fileattributes).
208  */
209 struct ATTR_DBR {
210    char *fname;                       /* full path & filename */
211    char *link;                        /* link if any */
212    char *attr;                        /* attributes statp */
213    int32_t FileIndex;
214    uint32_t Stream;
215    uint32_t FileType;
216    uint32_t DeltaSeq;
217    JobId_t  JobId;
218    DBId_t ClientId;
219    DBId_t PathId;
220    DBId_t FilenameId;
221    FileId_t FileId;
222    char *Digest;
223    int DigestType;
224 };
225 
226 struct ROBJECT_DBR {
227    char *object_name;
228    char *object;
229    char *plugin_name;
230    char *JobIds;
231    uint32_t object_len;
232    uint32_t object_full_len;
233    uint32_t object_index;
234    int32_t  object_compression;
235    int32_t FileIndex;
236    uint32_t Stream;
237    uint32_t FileType;
238    JobId_t  JobId;
239    DBId_t RestoreObjectId;
240 };
241 
242 
243 /* File record -- same format as database */
244 struct FILE_DBR {
245    FileId_t FileId;
246    int32_t FileIndex;
247    int32_t FileIndex2;
248    JobId_t  JobId;
249    DBId_t FilenameId;
250    DBId_t PathId;
251    JobId_t  MarkId;
252    uint32_t DeltaSeq;
253    char LStat[256];
254    char Digest[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
255    int DigestType;                    /* NO_SIG/MD5_SIG/SHA1_SIG */
256 };
257 
258 /* Pool record -- same format as database */
259 class POOL_DBR {
260 public:
261    /*
262     * Do not turn on constructor until all bmemset on POOL_DBR removed
263     *
264     * POOL_DBR() { bmemset(this, 0, sizeof(POOL_DBR)); };
265     * ~POOL_DBR() {  };
266     */
267    DBId_t PoolId;
268    char Name[MAX_NAME_LENGTH];        /* Pool name */
269    uint32_t NumVols;                  /* total number of volumes */
270    uint32_t MaxVols;                  /* max allowed volumes */
271    int32_t LabelType;                 /* Bacula/ANSI/IBM */
272    int32_t UseOnce;                   /* set to use once only */
273    int32_t UseCatalog;                /* set to use catalog */
274    int32_t AcceptAnyVolume;           /* set to accept any volume sequence */
275    int32_t AutoPrune;                 /* set to prune automatically */
276    int32_t Recycle;                   /* default Vol recycle flag */
277    uint32_t ActionOnPurge;            /* action on purge, e.g. truncate the disk volume */
278    utime_t  VolRetention;             /* retention period in seconds */
279    utime_t  CacheRetention;           /* cache retention period in seconds */
280    utime_t  VolUseDuration;           /* time in secs volume can be used */
281    uint32_t MaxVolJobs;               /* Max Jobs on Volume */
282    uint32_t MaxVolFiles;              /* Max files on Volume */
283    uint64_t MaxVolBytes;              /* Max bytes on Volume */
284    DBId_t RecyclePoolId;              /* RecyclePool destination when media is purged */
285    DBId_t ScratchPoolId;              /* ScratchPool source when media is needed */
286    char PoolType[MAX_NAME_LENGTH];
287    char LabelFormat[MAX_NAME_LENGTH];
288    /* Extra stuff not in DB */
289    faddr_t rec_addr;
290 };
291 
292 class DEVICE_DBR {
293 public:
294    DBId_t DeviceId;
295    char Name[MAX_NAME_LENGTH];        /* Device name */
296    DBId_t MediaTypeId;                /* MediaType */
297    DBId_t StorageId;                  /* Storage id if autochanger */
298    uint32_t DevMounts;                /* Number of times mounted */
299    uint32_t DevErrors;                /* Number of read/write errors */
300    uint64_t DevReadBytes;             /* Number of bytes read */
301    uint64_t DevWriteBytes;            /* Number of bytew written */
302    uint64_t DevReadTime;              /* time spent reading volume */
303    uint64_t DevWriteTime;             /* time spent writing volume */
304    uint64_t DevReadTimeSincCleaning;  /* read time since cleaning */
305    uint64_t DevWriteTimeSincCleaning; /* write time since cleaning */
306    time_t   CleaningDate;             /* time last cleaned */
307    utime_t  CleaningPeriod;           /* time between cleanings */
308 };
309 
310 class STORAGE_DBR {
311 public:
312    DBId_t StorageId;
313    char Name[MAX_NAME_LENGTH];        /* Device name */
314    int AutoChanger;                   /* Set if autochanger */
315 
316    /* Not in database */
317    bool created;                      /* set if created by db_create ... */
318 };
319 
320 class MEDIATYPE_DBR {
321 public:
322    DBId_t MediaTypeId;
323    char MediaType[MAX_NAME_LENGTH];   /* MediaType string */
324    int ReadOnly;                      /* Set if read-only */
325 };
326 
327 /* Media record -- same as the database */
328 class MEDIA_DBR {
329 public:
MEDIA_DBR()330    MEDIA_DBR() { memset(this, 0, sizeof(MEDIA_DBR)); };
~MEDIA_DBR()331    ~MEDIA_DBR() {  };
clear()332    void clear() { memset(this, 0, sizeof(MEDIA_DBR)); };
copy(MEDIA_DBR * omr)333    void copy(MEDIA_DBR *omr) { memcpy(this, omr, sizeof(MEDIA_DBR)); sid_group = NULL; };
334 
335    DBId_t MediaId;                    /* Unique volume id */
336    char VolumeName[MAX_NAME_LENGTH];  /* Volume name */
337    char MediaType[MAX_NAME_LENGTH];   /* Media type */
338    DBId_t PoolId;                     /* Pool id */
339    time_t   FirstWritten;             /* Time Volume first written this usage */
340    time_t   LastWritten;              /* Time Volume last written */
341    time_t   LabelDate;                /* Date/Time Volume labeled */
342    time_t   InitialWrite;             /* Date/Time Volume first written */
343    int32_t  LabelType;                /* Label (Bacula/ANSI/IBM) */
344    uint32_t VolJobs;                  /* number of jobs on this medium */
345    uint32_t VolFiles;                 /* Number of files */
346    uint32_t VolBlocks;                /* Number of blocks */
347    uint32_t VolParts;                 /* Number of cache parts */
348    uint32_t VolCloudParts;            /* Number of cloud parts */
349    uint32_t VolMounts;                /* Number of times mounted */
350    uint32_t VolErrors;                /* Number of read/write errors */
351    uint64_t VolWrites;                /* Number of writes */
352    uint64_t VolReads;                 /* Number of reads */
353    uint64_t VolBytes;                 /* Number of bytes written */
354    uint64_t VolABytes;                /* Size of aligned volume */
355    uint64_t VolHoleBytes;             /* The size of Holes */
356    uint32_t VolHoles;                 /* Number of holes */
357    uint32_t VolType;                  /* Device type of where Volume labeled */
358    uint64_t MaxVolBytes;              /* Max bytes to write to Volume */
359    uint64_t VolCapacityBytes;         /* capacity estimate */
360    uint64_t LastPartBytes;            /* Bytes in last part */
361    uint64_t VolReadTime;              /* time spent reading volume */
362    uint64_t VolWriteTime;             /* time spent writing volume */
363    utime_t  VolRetention;             /* Volume retention in seconds */
364    utime_t  CacheRetention;           /* Cache retention period in second */
365    utime_t  VolUseDuration;           /* time in secs volume can be used */
366    uint32_t ActionOnPurge;            /* action on purge, e.g. truncate the disk volume */
367    uint32_t MaxVolJobs;               /* Max Jobs on Volume */
368    uint32_t MaxVolFiles;              /* Max files on Volume */
369    int32_t  Recycle;                  /* recycle yes/no */
370    int32_t  Slot;                     /* slot in changer */
371    int32_t  Enabled;                  /* 0=disabled, 1=enabled, 2=archived */
372    int32_t  InChanger;                /* Volume currently in changer */
373    DBId_t   StorageId;                /* Storage record Id */
374    uint32_t EndFile;                  /* Last file on volume */
375    uint32_t EndBlock;                 /* Last block on volume */
376    uint32_t RecycleCount;             /* Number of times recycled */
377    char     VolStatus[20];            /* Volume status */
378    DBId_t   DeviceId;                 /* Device where Vol last written */
379    DBId_t   LocationId;               /* Where Volume is -- user defined */
380    DBId_t   ScratchPoolId;            /* Where to move if scratch */
381    DBId_t   RecyclePoolId;            /* Where to move when recycled */
382    /* Extra stuff not in DB */
383    faddr_t rec_addr;                  /* found record address */
384    /* Since the database returns times as strings, this is how we pass
385     *   them back.
386     */
387    char    cFirstWritten[MAX_TIME_LENGTH]; /* FirstWritten returned from DB */
388    char    cLastWritten[MAX_TIME_LENGTH];  /* LastWritten returned from DB */
389    char    cLabelDate[MAX_TIME_LENGTH];    /* LabelData returned from DB */
390    char    cInitialWrite[MAX_TIME_LENGTH]; /* InitialWrite returned from DB */
391    char   *exclude_list;                   /* Optionnal exclude list for db_find_next_volume() */
392    char   *sid_group;                 /* Storageid group string */
393    char    sid[30];                   /* edited StorageId */
394    bool    set_first_written;
395    bool    set_label_date;
396 };
397 
398 /* Client record -- same as the database */
399 struct CLIENT_DBR {
400    DBId_t ClientId;                   /* Unique Client id */
401    int AutoPrune;
402    utime_t FileRetention;
403    utime_t JobRetention;
404    char Name[MAX_NAME_LENGTH];        /* Client name */
405    char Uname[256];                   /* Uname for client */
406 };
407 
408 /* Counter record as in database */
409 struct COUNTER_DBR {
410    char Counter[MAX_NAME_LENGTH];
411    int32_t MinValue;
412    int32_t MaxValue;
413    int32_t CurrentValue;
414    char WrapCounter[MAX_NAME_LENGTH];
415 };
416 
417 
418 /* FileSet record -- same as the database */
419 struct FILESET_DBR {
420    DBId_t FileSetId;                  /* Unique FileSet id */
421    char FileSet[MAX_NAME_LENGTH];     /* FileSet name */
422    char MD5[50];                      /* MD5 signature of include/exclude */
423    time_t CreateTime;                 /* date created */
424    /*
425     * This is where we return CreateTime
426     */
427    char cCreateTime[MAX_TIME_LENGTH]; /* CreateTime as returned from DB */
428    /* Not in DB but returned by db_create_fileset() */
429    bool created;                      /* set when record newly created */
430 };
431 
432 class SNAPSHOT_DBR {
433 public:
SNAPSHOT_DBR()434    SNAPSHOT_DBR() {
435       memset(this, 0, sizeof(SNAPSHOT_DBR));
436    };
~SNAPSHOT_DBR()437    ~SNAPSHOT_DBR() {
438       reset();
439    };
debug(int level)440    void debug(int level) {
441       Dmsg8(DT_SNAPSHOT|level,
442             "Snapshot      %s:\n"
443             "  Volume:     %s\n"
444             "  Device:     %s\n"
445             "  Id:         %d\n"
446             "  FileSet:    %s\n"
447             "  CreateDate: %s\n"
448             "  Client:     %s\n"
449             "  Type:       %s\n",
450             Name, NPRT(Volume), NPRT(Device), SnapshotId,
451             FileSet, CreateDate, Client, Type);
452    };
as_arg(POOLMEM ** out)453    char *as_arg(POOLMEM **out) {
454       bash_spaces(Name);
455       bash_spaces(Type);
456 
457       if (Volume) {
458          bash_spaces(Volume);
459       }
460       if (Device) {
461          bash_spaces(Device);
462       }
463 
464       Mmsg(out, "name=%s volume=%s device=%s tdate=%d type=%s",
465            Name, NPRTB(Volume), NPRTB(Device), CreateTDate, Type);
466 
467       unbash_spaces(Name);
468       unbash_spaces(Type);
469       if (Volume) {
470          unbash_spaces(Volume);
471       }
472       if (Device) {
473          unbash_spaces(Device);
474       }
475       return *out;
476    };
reset()477    void reset() {
478       if (need_to_free) {
479          if (Volume) {
480             free(Volume);
481          }
482          if (Device) {
483             free(Device);
484          }
485          if (errmsg) {
486             free(errmsg);
487          }
488          errmsg = Volume = Device = NULL;
489       }
490       need_to_free = false;
491    };
492    bool    need_to_free;             /* Need to free the internal memory */
493    /* Used when searching snapshots */
494    char    created_after[MAX_TIME_LENGTH];
495    char    created_before[MAX_TIME_LENGTH];
496    bool    expired;                 /* Look for CreateTDate > (NOW - Retention) */
497    bool    sorted_client;           /* Results sorted by Client, SnapshotId */
498    int     status;                  /* Status of the snapshot */
499 
500    DBId_t  SnapshotId;              /* Unique Snapshot ID */
501    DBId_t  JobId;                   /* Related JobId */
502    DBId_t  FileSetId;               /* FileSetId if any */
503    DBId_t  ClientId;                /* From which client this snapshot comes */
504    char    Name[MAX_NAME_LENGTH];   /* Snapshot Name */
505    char    FileSet[MAX_NAME_LENGTH];/* FileSet name if any */
506    char    Client[MAX_NAME_LENGTH]; /* Client name */
507    char    Type[MAX_NAME_LENGTH];   /* zfs, btrfs, lvm, netapp, */
508    char    Comment[MAX_NAME_LENGTH];/* Comment */
509    char    CreateDate[MAX_TIME_LENGTH]; /* Create date as string */
510    time_t  CreateTDate;             /* Create TDate (in sec, since epoch) */
511    char   *Volume;                  /* Volume taken in snapshot */
512    char   *Device;                  /* Device, Pool, Directory, ...  */
513    char   *errmsg;                  /* Error associated with a snapshot */
514    utime_t Retention;               /* Number of second before pruning the snapshot */
515    uint64_t Size;                   /* Snapshot Size */
516 };
517 
518 /* Call back context for getting a 32/64 bit value from the database */
519 class db_int64_ctx {
520 public:
521    int64_t value;                     /* value returned */
522    int count;                         /* number of values seen */
523 
db_int64_ctx()524    db_int64_ctx() : value(0), count(0) {};
~db_int64_ctx()525    ~db_int64_ctx() {};
526 private:
527    db_int64_ctx(const db_int64_ctx&);            /* prohibit pass by value */
528    db_int64_ctx &operator=(const db_int64_ctx&); /* prohibit class assignment */
529 };
530 
531 /* Call back context for getting a list of comma separated strings from the
532  * database
533  */
534 class db_list_ctx {
535 public:
536    POOLMEM *list;                     /* list */
537    int count;                         /* number of values seen */
538 
db_list_ctx()539    db_list_ctx() { list = get_pool_memory(PM_FNAME); reset(); }
~db_list_ctx()540    ~db_list_ctx() { free_pool_memory(list); list = NULL; }
reset()541    void reset() { *list = 0; count = 0;}
add(const db_list_ctx & str)542    void add(const db_list_ctx &str) {
543       if (str.count > 0) {
544          if (*list) {
545             pm_strcat(list, ",");
546          }
547          pm_strcat(list, str.list);
548          count += str.count;
549       }
550    }
add(const char * str)551    void add(const char *str) {
552       if (count > 0) {
553          pm_strcat(list, ",");
554       }
555       pm_strcat(list, str);
556       count++;
557    }
558 private:
559    db_list_ctx(const db_list_ctx&);            /* prohibit pass by value */
560    db_list_ctx &operator=(const db_list_ctx&); /* prohibit class assignment */
561 };
562 
563 /* sql_query flags */
564 #define QF_STORE_RESULT 0x01
565 
566 /* sql_list.c */
567 enum e_list_type {
568    HORZ_LIST,                   /* list */
569    VERT_LIST,                   /* llist */
570    ARG_LIST,                    /* key1=v1 key2=v2 key3=v3 */
571    FAILED_JOBS,
572    INCOMPLETE_JOBS
573 };
574 
575 #include "bdb.h"
576 #include "protos.h"
577 #include "jcr.h"
578 #include "sql_cmds.h"
579 
580 
581 /* Object used in db_list_xxx function */
582 class LIST_CTX {
583 public:
584    char line[256];              /* Used to print last dash line */
585    int32_t num_rows;
586 
587    e_list_type type;            /* Vertical/Horizontal */
588    DB_LIST_HANDLER *send;       /* send data back */
589    bool once;                   /* Used to print header one time */
590    void *ctx;                   /* send() user argument */
591    BDB *mdb;
592    JCR *jcr;
593 
empty()594    void empty() {
595       once = false;
596       line[0] = '\0';
597    }
598 
send_dashes()599    void send_dashes() {
600       if (*line) {
601          send(ctx, line);
602       }
603    }
604 
LIST_CTX(JCR * j,BDB * m,DB_LIST_HANDLER * h,void * c,e_list_type t)605    LIST_CTX(JCR *j, BDB *m, DB_LIST_HANDLER *h, void *c, e_list_type t) {
606       line[0] = '\0';
607       once = false;
608       num_rows = 0;
609       type = t;
610       send = h;
611       ctx = c;
612       jcr = j;
613       mdb = m;
614    }
615 };
616 
617 /* Functions exported by sql.c for use within the cats directory. */
618 int list_result(void *vctx, int cols, char **row);
619 int list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type);
620 int get_sql_record_max(JCR *jcr, BDB *mdb);
621 void list_dashes(BDB *mdb, DB_LIST_HANDLER *send, void *ctx);
622 
623 void print_dashes(BDB *mdb);
624 void print_result(BDB *mdb);
625 int QueryDB(const char *file, int line, JCR *jcr, BDB *db, char *select_cmd);
626 int InsertDB(const char *file, int line, JCR *jcr, BDB *db, char *select_cmd);
627 int DeleteDB(const char *file, int line, JCR *jcr, BDB *db, char *delete_cmd);
628 void split_path_and_file(JCR *jcr, BDB *mdb, const char *fname);
629 
630 #endif  /* __CATS_H_ */
631