pg_cmd.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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_con* pcon;
  156. /* FIXME: The function should take the connection as one of parameters */
  157. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  158. if (pg_check_fld2pg(cmd->match, pcon->oid)) return -1;
  159. if (pg_check_fld2pg(cmd->vals, pcon->oid)) return -1;
  160. if (pg_check_pg2fld(cmd->result, pcon->oid)) return -1;
  161. return 0;
  162. }
  163. static int get_types(db_cmd_t* cmd)
  164. {
  165. struct pg_cmd* pcmd;
  166. struct pg_con* pcon;
  167. int i, n;
  168. pg_type_t *types;
  169. pcmd = DB_GET_PAYLOAD(cmd);
  170. /* FIXME */
  171. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  172. types = pcon->oid;
  173. pcmd->types = PQdescribePrepared(pcon->con, pcmd->name);
  174. if (PQresultStatus(pcmd->types) != PGRES_COMMAND_OK) {
  175. ERR("postgres: Error while obtaining description of prepared statement\n");
  176. return -1;
  177. }
  178. /* adapted from check_result() in db_mysql */
  179. n = PQnfields(pcmd->types);
  180. if (cmd->result == NULL) {
  181. /* The result set parameter of db_cmd function was empty, that
  182. * means the command is select * and we have to create the array
  183. * of result fields in the cmd structure manually.
  184. */
  185. cmd->result = db_fld(n + 1);
  186. cmd->result_count = n;
  187. for(i = 0; i < cmd->result_count; i++) {
  188. struct pg_fld *f;
  189. if (pg_fld(cmd->result + i, cmd->table.s) < 0) goto error;
  190. f = DB_GET_PAYLOAD(cmd->result + i);
  191. f->name = pkg_malloc(strlen(PQfname(pcmd->types, i))+1);
  192. if (f->name == NULL) {
  193. ERR("postgres: Out of private memory\n");
  194. goto error;
  195. }
  196. strcpy(f->name, PQfname(pcmd->types, i));
  197. cmd->result[i].name = f->name;
  198. }
  199. } else {
  200. if (cmd->result_count != n) {
  201. BUG("postgres: Number of fields in PQresult does not match number of parameters in DB API\n");
  202. goto error;
  203. }
  204. }
  205. /* Now iterate through all the columns in the result set and replace
  206. * any occurrence of DB_UNKNOWN type with the type of the column
  207. * retrieved from the database and if no column name was provided then
  208. * update it from the database as well.
  209. */
  210. for(i = 0; i < cmd->result_count; i++) {
  211. Oid type = PQftype(pcmd->types, i);
  212. if (cmd->result[i].type != DB_NONE) continue;
  213. if ((type == types[PG_INT2].oid) || (type == types[PG_INT4].oid) || (type == types[PG_INT8].oid))
  214. cmd->result[i].type = DB_INT;
  215. else if (type == types[PG_FLOAT4].oid)
  216. cmd->result[i].type = DB_FLOAT;
  217. else if (type == types[PG_FLOAT8].oid)
  218. cmd->result[i].type = DB_DOUBLE;
  219. else if ((type == types[PG_TIMESTAMP].oid) || (type == types[PG_TIMESTAMPTZ].oid))
  220. cmd->result[i].type = DB_DATETIME;
  221. else if ((type == types[PG_VARCHAR].oid) || (type == types[PG_CHAR].oid) || (type == types[PG_TEXT].oid))
  222. cmd->result[i].type = DB_STR;
  223. else if ((type == types[PG_BIT].oid) || (type == types[PG_VARBIT].oid))
  224. cmd->result[i].type = DB_BITMAP;
  225. else if (type == types[PG_BYTE].oid)
  226. cmd->result[i].type = DB_BLOB;
  227. else
  228. {
  229. ERR("postgres: Unsupported PostgreSQL column type: %d, table: %s, column: %s\n",
  230. type, cmd->table.s, PQfname(pcmd->types, i));
  231. goto error;
  232. }
  233. }
  234. return 0;
  235. error:
  236. return -1;
  237. }
  238. int pg_cmd(db_cmd_t* cmd)
  239. {
  240. struct pg_cmd* pcmd;
  241. pcmd = (struct pg_cmd*)pkg_malloc(sizeof(struct pg_cmd));
  242. if (pcmd == NULL) {
  243. ERR("postgres: No memory left\n");
  244. goto error;
  245. }
  246. memset(pcmd, '\0', sizeof(struct pg_cmd));
  247. if (db_drv_init(&pcmd->gen, pg_cmd_free) < 0) goto error;
  248. switch(cmd->type) {
  249. case DB_PUT:
  250. if (build_insert_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  251. break;
  252. case DB_DEL:
  253. if (build_delete_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  254. break;
  255. case DB_GET:
  256. if (build_select_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  257. break;
  258. case DB_UPD:
  259. if (build_update_sql(&pcmd->sql_cmd, cmd) < 0) goto error;
  260. break;
  261. case DB_SQL:
  262. pcmd->sql_cmd.s = (char*)pkg_malloc(cmd->table.len + 1);
  263. if (pcmd->sql_cmd.s == NULL) {
  264. ERR("postgres: Out of private memory\n");
  265. goto error;
  266. }
  267. memcpy(pcmd->sql_cmd.s,cmd->table.s, cmd->table.len);
  268. pcmd->sql_cmd.s[cmd->table.len] = '\0';
  269. pcmd->sql_cmd.len = cmd->table.len;
  270. break;
  271. }
  272. DB_SET_PAYLOAD(cmd, pcmd);
  273. /* Create parameter arrays for PostgreSQL API functions */
  274. if (create_pg_params(cmd) < 0) goto error;
  275. /* Generate a unique name for the command on the server */
  276. if (gen_cmd_name(cmd) != 0) goto error;
  277. /* Upload the command to the server */
  278. if (upload_cmd(cmd) != 0) goto error;
  279. /* Obtain the description of the uploaded command, this includes
  280. * information about result and parameter fields */
  281. if (get_types(cmd) != 0) goto error;
  282. /* Update fields based on the information retrieved from the */
  283. if (pg_resolve_param_oids(cmd->vals, cmd->match,
  284. cmd->vals_count, cmd->match_count,
  285. pcmd->types))
  286. goto error;
  287. if (pg_resolve_result_oids(cmd->result, cmd->result_count, pcmd->types))
  288. goto error;
  289. if (check_types(cmd)) goto error;
  290. return 0;
  291. error:
  292. if (pcmd) {
  293. DB_SET_PAYLOAD(cmd, NULL);
  294. free_pg_params(&pcmd->params);
  295. if (pcmd->types) PQclear(pcmd->types);
  296. if (pcmd->name) pkg_free(pcmd->name);
  297. if (pcmd->sql_cmd.s) pkg_free(pcmd->sql_cmd.s);
  298. db_drv_free(&pcmd->gen);
  299. pkg_free(pcmd);
  300. }
  301. return -1;
  302. }
  303. int pg_getopt(db_cmd_t* cmd, char* optname, va_list ap)
  304. {
  305. long long* id;
  306. if (!strcasecmp("last_id", optname)) {
  307. id = va_arg(ap, long long*);
  308. if (id == NULL) {
  309. BUG("postgres: NULL pointer passed to 'last_id' option\n");
  310. goto error;
  311. }
  312. return -1;
  313. } else {
  314. return 1;
  315. }
  316. return 0;
  317. error:
  318. return -1;
  319. }
  320. int pg_setopt(db_cmd_t* cmd, char* optname, va_list ap)
  321. {
  322. return 1;
  323. }
  324. /** Uploads a database command to PostgreSQL server.
  325. * This function uploads a pre-compiled database command to PostgreSQL
  326. * server using PQprepare.
  327. * @param cmd A database command
  328. * @retval 0 on success.
  329. * @retval A negative number on error.
  330. */
  331. static int upload_cmd(db_cmd_t* cmd)
  332. {
  333. struct pg_cmd* pcmd;
  334. struct pg_con* pcon;
  335. PGresult* res;
  336. int st;
  337. pcmd = DB_GET_PAYLOAD(cmd);
  338. /* FIXME: The function should take the connection as one of parameters */
  339. pcon = DB_GET_PAYLOAD(cmd->ctx->con[db_payload_idx]);
  340. DBG("postgres: Uploading command '%s': '%s'\n", pcmd->name,
  341. pcmd->sql_cmd.s);
  342. res = PQprepare(pcon->con, pcmd->name, pcmd->sql_cmd.s, (cmd->match_count + cmd->vals_count), NULL);
  343. st = PQresultStatus(res);
  344. if (st != PGRES_COMMAND_OK && st != PGRES_NONFATAL_ERROR &&
  345. st != PGRES_TUPLES_OK) {
  346. ERR("postgres: Error while uploading command to server: %d, %s",
  347. st, PQresultErrorMessage(res));
  348. ERR("postgres: Command: '%s'\n", pcmd->sql_cmd.s);
  349. PQclear(res);
  350. return -1;
  351. }
  352. PQclear(res);
  353. return 0;
  354. }
  355. int pg_cmd_exec(db_res_t* res, db_cmd_t* cmd)
  356. {
  357. PGresult* tmp;
  358. int i, err, stat;
  359. db_con_t* con;
  360. struct pg_cmd* pcmd;
  361. struct pg_con* pcon;
  362. struct pg_uri* puri;
  363. struct pg_res* pres;
  364. /* First things first: retrieve connection info from the currently active
  365. * connection and also mysql payload from the database command
  366. */
  367. con = cmd->ctx->con[db_payload_idx];
  368. pcmd = DB_GET_PAYLOAD(cmd);
  369. pcon = DB_GET_PAYLOAD(con);
  370. puri = DB_GET_PAYLOAD(con->uri);
  371. for(i = 0; i <= pg_retries; i++) {
  372. /* Convert parameters from DB-API format to the format accepted
  373. * by PostgreSQL */
  374. if (pg_fld2pg(&pcmd->params, 0, pcon->oid, cmd->match, pcon->flags) != 0)
  375. return 1;
  376. if (pg_fld2pg(&pcmd->params, cmd->match_count,
  377. pcon->oid, cmd->vals, pcon->flags) != 0) return 1;
  378. /* Execute the statement */
  379. tmp = PQexecPrepared(pcon->con, pcmd->name,
  380. pcmd->params.n,
  381. pcmd->params.val, pcmd->params.len,
  382. pcmd->params.fmt, 1);
  383. if (!tmp) {
  384. ERR("postgres: PQexecPrepared returned no result\n");
  385. continue;
  386. }
  387. switch(PQresultStatus(tmp)) {
  388. case PGRES_COMMAND_OK:
  389. case PGRES_NONFATAL_ERROR:
  390. case PGRES_TUPLES_OK:
  391. if (res) {
  392. pres = DB_GET_PAYLOAD(res);
  393. pres->res = tmp;
  394. pres->rows = PQntuples(tmp);
  395. } else {
  396. PQclear(tmp);
  397. }
  398. return 0;
  399. default:
  400. break;
  401. }
  402. ERR("postgres: Command on server %s failed: %s: %s\n",
  403. puri->host, PQresStatus(PQresultStatus(tmp)),
  404. PQresultErrorMessage(tmp));
  405. PQclear(tmp);
  406. /* Command failed, first of all determine the status of the connection
  407. * to server */
  408. if (PQstatus(pcon->con) != CONNECTION_OK) {
  409. INFO("postgres: Connection to server %s disconnected, attempting reconnect\n",
  410. puri->host);
  411. pg_con_disconnect(con);
  412. if (pg_con_connect(con)) {
  413. INFO("postgres: Failed to reconnect server %s, giving up\n",
  414. puri->host);
  415. return -1;
  416. }
  417. INFO("postgres: Successfully reconnected server on %s\n",
  418. puri->host);
  419. }
  420. /* Connection is either connected or has been successfully reconnected,
  421. * now figure out if the prepared command on the server still exist
  422. */
  423. tmp = PQdescribePrepared(pcon->con, pcmd->name);
  424. if (tmp == NULL) {
  425. ERR("postgres: PQdescribePrepared returned no result\n");
  426. continue;
  427. }
  428. stat = PQresultStatus(tmp);
  429. PQclear(tmp);
  430. switch (stat) {
  431. case PGRES_COMMAND_OK:
  432. case PGRES_NONFATAL_ERROR:
  433. case PGRES_TUPLES_OK:
  434. INFO("postgres: Command %s on server %s still exists, reusing\n",
  435. pcmd->name, puri->host);
  436. /* Command is there, retry */
  437. continue;
  438. default:
  439. break;
  440. }
  441. /* Upload again */
  442. INFO("postgres: Command %s on server %s missing, uploading\n",
  443. pcmd->name, puri->host);
  444. err = upload_cmd(cmd);
  445. if (err < 0) {
  446. continue;
  447. } else if (err > 0) {
  448. /* DB API error, this is a serious problem such
  449. * as memory allocation failure, bail out
  450. */
  451. return 1;
  452. }
  453. }
  454. INFO("postgres: Failed to execute command %s on server %s, giving up\n",
  455. pcmd->name, puri->host);
  456. return -1;
  457. }
  458. int pg_cmd_first(db_res_t* res)
  459. {
  460. struct pg_res* pres;
  461. pres = DB_GET_PAYLOAD(res);
  462. if (pres->rows <= 0) return 1; /* Empty table */
  463. pres->row = 0;
  464. return pg_cmd_next(res);
  465. }
  466. int pg_cmd_next(db_res_t* res)
  467. {
  468. struct pg_res* pres;
  469. struct pg_con* pcon;
  470. pres = DB_GET_PAYLOAD(res);
  471. pcon = DB_GET_PAYLOAD(res->cmd->ctx->con[db_payload_idx]);
  472. if (pres->row >= pres->rows) return 1;
  473. if (pg_pg2fld(res->cmd->result, pres->res, pres->row, pcon->oid, pcon->flags)) return -1;
  474. res->cur_rec->fld = res->cmd->result;
  475. pres->row++;
  476. return 0;
  477. }
  478. /** @} */