src/lib/srdb1/db_ut.c
cda1e991
 /*
  * Copyright (C) 2001-2003 FhG Fokus
bc4bf265
  * Copyright (C) 2007-2008 1&1 Internet AG
cda1e991
  *
d520eaf5
  * This file is part of Kamailio, a free SIP server.
cda1e991
  *
d520eaf5
  * Kamailio is free software; you can redistribute it and/or modify
cda1e991
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
d520eaf5
  * Kamailio is distributed in the hope that it will be useful,
cda1e991
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
b3da13a1
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
cda1e991
  */
 
bc4bf265
 /**
7d14b037
  * \file lib/srdb1/db_ut.c
bc4bf265
  * \brief Utility functions for database drivers.
  *
  * This utility methods are used from the database SQL driver to convert
  * values and print SQL queries from the internal API representation.
a33b9a87
  * \ingroup db1
bc4bf265
  */
cda1e991
 
94b24ee2
 
00ea7481
 #if defined (__OS_darwin) || defined (__OS_freebsd)
cf83221d
 #include "../../core/pvar.h"
123e831d
 #endif
 
28af5617
 /**
  * make strptime available
  * use 600 for 'Single UNIX Specification, Version 3'
  * _XOPEN_SOURCE creates conflict in swab definition in Solaris
  */
 #ifndef __OS_solaris
 	#define _XOPEN_SOURCE 600          /* glibc2 on linux, bsd */
 	#define _BSD_SOURCE 1              /* needed on linux to "fix" the effect
b3da13a1
 										* of the above define on
 										* features.h/unistd.h syscall() */
09470aed
 	#define _DEFAULT_SOURCE 1         /* _BSD_SOURCE is deprecated */
28af5617
 #else
 	#define _XOPEN_SOURCE_EXTENDED 1   /* solaris */
 #endif
 
 #include <time.h>
 
123e831d
 #ifndef __OS_solaris
 	#undef _XOPEN_SOURCE
 	#undef _XOPEN_SOURCE_EXTENDED
dee2478e
 #else  /* solaris */
 	#undef _XOPEN_SOURCE_EXTENDED
123e831d
 #endif
28af5617
 
cda1e991
 #include <limits.h>
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
cf83221d
 #include "../../core/mem/mem.h"
 #include "../../core/dprint.h"
28af5617
 
 #include "db_ut.h"
 
97841ad1
 
e62355f2
 /**
  *
  */
 int db_str2int(const char* _s, int* _v)
cda1e991
 {
 	long tmp;
af7d4496
 	char* p = NULL;
cda1e991
 
 	if (!_s || !_v) {
b3da13a1
 		LM_ERR("Invalid parameter value\n");
 		return -1;
cda1e991
 	}
 
af7d4496
 	tmp = strtoul(_s, &p, 10);
b3da13a1
 	if ((tmp == ULONG_MAX && errno == ERANGE) ||
 				(tmp < INT_MIN) || (tmp > UINT_MAX)) {
f001ee7d
 		LM_ERR("Value out of range\n");
cda1e991
 		return -1;
 	}
af7d4496
 	if (p && *p != '\0') {
 		LM_ERR("Unexpected characters: [%s]\n", p);
 		return -2;
 	}
cda1e991
 
 	*_v = (int)tmp;
 	return 0;
 }
 
 
e62355f2
 /**
  *
  */
 int db_str2uint(const char* _s, unsigned int* _v)
 {
 	unsigned long tmp;
 	char* p = NULL;
 
 	if (!_s || !_v) {
 		LM_ERR("Invalid parameter value\n");
 		return -1;
 	}
 
 	tmp = strtoul(_s, &p, 10);
 	if ((tmp == ULONG_MAX && errno == ERANGE) ||
989ea3d6
 				((long)tmp < INT_MIN) || (tmp > UINT_MAX)) {
e62355f2
 		LM_ERR("Value out of range\n");
 		return -1;
 	}
 	if (p && *p != '\0') {
 		LM_ERR("Unexpected characters: [%s]\n", p);
 		return -2;
 	}
 
 	*_v = (unsigned int)tmp;
 	return 0;
 }
 
 
 /**
  *
  */
 int db_str2longlong(const char* _s, long long * _v)
7746b450
 {
 	long long tmp;
af7d4496
 	char* p = NULL;
7746b450
 
 	if (!_s || !_v) {
b3da13a1
 		LM_ERR("Invalid parameter value\n");
 		return -1;
7746b450
 	}
 
af7d4496
 	tmp = strtoll(_s, &p, 10);
cc6c7a5f
 	if (errno == ERANGE) {
7746b450
 		LM_ERR("Value out of range\n");
 		return -1;
 	}
af7d4496
 	if (p && *p != '\0') {
 		LM_ERR("Unexpected characters: [%s]\n", p);
 		return -2;
 	}
7746b450
 
 	*_v = tmp;
 	return 0;
 }
 
 
e62355f2
 /**
  *
  */
 int db_str2ulonglong(const char* _s, unsigned long long * _v)
 {
 	unsigned long long tmp;
 	char* p = NULL;
 
 	if (!_s || !_v) {
 		LM_ERR("Invalid parameter value\n");
 		return -1;
 	}
 
 	tmp = strtoull(_s, &p, 10);
 	if (errno == ERANGE) {
 		LM_ERR("Value out of range\n");
 		return -1;
 	}
 	if (p && *p != '\0') {
 		LM_ERR("Unexpected characters: [%s]\n", p);
 		return -2;
 	}
 
 	*_v = tmp;
 	return 0;
 }
 
 
cda1e991
 /*
  * Convert a string to double
  */
e62355f2
 int db_str2double(const char* _s, double* _v)
cda1e991
 {
 	if ((!_s) || (!_v)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
cda1e991
 		return -1;
 	}
 
 	*_v = atof(_s);
 	return 0;
 }
 
 
 /*
  * Convert an integer to string
  */
e62355f2
 int db_int2str(int _v, char* _s, int* _l)
cda1e991
 {
 	int ret;
 
 	if ((!_s) || (!_l) || (!*_l)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
cda1e991
 		return -1;
 	}
 
 	ret = snprintf(_s, *_l, "%-d", _v);
 	if (ret < 0 || ret >= *_l) {
2b1249dc
 		LM_ERR("Error in snprintf\n");
cda1e991
 		return -1;
 	}
 	*_l = ret;
 
 	return 0;
 }
 
 
7746b450
 /*
e62355f2
  * Convert an unsigned integer to string
7746b450
  */
e62355f2
 int db_uint2str(unsigned int _v, char* _s, int* _l)
 {
 	int ret;
 
 	if ((!_s) || (!_l) || (!*_l)) {
 		LM_ERR("Invalid parameter value\n");
 		return -1;
 	}
 
 	ret = snprintf(_s, *_l, "%u", _v);
 	if (ret < 0 || ret >= *_l) {
 		LM_ERR("Error in snprintf\n");
 		return -1;
 	}
 	*_l = ret;
 
 	return 0;
 }
 
 
 /*
  * Convert a long long to string
  */
 int db_longlong2str(long long _v, char* _s, int* _l)
7746b450
 {
 	int ret;
 
 	if ((!_s) || (!_l) || (!*_l)) {
 		LM_ERR("Invalid parameter value\n");
 		return -1;
 	}
 
 	ret = snprintf(_s, *_l, "%-lld", _v);
 	if (ret < 0 || ret >= *_l) {
 		LM_ERR("Error in snprintf\n");
 		return -1;
 	}
 	*_l = ret;
 
 	return 0;
 }
 
 
e62355f2
 /*
  * Convert an unsigned long long to string
  */
 int db_ulonglong2str(unsigned long long _v, char* _s, int* _l)
 {
 	int ret;
 
 	if ((!_s) || (!_l) || (!*_l)) {
 		LM_ERR("Invalid parameter value\n");
 		return -1;
 	}
 
 	ret = snprintf(_s, *_l, "%llu", _v);
 	if (ret < 0 || ret >= *_l) {
 		LM_ERR("Error in snprintf\n");
 		return -1;
 	}
 	*_l = ret;
 
 	return 0;
 }
 
 
cda1e991
 /*
  * Convert a double to string
  */
e62355f2
 int db_double2str(double _v, char* _s, int* _l)
cda1e991
 {
 	int ret;
 
 	if ((!_s) || (!_l) || (!*_l)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
cda1e991
 		return -1;
 	}
 
e1dcdf5a
 	ret = snprintf(_s, *_l, "%-10.6f", _v);
cda1e991
 	if (ret < 0 || ret >= *_l) {
f001ee7d
 		LM_ERR("Error in snprintf\n");
cda1e991
 		return -1;
 	}
 	*_l = ret;
 
 	return 0;
 }
94b24ee2
 
 
b3da13a1
 /*
94b24ee2
  * Convert a string to time_t
  */
e62355f2
 int db_str2time(const char* _s, time_t* _v)
94b24ee2
 {
0cdfaf60
 	struct tm time;
 
0eece1d2
 	if ((!_s) || (!_v)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
94b24ee2
 		return -1;
 	}
 
0cdfaf60
 	/* Convert database time representation to time_t structure
b3da13a1
 	 * It is necessary to zero tm structure first */
94b24ee2
 	memset(&time, '\0', sizeof(struct tm));
f001ee7d
 	if (strptime(_s, "%Y-%m-%d %H:%M:%S", &time) == NULL) {
 		LM_ERR("Error during time conversion\n");
 		return -1;
 	}
94b24ee2
 
 	/* Daylight saving information got lost in the database
 	* so let mktime to guess it. This eliminates the bug when
 	* contacts reloaded from the database have different time
 	* of expiration by one hour when daylight saving is used
b3da13a1
 	*/
94b24ee2
 	time.tm_isdst = -1;
 	*_v = mktime(&time);
 
 	return 0;
 }
 
 
b3da13a1
 /**
  *
  */
e62355f2
 int db_time2str_ex(time_t _v, char* _s, int* _l, int _qmode)
94b24ee2
 {
c01633dc
 	struct tm t;
94b24ee2
 	int l;
 
0eece1d2
 	if ((!_s) || (!_l) || (*_l < 2)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
94b24ee2
 		return -1;
 	}
 
b3da13a1
 	if(_qmode) *_s++ = '\'';
09d410b4
 
0cdfaf60
 	/* Convert time_t structure to format accepted by the database */
c01633dc
 	localtime_r(&_v, &t);
 	l = strftime(_s, *_l -1, "%Y-%m-%d %H:%M:%S", &t);
94b24ee2
 
f001ee7d
 	if (l == 0) {
 		LM_ERR("Error during time conversion\n");
0cdfaf60
 		/* the value of _s is now unspecified */
f001ee7d
 		_s = NULL;
 		_l = 0;
 		return -1;
 	}
09d410b4
 	*_l = l;
f001ee7d
 
f54c723d
 	if(_qmode) {
 		*(_s + l) = '\'';
 		*_l = l + 2;
 	}
94b24ee2
 	return 0;
 }
0eece1d2
 
b3da13a1
 /**
  *
  */
e62355f2
 int db_time2str(time_t _v, char* _s, int* _l)
b3da13a1
 {
 	return db_time2str_ex(_v, _s, _l, 1);
 }
0eece1d2
 
 /*
  * Print list of columns separated by comma
  */
ffbe9c2c
 int db_print_columns(char* _b, const int _l, const db_key_t* _c, const int _n, const char *_tq)
0eece1d2
 {
bc4bf265
 	int i, ret, len = 0;
0eece1d2
 
 	if ((!_c) || (!_n) || (!_b) || (!_l)) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
0eece1d2
 		return -1;
 	}
 
 	for(i = 0; i < _n; i++)	{
 		if (i == (_n - 1)) {
ffbe9c2c
 			ret = snprintf(_b + len, _l - len, "%s%.*s%s ", _tq, _c[i]->len, _c[i]->s, _tq);
0eece1d2
 			if (ret < 0 || ret >= (_l - len)) goto error;
 			len += ret;
 		} else {
ffbe9c2c
 			ret = snprintf(_b + len, _l - len, "%s%.*s%s,", _tq, _c[i]->len, _c[i]->s, _tq);
0eece1d2
 			if (ret < 0 || ret >= (_l - len)) goto error;
 			len += ret;
 		}
 	}
 	return len;
 
 	error:
f001ee7d
 	LM_ERR("Error in snprintf\n");
0eece1d2
 	return -1;
 }
29ec8148
 
 
 /*
  * Print values of SQL statement
  */
a5071c50
 int db_print_values(const db1_con_t* _c, char* _b, const int _l, const db_val_t* _v,
 	const int _n, int (*val2str)(const db1_con_t*, const db_val_t*, char*, int*))
29ec8148
 {
bc4bf265
 	int i, l, len = 0;
29ec8148
 
 	if (!_c || !_b || !_l || !_v || !_n) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
29ec8148
 		return -1;
 	}
 
 	for(i = 0; i < _n; i++) {
bc4bf265
 		l = _l - len;
 		if ( (*val2str)(_c, _v + i, _b + len, &l) < 0) {
f001ee7d
 			LM_ERR("Error while converting value to string\n");
29ec8148
 			return -1;
 		}
bc4bf265
 		len += l;
29ec8148
 		if (i != (_n - 1)) {
bc4bf265
 			*(_b + len) = ',';
 			len++;
29ec8148
 		}
 	}
bc4bf265
 	return len;
29ec8148
 }
 
 
 /*
  * Print where clause of SQL statement
  */
a5071c50
 int db_print_where(const db1_con_t* _c, char* _b, const int _l, const db_key_t* _k,
bc4bf265
 	const db_op_t* _o, const db_val_t* _v, const int _n, int (*val2str)
a5071c50
 	(const 	db1_con_t*, const db_val_t*, char*, int*))
29ec8148
 {
bc4bf265
 	int i, l, ret, len = 0;
29ec8148
 
 	if (!_c || !_b || !_l || !_k || !_v || !_n) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
29ec8148
 		return -1;
 	}
 
 	for(i = 0; i < _n; i++) {
da4e2976
 		if (_o && strncmp(_o[i], OP_BITWISE_AND, 1) == 0) {
 			char tmp_buf[16];
 			int tmp_len = 15;
d7431dd1
 			memset(tmp_buf, 0, 16);
da4e2976
 			if ((*val2str)(_c, &(_v[i]), tmp_buf, &tmp_len) < 0) {
 				LM_ERR("Error while converting value to string\n");
 				return -1;
 			}
ffbe9c2c
 			ret = snprintf(_b + len, _l - len, "%s%.*s%s&%.*s=%.*s", CON_TQUOTESZ(_c),
 					_k[i]->len, _k[i]->s, CON_TQUOTESZ(_c), tmp_len, tmp_buf, tmp_len, tmp_buf);
29ec8148
 			if (ret < 0 || ret >= (_l - len)) goto error;
 			len += ret;
 		} else {
da4e2976
 			if (_o) {
ffbe9c2c
 				ret = snprintf(_b + len, _l - len, "%s%.*s%s%s", CON_TQUOTESZ(_c),
 						_k[i]->len, _k[i]->s, CON_TQUOTESZ(_c), _o[i]);
da4e2976
 				if (ret < 0 || ret >= (_l - len)) goto error;
 				len += ret;
 			} else {
ffbe9c2c
 				ret = snprintf(_b + len, _l - len, "%s%.*s%s=", CON_TQUOTESZ(_c),
 						_k[i]->len, _k[i]->s, CON_TQUOTESZ(_c));
da4e2976
 				if (ret < 0 || ret >= (_l - len)) goto error;
 				len += ret;
 			}
 			l = _l - len;
 			if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) {
 				LM_ERR("Error while converting value to string\n");
 				return -1;
 			}
 			len += l;
d5b80544
 		}
da4e2976
 
29ec8148
 		if (i != (_n - 1)) {
 			ret = snprintf(_b + len, _l - len, " AND ");
 			if (ret < 0 || ret >= (_l - len)) goto error;
 			len += ret;
 		}
 	}
 	return len;
 
b3da13a1
 error:
f001ee7d
 	LM_ERR("Error in snprintf\n");
29ec8148
 	return -1;
 }
 
 
 /*
  * Print set clause of update SQL statement
  */
a5071c50
 int db_print_set(const db1_con_t* _c, char* _b, const int _l, const db_key_t* _k,
 	const db_val_t* _v, const int _n, int (*val2str)(const db1_con_t*,
bc4bf265
 	const db_val_t*,char*, int*))
29ec8148
 {
bc4bf265
 	int i, l, ret, len = 0;
29ec8148
 
 	if (!_c || !_b || !_l || !_k || !_v || !_n) {
f001ee7d
 		LM_ERR("Invalid parameter value\n");
29ec8148
 		return -1;
 	}
 
 	for(i = 0; i < _n; i++) {
ffbe9c2c
 		ret = snprintf(_b + len, _l - len, "%s%.*s%s=",
 				CON_TQUOTESZ(_c), _k[i]->len, _k[i]->s, CON_TQUOTESZ(_c));
29ec8148
 		if (ret < 0 || ret >= (_l - len)) goto error;
 		len += ret;
 
 		l = _l - len;
d5b80544
 		if ( (*val2str)(_c, &(_v[i]), _b + len, &l) < 0) {
 			LM_ERR("Error while converting value to string\n");
 			return -1;
 		}
29ec8148
 		len += l;
 		if (i != (_n - 1)) {
 			if ((_l - len) >= 1) {
 				*(_b + len++) = ',';
 			}
 		}
 	}
 	return len;
 
b3da13a1
 error:
f001ee7d
 	LM_ERR("Error in snprintf\n");
29ec8148
 	return -1;
 }
ed78c096
 
 /*
  * Convert db_val to pv_spec
  */
 int db_val2pv_spec(struct sip_msg* msg, db_val_t *dbval, pv_spec_t *pvs)
 {
 	pv_value_t pv;
fa71c181
 #define LL_LEN 21   /* sign, 19 digits and \0 */
 	static char ll_buf[LL_LEN];
ed78c096
 
 	if(dbval->nul)
 	{
 		pv.flags = PV_VAL_NULL;
 	} else
 	{
 		switch(dbval->type)
 		{
 			case DB1_STRING:
 				pv.flags = PV_VAL_STR;
07b830d4
 				pv.rs.s = (char*)dbval->val.string_val;
 				pv.rs.len = strlen(pv.rs.s);
ed78c096
 			break;
 			case DB1_STR:
 				pv.flags = PV_VAL_STR;
07b830d4
 				pv.rs.s = (char*)dbval->val.str_val.s;
 				pv.rs.len = dbval->val.str_val.len;
ed78c096
 			break;
 			case DB1_BLOB:
 				pv.flags = PV_VAL_STR;
07b830d4
 				pv.rs.s = (char*)dbval->val.blob_val.s;
 				pv.rs.len = dbval->val.blob_val.len;
ed78c096
 			break;
 			case DB1_INT:
 				pv.flags = PV_VAL_INT | PV_TYPE_INT;
 				pv.ri = (int)dbval->val.int_val;
 			break;
 			case DB1_DATETIME:
 				pv.flags = PV_VAL_INT | PV_TYPE_INT;
 				pv.ri = (int)dbval->val.time_val;
 			break;
 			case DB1_BITMAP:
 				pv.flags = PV_VAL_INT | PV_TYPE_INT;
 				pv.ri = (int)dbval->val.bitmap_val;
 			break;
 			case DB1_BIGINT:
 				/* BIGINT is stored as string */
 				pv.flags = PV_VAL_STR;
fa71c181
 				pv.rs.len = LL_LEN;
07b830d4
 				db_longlong2str(dbval->val.ll_val, ll_buf, &pv.rs.len);
 				pv.rs.s = ll_buf;
58e81b6c
 				/* if it fits, also store as 32 bit integer*/
bdb1e938
 				if (! ((unsigned long long)dbval->val.ll_val & 0xffffffff80000000ULL)) {
58e81b6c
 					pv.flags |= PV_VAL_INT | PV_TYPE_INT;
 					pv.ri = (int)dbval->val.ll_val;
 				}
ed78c096
 			break;
 			default:
 				LM_NOTICE("unknown field type: %d, setting value to null\n",
b3da13a1
 							dbval->type);
ed78c096
 				pv.flags = PV_VAL_NULL;
 		}
 	}
 
 	/* null values are ignored for avp type PV */
 	if (pv.flags == PV_VAL_NULL && pvs->type == PVT_AVP)
 		return 0;
 
 	/* add value to result pv */
 	if (pv_set_spec_value(msg, pvs, 0, &pv) != 0)
 	{
 		LM_ERR("Failed to add value to spec\n");
 		return -1;
 	}
 
 	return 0;
 }