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