]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/cats/sql.c
e40b3c2114d1cabcdc3e96fa12073b2c5e72bf44
[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       if (verbose) {
92          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
93       }
94       return 0;
95    }
96    mdb->result = sql_store_result(mdb);
97    
98    return mdb->result != NULL;
99 }
100
101 /* 
102  * Utility routine to do inserts   
103  * Returns: 0 on failure      
104  *          1 on success
105  */
106 int
107 InsertDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
108 {
109    if (sql_query(mdb, cmd)) {
110       m_msg(file, line, &mdb->errmsg,  _("insert %s failed:\n%s\n"), cmd, sql_strerror(mdb));
111       j_msg(file, line, jcr, M_FATAL, 0, "%s", mdb->errmsg);
112       if (verbose) {
113          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
114       }
115       return 0;
116    }
117    if (mdb->have_insert_id) {
118       mdb->num_rows = sql_affected_rows(mdb);
119    } else {
120       mdb->num_rows = 1;
121    }
122    if (mdb->num_rows != 1) {
123       char ed1[30];
124       m_msg(file, line, &mdb->errmsg, _("Insertion problem: affected_rows=%s\n"), 
125          edit_uint64(mdb->num_rows, ed1));
126       return 0;
127    }
128    mdb->changes++;
129    return 1;
130 }
131
132 /* Utility routine for updates.
133  *  Returns: 0 on failure
134  *           1 on success  
135  */
136 int
137 UpdateDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
138 {
139
140    if (sql_query(mdb, cmd)) {
141       m_msg(file, line, &mdb->errmsg, _("update %s failed:\n%s\n"), cmd, sql_strerror(mdb));
142       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
143       if (verbose) {
144          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
145       }
146       return 0;
147    }
148    mdb->num_rows = sql_affected_rows(mdb);
149    if (mdb->num_rows != 1) {
150       char ed1[30];
151       m_msg(file, line, &mdb->errmsg, _("Update problem: affect_rows=%s\n"), 
152          edit_uint64(mdb->num_rows, ed1));
153       return 0;
154    }
155    mdb->changes++;
156    return 1;
157 }
158
159 /* Utility routine for deletes   
160  *
161  * Returns: -1 on error
162  *           n number of rows affected
163  */
164 int
165 DeleteDB(char *file, int line, void *jcr, B_DB *mdb, char *cmd)
166 {
167
168    if (sql_query(mdb, cmd)) {
169       m_msg(file, line, &mdb->errmsg, _("delete %s failed:\n%s\n"), cmd, sql_strerror(mdb));
170       j_msg(file, line, jcr, M_ERROR, 0, "%s", mdb->errmsg);
171       if (verbose) {
172          j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
173       }
174       return -1;
175    }
176    mdb->changes++;
177    return sql_affected_rows(mdb);
178 }
179
180
181 /*
182  * Get record max. Query is already in mdb->cmd
183  *  No locking done
184  *
185  * Returns: -1 on failure
186  *          count on success
187  */
188 int get_sql_record_max(void *jcr, B_DB *mdb)
189 {
190    SQL_ROW row;
191    int stat = 0;
192
193    if (QUERY_DB(jcr, mdb, mdb->cmd)) {
194       if ((row = sql_fetch_row(mdb)) == NULL) {
195          Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
196          stat = -1;
197       } else {
198          stat = atoi(row[0]);
199       }
200       sql_free_result(mdb);
201    } else {
202       Mmsg1(&mdb->errmsg, _("error fetching row: %s\n"), sql_strerror(mdb));
203       stat = -1;
204    }
205    return stat;
206 }
207
208 char *db_strerror(B_DB *mdb)
209 {
210    return mdb->errmsg;
211 }
212
213 void _db_lock(char *file, int line, B_DB *mdb)
214 {
215    int errstat;
216    if ((errstat=rwl_writelock(&mdb->lock)) != 0) {
217       e_msg(file, line, M_ABORT, 0, "rwl_writelock failure. ERR=%s\n",
218            strerror(errstat));
219    }
220 }    
221
222 void _db_unlock(char *file, int line, B_DB *mdb)
223 {
224    int errstat;
225    if ((errstat=rwl_writeunlock(&mdb->lock)) != 0) {
226       e_msg(file, line, M_ABORT, 0, "rwl_writeunlock failure. ERR=%s\n",
227            strerror(errstat));
228    }
229 }    
230
231 /*
232  * Start a transaction. This groups inserts and makes things
233  *  much more efficient. Usually started when inserting 
234  *  file attributes.
235  */
236 void db_start_transaction(void *jcr, B_DB *mdb)
237 {
238 #ifdef xAVE_SQLITE
239    db_lock(mdb);
240    /* Allow only 10,000 changes per transaction */
241    if (mdb->transaction && mdb->changes > 10000) {
242       db_end_transaction(jcr, mdb);
243    }
244    if (!mdb->transaction) {   
245       my_sqlite_query(mdb, "BEGIN");  /* begin transaction */
246       Dmsg0(400, "Start SQLite transaction\n");
247       mdb->transaction = 1;
248    }
249    db_unlock(mdb);
250 #endif
251
252 }
253
254 void db_end_transaction(void *jcr, B_DB *mdb)
255 {
256 #ifdef xAVE_SQLITE
257    db_lock(mdb);
258    if (mdb->transaction) {
259       my_sqlite_query(mdb, "COMMIT"); /* end transaction */
260       mdb->transaction = 0;
261       Dmsg1(400, "End SQLite transaction changes=%d\n", mdb->changes);
262    }
263    mdb->changes = 0;
264    db_unlock(mdb);
265 #endif
266 }
267
268 /*
269  * Given a full filename, split it into its path
270  *  and filename parts. They are returned in pool memory
271  *  in the mdb structure.
272  */
273 void split_path_and_filename(void *jcr, B_DB *mdb, char *fname)
274 {
275    char *p, *f;
276
277    /* Find path without the filename.  
278     * I.e. everything after the last / is a "filename".
279     * OK, maybe it is a directory name, but we treat it like
280     * a filename. If we don't find a / then the whole name
281     * must be a path name (e.g. c:).
282     */
283    for (p=f=fname; *p; p++) {
284       if (*p == '/') {
285          f = p;                       /* set pos of last slash */
286       }
287    }
288    if (*f == '/') {                   /* did we find a slash? */
289       f++;                            /* yes, point to filename */
290    } else {                           /* no, whole thing must be path name */
291       f = p;
292    }
293
294    /* If filename doesn't exist (i.e. root directory), we
295     * simply create a blank name consisting of a single 
296     * space. This makes handling zero length filenames
297     * easier.
298     */
299    mdb->fnl = p - f;
300    if (mdb->fnl > 0) {
301       mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1);
302       memcpy(mdb->fname, f, mdb->fnl);    /* copy filename */
303       mdb->fname[mdb->fnl] = 0;
304    } else {
305       mdb->fname[0] = ' ';            /* blank filename */
306       mdb->fname[1] = 0;
307       mdb->fnl = 1;
308    }
309
310    mdb->pnl = f - fname;    
311    if (mdb->pnl > 0) {
312       mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1);
313       memcpy(mdb->path, fname, mdb->pnl);
314       mdb->path[mdb->pnl] = 0;
315    } else {
316       Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname);
317       Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg);
318       mdb->path[0] = ' ';
319       mdb->path[1] = 0;
320       mdb->pnl = 1;
321    }
322
323    Dmsg2(100, "sllit path=%s file=%s\n", mdb->path, mdb->fname);
324 }
325
326 #endif /* HAVE_MYSQL | HAVE_SQLITE */