connection.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * $Id$
  3. *
  4. * UNIXODBC module
  5. *
  6. * Copyright (C) 2005-2006 Marco Lorrai
  7. * Copyright (C) 2008 1&1 Internet AG
  8. *
  9. * This file is part of Kamailio, a free SIP server.
  10. *
  11. * Kamailio is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2 of the License, or
  14. * (at your option) any later version
  15. *
  16. * Kamailio is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. *
  26. * History:
  27. * --------
  28. * 2005-12-01 initial commit (chgen)
  29. * 2006-01-10 UID (username) and PWD (password) attributes added to
  30. * connection string (bogdan)
  31. * 2006-05-05 extract_error passes back last error state on return (sgupta)
  32. */
  33. #include "connection.h"
  34. #include "../../mem/mem.h"
  35. #include "../../dprint.h"
  36. #include "../../ut.h"
  37. #include <time.h>
  38. #define DSN_ATTR "DSN="
  39. #define DSN_ATTR_LEN (sizeof(DSN_ATTR)-1)
  40. #define UID_ATTR "UID="
  41. #define UID_ATTR_LEN (sizeof(UID_ATTR)-1)
  42. #define PWD_ATTR "PWD="
  43. #define PWD_ATTR_LEN (sizeof(PWD_ATTR)-1)
  44. char *db_unixodbc_build_conn_str(const struct db_id* id, char *buf)
  45. {
  46. int len, ld, lu, lp;
  47. char *p;
  48. if (!buf) return 0;
  49. ld = id->database?strlen(id->database):0;
  50. lu = id->username?strlen(id->username):0;
  51. lp = id->password?strlen(id->password):0;
  52. len = (ld?(DSN_ATTR_LEN + ld + 1):0)
  53. + (lu?(UID_ATTR_LEN + lu + 1):0)
  54. + PWD_ATTR_LEN + lp + 1;
  55. if ( len>=MAX_CONN_STR_LEN ){
  56. LM_ERR("connection string too long! Increase MAX_CONN_STR_LEN"
  57. " and recompile\n");
  58. return 0;
  59. }
  60. p = buf;
  61. if (ld) {
  62. memcpy( p , DSN_ATTR, DSN_ATTR_LEN);
  63. p += DSN_ATTR_LEN;
  64. memcpy( p, id->database, ld);
  65. p += ld;
  66. }
  67. if (lu) {
  68. *(p++) = ';';
  69. memcpy( p , UID_ATTR, UID_ATTR_LEN);
  70. p += UID_ATTR_LEN;
  71. memcpy( p, id->username, lu);
  72. p += lu;
  73. }
  74. if (lp) {
  75. *(p++) = ';';
  76. memcpy( p , PWD_ATTR, PWD_ATTR_LEN);
  77. p += PWD_ATTR_LEN;
  78. memcpy( p, id->password, lp);
  79. p += lp;
  80. }
  81. *(p++) = ';';
  82. *p = 0 ; /* make it null terminated */
  83. return buf;
  84. }
  85. /*
  86. * Create a new connection structure,
  87. * open the UNIXODBC connection and set reference count to 1
  88. */
  89. struct my_con* db_unixodbc_new_connection(struct db_id* id)
  90. {
  91. SQLCHAR outstr[1024];
  92. SQLSMALLINT outstrlen;
  93. int ret;
  94. struct my_con* ptr;
  95. char conn_str[MAX_CONN_STR_LEN];
  96. if (!id)
  97. {
  98. LM_ERR("invalid parameter value\n");
  99. return 0;
  100. }
  101. ptr = (struct my_con*)pkg_malloc(sizeof(struct my_con));
  102. if (!ptr)
  103. {
  104. LM_ERR("no more memory left\n");
  105. return 0;
  106. }
  107. memset(ptr, 0, sizeof(struct my_con));
  108. ptr->ref = 1;
  109. // allocate environment handle
  110. ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(ptr->env));
  111. if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
  112. {
  113. LM_ERR("could not alloc a SQL handle\n");
  114. if (ptr) pkg_free(ptr);
  115. return 0;
  116. }
  117. // set the environment
  118. ret = SQLSetEnvAttr(ptr->env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
  119. if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
  120. {
  121. LM_ERR("could not set the environment\n");
  122. goto err1;
  123. }
  124. // allocate connection handle
  125. ret = SQLAllocHandle(SQL_HANDLE_DBC, ptr->env, &(ptr->dbc));
  126. if ((ret != SQL_SUCCESS) && (ret != SQL_SUCCESS_WITH_INFO))
  127. {
  128. LM_ERR("could not alloc a connection handle %d\n", ret);
  129. goto err1;
  130. }
  131. if (!db_unixodbc_build_conn_str(id, conn_str)) {
  132. LM_ERR("failed to build connection string\n");
  133. goto err2;
  134. }
  135. LM_DBG("opening connection: unixodbc://xxxx:xxxx@%s/%s\n", ZSW(id->host),
  136. ZSW(id->database));
  137. ret = SQLDriverConnect(ptr->dbc, NULL, (SQLCHAR*)conn_str, SQL_NTS,
  138. outstr, sizeof(outstr), &outstrlen,
  139. SQL_DRIVER_COMPLETE);
  140. if (SQL_SUCCEEDED(ret))
  141. {
  142. LM_DBG("connection succeeded with reply <%s>\n", outstr);
  143. if (ret == SQL_SUCCESS_WITH_INFO)
  144. {
  145. LM_DBG("driver reported the following diagnostics\n");
  146. db_unixodbc_extract_error("SQLDriverConnect", ptr->dbc, SQL_HANDLE_DBC, NULL);
  147. }
  148. }
  149. else
  150. {
  151. LM_ERR("failed to connect\n");
  152. db_unixodbc_extract_error("SQLDriverConnect", ptr->dbc, SQL_HANDLE_DBC, NULL);
  153. goto err2;
  154. }
  155. ptr->stmt_handle = NULL;
  156. ptr->timestamp = time(0);
  157. ptr->id = id;
  158. return ptr;
  159. err1:
  160. SQLFreeHandle(SQL_HANDLE_ENV, &(ptr->env));
  161. if (ptr) pkg_free(ptr);
  162. return 0;
  163. err2:
  164. SQLFreeHandle(SQL_HANDLE_ENV, &(ptr->env));
  165. SQLFreeHandle(SQL_HANDLE_DBC, &(ptr->dbc));
  166. if (ptr) pkg_free(ptr);
  167. return 0;
  168. }
  169. /*
  170. * Close the connection and release memory
  171. */
  172. void db_unixodbc_free_connection(struct my_con* con)
  173. {
  174. if (!con) return;
  175. SQLFreeHandle(SQL_HANDLE_ENV, con->env);
  176. SQLDisconnect(con->dbc);
  177. SQLFreeHandle(SQL_HANDLE_DBC, con->dbc);
  178. pkg_free(con);
  179. }
  180. void db_unixodbc_extract_error(const char *fn, const SQLHANDLE handle, const SQLSMALLINT type, char* stret)
  181. {
  182. SQLINTEGER i = 0;
  183. SQLINTEGER native;
  184. SQLCHAR state[ 7 ];
  185. SQLCHAR text[256];
  186. SQLSMALLINT len;
  187. SQLRETURN ret;
  188. do
  189. {
  190. ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
  191. sizeof(text), &len );
  192. if (SQL_SUCCEEDED(ret)) {
  193. LM_ERR("unixodbc:%s=%s:%ld:%ld:%s\n", fn, state, (long)i,
  194. (long)native, text);
  195. if(stret) strcpy( stret, (char*)state );
  196. }
  197. }
  198. while( ret == SQL_SUCCESS );
  199. }
  200. /*
  201. * Allocate a new row of cells, without any data
  202. */
  203. strn * db_unixodbc_new_cellrow(size_t ncols)
  204. {
  205. strn * temp_row;
  206. temp_row = (strn *)pkg_malloc(ncols * sizeof(strn));
  207. if (temp_row) memset(temp_row, 0, ncols * sizeof(strn));
  208. return temp_row;
  209. }
  210. /*
  211. * Free row of cells and all associated memory
  212. */
  213. void db_unixodbc_free_cellrow(size_t ncols, strn * row)
  214. {
  215. size_t i;
  216. for (i = 0; i < ncols; i++) {
  217. if (row[i].s != NULL) pkg_free(row[i].s);
  218. }
  219. pkg_free(row);
  220. }
  221. /*
  222. * Load ODBC cell data into a single cell
  223. */
  224. int db_unixodbc_load_cell(const db1_con_t* _h, int colindex, strn * cell, const db_type_t _t)
  225. {
  226. SQLRETURN ret = 0;
  227. unsigned int truesize = 0;
  228. unsigned char hasnull = (_t != DB1_BLOB) ? 1 : 0;
  229. do {
  230. SQLLEN indicator;
  231. int chunklen;
  232. char * s; /* Pointer to available area for next chunk */
  233. char * ns;
  234. if (cell->buflen > 0) {
  235. ns = (char *)pkg_realloc(cell->s, cell->buflen + STRN_LEN);
  236. if (ns == NULL) {
  237. LM_ERR("no memory left\n");
  238. return 0;
  239. }
  240. cell->s = ns;
  241. /* Overwrite the previous null terminator */
  242. s = cell->s + cell->buflen - hasnull;
  243. chunklen = STRN_LEN + hasnull;
  244. } else {
  245. ns = (char *)pkg_malloc(STRN_LEN);
  246. if (ns == NULL) {
  247. LM_ERR("no memory left\n");
  248. return 0;
  249. }
  250. cell->s = ns;
  251. s = cell->s;
  252. chunklen = STRN_LEN;
  253. }
  254. cell->buflen += STRN_LEN;
  255. ret = SQLGetData(CON_RESULT(_h), colindex, hasnull ? SQL_C_CHAR : SQL_C_BINARY,
  256. s, chunklen, &indicator);
  257. LM_DBG("SQLGetData returned ret=%d indicator=%d\n", (int)ret, (int)indicator);
  258. if (ret == SQL_SUCCESS) {
  259. if (indicator == SQL_NULL_DATA) {
  260. /* TODO: set buffer pointer to NULL instead of string "NULL" */
  261. strcpy(cell->s, "NULL");
  262. truesize = 4 + (1 - hasnull);
  263. } else {
  264. /* Get length of data that was available before last SQLGetData call */
  265. if (truesize == 0) truesize = indicator;
  266. }
  267. } else if (ret == SQL_SUCCESS_WITH_INFO) {
  268. SQLINTEGER i = 0;
  269. SQLINTEGER native;
  270. SQLCHAR state[ 7 ];
  271. SQLCHAR text[256];
  272. SQLSMALLINT len;
  273. SQLRETURN ret2;
  274. /* Check whether field data was truncated */
  275. do
  276. {
  277. ret2 = SQLGetDiagRec(SQL_HANDLE_STMT, CON_RESULT(_h), ++i, state, &native, text,
  278. sizeof(text), &len );
  279. if (SQL_SUCCEEDED(ret2)) {
  280. if (!strcmp("00000", (const char*)state)) break;
  281. if (strcmp("01004", (const char*)state) != 0) {
  282. /* Not a string truncation */
  283. LM_ERR("SQLGetData failed unixodbc: =%s:%ld:%ld:%s\n", state, (long)i,
  284. (long)native, text);
  285. return 0;
  286. }
  287. /* Get length of data that was available before last SQLGetData call */
  288. if (truesize == 0) truesize = indicator;
  289. } else if (ret2 == SQL_NO_DATA) {
  290. /* Reached end of diagnostic records */
  291. break;
  292. } else {
  293. /* Failed to get diagnostics */
  294. LM_ERR("SQLGetData failed, failed to get diagnostics (ret2=%d i=%d)\n",
  295. ret2, i);
  296. return 0;
  297. }
  298. }
  299. while( ret2 == SQL_SUCCESS );
  300. } else {
  301. LM_ERR("SQLGetData failed\n");
  302. }
  303. } while (ret == SQL_SUCCESS_WITH_INFO);
  304. LM_DBG("Total allocated for this cell (before resize): %d bytes\n", cell->buflen);
  305. if (cell->buflen > truesize) {
  306. cell->s[truesize] = '\0'; /* guarantee strlen() will terminate */
  307. }
  308. cell->buflen = truesize + hasnull;
  309. LM_DBG("Total allocated for this cell (after resize): %d bytes\n", cell->buflen);
  310. LM_DBG("strlen() reports for this cell: %d bytes\n", (int)strlen(cell->s));
  311. return 1;
  312. }