sqlops: add a limit of maximum 32 result containers
[kamailio] / src / modules / sqlops / sqlops.c
1 /**
2  * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 /*! \file
22  * \ingroup sqlops
23  * \brief Kamailio SQL-operations :: Module interface
24  *
25  * - Module: \ref sqlops
26  */
27
28 /*! \defgroup sqlops Kamailio :: SQL Operations
29  *
30  * The module adds support for raw SQL queries in the configuration file.
31  *
32  */
33
34
35
36
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <stdlib.h>
41 #include <sys/types.h>
42 #include <sys/ipc.h>
43 #include <unistd.h>
44 #include <fcntl.h>
45
46 #include "../../core/sr_module.h"
47 #include "../../core/dprint.h"
48 #include "../../core/pvar.h"
49 #include "../../core/kemi.h"
50
51 #include "sql_api.h"
52 #include "sql_var.h"
53 #include "sql_trans.h"
54
55
56 MODULE_VERSION
57
58 static int bind_sqlops(sqlops_api_t* api);
59
60 /** module functions */
61 static int sql_check_connection(sql_con_t*);
62 static int sql_query(struct sip_msg*, char*, char*, char*);
63 static int sql_query2(struct sip_msg*, char*, char*);
64 static int sql_query_async(struct sip_msg*, char*, char*);
65 static int sql_xquery(struct sip_msg *msg, char *dbl, char *query, char *res);
66 static int sql_pvquery(struct sip_msg *msg, char *dbl, char *query, char *res);
67 static int sql_rfree(struct sip_msg*, char*, char*);
68 static int mod_init(void);
69 static int child_init(int rank);
70 static void destroy(void);
71
72 static int fixup_sql_query(void** param, int param_no);
73 static int fixup_sql_xquery(void** param, int param_no);
74 static int fixup_sql_pvquery(void** param, int param_no);
75 static int fixup_sql_rfree(void** param, int param_no);
76
77 static int sql_con_param(modparam_t type, void* val);
78 static int sql_res_param(modparam_t type, void* val);
79
80 extern int sqlops_tr_buf_size;
81
82 static int sqlops_connect_mode = 0;
83
84 static pv_export_t mod_pvs[] = {
85         { {"dbr", sizeof("dbr")-1}, PVT_OTHER, pv_get_dbr, 0,
86                 pv_parse_dbr_name, 0, 0, 0 },
87         { {"sqlrows", sizeof("sqlrows")-1}, PVT_OTHER, pv_get_sqlrows, 0,
88                 pv_parse_con_name, 0, 0, 0 },
89         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
90 };
91
92 static cmd_export_t cmds[]={
93         {"sql_query",  (cmd_function)sql_query, 3, fixup_sql_query, 0,
94                 ANY_ROUTE},
95         {"sql_query",  (cmd_function)sql_query2, 2, fixup_sql_query, 0,
96                 ANY_ROUTE},
97         {"sql_query_async",  (cmd_function)sql_query_async, 2, fixup_sql_query, 0,
98                 ANY_ROUTE},
99         {"sql_xquery",  (cmd_function)sql_xquery, 3, fixup_sql_xquery, 0,
100                 ANY_ROUTE},
101         {"sql_pvquery",  (cmd_function)sql_pvquery, 3, fixup_sql_pvquery, 0,
102                 ANY_ROUTE},
103         {"sql_result_free",  (cmd_function)sql_rfree,  1, fixup_sql_rfree, 0,
104                 ANY_ROUTE},
105         {"bind_sqlops", (cmd_function)bind_sqlops, 0, 0, 0, 0},
106         {0,0,0,0,0,0}
107 };
108
109 static param_export_t params[]={
110         {"sqlcon",  PARAM_STRING|USE_FUNC_PARAM, (void*)sql_con_param},
111         {"sqlres",  PARAM_STRING|USE_FUNC_PARAM, (void*)sql_res_param},
112         {"tr_buf_size",     PARAM_INT,   &sqlops_tr_buf_size},
113         {"connect_mode",    PARAM_INT,   &sqlops_connect_mode},
114         {0,0,0}
115 };
116
117 static tr_export_t mod_trans[] = {
118         { {"sql", sizeof("sql")-1}, tr_parse_sql },
119         { { 0, 0 }, 0 }
120 };
121
122
123 /** module exports */
124 struct module_exports exports= {
125         "sqlops",   /* module name */
126         DEFAULT_DLFLAGS, /* dlopen flags */
127         cmds,       /* exported functions */
128         params,     /* exported parameters */
129         0,          /* exported rpc functions */
130         mod_pvs,    /* exported pseudo-variables */
131         0,          /* response handling function */
132         mod_init,   /* module init function */
133         child_init, /* per-child init function */
134         destroy     /* module destroy function */
135 };
136
137 static int mod_init(void)
138 {
139         if(sqlops_tr_buffer_init()<0) {
140                 return -1;
141         }
142         return 0;
143 }
144
145 static int child_init(int rank)
146 {
147         int ret;
148         if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
149                 return 0;
150
151         ret = sql_connect((sqlops_connect_mode == 1)?1:0);
152
153         LM_DBG("SQL result: %d \n", ret);
154
155         if (ret != 0 && sqlops_connect_mode == 1)
156         {
157                 LM_INFO("SQL result: %d but start_without_db_connection enabled - proceed\n",
158                                 ret);
159                 return 0;
160
161         } else {
162                 return ret;
163         }
164 }
165
166 /**
167  * destroy function
168  */
169 static void destroy(void)
170 {
171         sql_destroy();
172 }
173
174 /**
175  * parse sqlcon module parameter
176  */
177 int sql_con_param(modparam_t type, void *val)
178 {
179         if(val==NULL)
180                 goto error;
181
182         return sql_parse_param((char*)val);
183 error:
184         return -1;
185
186 }
187
188 /**
189  * parse sqlres module parameter
190  */
191 int sql_res_param(modparam_t type, void *val)
192 {
193         sql_result_t *res = NULL;
194         str s;
195
196         if(val==NULL)
197         {
198                 LM_ERR("invalid parameter\n");
199                 goto error;
200         }
201
202         s.s = (char*)val;
203         s.len = strlen(s.s);
204
205         res = sql_get_result(&s);
206         if(res==NULL)
207         {
208                 LM_ERR("invalid result container [%s]\n", s.s);
209                 goto error;
210         }
211         return 0;
212 error:
213         return -1;
214 }
215
216 static int sql_check_connection(sql_con_t *dbl)
217 {
218         if (dbl->dbh != NULL) {
219                 return 0;
220         }
221
222         if(sqlops_connect_mode != 1) {
223                 LM_CRIT("no database handle with reconnect disabled\n");
224                 return -1;
225         }
226
227     LM_DBG("try to establish SQL connection\n");
228         if(sql_reconnect(dbl)<0) {
229                 LM_ERR("failed to connect to database\n");
230                 return -1;
231         }
232         return 0;
233 }
234
235 /**
236  *
237  */
238 static int sql_query(struct sip_msg *msg, char *dbl, char *query, char *res)
239 {
240         str sq;
241         if(sql_check_connection((sql_con_t*)dbl)<0) {
242                 LM_ERR("invalid connection to database");
243                 return -2;
244         }
245         if(pv_printf_s(msg, (pv_elem_t*)query, &sq)!=0) {
246                 LM_ERR("cannot print the sql query\n");
247                 return -1;
248         }
249         return sql_do_query((sql_con_t*)dbl, &sq, (sql_result_t*)res);
250 }
251
252 static int sql_query2(struct sip_msg *msg, char *dbl, char *query)
253 {
254         return sql_query(msg, dbl, query, NULL);
255 }
256
257 static int sql_query_async(struct sip_msg *msg, char *dbl, char *query)
258 {
259         str sq;
260         if(sql_check_connection((sql_con_t*)dbl)<0) {
261                 LM_ERR("invalid connection to database");
262                 return -2;
263         }
264         if(pv_printf_s(msg, (pv_elem_t*)query, &sq)!=0)
265         {
266                 LM_ERR("cannot print the sql query\n");
267                 return -1;
268         }
269         return sql_do_query_async((sql_con_t*)dbl, &sq);
270 }
271
272
273 /**
274  *
275  */
276 static int sql_xquery(struct sip_msg *msg, char *dbl, char *query, char *res)
277 {
278         if(sql_check_connection((sql_con_t*)dbl)<0) {
279                 LM_ERR("invalid connection to database");
280                 return -2;
281         }
282         return sql_do_xquery(msg, (sql_con_t*)dbl, (pv_elem_t*)query, (pv_elem_t*)res);
283 }
284
285 /**
286  *
287  */
288 static int sql_pvquery(struct sip_msg *msg, char *dbl, char *query, char *res)
289 {
290         if(sql_check_connection((sql_con_t*)dbl)<0) {
291                 LM_ERR("invalid connection to database");
292                 return -2;
293         }
294         return sql_do_pvquery(msg, (sql_con_t*)dbl, (pv_elem_t*)query, (pvname_list_t*)res);
295 }
296
297 /**
298  *
299  */
300 static int sql_rfree(struct sip_msg *msg, char *res, char *s2)
301 {
302         sql_reset_result((sql_result_t*)res);
303         return 1;
304 }
305
306 /**
307  *
308  */
309 static int fixup_sql_query(void** param, int param_no)
310 {
311         sql_con_t *con = NULL;
312         pv_elem_t *query = NULL;
313         sql_result_t *res = NULL;
314         str s;
315
316         s.s = (char*)(*param);
317         s.len = strlen(s.s);
318
319         if (param_no==1) {
320                 con = sql_get_connection(&s);
321                 if(con==NULL)
322                 {
323                         LM_ERR("invalid connection [%s]\n", s.s);
324                         return E_UNSPEC;
325                 }
326                 *param = (void*)con;
327         } else if (param_no==2) {
328                 if(pv_parse_format(&s, &query)<0)
329                 {
330                         LM_ERR("invalid query string [%s]\n", s.s);
331                         return E_UNSPEC;
332                 }
333                 *param = (void*)query;
334         } else if (param_no==3) {
335                 res = sql_get_result(&s);
336                 if(res==NULL)
337                 {
338                         LM_ERR("invalid result container [%s]\n", s.s);
339                         return E_UNSPEC;
340                 }
341                 *param = (void*)res;
342         }
343         return 0;
344 }
345
346 /**
347  *
348  */
349 static int fixup_sql_xquery(void** param, int param_no)
350 {
351         sql_con_t *con = NULL;
352         pv_elem_t *pv = NULL;
353         str s;
354
355         s.s = (char*)(*param);
356         s.len = strlen(s.s);
357
358         if (param_no==1) {
359                 con = sql_get_connection(&s);
360                 if(con==NULL)
361                 {
362                         LM_ERR("invalid connection [%s]\n", s.s);
363                         return E_UNSPEC;
364                 }
365                 *param = (void*)con;
366         } else if (param_no==2) {
367                 if(pv_parse_format(&s, &pv)<0)
368                 {
369                         LM_ERR("invalid query string [%s]\n", s.s);
370                         return E_UNSPEC;
371                 }
372                 *param = (void*)pv;
373         } else if (param_no==3) {
374                 if(pv_parse_format(&s, &pv)<0)
375                 {
376                         LM_ERR("invalid result [%s]\n", s.s);
377                         return E_UNSPEC;
378                 }
379                 *param = (void*)pv;
380         }
381         return 0;
382 }
383
384 /**
385  *
386  */
387 static int fixup_sql_pvquery(void** param, int param_no)
388 {
389         sql_con_t *con = NULL;
390         pv_elem_t *pv = NULL;
391         pvname_list_t *res = NULL;
392         pvname_list_t *pvl = NULL;
393         str s;
394         int i;
395
396         if(*param == NULL)
397         {
398                 LM_ERR("missing parameter %d\n", param_no);
399                 return E_UNSPEC;
400         }
401         s.s = (char*)(*param);
402         s.len = strlen(s.s);
403
404         if (param_no==1) {
405                 con = sql_get_connection(&s);
406                 if(con==NULL)
407                 {
408                         LM_ERR("invalid connection [%s]\n", s.s);
409                         return E_UNSPEC;
410                 }
411                 *param = (void*)con;
412         } else if (param_no==2) {
413                 if(pv_parse_format(&s, &pv)<0)
414                 {
415                         LM_ERR("invalid query string [%s]\n", s.s);
416                         return E_UNSPEC;
417                 }
418                 *param = (void*)pv;
419         } else if (param_no==3) {
420                 /* parse result variables into list of pv_spec_t's */
421                 res = parse_pvname_list(&s, 0);
422                 if(res==NULL)
423                 {
424                         LM_ERR("invalid result parameter [%s]\n", s.s);
425                         return E_UNSPEC;
426                 }
427                 /* check if all result variables are writable */
428                 pvl = res;
429                 i = 1;
430                 while (pvl) {
431                         if (pvl->sname.setf == NULL)
432                         {
433                                 LM_ERR("result variable [%d] is read-only\n", i);
434                                 free_pvname_list(res);
435                                 return E_UNSPEC;
436                         }
437                         i++;
438                         pvl = pvl->next;
439                 }
440                 *param = (void*)res;
441                 return 0;
442         }
443         return 0;
444 }
445
446 /**
447  *
448  */
449 static int fixup_sql_rfree(void** param, int param_no)
450 {
451         sql_result_t *res = NULL;
452         str s;
453
454         s.s = (char*)(*param);
455         s.len = strlen(s.s);
456
457         if (param_no==1) {
458                 res = sql_get_result(&s);
459                 if(res==NULL)
460                 {
461                         LM_ERR("invalid result container [%s]\n", s.s);
462                         return E_UNSPEC;
463                 }
464                 *param = (void*)res;
465         }
466         return 0;
467 }
468
469 /**
470  * @brief bind functions to SQLOPS API structure
471  */
472 static int bind_sqlops(sqlops_api_t* api)
473 {
474         if (!api) {
475                 ERR("Invalid parameter value\n");
476                 return -1;
477         }
478         api->query = sqlops_do_query;
479         api->value = sqlops_get_value;
480         api->is_null = sqlops_is_null;
481         api->column  = sqlops_get_column;
482         api->reset   = sqlops_reset_result;
483         api->nrows   = sqlops_num_rows;
484         api->ncols   = sqlops_num_columns;
485         api->xquery  = sqlops_do_xquery;
486
487         return 0;
488 }
489
490 static int ki_sqlops_query(sip_msg_t *msg, str *scon, str *squery, str *sres)
491 {
492         return sqlops_do_query(scon, squery, sres);
493 }
494
495 static int ki_sqlops_query_async(sip_msg_t *msg, str *scon, str *squery)
496 {
497         sql_con_t *con = NULL;
498
499         if (scon == NULL || scon->s == NULL || scon->len<=0) {
500                 LM_ERR("invalid connection name\n");
501                 return -1;
502         }
503
504         con = sql_get_connection(scon);
505         if(con==NULL) {
506                 LM_ERR("invalid connection [%.*s]\n", scon->len, scon->s);
507                 return -1;
508         }
509
510         return sql_do_query_async(con, squery);
511 }
512
513 static int ki_sqlops_reset_result(sip_msg_t *msg, str *sres)
514 {
515         sqlops_reset_result(sres);
516         return 1;
517 }
518
519 static int ki_sqlops_num_rows(sip_msg_t *msg, str *sres)
520 {
521         return sqlops_num_rows(sres);
522 }
523
524 static int ki_sqlops_num_columns(sip_msg_t *msg, str *sres)
525 {
526         return sqlops_num_columns(sres);
527 }
528
529 static int ki_sqlops_is_null(sip_msg_t *msg, str *sres, int i, int j)
530 {
531         return sqlops_is_null(sres, i, j);
532 }
533
534 /**
535  *
536  */
537 /* clang-format off */
538 static sr_kemi_t sr_kemi_sqlops_exports[] = {
539         { str_init("sqlops"), str_init("sql_query"),
540                 SR_KEMIP_INT, ki_sqlops_query,
541                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
542                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
543         },
544         { str_init("sqlops"), str_init("sql_result_free"),
545                 SR_KEMIP_INT, ki_sqlops_reset_result,
546                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
547                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
548         },
549         { str_init("sqlops"), str_init("sql_num_rows"),
550                 SR_KEMIP_INT, ki_sqlops_num_rows,
551                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
552                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
553         },
554         { str_init("sqlops"), str_init("sql_num_columns"),
555                 SR_KEMIP_INT, ki_sqlops_num_columns,
556                 { SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE,
557                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
558         },
559         { str_init("sqlops"), str_init("sql_is_null"),
560                 SR_KEMIP_INT, ki_sqlops_is_null,
561                 { SR_KEMIP_STR, SR_KEMIP_INT, SR_KEMIP_INT,
562                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
563         },
564         { str_init("sqlops"), str_init("sql_xquery"),
565                 SR_KEMIP_INT, sqlops_do_xquery,
566                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_STR,
567                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
568         },
569         { str_init("sqlops"), str_init("sql_query_async"),
570                 SR_KEMIP_INT, ki_sqlops_query_async,
571                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
572                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
573         },
574
575         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
576 };
577 /* clang-format on */
578
579 /**
580  *
581  */
582 int mod_register(char *path, int *dlflags, void *p1, void *p2)
583 {
584         sr_kemi_modules_add(sr_kemi_sqlops_exports);
585         return register_trans_mod(path, mod_trans);
586 }