Coverage Report

Created: 2025-07-11 06:28

/src/opensips/db/db.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 * Copyright (C) 2007-2008 1&1 Internet AG
4
 *
5
 * This file is part of opensips, a free SIP server.
6
 *
7
 * opensips is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version
11
 *
12
 * opensips is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
20
 */
21
 /*
22
  * History:
23
  * --------
24
  *  2004-06-06  bind_dbmod takes dbf as parameter (andrei)
25
  *  2006-10-10  Added support for retrieving the last inserted ID (Carsten Bock, BASIS AudioNet GmbH)
26
  */
27
28
/**
29
 * \file db/db.c
30
 * \brief Generic Database Interface
31
 *
32
 * This is a generic database interface for modules that need to utilize a
33
 * database. The interface should be used by all modules that access database.
34
 * The interface will be independent of the underlying database server.
35
 * Notes:
36
 * If possible, use the predefined macros if you need to access any structure
37
 * attributes.
38
 * For additional description, see the comments in the sources of mysql module.
39
 *
40
 * If you want to see more complicated examples of how the API could be used,
41
 * take a look at the sources of the usrloc or auth modules.
42
 */
43
44
#include "../dprint.h"
45
#include "../sr_module.h"
46
#include "../mem/mem.h"
47
#include "../mem/meminfo.h"
48
#include "../ut.h"
49
#include "db_cap.h"
50
#include "db_id.h"
51
#include "db_pool.h"
52
#include "db.h"
53
54
#include "db_insertq.h"
55
56
char *db_version_table = VERSION_TABLE;
57
char *db_default_url = NULL;
58
int   db_max_async_connections = 10;
59
60
/** maximal length of a SQL URL */
61
static unsigned int MAX_URL_LENGTH = 255;
62
#define COLUMN_OVERHEAD 256
63
64
stat_var *sql_total_queries;
65
stat_var *sql_slow_queries;
66
67
int init_db_support(void)
68
0
{
69
  /* init query list now in shm
70
   * so all processes that will be forked from now on
71
   * will have access to it
72
   *
73
   * if it fails, give it a try and carry on */
74
0
  if (init_ql_support() != 0) {
75
0
    LM_ERR("failed to initialise buffering query list\n");
76
0
    query_buffer_size = 0;
77
0
    *query_list = NULL;
78
0
  }
79
80
0
  if (register_stat("sql", "sql_total_queries", &sql_total_queries, 0) ||
81
0
      register_stat("sql", "sql_slow_queries", &sql_slow_queries, 0)) {
82
0
    LM_ERR("failed to register SQL stats\n");
83
0
    return -1;
84
0
  }
85
86
0
  return 0;
87
0
}
88
89
int estimate_available_rows(int payload_size, int column_count)
90
0
{
91
0
  struct mem_info info;
92
0
  memset(&info, 0, sizeof (struct mem_info));
93
94
95
#ifdef pkg_info
96
97
  pkg_info(&info);
98
  return (int) (info.free / (payload_size + column_count * COLUMN_OVERHEAD));
99
#else
100
101
0
  return 0;
102
0
#endif
103
104
105
0
}
106
107
int db_check_api(db_func_t* dbf, char *mname)
108
0
{
109
0
  if(dbf==NULL)
110
0
    return -1;
111
112
  /* All modules must export db_use_table */
113
0
  if (dbf->use_table == 0) {
114
0
    LM_ERR("module %s does not export db_use_table function\n", mname);
115
0
    goto error;
116
0
  }
117
118
  /* All modules must export db_init */
119
0
  if (dbf->init == 0) {
120
0
    LM_ERR("module %s does not export db_init function\n", mname);
121
0
    goto error;
122
0
  }
123
124
  /* All modules must export db_close */
125
0
  if (dbf->close == 0) {
126
0
    LM_ERR("module %s does not export db_close function\n", mname);
127
0
    goto error;
128
0
  }
129
130
0
  if (dbf->query) {
131
0
    dbf->cap |= DB_CAP_QUERY;
132
0
  }
133
134
0
  if (dbf->fetch_result) {
135
0
    dbf->cap |= DB_CAP_FETCH;
136
0
  }
137
138
0
  if (dbf->raw_query) {
139
0
    dbf->cap |= DB_CAP_RAW_QUERY;
140
0
  }
141
142
  /* Free result must be exported if DB_CAP_QUERY or
143
   * DB_CAP_RAW_QUERY is set */
144
0
  if ((dbf->cap & (DB_CAP_QUERY|DB_CAP_RAW_QUERY)) && (dbf->free_result==0)) {
145
0
    LM_ERR("module %s supports queries but does not export free_result\n",
146
0
        mname);
147
0
    goto error;
148
0
  }
149
150
0
  if (dbf->insert) {
151
0
    dbf->cap |= DB_CAP_INSERT;
152
0
  }
153
154
0
  if (dbf->delete) {
155
0
    dbf->cap |= DB_CAP_DELETE;
156
0
  }
157
158
0
  if (dbf->update) {
159
0
    dbf->cap |= DB_CAP_UPDATE;
160
0
  }
161
162
0
  if (dbf->replace) {
163
0
    dbf->cap |= DB_CAP_REPLACE;
164
0
  }
165
166
0
  if (dbf->last_inserted_id) {
167
0
    dbf->cap |= DB_CAP_LAST_INSERTED_ID;
168
0
  }
169
170
0
  if (dbf->insert_update) {
171
0
    dbf->cap |= DB_CAP_INSERT_UPDATE;
172
0
  }
173
174
0
  if (dbf->async_raw_query || dbf->async_resume || dbf->async_free_result) {
175
0
    if (!dbf->async_raw_query || !dbf->async_resume || !dbf->async_free_result) {
176
0
      LM_BUG("NULL async raw_query | resume | free_result in %s", mname);
177
0
      return -1;
178
0
    }
179
180
0
    dbf->cap |= DB_CAP_ASYNC_RAW_QUERY;
181
0
  }
182
183
0
  return 0;
184
0
error:
185
0
  return -1;
186
0
}
187
188
/* fills mydbf with the corresponding db module callbacks
189
 * returns 0 on success, -1 on error
190
 * on error mydbf will contain only 0s */
191
int db_bind_mod(const str* mod, db_func_t* mydbf)
192
0
{
193
0
  char *name, *tmp, *p;
194
0
  int len;
195
0
  db_func_t dbf;
196
0
  db_bind_api_f dbind;
197
198
0
  if (!mod || !mod->s) {
199
0
    LM_CRIT("null database module name\n");
200
0
    return -1;
201
0
  }
202
0
  if (mydbf==0) {
203
0
    LM_CRIT("null dbf parameter\n");
204
0
    return -1;
205
0
  }
206
0
  if (mod->len > MAX_URL_LENGTH)
207
0
  {
208
0
    LM_ERR("SQL URL too long\n");
209
0
    return 0;
210
0
  }
211
  // add the prefix
212
0
  name = pkg_malloc(mod->len + 4);
213
0
  if (!name) {
214
0
    LM_ERR("no private memory left\n");
215
0
    return -1;
216
0
  }
217
0
  memcpy(name, "db_", 3);
218
0
  memcpy(name+3, mod->s, mod->len);
219
0
  name[mod->len+3] = 0;
220
221
  /* for safety we initialize mydbf with 0 (this will cause
222
   *  a segfault immediately if someone tries to call a function
223
   *  from it without checking the return code from bind_dbmod */
224
0
  memset((void*)mydbf, 0, sizeof(db_func_t));
225
226
0
  p = strchr(name, ':');
227
0
  if (p) {
228
0
    len = p - name;
229
0
    tmp = (char*)pkg_malloc(len + 4);
230
0
    if (!tmp) {
231
0
      LM_ERR("no private memory left\n");
232
0
      pkg_free(name);
233
0
      return -1;
234
0
    }
235
0
    memcpy(tmp, name, len);
236
0
    tmp[len] = '\0';
237
0
    pkg_free(name);
238
0
  } else {
239
0
    tmp = name;
240
0
  }
241
242
0
  dbind = (db_bind_api_f)find_mod_export(tmp, "db_bind_api", 0);
243
0
  if(dbind != NULL)
244
0
  {
245
0
    LM_DBG("using db bind api for %s\n", tmp);
246
0
    if(dbind(mod, &dbf)<0)
247
0
    {
248
0
      LM_ERR("db_bind_api returned error for module %s\n", tmp);
249
0
      goto error;
250
0
    }
251
0
  } else {
252
0
    memset(&dbf, 0, sizeof(db_func_t));
253
0
    LM_DBG("using export interface to bind %s\n", tmp);
254
0
    dbf.use_table = (db_use_table_f)find_mod_export(tmp,
255
0
      "db_use_table", 0);
256
0
    dbf.init = (db_init_f)find_mod_export(tmp, "db_init", 0);
257
0
    dbf.close = (db_close_f)find_mod_export(tmp, "db_close", 0);
258
0
    dbf.query = (db_query_f)find_mod_export(tmp, "db_query", 0);
259
0
    dbf.fetch_result = (db_fetch_result_f)find_mod_export(tmp,
260
0
      "db_fetch_result", 0);
261
0
    dbf.raw_query = (db_raw_query_f)find_mod_export(tmp,
262
0
      "db_raw_query", 0);
263
0
    dbf.free_result = (db_free_result_f)find_mod_export(tmp,
264
0
      "db_free_result", 0);
265
0
    dbf.insert = (db_insert_f)find_mod_export(tmp, "db_insert", 0);
266
0
    dbf.delete = (db_delete_f)find_mod_export(tmp, "db_delete", 0);
267
0
    dbf.update = (db_update_f)find_mod_export(tmp, "db_update", 0);
268
0
    dbf.replace = (db_replace_f)find_mod_export(tmp, "db_replace", 0);
269
0
    dbf.last_inserted_id= (db_last_inserted_id_f)find_mod_export(tmp,
270
0
      "db_last_inserted_id", 0);
271
0
    dbf.insert_update = (db_insert_update_f)find_mod_export(tmp,
272
0
      "db_insert_update", 0);
273
0
  }
274
  /* check if the module pre-populated the capabilities, or we need to
275
   * compute them ourselves - we check for the INSERT capability, because
276
   * it's the only one that should be exported by all modules */
277
0
  if(!DB_CAPABILITY(dbf, DB_CAP_INSERT) && db_check_api(&dbf, tmp)!=0)
278
0
    goto error;
279
280
0
  *mydbf=dbf; /* copy */
281
0
  pkg_free(tmp);
282
0
  return 0;
283
284
0
error:
285
0
  pkg_free(tmp);
286
0
  return -1;
287
0
}
288
289
290
/*
291
 * Initialize database module
292
 * No function should be called before this
293
 */
294
db_con_t* db_do_init(const str* url, void* (*new_connection)(const struct db_id *))
295
0
{
296
0
  struct db_id *id = NULL;
297
0
  struct pool_con *con = NULL;
298
0
  db_con_t *res = NULL;
299
0
  int con_size = 0;
300
301
0
  if (!url || !url->s || !new_connection) {
302
0
    LM_ERR("invalid parameter value\n");
303
0
    return 0;
304
0
  }
305
306
0
  con_size = sizeof(db_con_t) + sizeof(void *) + url->len;
307
0
  if (url->len > MAX_URL_LENGTH)
308
0
  {
309
0
    LM_ERR("SQL URL too long\n");
310
0
    return 0;
311
0
  }
312
313
  /* this is the root memory for this database connection. */
314
0
  res = (db_con_t*)pkg_malloc(con_size);
315
0
  if (!res) {
316
0
    LM_ERR("no private memory left\n");
317
0
    return 0;
318
0
  }
319
320
0
  memset(res, 0, con_size);
321
322
  /* fill in the URL info */
323
0
  res->url.s = (char *)res + sizeof(db_con_t) + sizeof(void *);
324
0
  res->url.len = url->len;
325
0
  memcpy(res->url.s,url->s,url->len);
326
327
0
  id = new_db_id(url);
328
0
  if (!id) {
329
0
    LM_ERR("cannot parse URL '%.*s'\n", url->len, url->s);
330
0
    goto err;
331
0
  }
332
333
  /* Find the connection in the pool */
334
0
  con = pool_get(id);
335
0
  if (!con) {
336
0
    LM_DBG("connection %p not found in pool\n", id);
337
    /* Not in the pool yet */
338
0
    con = (struct pool_con *)new_connection(id);
339
0
    if (!con) {
340
0
      LM_ERR("could not add connection to the pool\n");
341
0
      goto err;
342
0
    }
343
0
    pool_insert(con);
344
0
    LM_DBG("connection %p inserted in pool as %p\n", id,con);
345
0
  } else {
346
0
    LM_DBG("connection %p found in pool as %p\n", id,con);
347
0
    free_db_id(id);
348
0
  }
349
350
0
  if (!con->transfers) {
351
0
    con->transfers = pkg_malloc(db_max_async_connections *
352
0
                    sizeof *con->transfers);
353
0
    if (!con->transfers) {
354
0
      LM_ERR("no more pkg\n");
355
0
      goto err;
356
0
    }
357
0
  }
358
359
0
  res->tail = (unsigned long)con;
360
0
  return res;
361
362
0
 err:
363
0
  if (id) free_db_id(id);
364
0
  if (res) pkg_free(res);
365
0
  return 0;
366
0
}
367
368
369
/*
370
 * Shut down database module
371
 * No function should be called after this
372
 */
373
void db_do_close(db_con_t* _h, void (*free_connection)(struct pool_con*))
374
0
{
375
0
  struct pool_con* con;
376
377
0
  if (!_h) {
378
0
    LM_ERR("invalid parameter value\n");
379
0
    return;
380
0
  }
381
382
0
  con = (struct pool_con*)_h->tail;
383
0
  if (pool_remove(con) == 1) {
384
0
    free_connection(con);
385
0
  }
386
387
0
  pkg_free(_h);
388
0
}
389
390
391
392
/*
393
 * Get version of a table
394
 * If there is no row for the given table, return version 0
395
 */
396
int db_table_version(const db_func_t* dbf, db_con_t* connection, const str* table)
397
0
{
398
0
  db_key_t key[1], col[1];
399
0
  db_val_t val[1];
400
0
  db_res_t* res = NULL;
401
0
  db_val_t* ver = 0;
402
0
  str version;
403
0
  int ret;
404
405
406
0
  if (!dbf||!connection || !table || !table->s) {
407
0
    LM_CRIT("invalid parameter value\n");
408
0
    return -1;
409
0
  }
410
411
0
  version.s = db_version_table;
412
0
  version.len = strlen(version.s);
413
414
0
  if (dbf->use_table(connection, &version) < 0) {
415
0
    LM_ERR("error while changing table\n");
416
0
    return -1;
417
0
  }
418
0
  str tmp1 = str_init(TABLENAME_COLUMN);
419
0
  key[0] = &tmp1;
420
421
0
  VAL_TYPE(val) = DB_STR;
422
0
  VAL_NULL(val) = 0;
423
0
  VAL_STR(val) = *table;
424
425
0
  str tmp2 = str_init(VERSION_COLUMN);
426
0
  col[0] = &tmp2;
427
428
0
  if (dbf->query(connection, key, 0, val, col, 1, 1, 0, &res) < 0) {
429
0
    LM_ERR("error in db_query\n");
430
0
    return -1;
431
0
  }
432
433
0
  if (RES_ROW_N(res) == 0) {
434
0
    LM_DBG("no row for table %.*s found\n",
435
0
      table->len, ZSW(table->s));
436
0
    return 0;
437
0
  }
438
439
0
  if (RES_ROW_N(res) != 1) {
440
0
    LM_ERR("invalid number of rows received:"
441
0
      " %d, %.*s\n", RES_ROW_N(res), table->len, ZSW(table->s));
442
0
    dbf->free_result(connection, res);
443
0
    return -1;
444
0
  }
445
446
0
  ver = ROW_VALUES(RES_ROWS(res));
447
0
  if ((VAL_TYPE(ver)!=DB_INT && VAL_TYPE(ver)!=DB_BIGINT) || VAL_NULL(ver)) {
448
0
    LM_ERR("invalid type (%d) or nul (%d) version "
449
0
      "columns for %.*s\n", VAL_TYPE(ver), VAL_NULL(ver),
450
0
      table->len, ZSW(table->s));
451
0
    dbf->free_result(connection, res);
452
0
    return -1;
453
0
  }
454
455
0
  if (VAL_TYPE(ver)==DB_INT)
456
0
    ret = VAL_INT(ver);
457
0
  else
458
0
    ret = (int)VAL_BIGINT(ver);
459
460
0
  dbf->free_result(connection, res);
461
0
  return ret;
462
0
}
463
464
/*
465
 * Check the table version
466
 * 0 means ok, -1 means an error occurred
467
 */
468
int db_check_table_version(db_func_t* dbf, db_con_t* dbh, const str* table, const unsigned int version)
469
0
{
470
0
  int ver;
471
472
  /* if DB does not support QUERY, return TRUE */
473
0
  if (!DB_CAPABILITY(*dbf, DB_CAP_QUERY))
474
0
    return 0;
475
476
0
  ver = db_table_version(dbf, dbh, table);
477
0
  if (ver < 0) {
478
0
    LM_ERR("querying version for table %.*s\n", table->len, table->s);
479
0
    return -1;
480
0
  } else if (ver != version) {
481
0
    LM_ERR("invalid version %d for table %.*s found, expected %d\n",
482
0
      ver, table->len, table->s, version);
483
0
    return -1;
484
0
  }
485
0
  return 0;
486
0
}
487
488
/*
489
 * Store name of table that will be used by
490
 * subsequent database functions
491
 */
492
int db_use_table(db_con_t* _h, const str* _t)
493
0
{
494
0
  if (!_h || !_t || !_t->s) {
495
0
    LM_ERR("invalid parameter value %p, %p\n", _h, _t);
496
0
    return -1;
497
0
  }
498
499
0
  CON_TABLE(_h) = _t;
500
0
  return 0;
501
0
}