res.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. * $Id$
  3. *
  4. * Postgres module result related functions
  5. *
  6. * Portions Copyright (C) 2001-2003 FhG FOKUS
  7. * Copyright (C) 2003 August.Net Services, LLC
  8. * Portions Copyright (C) 2005 iptelorg GmbH
  9. *
  10. * This file is part of ser, a free SIP server.
  11. *
  12. * ser is free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version
  16. *
  17. * For a license to use the ser software under conditions
  18. * other than those described here, or to purchase support for this
  19. * software, please contact iptel.org by e-mail at the following addresses:
  20. * [email protected]
  21. *
  22. * ser is distributed in the hope that it will be useful,
  23. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  25. * GNU General Public License for more details.
  26. *
  27. * You should have received a copy of the GNU General Public License
  28. * along with this program; if not, write to the Free Software
  29. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  30. */
  31. #include "../../mem/mem.h"
  32. #include "../../dprint.h"
  33. #include "pg_type.h"
  34. #include "pg_con.h"
  35. #include "db_mod.h"
  36. #include "res.h"
  37. #include <netinet/in.h>
  38. #include <string.h>
  39. /*
  40. * Get and convert columns from a result
  41. */
  42. static inline int get_columns(db_res_t* res)
  43. {
  44. PGresult* pgres;
  45. int n, i, type;
  46. if (!res) {
  47. ERR("Invalid parameter\n");
  48. goto err;
  49. }
  50. pgres = (PGresult*)res->data;
  51. if (!pgres) {
  52. ERR("No postgres result found\n");
  53. goto err;
  54. }
  55. n = PQnfields(pgres);
  56. if (!n) {
  57. ERR("No columns\n");
  58. goto err;
  59. }
  60. if (n == 0) return 0;
  61. res->col.names = (db_key_t*)pkg_malloc(sizeof(db_key_t) * n);
  62. if (!res->col.names) {
  63. ERR("No memory left\n");
  64. goto err;
  65. }
  66. res->col.types = (db_type_t*)pkg_malloc(sizeof(db_type_t) * n);
  67. if (!res->col.types) {
  68. ERR("No memory left\n");
  69. goto err;
  70. }
  71. res->col.n = n;
  72. for(i = 0; i < n; i++) {
  73. if (PQfformat(pgres, i) == 0) {
  74. ERR("Text format of columns not supported\n");
  75. goto err;
  76. }
  77. res->col.names[i] = PQfname(pgres, i);
  78. type = PQftype(pgres, i);
  79. switch(type) {
  80. case BOOLOID: /* boolean, 'true'/'false' */
  81. case INT2OID: /* -32 thousand to 32 thousand, 2-byte storage */
  82. case INT4OID: /* -2 billion to 2 billion integer, 4-byte storage */
  83. case INT8OID: /* ~18 digit integer, 8-byte storage */
  84. res->col.types[i] = DB_INT;
  85. break;
  86. case FLOAT4OID: /* single-precision floating point number, 4-byte storage */
  87. res->col.types[i] = DB_FLOAT;
  88. break;
  89. case FLOAT8OID: /* double-precision floating point number, 8-byte storage */
  90. res->col.types[i] = DB_DOUBLE;
  91. break;
  92. case TIMESTAMPOID: /* date and time */
  93. case TIMESTAMPTZOID: /* date and time with time zone */
  94. res->col.types[i] = DB_DATETIME;
  95. break;
  96. case CHAROID: /* single character */
  97. case TEXTOID: /* variable-length string, no limit specified */
  98. case BPCHAROID: /* char(length), blank-padded string, fixed storage length */
  99. case VARCHAROID: /* varchar(length), non-blank-padded string, variable storage length */
  100. res->col.types[i] = DB_STRING;
  101. break;
  102. case BYTEAOID: /* variable-length string, binary values escaped" */
  103. res->col.types[i] = DB_BLOB;
  104. break;
  105. case BITOID: /* fixed-length bit string */
  106. case VARBITOID: /* variable-length bit string */
  107. res->col.types[i] = DB_BITMAP;
  108. break;
  109. default:
  110. ERR("Unsupported column type with oid %d\n", type);
  111. goto err;
  112. }
  113. }
  114. return 0;
  115. err:
  116. if (res->col.types) pkg_free(res->col.types);
  117. if (res->col.names) pkg_free(res->col.names);
  118. res->col.types = 0;
  119. res->col.names = 0;
  120. return -1;
  121. }
  122. /*
  123. * Release memory used by columns
  124. */
  125. static inline int free_columns(db_res_t* res)
  126. {
  127. if (!res) {
  128. ERR("Invalid parameter\n");
  129. return -1;
  130. }
  131. if (res->col.names) pkg_free(res->col.names);
  132. if (res->col.types) pkg_free(res->col.types);
  133. return 0;
  134. }
  135. /*
  136. * Release memory used by rows
  137. */
  138. static inline void free_rows(db_res_t* res)
  139. {
  140. int r;
  141. if (!res->rows) return;
  142. for(r = 0; r < res->n; r++) {
  143. pkg_free(res->rows[r].values);
  144. }
  145. pkg_free(res->rows);
  146. }
  147. static inline int convert_cell(db_con_t* con, db_res_t* res, int row, int col)
  148. {
  149. static str dummy_str = STR_STATIC_INIT("");
  150. PGresult* pgres;
  151. db_val_t* val;
  152. int type, pglen;
  153. const char* pgval;
  154. union {
  155. int i4;
  156. long long i8;
  157. float f4;
  158. double f8;
  159. char c[8];
  160. } tmp;
  161. val = &res->rows[row].values[col];
  162. pgres = (PGresult*)res->data;
  163. val->type = res->col.types[col];
  164. if (PQgetisnull(pgres, row, col)) {
  165. val->nul = 1;
  166. switch(res->col.types[col]) {
  167. case DB_INT: val->val.int_val = 0; break;
  168. case DB_FLOAT: val->val.float_val = 0; break;
  169. case DB_DOUBLE: val->val.double_val = 0; break;
  170. case DB_STRING: val->val.string_val = dummy_str.s; break;
  171. case DB_STR: val->val.str_val = dummy_str; break;
  172. case DB_DATETIME: val->val.time_val = 0; break;
  173. case DB_BLOB: val->val.blob_val = dummy_str; break;
  174. case DB_BITMAP: val->val.bitmap_val = 0; break;
  175. }
  176. return 0;
  177. }
  178. val->nul = 0;
  179. type = PQftype(pgres, col);
  180. pgval = PQgetvalue(pgres, row, col);
  181. pglen = PQgetlength(pgres, row, col);
  182. /* Postgres delivers binary parameters in network byte order,
  183. * thus we have to convert them to host byte order. All data
  184. * returned by PQgetvalue is zero terminated. Memory allocator
  185. * in libpq aligns data in memory properly so reading multibyte
  186. * values from memory at once is safe.
  187. */
  188. switch(type) {
  189. case BOOLOID:
  190. val->val.int_val = *pgval;
  191. break;
  192. case INT2OID:
  193. val->val.int_val = ntohs(*(unsigned short*)pgval);
  194. break;
  195. case FLOAT4OID:
  196. /* FLOAT4 will be stored in (8-byte) double */
  197. /* FIXME: More efficient implementation could be done here
  198. * provided that we know that the numbers are stored in IEEE 754
  199. */
  200. tmp.i4 = ntohl(*(unsigned int*)pgval);
  201. val->val.double_val = tmp.f4;
  202. break;
  203. case INT4OID:
  204. val->val.int_val = ntohl(*(unsigned int*)pgval);
  205. break;
  206. case INT8OID:
  207. val->val.int_val = ntohl(*(unsigned int*)(pgval + 4));
  208. break;
  209. case FLOAT8OID:
  210. tmp.i8 = (((unsigned long long)ntohl(*(unsigned int*)pgval)) << 32) +
  211. (unsigned int)ntohl(*(unsigned int*)(pgval + 4));
  212. val->val.double_val = tmp.f8;
  213. break;
  214. case TIMESTAMPOID:
  215. case TIMESTAMPTZOID:
  216. tmp.i8 = (((unsigned long long)ntohl(*(unsigned int*)pgval)) << 32) +
  217. (unsigned int)ntohl(*(unsigned int*)(pgval + 4));
  218. if (CON_FLAGS(con) & PG_INT8_TIMESTAMP) {
  219. /* int8 format */
  220. val->val.time_val = tmp.i8 / 1000000 + PG_EPOCH_TIME;
  221. } else {
  222. /* double format */
  223. val->val.time_val = PG_EPOCH_TIME + (long long)tmp.f8;
  224. }
  225. break;
  226. case CHAROID: /* single character */
  227. case TEXTOID: /* variable-length string, no limit specified */
  228. case BPCHAROID: /* char(length), blank-padded string, fixed storage length */
  229. case VARCHAROID: /* varchar(length), non-blank-padded string, variable storage length */
  230. val->val.str_val.s = (char*)pgval;
  231. val->val.str_val.len = pglen;
  232. break;
  233. case BYTEAOID: /* variable-length string, binary values escaped" */
  234. val->val.blob_val.s = (char*)pgval;
  235. val->val.blob_val.len = pglen;
  236. break;
  237. case BITOID: /* fixed-length bit string */
  238. case VARBITOID: /* variable-length bit string */
  239. if (ntohl(*(unsigned int*)pgval) != 32) {
  240. ERR("Only 32-bit long bitfieds supported\n");
  241. return -1;
  242. }
  243. val->val.bitmap_val = ntohl(*(unsigned int*)(pgval + 4));
  244. break;
  245. default:
  246. ERR("Unsupported column type with oid %d\n", type);
  247. return -1;
  248. }
  249. return 0;
  250. }
  251. /*
  252. * Convert rows from postgres to db API representation
  253. */
  254. static inline int convert_rows(db_con_t* con, db_res_t* res)
  255. {
  256. db_row_t* row;
  257. int r, c;
  258. if (!res) {
  259. ERR("Invalid parameter\n");
  260. return -1;
  261. }
  262. res->n = PQntuples((PGresult*)res->data); /* Number of rows */
  263. /* Assert: number of columns is > 0, otherwise get_columns would fail */
  264. if (!res->n) {
  265. res->rows = 0;
  266. return 0;
  267. }
  268. res->rows = (struct db_row*)pkg_malloc(sizeof(db_row_t) * res->n);
  269. if (!res->rows) {
  270. ERR("No memory left\n");
  271. goto err;
  272. }
  273. for(r = 0; r < res->n; r++) {
  274. row = &res->rows[r];
  275. row->values = (db_val_t*)pkg_malloc(sizeof(db_val_t) * res->col.n);
  276. if (!row->values) {
  277. ERR("No memory left to allocate row\n");
  278. res->n = r; /* This is to make sure that the cleanup function release only rows
  279. * that has been really allocated
  280. */
  281. goto err;
  282. }
  283. row->n = res->col.n;
  284. for(c = 0; c < row->n; c++) {
  285. if (convert_cell(con, res, r, c) < 0) {
  286. row->n = c;
  287. res->n = r;
  288. goto err;
  289. }
  290. }
  291. }
  292. return 0;
  293. err:
  294. /* free_rows(res); Do not free here, pg_free_result will take care of it */
  295. return -1;
  296. }
  297. /*
  298. * Create a new result structure and initialize it
  299. */
  300. db_res_t* pg_new_result(PGresult* pgres)
  301. {
  302. db_res_t* r;
  303. r = (db_res_t*)pkg_malloc(sizeof(db_res_t));
  304. if (!r) {
  305. ERR("No memory left\n");
  306. return 0;
  307. }
  308. memset(r, 0, sizeof(db_res_t));
  309. r->data = pgres;
  310. return r;
  311. }
  312. /*
  313. * Fill the structure with data from database
  314. */
  315. int pg_convert_result(db_res_t* res, db_con_t* con)
  316. {
  317. if (!res) {
  318. ERR("Invalid parameter\n");
  319. return -1;
  320. }
  321. if (get_columns(res) < 0) {
  322. ERR("Error while getting column names\n");
  323. return -2;
  324. }
  325. if (convert_rows(con, res) < 0) {
  326. ERR("Error while converting rows\n");
  327. /* Do not free columns here, pg_free_result will do it */
  328. return -3;
  329. }
  330. return 0;
  331. }
  332. /*
  333. * Release memory used by a result structure
  334. */
  335. int pg_free_result(db_res_t* res)
  336. {
  337. if (!res) {
  338. ERR("Invalid parameter\n");
  339. return -1;
  340. }
  341. free_columns(res);
  342. free_rows(res);
  343. if (res->data) PQclear((PGresult*)res->data);
  344. pkg_free(res);
  345. return 0;
  346. }