]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
Add jcr to DB arguments
[bacula/bacula] / bacula / src / cats / sql.c
1 /*
2  * Bacula Catalog Database interface routines
3  * 
4  *     Almost generic set of SQL database interface routines
5  *      (with a little more work)
6  *
7  *    Kern Sibbald, March 2000
8  *
9  *    Version $Id$
10  */
11
12 /*
13    Copyright (C) 2000-2003 Kern Sibbald and John Walker
14
15    This program is free software; you can redistribute it and/or
16    modify it under the terms of the GNU General Public License as
17    published by the Free Software Foundation; either version 2 of
18    the License, or (at your option) any later version.
19
20    This program is distributed in the hope that it will be useful,
21    but WITHOUT ANY WARRANTY; without even the implied warranty of
22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23    General Public License for more details.
24
25    You should have received a copy of the GNU General Public
26    License along with this program; if not, write to the Free
27    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28    MA 02111-1307, USA.
29
30  */
31
32 /* The following is necessary so that we do not include
33  * the dummy external definition of B_DB.
34  */
35 #define __SQL_C                       /* indicate that this is sql.c */
36
37 #include "bacula.h"
38 #include "cats.h"
39
40 #if    HAVE_MYSQL | HAVE_SQLITE
41
42 /* Forward referenced subroutines */
43 void print_dashes(B_DB *mdb);
44 void print_result(B_DB *mdb);
45
46 /*
47  * Called here to retrieve an integer from the database
48  */
49 static int int_handler(void *ctx, int num_fields, char **row)
50 {
51    uint32_t *val = (uint32_t *)ctx;
52
53    if (row[0]) {
54       *val = atoi(row[0]);
55    } else {
56       *val = 0;
57    }
58    return 0;
59 }
60        
61
62
63 /* NOTE!!! The following routines expect that the
64  *  calling subroutine sets and clears the mutex
65  */
66
67 /* Check that the tables correspond to the version we want */
68 int check_tables_version(void *jcr, B_DB *mdb)
69 {
70    uint32_t version;
71    char *query = "SELECT VersionId FROM Version";
72   
73    version = 0;
74    db_sql_query(mdb, query, int_handler, (void *)&version);
75    if (version != BDB_VERSION) {
76       Mmsg(&mdb->errmsg, "Version error for database \"%s\". Wanted %d, got %d\n",
77          mdb->db_name, BDB_VERSION, version);
78       Jmsg(jcr, M_FATAL, 0, "%s", mdb->errmsg);
79       return 0;
80    }
81    return 1;
82 }
83
84 /* Utility routine for queries */
85 int
86 QueryDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
87 {
88    if (sql_query(mdb, cmd)) {
89       m_msg(file, line, &mdb->errmsg, _("query %s failed:\n%s\n"), cmd, sql_strerror(mdb));
90       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
91       return 0;
92    }
93    mdb->result = sql_store_result(mdb);
94    
95    return mdb->result != NULL;
96 }
97
98 /* 
99  * Utility routine to do inserts   
100  * Returns: 0 on failure      
101  *          1 on success
102  */
103 int
104 InsertDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
105 {
106    if (sql_query(mdb, cmd)) {
107       m_msg(file, line, &mdb->errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb));
108       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
109       return 0;
110    }
111    if (mdb->have_insert_id) {
112       mdb->num_rows = sql_affected_rows(mdb);
113    } else {
114       mdb->num_rows = 1;
115    }
116    if (mdb->num_rows != 1) {
117       char ed1[30];
118       m_msg(file, line, &mdb->errmsg, _("Insertion problem: affect_rows=%s\n"), 
119          edit_uint64(mdb->num_rows, ed1));
120       return 0;
121    }
122    mdb->changes++;
123    return 1;
124 }
125
126 /* Utility routine for updates.
127  *  Returns: 0 on failure
128  *           1 on success  
129  */
130 int
131 UpdateDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
132 {
133
134    if (sql_query(mdb, cmd)) {
135       m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb));
136       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
137       j_msg(file, line, jcr, M_ERROR, 0, "%s\n", cmd);
138       return 0;
139    }
140    mdb->num_rows = sql_affected_rows(mdb);
141    if (mdb->num_rows != 1) {
142       char ed1[30];
143       m_msg(file, line, &mdb->errmsg, _("Update problem: affect_rows=%s\n"), 
144          edit_uint64(mdb->num_rows, ed1));
145       return 0;
146    }
147    mdb->changes++;
148    return 1;
149 }
150
151 /* Utility routine for deletes   
152  *
153  * Returns: -1 on error
154  *           n number of rows affected
155  */
156 int
157 DeleteDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
158 {
159
160    if (sql_query(mdb, cmd)) {
161       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
162       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
163       return -1;
164    }
165    mdb->changes++;
166    return sql_affected_rows(mdb);
167 }
168
169
170 /*
171  * Get record max. Query is already in mdb->cmd
172  *  No locking done
173  *
174  * Returns: -1 on failure
175  *          count on success
176  */
177 int get_sql_record_max(void *jcr, B_DB *mdb)
178 {
179    SQL_ROW row;
180    int stat = 0;
181
182    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
183       if ((row = sql_fetch_row(mdb)) == NULL) {
184          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
185          stat = -1;
186       } else {
187          stat = atoi(row[0]);
188       }
189       sql_free_result(mdb);
190    } else {
191       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
192       stat = -1;
193    }
194    return stat;
195 }
196
197 char *db_strerror(B_DB *mdb)
198 {
199    return mdb->errmsg;
200 }
201
202 void _db_lock(char *file, int line, B_DB *mdb)
203 {
204    int errstat;
205    if ((errstat=rwl_writelock(&mdb->lock)) != 0) {
206       e_msg(file, line, M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
207            strerror(errstat));
208    }
209 }    
210
211 void _db_unlock(char *file, int line, B_DB *mdb)
212 {
213    int errstat;
214    if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) {
215       e_msg(file, line, M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
216            strerror(errstat));
217    }
218 }    
219
220 /*
221  * Start a transaction. This groups inserts and makes things
222  *  much more efficient. Usually started when inserting 
223  *  file attributes.
224  */
225 void db_start_transaction(void *jcr, B_DB *mdb)
226 {
227 #ifdef xAVE_SQLITE
228    db_lock(mdb);
229    /* Allow only 10,000 changes per transaction */
230    if (mdb->transaction && mdb->changes > 10000) {
231       db_end_transaction(jcr, mdb);
232    }
233    if (!mdb->transaction) {   
234       my_sqlite_query(mdb, "BEGIN");  /* begin transaction */
235       Dmsg0(400, "Start SQLite transaction\n");
236       mdb->transaction = 1;
237    }
238    db_unlock(mdb);
239 #endif
240
241 }
242
243 void db_end_transaction(void *jcr, B_DB *mdb)
244 {
245 #ifdef xAVE_SQLITE
246    db_lock(mdb);
247    if (mdb->transaction) {
248       my_sqlite_query(mdb, "COMMIT"); /* end transaction */
249       mdb->transaction = 0;
250       Dmsg1(400, "End SQLite transaction changes=%d\n", mdb->changes);
251    }
252    mdb->changes = 0;
253    db_unlock(mdb);
254 #endif
255 }
256
257 /*
258  * Given a full filename, split it into its path
259  *  and filename parts. They are returned in pool memory
260  *  in the mdb structure.
261  */
262 void split_path_and_filename(void *jcr, B_DB *mdb, char *fname)
263 {
264    char *p, *f;
265
266    /* Find path without the filename.  
267     * I.e. everything after the last / is a "filename".
268     * OK, maybe it is a directory name, but we treat it like
269     * a filename. If we don't find a / then the whole name
270     * must be a path name (e.g. c:).
271     */
272    for (p=f=fname; *p; p++) {
273       if (*p == '/') {
274          f = p;                       /* set pos of last slash */
275       }
276    }
277    if (*f == '/') {                   /* did we find a slash? */
278       f++;                            /* yes, point to filename */
279    } else {                           /* no, whole thing must be path name */
280       f = p;
281    }
282
283    /* If filename doesn't exist (i.e. root directory), we
284     * simply create a blank name consisting of a single 
285     * space. This makes handling zero length filenames
286     * easier.
287     */
288    mdb->fnl = p - f;
289    if (mdb->fnl > 0) {
290       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
291       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
292       mdb->fname[mdb->fnl] = 0;
293    } else {
294       mdb->fname[0] = ' ';            /* blank filename */
295       mdb->fname[1] = 0;
296       mdb->fnl = 1;
297    }
298
299    mdb->pnl = f - fname;    
300    if (mdb->pnl > 0) {
301       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
302       memcpy(mdb->path, fname, mdb->pnl);
303       mdb->path[mdb->pnl] = 0;
304    } else {
305       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
306       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
307       mdb->path[0] = ' ';
308       mdb->path[1] = 0;
309       mdb->pnl = 1;
310    }
311
312    Dmsg2(100, "sllit path=%s file=%s\n", mdb->path, mdb->fname);
313 }
314
315 #endif /* HAVE_MYSQL | HAVE_SQLITE */