pg_cmd.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. /*
  2. * $Id$
  3. *
  4. * PostgreSQL Database Driver for SER
  5. *
  6. * Portions Copyright (C) 2001-2003 FhG FOKUS
  7. * Copyright (C) 2003 August.Net Services, LLC
  8. * Portions Copyright (C) 2005-2008 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 it under the
  13. * terms of the GNU General Public License as published by the Free Software
  14. * Foundation; either version 2 of the License, or (at your option) any later
  15. * version
  16. *
  17. * For a license to use the ser software under conditions other than those
  18. * described here, or to purchase support for this software, please contact
  19. * iptel.org by e-mail at the following addresses: [email protected]
  20. *
  21. * SER is distributed in the hope that it will be useful, but WITHOUT ANY
  22. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  23. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  24. * details.
  25. *
  26. * You should have received a copy of the GNU General Public License along
  27. * with this program; if not, write to the Free Software Foundation, Inc., 59
  28. * Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29. */
  30. /** \addtogroup postgres
  31. * @{
  32. */
  33. /** \file
  34. * Implementation of functions related to database commands.
  35. */
  36. #include "pg_cmd.h"
  37. #include "pg_sql.h"
  38. #include "pg_fld.h"
  39. #include "pg_con.h"
  40. #include "pg_mod.h"
  41. #include "pg_uri.h"
  42. #include "pg_res.h"
  43. #include "../../mem/mem.h"
  44. #include "../../dprint.h"
  45. #include "../../ut.h"
  46. #include <string.h>
  47. /** A global counter used to generate unique command names.
  48. * This variable implements a global counter which is used to generate unique
  49. * names for server-side commands.
  50. */
  51. static int server_query_no = 0;
  52. static int upload_cmd(db_cmd_t* cmd);
  53. static void free_pg_params(struct pg_params* cmd);
  54. /** Destroys a pg_cmd structure.
  55. * This function frees all memory used by pg_cmd structure.
  56. * @param cmd A pointer to generic db_cmd command being freed.
  57. * @param payload A pointer to pg_cmd structure to be freed.
  58. */
  59. static void pg_cmd_free(db_cmd_t* cmd, struct pg_cmd* payload)
  60. {
  61. db_drv_free(&payload->gen);
  62. if (payload->sql_cmd.s) pkg_free(payload->sql_cmd.s);
  63. free_pg_params(&payload->params);
  64. if (payload->name) pkg_free(payload->name);
  65. if (payload->types) PQclear(payload->types);
  66. pkg_free(payload);
  67. }
  68. /** Generate a unique name for a server-side PostgreSQL command.
  69. * This function generates a unique name for each command that will be used to
  70. * identify the prepared statement on the server. The name has only has to be
  71. * unique within a connection to the server so we just keep a global counter
  72. * and the name will be that number converted to text.
  73. *
  74. * @param cmd A command whose name is to be generated
  75. * @return A string allocated using pkg_malloc containing the name or NULL on
  76. * error.
  77. */
  78. static int gen_cmd_name(db_cmd_t* cmd)
  79. {
  80. struct pg_cmd* pcmd;
  81. char* c;
  82. int len;
  83. pcmd = DB_GET_PAYLOAD(cmd);
  84. c = int2str(server_query_no, &len);
  85. pcmd->name = pkg_malloc(len + 1);
  86. if (pcmd->name == NULL) {
  87. ERR("postgres: No memory left\n");
  88. return -1;
  89. }
  90. memcpy(pcmd->name, c, len);
  91. pcmd->name[len] = '\0';
  92. server_query_no++;
  93. return 0;
  94. }
  95. /** Creates parameter data structures for PQexecPrepared.
  96. * This function creates auxiliary data structures that will be used to pass
  97. * parameter value and types to PQexecPrepared. The function only allocates
  98. * memory buffers and determines oids of parameters, actual values will be
  99. * assigned by another function at runtime.
  100. * @param cmd A command where the data strctures will be created.
  101. * @retval 0 on success.
  102. * @retval A negative number on error.
  103. */
  104. static int create_pg_params(db_cmd_t* cmd)
  105. {
  106. int num;
  107. struct pg_cmd* pcmd;
  108. pcmd = DB_GET_PAYLOAD(cmd);
  109. num = cmd->match_count + cmd->vals_count;
  110. if (num == 0) return 0;
  111. pcmd->params.val = (const char**)pkg_malloc(sizeof(const char*) * num);
  112. pcmd->params.len = (int*)pkg_malloc(sizeof(int) * num);
  113. pcmd->params.fmt = (int*)pkg_malloc(sizeof(int) * num);
  114. if (!pcmd->params.val ||
  115. !pcmd->params.len || !pcmd->params.fmt) {
  116. ERR("postgres: No memory left\n");
  117. goto error;
  118. }
  119. memset(pcmd->params.val, '\0', sizeof(const char*) * num);
  120. memset(pcmd->params.len, '\0', sizeof(int) * num);
  121. memset(pcmd->params.fmt, '\0', sizeof(int) * num);
  122. pcmd->params.n = num;
  123. return 0;
  124. error:
  125. free_pg_params(&pcmd->params);
  126. return -1;
  127. }
  128. /**
  129. * Free all memory used for PQexecParam parameters. That is
  130. * the arrays of Oids, values, lengths, and formats supplied
  131. * to PostgreSQL client API functions like PQexecParams.
  132. */
  133. static void free_pg_params(struct pg_params* params)
  134. {
  135. if (params == NULL) return;
  136. if (params->val) pkg_free(params->val);
  137. params->val = NULL;
  138. if (params->len) pkg_free(params->len);
  139. params->len = NULL;
  140. if (params->fmt) pkg_free(params->fmt);
  141. params->fmt = NULL;
  142. }
  143. /** Verify field type compatibility.
  144. * This function verifies the types of all parameters of a database command
  145. * with the types of corresponding fields on the server to make sure that they
  146. * can be converted.
  147. * @param cmd A command structure whose parameters are to be checked.
  148. * @retval 0 on success.
  149. * @retval A negative number if at least one field type does not match.
  150. * @todo Store oid and length as part of pg_fld, instead of the arrays used
  151. * as parameters to PQ functions
  152. */
  153. static int check_types(db_cmd_t* cmd)
  154. {
  155. struct pg_cmd* pcmd;
  156. struct pg_con* pcon;
  157. pcmd = DB_GET_PAYLOAD(cmd);
  158. /* FIXME: The function should take the connection as one of parameters */
  159. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  160. if (pg_check_fld2pg(cmd->match, pcon->oid)) return -1;
  161. if (pg_check_fld2pg(cmd->vals, pcon->oid)) return -1;
  162. if (pg_check_pg2fld(cmd->result, pcon->oid)) return -1;
  163. return 0;
  164. }
  165. static int get_types(db_cmd_t* cmd)
  166. {
  167. struct pg_cmd* pcmd;
  168. struct pg_con* pcon;
  169. int i, n;
  170. pg_type_t *types;
  171. pcmd = DB_GET_PAYLOAD(cmd);
  172. /* FIXME */
  173. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  174. types = pcon->oid;
  175. pcmd->types = PQdescribePrepared(pcon->con, pcmd->name);
  176. if (PQresultStatus(pcmd->types) != PGRES_COMMAND_OK) {
  177. ERR("postgres: Error while obtaining description of prepared statement\n");
  178. return -1;
  179. }
  180. /* adapted from check_result() in db_mysql */
  181. n = PQnfields(pcmd->types);
  182. if (cmd->result == NULL) {
  183. /* The result set parameter of db_cmd function was empty, that
  184. * means the command is select * and we have to create the array
  185. * of result fields in the cmd structure manually.
  186. */
  187. cmd->result = db_fld(n + 1);
  188. cmd->result_count = n;
  189. for(i = 0; i < cmd->result_count; i++) {
  190. struct pg_fld *f;
  191. if (pg_fld(cmd->result + i, cmd->table.s) < 0) goto error;
  192. f = DB_GET_PAYLOAD(cmd->result + i);
  193. f->name = pkg_malloc(strlen(PQfname(pcmd->types, i))+1);
  194. if (f->name == NULL) {
  195. ERR("postgres: Out of private memory\n");
  196. goto error;
  197. }
  198. strcpy(f->name, PQfname(pcmd->types, i));
  199. cmd->result[i].name = f->name;
  200. }
  201. } else {
  202. if (cmd->result_count != n) {
  203. BUG("postgres: Number of fields in PQresult does not match number of parameters in DB API\n");
  204. goto error;
  205. }
  206. }
  207. /* Now iterate through all the columns in the result set and replace
  208. * any occurrence of DB_UNKNOWN type with the type of the column
  209. * retrieved from the database and if no column name was provided then
  210. * update it from the database as well.
  211. */
  212. for(i = 0; i < cmd->result_count; i++) {
  213. Oid type = PQftype(pcmd->types, i);
  214. if (cmd->result[i].type != DB_NONE) continue;
  215. if ((type == types[PG_INT2].oid) || (type == types[PG_INT4].oid) || (type == types[PG_INT8].oid))
  216. cmd->result[i].type = DB_INT;
  217. else if (type == types[PG_FLOAT4].oid)
  218. cmd->result[i].type = DB_FLOAT;
  219. else if (type == types[PG_FLOAT8].oid)
  220. cmd->result[i].type = DB_DOUBLE;
  221. else if ((type == types[PG_TIMESTAMP].oid) || (type == types[PG_TIMESTAMPTZ].oid))
  222. cmd->result[i].type = DB_DATETIME;
  223. else if ((type == types[PG_VARCHAR].oid) || (type == types[PG_CHAR].oid) || (type == types[PG_TEXT].oid))
  224. cmd->result[i].type = DB_STR;
  225. else if ((type == types[PG_BIT].oid) || (type == types[PG_VARBIT].oid))
  226. cmd->result[i].type = DB_BITMAP;
  227. else if (type == types[PG_BYTE].oid)
  228. cmd->result[i].type = DB_BLOB;
  229. else
  230. {
  231. ERR("postgres: Unsupported PostgreSQL column type: %d, table: %s, column: %s\n",
  232. type, cmd->table.s, PQfname(pcmd->types, i));
  233. goto error;
  234. }
  235. }
  236. return 0;
  237. error:
  238. return -1;
  239. }
  240. int pg_cmd(db_cmd_t* cmd)
  241. {
  242. struct pg_cmd* pcmd;
  243. pcmd = (struct pg_cmd*)pkg_malloc(sizeof(struct pg_cmd));
  244. if (pcmd == NULL) {
  245. ERR("postgres: No memory left\n");
  246. goto error;
  247. }
  248. memset(pcmd, '\0', sizeof(struct pg_cmd));
  249. if (db_drv_init(&pcmd->gen, pg_cmd_free) < 0) goto error;
  250. switch(cmd->type) {
  251. case DB_PUT:
  252. if (build_insert_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  253. break;
  254. case DB_DEL:
  255. if (build_delete_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  256. break;
  257. case DB_GET:
  258. if (build_select_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  259. break;
  260. case DB_UPD:
  261. if (build_update_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  262. break;
  263. case DB_SQL:
  264. pcmd->sql_cmd.s = (char*)pkg_malloc(cmd->table.len + 1);
  265. if (pcmd->sql_cmd.s == NULL) {
  266. ERR("postgres: Out of private memory\n");
  267. goto error;
  268. }
  269. memcpy(pcmd->sql_cmd.s,cmd->table.s, cmd->table.len);
  270. pcmd->sql_cmd.s[cmd->table.len] = '\0';
  271. pcmd->sql_cmd.len = cmd->table.len;
  272. break;
  273. }
  274. DB_SET_PAYLOAD(cmd, pcmd);
  275. /* Create parameter arrays for PostgreSQL API functions */
  276. if (create_pg_params(cmd) < 0) goto error;
  277. /* Generate a unique name for the command on the server */
  278. if (gen_cmd_name(cmd) != 0) goto error;
  279. /* Upload the command to the server */
  280. if (upload_cmd(cmd) != 0) goto error;
  281. /* Obtain the description of the uploaded command, this includes
  282. * information about result and parameter fields */
  283. if (get_types(cmd) != 0) goto error;
  284. /* Update fields based on the information retrieved from the */
  285. if (pg_resolve_param_oids(cmd->vals, cmd->match,
  286. cmd->vals_count, cmd->match_count,
  287. pcmd->types))
  288. goto error;
  289. if (pg_resolve_result_oids(cmd->result, cmd->result_count, pcmd->types))
  290. goto error;
  291. if (check_types(cmd)) goto error;
  292. return 0;
  293. error:
  294. if (pcmd) {
  295. DB_SET_PAYLOAD(cmd, NULL);
  296. free_pg_params(&pcmd->params);
  297. if (pcmd->types) PQclear(pcmd->types);
  298. if (pcmd->name) pkg_free(pcmd->name);
  299. if (pcmd->sql_cmd.s) pkg_free(pcmd->sql_cmd.s);
  300. db_drv_free(&pcmd->gen);
  301. pkg_free(pcmd);
  302. }
  303. return -1;
  304. }
  305. int pg_getopt(db_cmd_t* cmd, char* optname, va_list ap)
  306. {
  307. struct pg_cmd* pcmd;
  308. long long* id;
  309. pcmd = (struct pg_cmd*)DB_GET_PAYLOAD(cmd);
  310. if (!strcasecmp("last_id", optname)) {
  311. id = va_arg(ap, long long*);
  312. if (id == NULL) {
  313. BUG("postgres: NULL pointer passed to 'last_id' option\n");
  314. goto error;
  315. }
  316. return -1;
  317. } else {
  318. return 1;
  319. }
  320. return 0;
  321. error:
  322. return -1;
  323. }
  324. int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap)
  325. {
  326. struct pg_cmd* pcmd;
  327. pcmd = (struct pg_cmd*)DB_GET_PAYLOAD(cmd);
  328. return 1;
  329. }
  330. /** Uploads a database command to PostgreSQL server.
  331. * This function uploads a pre-compiled database command to PostgreSQL
  332. * server using PQprepare.
  333. * @param cmd A database command
  334. * @retval 0 on success.
  335. * @retval A negative number on error.
  336. */
  337. static int upload_cmd(db_cmd_t* cmd)
  338. {
  339. struct pg_cmd* pcmd;
  340. struct pg_con* pcon;
  341. PGresult* res;
  342. int st;
  343. pcmd = DB_GET_PAYLOAD(cmd);
  344. /* FIXME: The function should take the connection as one of parameters */
  345. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  346. DBG("postgres: Uploading command '%s': '%s'\n", pcmd->name,
  347. pcmd->sql_cmd.s);
  348. res = PQprepare(pcon->con, pcmd->name, pcmd->sql_cmd.s, (cmd->match_count + cmd->vals_count), NULL);
  349. st = PQresultStatus(res);
  350. if (st != PGRES_COMMAND_OK && st != PGRES_NONFATAL_ERROR &&
  351. st != PGRES_TUPLES_OK) {
  352. ERR("postgres: Error while uploading command to server: %d, %s",
  353. st, PQresultErrorMessage(res));
  354. ERR("postgres: Command: '%s'\n", pcmd->sql_cmd.s);
  355. PQclear(res);
  356. return -1;
  357. }
  358. PQclear(res);
  359. return 0;
  360. }
  361. int pg_cmd_exec(db_res_t* res, db_cmd_t* cmd)
  362. {
  363. PGresult* tmp;
  364. int i, err, stat;
  365. db_con_t* con;
  366. struct pg_cmd* pcmd;
  367. struct pg_con* pcon;
  368. struct pg_uri* puri;
  369. struct pg_res* pres;
  370. /* First things first: retrieve connection info from the currently active
  371. * connection and also mysql payload from the database command
  372. */
  373. con = cmd->ctx->con[db_payload_idx];
  374. pcmd = DB_GET_PAYLOAD(cmd);
  375. pcon = DB_GET_PAYLOAD(con);
  376. puri = DB_GET_PAYLOAD(con->uri);
  377. for(i = 0; i <= pg_retries; i++) {
  378. /* Convert parameters from DB-API format to the format accepted
  379. * by PostgreSQL */
  380. if (pg_fld2pg(&pcmd->params, 0, pcon->oid, cmd->match, pcon->flags) != 0)
  381. return 1;
  382. if (pg_fld2pg(&pcmd->params, cmd->match_count,
  383. pcon->oid, cmd->vals, pcon->flags) != 0) return 1;
  384. /* Execute the statement */
  385. tmp = PQexecPrepared(pcon->con, pcmd->name,
  386. pcmd->params.n,
  387. pcmd->params.val, pcmd->params.len,
  388. pcmd->params.fmt, 1);
  389. if (!tmp) {
  390. ERR("postgres: PQexecPrepared returned no result\n");
  391. continue;
  392. }
  393. switch(PQresultStatus(tmp)) {
  394. case PGRES_COMMAND_OK:
  395. case PGRES_NONFATAL_ERROR:
  396. case PGRES_TUPLES_OK:
  397. if (res) {
  398. pres = DB_GET_PAYLOAD(res);
  399. pres->res = tmp;
  400. pres->rows = PQntuples(tmp);
  401. } else {
  402. PQclear(tmp);
  403. }
  404. return 0;
  405. default:
  406. break;
  407. }
  408. ERR("postgres: Command on server %s failed: %s: %s\n",
  409. puri->host, PQresStatus(PQresultStatus(tmp)),
  410. PQresultErrorMessage(tmp));
  411. PQclear(tmp);
  412. /* Command failed, first of all determine the status of the connection
  413. * to server */
  414. if (PQstatus(pcon->con) != CONNECTION_OK) {
  415. INFO("postgres: Connection to server %s disconnected, attempting reconnect\n",
  416. puri->host);
  417. pg_con_disconnect(con);
  418. if (pg_con_connect(con)) {
  419. INFO("postgres: Failed to reconnect server %s, giving up\n",
  420. puri->host);
  421. return -1;
  422. }
  423. INFO("postgres: Successfully reconnected server on %s\n",
  424. puri->host);
  425. }
  426. /* Connection is either connected or has been successfully reconnected,
  427. * now figure out if the prepared command on the server still exist
  428. */
  429. tmp = PQdescribePrepared(pcon->con, pcmd->name);
  430. if (tmp == NULL) {
  431. ERR("postgres: PQdescribePrepared returned no result\n");
  432. continue;
  433. }
  434. stat = PQresultStatus(tmp);
  435. PQclear(tmp);
  436. switch (stat) {
  437. case PGRES_COMMAND_OK:
  438. case PGRES_NONFATAL_ERROR:
  439. case PGRES_TUPLES_OK:
  440. INFO("postgres: Command %s on server %s still exists, reusing\n",
  441. pcmd->name, puri->host);
  442. /* Command is there, retry */
  443. continue;
  444. default:
  445. break;
  446. }
  447. /* Upload again */
  448. INFO("postgres: Command %s on server %s missing, uploading\n",
  449. pcmd->name, puri->host);
  450. err = upload_cmd(cmd);
  451. if (err < 0) {
  452. continue;
  453. } else if (err > 0) {
  454. /* DB API error, this is a serious problem such
  455. * as memory allocation failure, bail out
  456. */
  457. return 1;
  458. }
  459. }
  460. INFO("postgres: Failed to execute command %s on server %s, giving up\n",
  461. pcmd->name, puri->host);
  462. return -1;
  463. }
  464. int pg_cmd_first(db_res_t* res)
  465. {
  466. struct pg_res* pres;
  467. pres = DB_GET_PAYLOAD(res);
  468. if (pres->rows <= 0) return 1; /* Empty table */
  469. pres->row = 0;
  470. return pg_cmd_next(res);
  471. }
  472. int pg_cmd_next(db_res_t* res)
  473. {
  474. struct pg_res* pres;
  475. struct pg_con* pcon;
  476. pres = DB_GET_PAYLOAD(res);
  477. pcon = DB_GET_PAYLOAD(res->cmd->ctx->con[db_payload_idx]);
  478. if (pres->row >= pres->rows) return 1;
  479. if (pg_pg2fld(res->cmd->result, pres->res, pres->row, pcon->oid, pcon->flags)) return -1;
  480. res->cur_rec->fld = res->cmd->result;
  481. pres->row++;
  482. return 0;
  483. }
  484. /** @} */