commit 09d0be81bbc95caf323b2ce3da8bf4f1ee8aa9d4 Author: Natanael Copa Date: Tue Aug 24 13:13:34 2010 +0000 add support for sqlite diff --git a/modules_k/db_sqlite/Makefile b/modules_k/db_sqlite/Makefile new file mode 100644 index 0000000..9a75593 --- /dev/null +++ b/modules_k/db_sqlite/Makefile @@ -0,0 +1,15 @@ +# $Id$ +# +# WARNING: do not run this directly, it should be run by the master Makefile + +include ../../Makefile.defs +auto_gen= +NAME=db_sqlite.so + +LIBS +=-lsqlite3 + +DEFS+=-DOPENSER_MOD_INTERFACE + +SERLIBPATH=../../lib +SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1 +include ../../Makefile.modules diff --git a/modules_k/db_sqlite/db_sqlite.c b/modules_k/db_sqlite/db_sqlite.c new file mode 100644 index 0000000..1ec1267 --- /dev/null +++ b/modules_k/db_sqlite/db_sqlite.c @@ -0,0 +1,92 @@ +/* + * $Id$ + * + * SQlite module interface + * + * Copyright (C) 2010 Timo Teräs + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * 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 + * + * Kamailio is distributed in the hope that it will be useful, + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "../../sr_module.h" +#include "../../lib/srdb1/db.h" +#include "dbase.h" + +MODULE_VERSION + +static int sqlite_bind_api(db_func_t *dbb) +{ + if(dbb==NULL) + return -1; + + memset(dbb, 0, sizeof(db_func_t)); + + dbb->use_table = db_sqlite_use_table; + dbb->init = db_sqlite_init; + dbb->close = db_sqlite_close; + dbb->free_result = db_sqlite_free_result; + dbb->query = db_sqlite_query; + dbb->insert = db_sqlite_insert; + dbb->delete = db_sqlite_delete; + dbb->update = db_sqlite_update; +#if 0 + dbb->raw_query = db_oracle_raw_query; +#endif + + return 0; +} + +static cmd_export_t cmds[] = { + {"db_bind_api", (cmd_function)sqlite_bind_api, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +static int sqlite_mod_init(void) +{ + sqlite3_initialize(); + + LM_INFO("SQlite library version %s (compiled using %s)\n", + sqlite3_libversion(), + SQLITE_VERSION); + return 0; +} + + +static void sqlite_mod_destroy(void) +{ + LM_INFO("SQlite terminate\n"); + + sqlite3_shutdown(); +} + +struct module_exports exports = { + "db_sqlite", + DEFAULT_DLFLAGS, /* dlopen flags */ + cmds, /* module commands */ + 0, /* module parameters */ + 0, /* exported statistics */ + 0, /* exported MI functions */ + 0, /* exported pseudo-variables */ + 0, /* extra processes */ + sqlite_mod_init, /* module initialization function */ + 0, /* response function*/ + sqlite_mod_destroy, /* destroy function */ + 0 /* per-child init function */ +}; diff --git a/modules_k/db_sqlite/dbase.c b/modules_k/db_sqlite/dbase.c new file mode 100644 index 0000000..9ae1bdb --- /dev/null +++ b/modules_k/db_sqlite/dbase.c @@ -0,0 +1,504 @@ +/* + * $Id$ + * + * SQlite module core functions + * + * Copyright (C) 2010 Timo Teräs + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * 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 + * + * Kamailio is distributed in the hope that it will be useful, + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../../mem/mem.h" +#include "../../dprint.h" +#include "../../lib/srdb1/db_pool.h" +#include "../../lib/srdb1/db_ut.h" +#include "../../lib/srdb1/db_res.h" +#include "../../lib/srdb1/db_query.h" +#include "dbase.h" + +static time_t sqlite_to_timet(double rT) +{ + return 86400.0*(rT - 2440587.5) + 0.5; +} + +static double timet_to_sqlite(time_t t) +{ + return ((((double) t) - 0.5) / 86400.0) + 2440587.5; +} + +/* + * Initialize database module + * No function should be called before this + */ + +static struct sqlite_connection * db_sqlite_new_connection(const struct db_id* id) +{ + struct sqlite_connection *con; + int rc; + + con = pkg_malloc(sizeof(*con)); + if (!con) { + LM_ERR("failed to allocate driver connection\n"); + return NULL; + } + + memset(con, 0, sizeof(*con)); + con->hdr.ref = 1; + con->hdr.id = (struct db_id*) id; /* set here - freed on error */ + + rc = sqlite3_open_v2(id->database, &con->conn, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + if (rc != SQLITE_OK) { + pkg_free(con); + LM_ERR("failed to open sqlite database '%s'\n", id->database); + return NULL; + } + + return con; +} + +db1_con_t* db_sqlite_init(const str* _url) +{ + return db_do_init(_url, (void *) db_sqlite_new_connection); +} + + +/* + * Shut down database module + * No function should be called after this + */ + +static void db_sqlite_free_connection(struct sqlite_connection* con) +{ + if (!con) return; + + sqlite3_close(con->conn); + free_db_id(con->hdr.id); + pkg_free(con); +} + +void db_sqlite_close(db1_con_t* _h) +{ + db_do_close(_h, db_sqlite_free_connection); +} + +/* + * Release a result set from memory + */ +int db_sqlite_free_result(db1_con_t* _h, db1_res_t* _r) +{ + if (!_h || !_r) { + LM_ERR("invalid parameter value\n"); + return -1; + } + + if (db_free_result(_r) < 0) + { + LM_ERR("failed to free result structure\n"); + return -1; + } + return 0; +} + +/* + * Store name of table that will be used by + * subsequent database functions + */ +int db_sqlite_use_table(db1_con_t* _h, const str* _t) +{ + return db_use_table(_h, _t); +} + +/* + * Reset query context + */ +static void db_sqlite_cleanup_query(const db1_con_t* _c) +{ + struct sqlite_connection *conn = CON_SQLITE(_c); + int rc; + + if (conn->stmt != NULL) { + rc = sqlite3_finalize(conn->stmt); + if (rc != SQLITE_OK) + LM_ERR("finalize failed: %s\n", + sqlite3_errmsg(conn->conn)); + } + + conn->stmt = NULL; + conn->bindpos = 0; +} + +/* + * Convert value to sql-string as db bind index + */ +static int db_sqlite_val2str(const db1_con_t* _c, const db_val_t* _v, char* _s, int* _len) +{ + struct sqlite_connection *conn; + int ret; + + if (!_c || !_v || !_s || !_len || *_len <= 0) { + LM_ERR("invalid parameter value\n"); + return -1; + } + + conn = CON_SQLITE(_c); + if (conn->bindpos >= DB_SQLITE_MAX_BINDS) { + LM_ERR("too many bindings, recompile with larger DB_SQLITE_MAX_BINDS\n"); + return -2; + } + + conn->bindarg[conn->bindpos] = _v; + ret = snprintf(_s, *_len, "?%u", ++conn->bindpos); + if ((unsigned)ret >= (unsigned) *_len) + return -11; + + *_len = ret; + return 0; +} + +/* + * Send an SQL query to the server + */ +static int db_sqlite_submit_query(const db1_con_t* _h, const str* _s) +{ + struct sqlite_connection *conn = CON_SQLITE(_h); + sqlite3_stmt *stmt; + const db_val_t *val; + int rc, i; + + LM_DBG("submit_query: %.*s\n", _s->len, _s->s); + + rc = sqlite3_prepare_v2(conn->conn, _s->s, _s->len, &stmt, NULL); + if (rc != SQLITE_OK) { + LM_ERR("failed to prepare statement: %s\n", + sqlite3_errmsg(conn->conn)); + return -1; + } + conn->stmt = stmt; + + for (i = 1; i <= conn->bindpos; i++) { + val = conn->bindarg[i-1]; + if (VAL_NULL(val)) { + rc = sqlite3_bind_null(stmt, i); + } else switch (VAL_TYPE(val)) { + case DB1_INT: + rc = sqlite3_bind_int(stmt, i, VAL_INT(val)); + break; + case DB1_BIGINT: + rc = sqlite3_bind_int64(stmt, i, VAL_BIGINT(val)); + break; + case DB1_DOUBLE: + rc = sqlite3_bind_double(stmt, i, VAL_DOUBLE(val)); + break; + case DB1_STRING: + rc = sqlite3_bind_text(stmt, i, + VAL_STRING(val), -1, NULL); + break; + case DB1_STR: + rc = sqlite3_bind_text(stmt, i, + VAL_STR(val).s, VAL_STR(val).len, NULL); + break; + case DB1_DATETIME: + rc = sqlite3_bind_double(stmt, i, timet_to_sqlite(VAL_TIME(val))); + break; + case DB1_BLOB: + rc = sqlite3_bind_blob(stmt, i, + VAL_BLOB(val).s, VAL_BLOB(val).len, + NULL); + break; + case DB1_BITMAP: + rc = sqlite3_bind_int(stmt, i, VAL_BITMAP(val)); + break; + default: + LM_ERR("unknown bind value type %d\n", VAL_TYPE(val)); + return -1; + } + if (rc != SQLITE_OK) { + LM_ERR("Parameter bind failed: %s\n", + sqlite3_errmsg(conn->conn)); + return -1; + } + } + + return 0; +} + +static int decltype_to_dbtype(const char *decltype) +{ + /* SQlite3 has dynamic typing. It does not store the actual + * exact type, instead it uses 'affinity' depending on the + * value. We have to go through the declaration types to see + * what to return. */ + if (strstr(decltype, "INT") != NULL || + strncasecmp(decltype, "SERIAL", 6) == 0) + return DB1_INT; + if (strstr(decltype, "CHAR") != NULL || + strstr(decltype, "TEXT") != NULL) + return DB1_STR; + if (strstr(decltype, "REAL") != NULL || + strstr(decltype, "FLOA") != NULL || + strstr(decltype, "DOUB") != NULL) + return DB1_DOUBLE; + if (strstr(decltype, "BLOB") != NULL) + return DB1_BLOB; + if (strncasecmp(decltype, "TIME", 4) == 0 || + strncasecmp(decltype, "DATE", 4) == 0) + return DB1_DATETIME; + + LM_ERR("sqlite decltype '%s' not recognized, defaulting to int", + decltype); + return DB1_INT; +} + +static str* str_dup(const char *_s) +{ + str *s; + int len = strlen(_s); + + s = (str*) pkg_malloc(sizeof(str)+len+1); + s->len = len; + s->s = ((char*)s) + sizeof(str); + memcpy(s->s, _s, len); + s->s[len] = '\0'; + + return s; +} + +static void str_assign(str* s, const char *_s, int len) +{ + s->s = (char *) pkg_malloc(len); + s->len = len; + memcpy(s->s, _s, len); +} + +/* + * Read database answer and fill the structure + */ +int db_sqlite_store_result(const db1_con_t* _h, db1_res_t** _r) +{ + struct sqlite_connection *conn = CON_SQLITE(_h); + db1_res_t *res; + int i, rc, num_rows = 0, num_alloc = 0; + db_row_t *rows = NULL, *row; + db_val_t *val; + + res = db_new_result(); + if (res == NULL) + goto no_mem; + + while (1) { + rc = sqlite3_step(conn->stmt); + if (rc == SQLITE_DONE) { + *_r = res; + return 0; + } + if (rc != SQLITE_ROW) { + LM_INFO("sqlite3_step failed: %s\n", sqlite3_errmsg(conn->conn)); + goto err; + } + if (num_rows == 0) { + /* get column types */ + rc = sqlite3_column_count(conn->stmt); + if (db_allocate_columns(res, rc) != 0) + goto err; + RES_COL_N(res) = rc; + + for (i = 0; i < RES_COL_N(res); i++) { + RES_NAMES(res)[i] = str_dup(sqlite3_column_name(conn->stmt, i)); + RES_TYPES(res)[i] = decltype_to_dbtype(sqlite3_column_decltype(conn->stmt, i)); + } + } + if (num_rows >= num_alloc) { + if (num_alloc) + num_alloc *= 2; + else + num_alloc = 8; + rows = pkg_realloc(rows, sizeof(db_row_t) * num_alloc); + if (rows == NULL) + goto no_mem; + RES_ROWS(res) = rows; + } + + row = &RES_ROWS(res)[num_rows]; + num_rows++; + RES_ROW_N(res) = num_rows; /* rows in this result set */ + RES_NUM_ROWS(res) = num_rows; /* rows in total */ + + if (db_allocate_row(res, row) != 0) + goto no_mem; + + for (i = 0, val = ROW_VALUES(row); i < RES_COL_N(res); i++, val++) { + VAL_TYPE(val) = RES_TYPES(res)[i]; + VAL_NULL(val) = 0; + VAL_FREE(val) = 0; + if (sqlite3_column_type(conn->stmt, i) == SQLITE_NULL) { + VAL_NULL(val) = 1; + } else switch (VAL_TYPE(val)) { + case DB1_INT: + VAL_INT(val) = sqlite3_column_int(conn->stmt, i); + break; + case DB1_BIGINT: + VAL_BIGINT(val) = sqlite3_column_int64(conn->stmt, i); + break; + case DB1_STR: + str_assign(&VAL_STR(val), + (const char*) sqlite3_column_text(conn->stmt, i), + sqlite3_column_bytes(conn->stmt, i)); + VAL_FREE(val) = 1; + break; + case DB1_DOUBLE: + VAL_DOUBLE(val) = sqlite3_column_double(conn->stmt, i); + break; + case DB1_DATETIME: + VAL_TIME(val) = sqlite_to_timet(sqlite3_column_double(conn->stmt, i)); + break; + case DB1_BLOB: + str_assign(&VAL_BLOB(val), + (const char*) sqlite3_column_blob(conn->stmt, i), + sqlite3_column_bytes(conn->stmt, i)); + VAL_FREE(val) = 1; + break; + default: + LM_ERR("unhandled db-type\n"); + goto err; + } + } + } + +no_mem: + LM_ERR("no private memory left\n"); +err: + if (res) + db_free_result(res); + return -1; +} + +/* + * Query table for specified rows + * _h: structure representing database connection + * _k: key names + * _op: operators + * _v: values of the keys that must match + * _c: column names to return + * _n: number of key=values pairs to compare + * _nc: number of columns to return + * _o: order by the specified column + */ +int db_sqlite_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op, + const db_val_t* _v, const db_key_t* _c, int _n, int _nc, + const db_key_t _o, db1_res_t** _r) +{ + int rc; + + rc = db_do_query(_h, _k, _op, _v, _c, _n, _nc, _o, _r, + db_sqlite_val2str, + db_sqlite_submit_query, + db_sqlite_store_result); + db_sqlite_cleanup_query(_h); + + return rc; +} + +static int db_sqlite_commit(const db1_con_t* _h) +{ + struct sqlite_connection *conn = CON_SQLITE(_h); + int rc; + + rc = sqlite3_step(conn->stmt); + if (rc != SQLITE_DONE && rc != SQLITE_OK) { + LM_ERR("sqlite commit failed: %s\n", + sqlite3_errmsg(conn->conn)); + return -1; + } + + return 0; +} + +/* + * Insert a row into specified table + * _h: structure representing database connection + * _k: key names + * _v: values of the keys + * _n: number of key=value pairs + */ +int db_sqlite_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t* _v, + int _n) +{ + int rc = -1; + + rc = db_do_insert(_h, _k, _v, _n, + db_sqlite_val2str, + db_sqlite_submit_query); + if (rc == 0) + rc = db_sqlite_commit(_h); + db_sqlite_cleanup_query(_h); + + return rc; +} + + +/* + * Delete a row from the specified table + * _h: structure representing database connection + * _k: key names + * _o: operators + * _v: values of the keys that must match + * _n: number of key=value pairs + */ +int db_sqlite_delete(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o, + const db_val_t* _v, int _n) +{ + int rc; + + rc = db_do_delete(_h, _k, _o, _v, _n, + db_sqlite_val2str, + db_sqlite_submit_query); + if (rc == 0) + rc = db_sqlite_commit(_h); + db_sqlite_cleanup_query(_h); + + return rc; +} + + +/* + * Update some rows in the specified table + * _h: structure representing database connection + * _k: key names + * _o: operators + * _v: values of the keys that must match + * _uk: updated columns + * _uv: updated values of the columns + * _n: number of key=value pairs + * _un: number of columns to update + */ +int db_sqlite_update(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o, + const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, + int _n, int _un) +{ + int rc; + + rc = db_do_update(_h, _k, _o, _v, _uk, _uv, _n, _un, + db_sqlite_val2str, + db_sqlite_submit_query); + if (rc == 0) + rc = db_sqlite_commit(_h); + db_sqlite_cleanup_query(_h); + + return rc; +} + diff --git a/modules_k/db_sqlite/dbase.h b/modules_k/db_sqlite/dbase.h new file mode 100644 index 0000000..d1e57f4 --- /dev/null +++ b/modules_k/db_sqlite/dbase.h @@ -0,0 +1,70 @@ +/* + * $Id$ + * + * SQlite module core functions + * + * Copyright (C) 2010 Timo Teräs + * + * This file is part of Kamailio, a free SIP server. + * + * Kamailio is free software; you can redistribute it and/or modify + * 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 + * + * Kamailio is distributed in the hope that it will be useful, + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef DBASE_H +#define DBASE_H + +#include + +#include "../../lib/srdb1/db_pool.h" +#include "../../lib/srdb1/db_con.h" +#include "../../lib/srdb1/db_res.h" +#include "../../lib/srdb1/db_key.h" +#include "../../lib/srdb1/db_op.h" +#include "../../lib/srdb1/db_val.h" + +#define DB_SQLITE_MAX_BINDS 64 + +struct sqlite_connection { + struct pool_con hdr; + + sqlite3 *conn; + int bindpos; + + sqlite3_stmt *stmt; + const db_val_t *bindarg[DB_SQLITE_MAX_BINDS]; +}; + +#define CON_SQLITE(db_con) ((struct sqlite_connection *) db_con->tail) + +db1_con_t* db_sqlite_init(const str* _sqlurl); +void db_sqlite_close(db1_con_t* _h); + +int db_sqlite_free_result(db1_con_t* _h, db1_res_t* _r); + +int db_sqlite_query(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _op, + const db_val_t* _v, const db_key_t* _c, int _n, int _nc, + const db_key_t _o, db1_res_t** _r); +int db_sqlite_insert(const db1_con_t* _h, const db_key_t* _k, const db_val_t* _v, + int _n); +int db_sqlite_delete(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o, + const db_val_t* _v, int _n); +int db_sqlite_update(const db1_con_t* _h, const db_key_t* _k, const db_op_t* _o, + const db_val_t* _v, const db_key_t* _uk, const db_val_t* _uv, + int _n, int _un); +int db_sqlite_use_table(db1_con_t* _h, const str* _t); + + +#endif /* DBASE_H */