/* * Core functions. * See db/db.h for description. * * @todo add paranoid checks for sql statement buffer overflow, and protect * the checks with macro which is switched off by default. * * Copyright (C) 2005 RingCentral Inc. * Created by Dmitry Semyonov * * * This file is part of ser, a free SIP server. * * ser 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 * * For a license to use the ser software under conditions * other than those described here, or to purchase support for this * software, please contact iptel.org by e-mail at the following addresses: * info@iptel.org * * ser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../../mem/mem.h" #include "../../dprint.h" #include "../../ut.h" #include "common.h" #include "dbase.h" #include "utils.h" #include "prepare.h" static ora_param_t ora_params[ORA_PARAMS_MAX]; static sb2 ora_bind_inds[ORA_BINDS_MAX]; static int bind_values(db_con_t* _h, db_val_t* _v, int _n, int bound_count); static int define_params(db_con_t* _h, int _nc); static void free_params(void); static int map_int_param_type_and_size_to_ext(db_con_t* _h, dvoid* p_param, int ora_param_num); static int init_db_res(db_res_t** _r, int params_num); static int convert_row(int params_num, db_res_t* _r); static db_type_t sqlt_to_db_type(ub2 sqlt); static int oci_prepare(db_con_t *_h); static int oci_execute(db_con_t *_h, ub4 iters); #if !DB_PREALLOC_SQLT_HANDLE static int oci_cleanup(db_con_t *_h); #endif int db_use_table(db_con_t* _h, const char* _t) { if (_h == NULL || _t == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s(%s)\n", __FUNCTION__, _t); CON_TABLE(_h) = _t; /* XXX: shouldn't we copy the string here instead? */ return 0; } db_con_t* db_init(const char* _sqlurl) { db_con_t *p_db_con = NULL; ora_con_t *p_ora_con = NULL; sword rc = OCI_ERROR; int res = -1; char sz_user[32]; char sz_passwd[32]; char sz_db[32]; LOG(L_ALERT, "WARNING! This module is experimental and may crash SER " "or create unexpected results. You use the module at your own " "risk. Please submit bugs at http://bugs.sip-router.org/\n"); if (_sqlurl == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); goto db_init_err; } DBG("%s(%s)\n", __FUNCTION__, _sqlurl); if ((res = parse_sql_url(_sqlurl, sz_user, sz_passwd, sz_db)) < 0) { ERR("%s: Error %d while parsing %s\n", __FUNCTION__, res, _sqlurl); goto db_init_err; } p_db_con = pkg_malloc(sizeof(db_con_t)); p_ora_con = pkg_malloc(sizeof(ora_con_t)); if (p_db_con == NULL || p_ora_con == NULL) { ERR("%s: no memory\n", __FUNCTION__); goto db_init_err; } memset(p_db_con, 0, sizeof(db_con_t)); memset(p_ora_con, 0, sizeof(ora_con_t)); CON_TABLE(p_db_con) = NULL; /* CON_ORA() could not be used as lvalue due to deprecated lvalue casts. */ CON_TAIL(p_db_con) = (unsigned long)p_ora_con; rc = OCIEnvCreate(&(p_ora_con->env.ptr), OCI_DEFAULT, NULL, NULL, NULL, NULL, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->err.dvoid_ptr), OCI_HTYPE_ERROR, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->err.ptr, rc); goto db_init_err; } rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->svc.dvoid_ptr), OCI_HTYPE_SVCCTX, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } rc = OCILogon(p_ora_con->env.ptr, p_ora_con->err.ptr, &(p_ora_con->svc.ptr), (OraText *)sz_user, strlen(sz_user), (OraText *)sz_passwd, strlen(sz_passwd), (OraText *)sz_db, strlen(sz_db)); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->err.ptr, rc); goto db_init_err; } #if DB_PREALLOC_SQLT_HANDLE rc = OCIHandleAlloc(p_ora_con->env.ptr, &(p_ora_con->stmt.dvoid_ptr), OCI_HTYPE_STMT, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(p_ora_con->env.ptr, rc); goto db_init_err; } #endif return p_db_con; db_init_err: #if DB_PREALLOC_SQLT_HANDLE if (p_ora_con->stmt.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->stmt.ptr, OCI_HTYPE_STMT)); #endif if (p_ora_con->svc.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->svc.ptr, OCI_HTYPE_SVCCTX)); if (p_ora_con->err.ptr != NULL) OCICHECK(p_ora_con->env.ptr, OCIHandleFree(p_ora_con->err.ptr, OCI_HTYPE_ERROR)); if (p_ora_con) { pkg_free(p_ora_con); p_ora_con = NULL; } if (p_db_con) { pkg_free(p_db_con); p_db_con = NULL; } ERR("%s failed\n", __FUNCTION__); return NULL; } void db_close(db_con_t* _h) { sword rc = OCI_ERROR; if (_h == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); return; } DBG("%s\n", __FUNCTION__); #if DB_PREALLOC_SQLT_HANDLE rc = OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } #endif rc = OCILogoff(CON_ORA(_h)->svc.ptr, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); } /* Note: svc.ptr handle was released by OCILogoff() function. */ rc = OCIHandleFree(CON_ORA(_h)->err.ptr, OCI_HTYPE_ERROR); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } rc = OCITerminate(OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); } if (CON_ORA(_h) != NULL) { pkg_free(CON_ORA(_h)); CON_TAIL(_h) = (unsigned long)NULL; } if (_h != NULL) { pkg_free(_h); _h = NULL; } } int db_query(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _c, int _n, int _nc, db_key_t _o, db_res_t** _r) { sword rc = OCI_ERROR; int params_num; *_r = NULL; /* Required for processing under err_cleanup label, and simply to be on the safe side. */ if (_h == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } if (ora_params[0].p_data != NULL) { ERR("%s: Nested calls to db_query() are not allowed!\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_select(_c, _nc); prepare_from(CON_TABLE(_h)); prepare_where(_k, _op, _v, _n); prepare_order_by(_o); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) return -1; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 0); if (rc != 0) goto err_cleanup; params_num = define_params(_h, _nc); if (params_num <= 0) { rc = -1; goto err_cleanup; } rc = init_db_res(_r, params_num); if (rc != 0) goto err_cleanup; while ((rc = OCIStmtFetch(CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, 1, 0, 0)) == OCI_SUCCESS) { rc = convert_row(params_num, *_r); if (rc != 0) goto err_cleanup; } if (rc != OCI_SUCCESS && rc != OCI_NO_DATA) { OCICHECK(CON_ORA(_h)->err.ptr, rc); rc = -1; goto err_cleanup; } else { rc = 0; } if (RES_ROW_N(*_r) == 0) { DBG("%s: no data\n", __FUNCTION__); } err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif if (rc != 0) { (void)db_free_result(_h, *_r); return -1; } else { return 0; } } int db_raw_query(db_con_t* _h, char* _s, db_res_t** _r) { ERR("%s: unimplemented\n", __FUNCTION__); *_r = NULL; /* Just in case */ return -1; } int db_free_result(db_con_t* _h, db_res_t* _r) { db_row_t *p_row; db_val_t *p_val; if (_h == NULL) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); free_params(); if (_r == NULL) return 0; /* Nothing else to free. */ if (RES_ROW_N(_r) < 0) { ERR("%s: invalid RES_ROW_N(_r) value!\n", __FUNCTION__); } p_row = RES_ROWS(_r); while (RES_ROW_N(_r)-- > 0) { p_val = ROW_VALUES(p_row); while (ROW_N(p_row)--) { if (VAL_NULL(p_val) == 0) { if (VAL_TYPE(p_val) == DB_STRING && VAL_STRING(p_val) != NULL) { pkg_free((void *)VAL_STRING(p_val)); /* XXX Get rid of cast */ VAL_STRING(p_val) = NULL; DBG("%s: DB_STRING memory released\n", __FUNCTION__); } else if (VAL_TYPE(p_val) == DB_BLOB && VAL_BLOB(p_val).s != NULL) { pkg_free(VAL_BLOB(p_val).s); VAL_BLOB(p_val).s = NULL; DBG("%s: DB_BLOB memory released\n", __FUNCTION__); } else if (VAL_TYPE(p_val) == DB_STR && VAL_STR(p_val).s != NULL) { pkg_free(VAL_STR(p_val).s); VAL_STR(p_val).s = NULL; DBG("%s: DB_STR memory released\n", __FUNCTION__); } } else { DBG("%s: NULL value skipped\n", __FUNCTION__); } ++p_val; } pkg_free(ROW_VALUES(p_row)); ROW_VALUES(p_row) = NULL; DBG("%s: row memory released\n", __FUNCTION__); ++p_row; } if (RES_ROWS(_r) != NULL) { pkg_free(RES_ROWS(_r)); RES_ROWS(_r) = NULL; DBG("%s: rows memory released\n", __FUNCTION__); } if (RES_TYPES(_r) != NULL) { pkg_free(RES_TYPES(_r)); RES_TYPES(_r) = NULL; DBG("%s: types memory released\n", __FUNCTION__); } if (RES_NAMES(_r) != NULL) { pkg_free(RES_NAMES(_r)); RES_NAMES(_r) = NULL; DBG("%s: names memory released\n", __FUNCTION__); } return 0; } int db_insert(db_con_t* _h, db_key_t* _k, db_val_t* _v, int _n) { sword rc; if (_h == NULL || _k == NULL || _v == NULL || _n <= 0) { ERR("%s: invalid (NULL) argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_insert(CON_TABLE(_h)); prepare_insert_columns(_k, _n); prepare_insert_values(_v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } int db_delete(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, int _n) { sword rc; if (_h == NULL) { ERR("%s: invalid (NULL) argument\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_delete(CON_TABLE(_h)); prepare_where(_k, _op, _v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _v, _n, 0); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } int db_update(db_con_t* _h, db_key_t* _k, db_op_t* _op, db_val_t* _v, db_key_t* _uk, db_val_t* _uv, int _n, int _un) { sword rc; if (_h == NULL || _uk == NULL || _uv == NULL || _un <= 0) { ERR("%s: invalid argument(s)\n", __FUNCTION__); return -1; } DBG("%s\n", __FUNCTION__); prepare_update(CON_TABLE(_h)); prepare_update_set(_uk, _uv, _un); prepare_where(_k, _op, _v, _n); DBG("%.*s\n", prepared_sql_len(), prepared_sql()); rc = oci_prepare(_h); if (rc != 0) goto err_cleanup; rc = bind_values(_h, _uv, _un, 0); if (rc < 0) goto err_cleanup; rc = bind_values(_h, _v, _n, rc); if (rc < 0) goto err_cleanup; rc = oci_execute(_h, 1); if (rc != 0) goto err_cleanup; err_cleanup: #if !DB_PREALLOC_SQLT_HANDLE (void)oci_cleanup(_h); #endif return rc != 0 ? -1 : 0; } /* Return: number of bound values (it could be different from _n parameter * due to null values), or -1 in case of failure. */ static int bind_values(db_con_t* _h, db_val_t* _v, int _n, int bound_count) { sword rc; int i; OCIBind *p_bnd; dvoid *p_bind_val; sb4 bind_val_sz; ub2 bind_val_type; /* XXX Will fail if more than single date is inserted! * It is necessary to allocate them dynamically! */ static unsigned char ora_date[7] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0' }; /* Oracle date format */ for (i = 0; i < _n; ++i, ++_v) { if (VAL_NULL(_v)) { DBG("%s: (null)\n", __FUNCTION__); /* 'null' is used in queries. So, nothing to bind. But we should keep correct index and 'for' condition. */ --i; --_n; continue; } else { ora_bind_inds[i] = OCI_IND_NOTNULL; switch (VAL_TYPE(_v)) { case DB_INT: p_bind_val = &VAL_INT(_v); bind_val_sz = sizeof(VAL_INT(_v)); bind_val_type = SQLT_INT; DBG("%s: DB_INT - %d\n", __FUNCTION__, VAL_INT(_v)); break; case DB_DOUBLE: p_bind_val = &VAL_DOUBLE(_v); bind_val_sz = sizeof(VAL_DOUBLE(_v)); bind_val_type = SQLT_FLT; DBG("%s: DB_DOUBLE - %e\n", __FUNCTION__, VAL_DOUBLE(_v)); break; case DB_STRING: p_bind_val = &VAL_STRING(_v); bind_val_sz = strlen(VAL_STRING(_v)); /* XXX length should not exceed 4000 bytes here */ bind_val_type = SQLT_CHR; DBG("%s: DB_STRING - %s\n", __FUNCTION__, VAL_STRING(_v)); break; case DB_STR: p_bind_val = VAL_STR(_v).s; bind_val_sz = VAL_STR(_v).len; /* XXX length should not exceed 4000 bytes here */ bind_val_type = SQLT_CHR; DBG("%s: DB_STR - %.*s\n", __FUNCTION__, VAL_STR(_v).len, ZSW(VAL_STR(_v).s) ); break; case DB_DATETIME: { struct tm *p_tm = localtime(&VAL_TIME(_v)); if (p_tm == NULL) { ERR("%s: time_t -> struct tm conversion failure %ld\n", __FUNCTION__, VAL_TIME(_v)); return -1; } p_tm->tm_year += 1900; /* tm_year is stored as (Year - 1900) value. */ ora_date[0] = p_tm->tm_year / 100 + 100; ora_date[1] = p_tm->tm_year % 100 + 100; ora_date[2] = p_tm->tm_mon + 1; /* tm_mon is from [0-11] interval */ ora_date[3] = p_tm->tm_mday; ora_date[4] = p_tm->tm_hour + 1; ora_date[5] = p_tm->tm_min + 1; ora_date[6] = p_tm->tm_sec + 1; /* XXX: Should tm_isdst, etc. be processed? */ p_bind_val = ora_date; bind_val_sz = sizeof(ora_date); bind_val_type = SQLT_DAT; DBG("%s: DB_DATETIME - %s\n", __FUNCTION__, ctime(&VAL_TIME(_v))); break; } case DB_BLOB: p_bind_val = VAL_BLOB(_v).s; bind_val_sz = VAL_BLOB(_v).len; /* XXX length should not exceed 4000 bytes here (?) */ bind_val_type = SQLT_VCS; /* XXX review */ DBG("%s: DB_BLOB, length %d\n", __FUNCTION__, VAL_BLOB(_v).len); break; case DB_BITMAP: p_bind_val = &VAL_BITMAP(_v); bind_val_sz = sizeof(VAL_BITMAP(_v)); bind_val_type = SQLT_UIN; DBG("%s: DB_BITMAP - %#x\n", __FUNCTION__, VAL_BITMAP(_v)); break; default: ERR("%s: Unsupported DB value type - %d\n", __FUNCTION__, VAL_TYPE(_v)); return -1; } } p_bnd = NULL; /* XXX Not used so far */ rc = OCIBindByPos(CON_ORA(_h)->stmt.ptr, &p_bnd, CON_ORA(_h)->err.ptr, bound_count + i + 1, p_bind_val, bind_val_sz, bind_val_type, &ora_bind_inds[i], 0, 0, 0, 0, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } } return _n; } /* Used by db_query(). * free_params() must be called after the query is finished. * Return: number of parameters or -1 in case of failure; * (free_params() must be called in both cases). * @sa free_params() */ static int define_params(db_con_t* _h, int _nc) { OCIDefine *p_dfn = NULL; dvoid *p_param = NULL; int ora_params_num = 0; sword rc; #if DB_DEBUG int i = 0; for (; i < sizeof(ora_params) / sizeof(ora_params[0]); ++i) { if (ora_params[i].p_data != NULL) { ERR("%s: Internal logic is broken!\n", __FUNCTION__); return -1; } } #endif /* XXX handle case when _nc is defined separately to avoid extra call * to OCIParamGet() */ while ((rc = OCIParamGet(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT, CON_ORA(_h)->err.ptr, &p_param, ora_params_num + 1)) == OCI_SUCCESS) { if (ora_params_num == ORA_PARAMS_MAX) { /* XXX log more info */ ERR("%s: Too many table columns!\n", __FUNCTION__); return -1; } rc = map_int_param_type_and_size_to_ext(_h, p_param, ora_params_num); if (rc != 0) return -1; ora_params[ora_params_num].p_data = pkg_malloc(ora_params[ora_params_num].size); p_dfn = NULL; /* XXX currently unused */ rc = OCIDefineByPos(CON_ORA(_h)->stmt.ptr, &p_dfn, CON_ORA(_h)->err.ptr, ora_params_num + 1, ora_params[ora_params_num].p_data, ora_params[ora_params_num].size, ora_params[ora_params_num].type, &ora_params[ora_params_num].ind, 0, 0, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } ++ora_params_num; } if (rc != OCI_SUCCESS) { /* * OCI_ERROR + ORA-24334 are returned in case of invalid pos parameter of * OCIParamGet(). This is the only(?) way to handle 'select *' * statement while using non-scrollable cursor. */ if (OCICHECK(CON_ORA(_h)->err.ptr, rc) != 24334 || rc != OCI_ERROR) { return -1; } } DBG("%s: ora_params_num = %d\n", __FUNCTION__, ora_params_num); return ora_params_num; } /* A companion for define_params() function * @sa define_params() */ static void free_params(void) { int i = 0; int freed_count = 0; DBG("%s\n", __FUNCTION__); for (; i < sizeof(ora_params) / sizeof(ora_params[0]); ++i) { if (ora_params[i].p_data != NULL) { pkg_free(ora_params[i].p_data); ora_params[i].p_data = NULL; freed_count++; } } DBG("%s: %d parameter(s) freed\n", __FUNCTION__, freed_count); } /* * Called by define_params(). * Uses and updates global ora_params[ora_params_num]. * Defines the conversion rules that will be applied during fetching. * Don't forget to update sqlt_to_db_type() if output types are changed. */ static int map_int_param_type_and_size_to_ext(db_con_t* _h, dvoid* p_param, int ora_params_num) { sword rc; rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &ora_params[ora_params_num].type, 0, OCI_ATTR_DATA_TYPE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } DBG1("%s: Oracle type %s\n", __FUNCTION__, sqlt_to_str(ora_params[ora_params_num].type)); switch (ora_params[ora_params_num].type) { case SQLT_STR: case SQLT_VCS: case SQLT_CHR: { rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &ora_params[ora_params_num].size, 0, OCI_ATTR_DATA_SIZE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } DBG1("%s: SQLT_CHR/VCS/STR size %d\n", __FUNCTION__, ora_params[ora_params_num].size); ora_params[ora_params_num].type = SQLT_STR; break; } case SQLT_INT: ora_params[ora_params_num].size = sizeof(int); break; case SQLT_UIN: ora_params[ora_params_num].size = sizeof(unsigned int); break; case SQLT_FLT: ora_params[ora_params_num].size = sizeof(double); break; case SQLT_DATE: case SQLT_TIMESTAMP: case SQLT_TIMESTAMP_TZ: case SQLT_TIMESTAMP_LTZ: /* * Let OCI make a conversion of date types * to the one that we understand. */ ora_params[ora_params_num].type = SQLT_DAT; ora_params[ora_params_num].size = 7; break; case SQLT_DAT: ora_params[ora_params_num].size = 7; break; case SQLT_NUM: { sb2 precision; sb1 scale; rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &precision, 0, OCI_ATTR_PRECISION, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } rc = OCIAttrGet(p_param, OCI_DTYPE_PARAM, &scale, 0, OCI_ATTR_SCALE, CON_ORA(_h)->err.ptr); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } /* * Depending on precision and scale attributes we should decide * what external number type to use. */ DBG1("%s: SQLT_NUM precision %d, scale %d\n", __FUNCTION__, precision, scale); if (precision < 9 && scale == 0) { /* * MAX_INT (= 134217727) occupies 9 digits. So, maximum integer * number supported is 99 999 999 (8 digits). */ ora_params[ora_params_num].type = SQLT_INT; ora_params[ora_params_num].size = sizeof(int); } else { ora_params[ora_params_num].type = SQLT_FLT; ora_params[ora_params_num].size = sizeof(double); } break; } default: ERR("%s: Unsupported data type: %d!\n", __FUNCTION__, ora_params[ora_params_num].type); return -1; } DBG1("%s: External type %s, size %d\n", __FUNCTION__, sqlt_to_str(ora_params[ora_params_num].type), ora_params[ora_params_num].size); return 0; } /* @sa db_free_result() */ static int init_db_res(db_res_t** _r, int params_num) { static db_res_t res; int i; *_r = &res; RES_NAMES(*_r) = (db_key_t *)pkg_malloc(sizeof(db_key_t) * params_num); RES_TYPES(*_r) = (db_type_t *)pkg_malloc(sizeof(db_type_t) * params_num); if (RES_NAMES(*_r) == NULL || RES_TYPES(*_r) == NULL) { ERR("%s: No memory for %d bytes!\n", __FUNCTION__, (sizeof(db_key_t) + sizeof(db_type_t)) * params_num); if ( RES_NAMES(*_r) != NULL ) { pkg_free(RES_NAMES(*_r)); RES_NAMES(*_r) = NULL; } if ( RES_TYPES(*_r) != NULL ) { pkg_free(RES_TYPES(*_r)); RES_TYPES(*_r) = NULL; } return -1; } RES_COL_N(*_r) = params_num; RES_ROWS(*_r) = NULL; RES_ROW_N(*_r) = 0; for (i = 0; i < params_num; i++) { RES_NAMES(*_r)[i] = NULL; /* XXX unimplemented */ RES_TYPES(*_r)[i] = sqlt_to_db_type(ora_params[i].type); } return 0; } /* * Only limited subset of SQLT_* types is allowed! The subset comes from * map_int_param_type_and_size_to_ext() function */ static db_type_t sqlt_to_db_type(ub2 sqlt) { switch (sqlt) { case SQLT_STR: return DB_STR; case SQLT_INT: return DB_INT; case SQLT_UIN: return DB_BITMAP; case SQLT_FLT: return DB_DOUBLE; case SQLT_DAT: return DB_DATETIME; default: ERR("%s: Internal logic is broken!\n", __FUNCTION__); return -1; } } /* @sa db_free_result() */ static int convert_row(int params_num, db_res_t* _r) { static int allocated_rows; int i; db_val_t *p_val; ora_param_t *p_param; DBG("%s: params_num = %d, _r = %p\n", __FUNCTION__, params_num, _r); /* Detect initial invocation. */ if (RES_ROWS(_r) == NULL) { allocated_rows = 0; } /* * Since we don't know number of rows aforetime, we aggressively * pre-allocate them to avoid multiple memory re-allocations. * The exact pre-allocation algorithm should be adjusted according * to actual queries. Let's use factor 10 for the beginning. */ if (RES_ROW_N(_r) == allocated_rows) { void *tmp_ptr; allocated_rows *= 10; ++allocated_rows; /* Handling initial 0 value. */ /* * We can't assign directly to RES_ROWS(_r) here, since we don't want * to loose original pointer in case of realloc failure. */ tmp_ptr = pkg_realloc(RES_ROWS(_r), sizeof(db_row_t) * allocated_rows); if (tmp_ptr == NULL) { ERR("%s: No memory for %d bytes!\n", __FUNCTION__, sizeof(db_row_t) * allocated_rows); return -1; } else { RES_ROWS(_r) = tmp_ptr; } } ROW_N(RES_ROWS(_r) + RES_ROW_N(_r)) = params_num; ROW_VALUES(RES_ROWS(_r) + RES_ROW_N(_r)) = p_val = pkg_malloc(sizeof(db_val_t) * params_num); ++RES_ROW_N(_r); p_param = ora_params; for (i = 0; i < params_num; i++, p_val++, p_param++) { VAL_TYPE(p_val) = sqlt_to_db_type(p_param->type); if (p_param->ind == OCI_IND_NULL) { VAL_NULL(p_val) = 1; DBG("OCI_IND_NULL, type %s\n", sqlt_to_str(p_param->type)); continue; } else if (p_param->ind != OCI_IND_NOTNULL) { ERR("ind = %d, type %s\n", p_param->ind, sqlt_to_str(p_param->type)); } VAL_NULL(p_val) = 0; switch (p_param->type) { case SQLT_STR: { /* We construct correct value of type DB_STR and at the same time of type DB_STRING (as long as _union_ is used inside db_val_t structure and string pointer is the _first_ variable of _str structure). This is necessary since the callers tend to ignore returned value types. Reported type is DB_STR. */ size_t buflen = strlen(p_param->p_data) + 1; VAL_STR(p_val).s = pkg_malloc(buflen); if (VAL_STR(p_val).s == NULL) { /* * Set number of coulumns to actually processed value. * db_free_result() must honor this number during cleanup. */ ROW_N(RES_ROWS(_r) + RES_ROW_N(_r) - 1) = i; return -1; } memcpy(VAL_STR(p_val).s, p_param->p_data, buflen); VAL_STR(p_val).len = buflen - 1; DBG("SQLT_STR: %.*s\n", VAL_STR(p_val).len, ZSW(VAL_STR(p_val).s)); break; } case SQLT_INT: VAL_INT(p_val) = *(int *)(p_param->p_data); DBG("SQLT_INT: %d\n", *(int *)(p_param->p_data)); break; case SQLT_UIN: VAL_BITMAP(p_val) = *(unsigned int *)(p_param->p_data); DBG("SQLT_UIN: %#x\n", *(unsigned int *)(p_param->p_data)); break; case SQLT_FLT: VAL_DOUBLE(p_val) = *(double *)(p_param->p_data); DBG("SQLT_FLT: %e\n", *(double *)(p_param->p_data)); break; case SQLT_DAT: { struct tm tm; time_t time; char *p_ora_date = p_param->p_data; tm.tm_year = (*p_ora_date++ - 100) * 100; /* * Second step is necessary here, since single step operation * could result in wrong value due to double pointer++ operation. * * 1900 is substracted since tm_year is stored as (Year - 1900) value. */ tm.tm_year += *p_ora_date++ - 100 - 1900; tm.tm_mon = *p_ora_date++ - 1; /* tm_mon is from [0-11] interval */ tm.tm_mday = *p_ora_date++; tm.tm_hour = *p_ora_date++ - 1; tm.tm_min = *p_ora_date++ - 1; tm.tm_sec = *p_ora_date++ - 1; tm.tm_isdst = -1; /* XXX: unknown DST. Is it correct? */ /* XXX Is it necessary to preset tm_gmtoff, tm_zone? */ tm.tm_gmtoff = 0; tm.tm_zone = 0; time = mktime(&tm); if (time == -1) { ERR("%s: SQLT_DAT -> time_t conversion failed\n", __FUNCTION__); return -1; } VAL_TIME(p_val) = time; DBG("SQLT_DAT: %s\n", ctime(&time)); break; } default: /* All unsupported types must had been catched before. */ ERR("%s: Internal logic is broken: %d!\n", __FUNCTION__, p_param->type); return -1; } } return 0; } /* Allocate and prepare SQL statement */ static int oci_prepare(db_con_t *_h) { DBG("%s\n", __FUNCTION__); #if !DB_PREALLOC_SQLT_HANDLE sword rc = OCIHandleAlloc(CON_ORA(_h)->env.ptr, (dvoid **)&(CON_ORA(_h)->stmt.ptr), OCI_HTYPE_STMT, 0, NULL); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); return -1; } #else sword #endif rc = OCIStmtPrepare(CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, (OraText *)prepared_sql(), prepared_sql_len(), OCI_NTV_SYNTAX, OCI_DEFAULT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); #if !DB_PREALLOC_SQLT_HANDLE if (CON_ORA(_h)->stmt.ptr != NULL) { OCICHECK(CON_ORA(_h)->env.ptr, OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT)); } #endif return -2; } return 0; } /* 'iters' must be set to 0 for 'select' statement, and to 1 for others. */ static int oci_execute(db_con_t *_h, ub4 iters) { DBG("%s\n", __FUNCTION__); sword rc = OCIStmtExecute(CON_ORA(_h)->svc.ptr, CON_ORA(_h)->stmt.ptr, CON_ORA(_h)->err.ptr, iters, 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->err.ptr, rc); return -1; } return 0; } #if !DB_PREALLOC_SQLT_HANDLE static int oci_cleanup(db_con_t *_h) { DBG("%s\n", __FUNCTION__); sword rc = OCIHandleFree(CON_ORA(_h)->stmt.ptr, OCI_HTYPE_STMT); if (rc != OCI_SUCCESS) { OCICHECK(CON_ORA(_h)->env.ptr, rc); return -1; } return 0; } #endif