1 /*
2 * BDB Database Driver for Kamailio
3 *
4 * Copyright (C) 2008 iptelorg GmbH
5 *
6 * This file is part of Kamailio, a free SIP server.
7 *
8 * Kamailio is free software; you can redistribute it and/or modify it under the
9 * terms of the GNU General Public License as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option) any later
11 * version.
12 *
13 * Kamailio is distributed in the hope that it will be useful, but WITHOUT ANY
14 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 /*! \addtogroup bdb
24 * @{
25 */
26
27 /*! \file
28 * Berkeley DB : Implementation of functions related to database commands.
29 *
30 * \ingroup database
31 */
32
33 #include <string.h>
34
35 #include "../../core/mem/mem.h"
36 #include "../../core/dprint.h"
37 #include "../../core/ut.h"
38
39 #include "bdb_cmd.h"
40 #include "bdb_fld.h"
41 #include "bdb_con.h"
42 #include "bdb_uri.h"
43 #include "bdb_res.h"
44 #include "bdb_lib.h"
45 #include "bdb_crs_compat.h"
46
47 #define BDB_BUF_SIZE 1024
48
49 /** Destroys a bdb_cmd structure.
50 * This function frees all memory used by ld_cmd structure.
51 * @param cmd A pointer to generic db_cmd command being freed.
52 * @param payload A pointer to ld_cmd structure to be freed.
53 */
bdb_cmd_free(db_cmd_t * cmd,bdb_cmd_t * payload)54 static void bdb_cmd_free(db_cmd_t *cmd, bdb_cmd_t *payload)
55 {
56 db_drv_free(&payload->gen);
57 if(payload->dbcp)
58 payload->dbcp->CLOSE_CURSOR(payload->dbcp);
59 if(payload->skey.s)
60 pkg_free(payload->skey.s);
61 pkg_free(payload);
62 }
63
64 /** Prepare a query
65 * @param cmd DB command structure
66 * @param bcmd berkey DB command structure
67 * @return 0 on success, -1 on error
68 */
bdb_prepare_query(db_cmd_t * cmd,bdb_cmd_t * bcmd)69 int bdb_prepare_query(db_cmd_t *cmd, bdb_cmd_t *bcmd)
70 {
71 bdb_tcache_t *tbc = NULL;
72 bdb_table_t *tp = NULL;
73 bdb_fld_t *f;
74 db_fld_t *fld;
75 int mode;
76 int i;
77
78 if(bcmd->bcon == NULL || bcmd->bcon->dbp == NULL)
79 return -1;
80
81 tbc = bdblib_get_table(bcmd->bcon->dbp, &cmd->table);
82 if(tbc == NULL) {
83 ERR("bdb: table does not exist!\n");
84 return -1;
85 }
86
87 tp = tbc->dtp;
88 if(tp == NULL || tp->db == NULL) {
89 ERR("bdb: table not loaded!\n");
90 return -1;
91 }
92
93 mode = 0;
94 if(!DB_FLD_EMPTY(cmd->result)) { /* columns to be returned provided */
95 if(cmd->result_count > tp->ncols) {
96 ERR("bdb: too many columns in query\n");
97 goto error;
98 }
99 } else {
100 mode = 1;
101 cmd->result = db_fld(tp->ncols + 1);
102 cmd->result_count = tp->ncols;
103 for(i = 0; i < cmd->result_count; i++) {
104 if(bdb_fld(cmd->result + i, cmd->table.s) < 0)
105 goto error;
106 }
107 }
108
109 for(i = 0; i < cmd->result_count; i++) {
110 fld = cmd->result + i;
111 f = DB_GET_PAYLOAD(fld);
112 if(mode == 1) {
113 DBG("bdb: column name [%.*s]\n", tp->colp[i]->name.len,
114 tp->colp[i]->name.s);
115
116 f->name = pkg_malloc(tp->colp[i]->name.len + 1);
117 if(f->name == NULL) {
118 ERR("bdb: Out of private memory\n");
119 goto error;
120 }
121 strncpy(f->name, tp->colp[i]->name.s, tp->colp[i]->name.len);
122 f->name[tp->colp[i]->name.len] = '\0';
123 fld->name = f->name;
124 fld->type = tp->colp[i]->type;
125 f->col_pos = i;
126 } else {
127 f->col_pos = bdb_get_colpos(tp, fld->name);
128 if(f->col_pos == -1) {
129 ERR("bdb: Column not found\n");
130 goto error;
131 }
132 }
133 switch(fld->type) {
134 case DB_INT:
135 case DB_BITMAP:
136 case DB_FLOAT:
137 case DB_DOUBLE:
138 case DB_DATETIME:
139 case DB_STR:
140 if(!f->buf.s)
141 f->buf.s = pkg_malloc(BDB_BUF_SIZE);
142 if(f->buf.s == NULL) {
143 ERR("bdb: No memory left\n");
144 goto error;
145 }
146 fld[i].v.lstr.s = f->buf.s;
147 break;
148
149 case DB_CSTR:
150 if(!f->buf.s)
151 f->buf.s = pkg_malloc(BDB_BUF_SIZE);
152 if(f->buf.s == NULL) {
153 ERR("bdb: No memory left\n");
154 goto error;
155 }
156 fld[i].v.cstr = f->buf.s;
157 break;
158
159 case DB_BLOB:
160 if(!f->buf.s)
161 f->buf.s = pkg_malloc(BDB_BUF_SIZE);
162 if(f->buf.s == NULL) {
163 ERR("mysql: No memory left\n");
164 goto error;
165 }
166 fld[i].v.blob.s = f->buf.s;
167 break;
168
169 case DB_NONE:
170 /* Eliminates gcc warning */
171 break;
172 }
173 }
174
175 if(!DB_FLD_EMPTY(cmd->match)) {
176 if(cmd->match_count > tp->ncols) {
177 ERR("bdb: too many columns in match struct of query\n");
178 goto error;
179 }
180 for(i = 0; i < cmd->match_count; i++) {
181 fld = cmd->result + i;
182 f = DB_GET_PAYLOAD(fld);
183 f->col_pos = bdb_get_colpos(tp, fld->name);
184 if(f->col_pos == -1) {
185 ERR("bdb: Match column not found\n");
186 goto error;
187 }
188 }
189 }
190
191 return 0;
192
193 error:
194 return -1;
195 }
196
197 /**
198 * Execute a query
199 * @param cmd DB command structure
200 * @param bcmd Berkely DB command structure
201 * @return 0 on success, -1 on error
202 */
bdb_query(db_cmd_t * cmd,bdb_cmd_t * bcmd)203 int bdb_query(db_cmd_t *cmd, bdb_cmd_t *bcmd)
204 {
205 DBT key;
206 DB *db;
207 static char kbuf[MAX_ROW_SIZE];
208 int klen;
209
210 bdb_tcache_t *tbc = NULL;
211 bdb_table_t *tp = NULL;
212
213 if(bcmd->bcon == NULL || bcmd->bcon->dbp == NULL)
214 return -1;
215
216 tbc = bdblib_get_table(bcmd->bcon->dbp, &cmd->table);
217 if(tbc == NULL) {
218 ERR("bdb: table does not exist!\n");
219 return -1;
220 }
221
222 tp = tbc->dtp;
223 if(tp == NULL) {
224 ERR("bdb: table not loaded!\n");
225 return -1;
226 }
227 db = tp->db;
228 if(db == NULL) {
229 ERR("bdb: db structure not initialized!\n");
230 return -1;
231 }
232
233 if(DB_FLD_EMPTY(cmd->match)) { /* no match constraint */
234 if(db->cursor(db, NULL, &bcmd->dbcp, 0) != 0) {
235 ERR("bdb: error creating cursor\n");
236 goto error;
237 }
238 bcmd->skey.len = 0;
239 return 0;
240 }
241
242 memset(&key, 0, sizeof(DBT));
243 memset(kbuf, 0, MAX_ROW_SIZE);
244
245 klen = MAX_ROW_SIZE;
246 if(bdblib_valtochar(tp, cmd->match, cmd->match_count, kbuf, &klen, BDB_KEY)
247 != 0) {
248 ERR("bdb: error creating key\n");
249 goto error;
250 }
251
252 if(klen > bcmd->skey_size || bcmd->skey.s == NULL) {
253 if(bcmd->skey.s != NULL)
254 pkg_free(bcmd->skey.s);
255 bcmd->skey.s = (char *)pkg_malloc(klen * sizeof(char));
256 if(bcmd->skey.s == NULL) {
257 ERR("bdb: no pkg memory\n");
258 goto error;
259 }
260 bcmd->skey_size = klen;
261 }
262 memcpy(bcmd->skey.s, kbuf, klen);
263 bcmd->skey.len = klen;
264
265 return 0;
266 error:
267 return -1;
268 }
269
bdb_cmd(db_cmd_t * cmd)270 int bdb_cmd(db_cmd_t *cmd)
271 {
272 bdb_cmd_t *bcmd;
273 db_con_t *con;
274 bdb_con_t *bcon;
275
276 bcmd = (bdb_cmd_t *)pkg_malloc(sizeof(bdb_cmd_t));
277 if(bcmd == NULL) {
278 ERR("bdb: No memory left\n");
279 goto error;
280 }
281 memset(bcmd, '\0', sizeof(bdb_cmd_t));
282 if(db_drv_init(&bcmd->gen, bdb_cmd_free) < 0)
283 goto error;
284
285 con = cmd->ctx->con[db_payload_idx];
286 bcon = DB_GET_PAYLOAD(con);
287 bcmd->bcon = bcon;
288
289 switch(cmd->type) {
290 case DB_PUT:
291 case DB_DEL:
292 case DB_UPD:
293 ERR("bdb: The driver does not support DB modifications yet.\n");
294 goto error;
295 break;
296
297 case DB_GET:
298 if(bdb_prepare_query(cmd, bcmd) != 0) {
299 ERR("bdb: error preparing query.\n");
300 goto error;
301 }
302 break;
303
304 case DB_SQL:
305 ERR("bdb: The driver does not support raw queries yet.\n");
306 goto error;
307 }
308
309 DB_SET_PAYLOAD(cmd, bcmd);
310 return 0;
311
312 error:
313 if(bcmd) {
314 DB_SET_PAYLOAD(cmd, NULL);
315 db_drv_free(&bcmd->gen);
316 pkg_free(bcmd);
317 }
318 return -1;
319 }
320
321
bdb_cmd_exec(db_res_t * res,db_cmd_t * cmd)322 int bdb_cmd_exec(db_res_t *res, db_cmd_t *cmd)
323 {
324 db_con_t *con;
325 bdb_cmd_t *bcmd;
326 bdb_con_t *bcon;
327
328 /* First things first: retrieve connection info from the currently active
329 * connection and also mysql payload from the database command
330 */
331 con = cmd->ctx->con[db_payload_idx];
332 bcmd = DB_GET_PAYLOAD(cmd);
333 bcon = DB_GET_PAYLOAD(con);
334
335 if((bcon->flags & BDB_CONNECTED) == 0) {
336 ERR("bdb: not connected\n");
337 return -1;
338 }
339 bcmd->next_flag = -1;
340 switch(cmd->type) {
341 case DB_DEL:
342 case DB_PUT:
343 case DB_UPD:
344 /* no result expected - cleanup */
345 DBG("bdb: query with no result.\n");
346 break;
347 case DB_GET:
348 return bdb_query(cmd, bcmd);
349 break;
350 default:
351 /* result expected - no cleanup */
352 DBG("bdb: query with result.\n");
353 }
354
355 return 0;
356 }
357
bdb_update_result(db_cmd_t * cmd,DBT * data)358 int bdb_update_result(db_cmd_t *cmd, DBT *data)
359 {
360 bdb_fld_t *f;
361 db_fld_t *fld;
362 int i;
363 int col;
364 char *s;
365 static str col_map[MAX_NUM_COLS];
366
367 memset(col_map, 0, MAX_NUM_COLS * sizeof(str));
368
369 col = 0;
370 s = (char *)data->data;
371 col_map[col].s = s;
372 while(*s != '\0') {
373 if(*s == *DELIM) {
374 col_map[col].len = s - col_map[col].s;
375 col++;
376 col_map[col].s = s + 1;
377 }
378 s++;
379 }
380 col_map[col].len = s - col_map[col].s;
381
382 for(i = 0; i < cmd->result_count; i++) {
383 fld = cmd->result + i;
384 f = DB_GET_PAYLOAD(fld);
385 if(col_map[f->col_pos].len == 0) {
386 fld->flags |= DB_NULL;
387 continue;
388 }
389 fld->flags &= ~DB_NULL;
390
391 switch(fld->type) {
392 case DB_STR:
393 fld->v.lstr.s = f->buf.s;
394 if(col_map[f->col_pos].len < BDB_BUF_SIZE) {
395 fld->v.lstr.len = col_map[f->col_pos].len;
396 } else {
397 /* truncate ?!? */
398 fld->v.lstr.len = BDB_BUF_SIZE - 1;
399 }
400 memcpy(fld->v.lstr.s, col_map[f->col_pos].s, fld->v.lstr.len);
401 break;
402
403 case DB_BLOB:
404 fld->v.blob.s = f->buf.s;
405 if(col_map[f->col_pos].len < BDB_BUF_SIZE) {
406 fld->v.blob.len = col_map[f->col_pos].len;
407 } else {
408 /* truncate ?!? */
409 fld->v.blob.len = BDB_BUF_SIZE - 1;
410 }
411 memcpy(fld->v.blob.s, col_map[f->col_pos].s, fld->v.blob.len);
412
413 break;
414
415 case DB_CSTR:
416 fld->v.cstr = f->buf.s;
417 if(col_map[f->col_pos].len < BDB_BUF_SIZE) {
418 memcpy(fld->v.cstr, col_map[f->col_pos].s,
419 col_map[f->col_pos].len);
420 fld->v.cstr[col_map[f->col_pos].len] = '\0';
421 } else {
422 /* truncate ?!? */
423 memcpy(fld->v.cstr, col_map[f->col_pos].s,
424 BDB_BUF_SIZE - 1);
425 fld->v.cstr[BDB_BUF_SIZE - 1] = '\0';
426 ;
427 }
428
429 break;
430
431 case DB_DATETIME:
432 /* str to time */
433 col_map[f->col_pos].s[col_map[f->col_pos].len] = '\0';
434 if(bdb_str2time(col_map[f->col_pos].s, &fld->v.time) < 0) {
435 ERR("Error while converting INT value from string\n");
436 return -1;
437 }
438 break;
439
440 case DB_INT:
441 /* str to int */
442 col_map[f->col_pos].s[col_map[f->col_pos].len] = '\0';
443 if(bdb_str2int(col_map[f->col_pos].s, &fld->v.int4) < 0) {
444 ERR("Error while converting INT value from string\n");
445 return -1;
446 }
447 break;
448
449 case DB_FLOAT:
450 case DB_DOUBLE:
451 /* str to dowuble */
452 col_map[f->col_pos].s[col_map[f->col_pos].len] = '\0';
453 if(bdb_str2double(col_map[f->col_pos].s, &fld->v.dbl) < 0) {
454 ERR("Error while converting DOUBLE value from string\n");
455 return -1;
456 }
457 break;
458
459 case DB_BITMAP:
460 /* str to int */
461 col_map[f->col_pos].s[col_map[f->col_pos].len] = '\0';
462 if(bdb_str2int(col_map[f->col_pos].s, &fld->v.int4) < 0) {
463 ERR("Error while converting BITMAP value from string\n");
464 return -1;
465 }
466 break;
467
468 case DB_NONE:
469 break;
470 }
471 }
472 return 0;
473 }
474
bdb_cmd_first(db_res_t * res)475 int bdb_cmd_first(db_res_t *res)
476 {
477 bdb_cmd_t *bcmd;
478
479 bcmd = DB_GET_PAYLOAD(res->cmd);
480 switch(bcmd->next_flag) {
481 case -2: /* table is empty */
482 return 1;
483 case 0: /* cursor position is 0 */
484 return 0;
485 case 1: /* next row */
486 case 2: /* EOF */
487 ERR("bdb: no next row.\n");
488 return -1;
489 default:
490 return bdb_cmd_next(res);
491 }
492 }
493
494
bdb_cmd_next(db_res_t * res)495 int bdb_cmd_next(db_res_t *res)
496 {
497 bdb_cmd_t *bcmd;
498 DBT key, data;
499 int ret;
500 static char dbuf[MAX_ROW_SIZE];
501
502 bcmd = DB_GET_PAYLOAD(res->cmd);
503
504 if(bcmd->next_flag == 2 || bcmd->next_flag == -2)
505 return 1;
506
507 memset(&key, 0, sizeof(DBT));
508 memset(&data, 0, sizeof(DBT));
509 memset(dbuf, 0, MAX_ROW_SIZE);
510
511 data.data = dbuf;
512 data.ulen = MAX_ROW_SIZE;
513 data.flags = DB_DBT_USERMEM;
514
515 ret = 0;
516 if(bcmd->skey.len == 0) {
517 while((ret = bcmd->dbcp->c_get(bcmd->dbcp, &key, &data, DB_NEXT))
518 == 0) {
519 if(!strncasecmp((char *)key.data, "METADATA", 8))
520 continue;
521 break;
522 }
523 if(ret != 0) {
524 bcmd->next_flag = bcmd->next_flag < 0 ? -2 : 2;
525 return 1;
526 }
527 } else {
528 key.data = bcmd->skey.s;
529 key.ulen = bcmd->skey_size;
530 key.flags = DB_DBT_USERMEM;
531 key.size = bcmd->skey.len;
532 ret = bcmd->dbcp->c_get(bcmd->dbcp, &key, &data, DB_NEXT);
533 if(ret != 0) {
534 bcmd->next_flag = bcmd->next_flag < 0 ? -2 : 2;
535 return 1;
536 }
537 }
538
539 if(bcmd->next_flag <= 0) {
540 bcmd->next_flag++;
541 }
542
543 if(bdb_update_result(res->cmd, &data) < 0) {
544 return -1;
545 }
546
547 res->cur_rec->fld = res->cmd->result;
548 return 0;
549 }
550
551
552 /** @} */
553