pg_con.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. * Functions related to connections to PostgreSQL servers.
  35. */
  36. #include "pg_con.h"
  37. #include "pg_uri.h"
  38. #include "pg_sql.h"
  39. #include "../../mem/mem.h"
  40. #include "../../dprint.h"
  41. #include "../../ut.h"
  42. #include <stdlib.h>
  43. #include <string.h>
  44. #include <netinet/in.h>
  45. #include <time.h>
  46. /* Override the default notice processor to output the messages
  47. * using SER's output subsystem.
  48. */
  49. static void notice_processor(void* arg, const char* message)
  50. {
  51. LOG(L_NOTICE, "postgres: %s\n", message);
  52. }
  53. /** Determine the format of timestamps used by the server.
  54. * A PostgresSQL server can be configured to store timestamps either as 8-byte
  55. * integers or floating point numbers with double precision. This functions
  56. * sends a simple SQL query to the server and tries to determine the format of
  57. * timestamps from the reply. This function is executed once after connecting
  58. * to a PostgreSQL server and the result of the detection is then stored in
  59. * form of a flag in pg_con connection structure.
  60. * @param con A PostgreSQL connection handle
  61. * @retval 0 If the server stores timestamps as floating point numbers.
  62. * @retval 1 If the server stores timestamps as 8-byte integers.
  63. * @retval A negative number on error.
  64. */
  65. static int timestamp_format(PGconn* con)
  66. {
  67. unsigned long long offset;
  68. PGresult* res = 0;
  69. char* val;
  70. str sql;
  71. if (build_timestamp_format_sql(&sql) != 0) {
  72. ERR("postgres: Error while building SQL query to obtain timestamp format\n");
  73. return -1;
  74. }
  75. res = PQexecParams(con, sql.s, 0, 0, 0, 0, 0, 1);
  76. pkg_free(sql.s);
  77. if (PQfformat(res, 0) != 1) {
  78. ERR("postgres: Binary format expected but server sent text\n");
  79. goto error;
  80. }
  81. if (PQntuples(res) != 1) {
  82. ERR("postgres: Only one column expected, %d received\n", PQntuples(res));
  83. goto error;
  84. }
  85. if (PQnfields(res) != 1) {
  86. ERR("postgres: Only one row expected, %d received\n", PQnfields(res));
  87. goto error;
  88. }
  89. val = PQgetvalue(res, 0, 0);
  90. offset = ((unsigned long long)ntohl(((unsigned int*)val)[0]) << 32)
  91. + ntohl(((unsigned int*)val)[1]);
  92. PQclear(res);
  93. /* Server using int8 timestamps would return 1000000, because it stores
  94. * timestamps in microsecond resolution across the whole range. Server
  95. * using double timestamps would return 1 (encoded as double) here because
  96. * subsection fraction is stored as fractional part in the IEEE
  97. * representation. 1 stored as double would result in 4607182418800017408
  98. * when the memory location occupied by the variable is read as unsigned
  99. * long long.
  100. */
  101. if (offset == 1000000) {
  102. DBG("postgres: Server uses int8 format for timestamps.\n");
  103. return 1;
  104. } else {
  105. DBG("postgres: Server uses double format for timestamps.\n");
  106. return 0;
  107. }
  108. error:
  109. PQclear(res);
  110. return -1;
  111. }
  112. /** Retrieves a list of all supported field types from the server.
  113. * This function retrieves a list of all supported field types and their Oids
  114. * from system catalogs of the server. The list is then stored in pg_con
  115. * connection structure and it is used to map field type names, such as int2,
  116. * int4, float4, etc. to Oids. Every PostgreSQL server can map field types to
  117. * different Oids so we need to store the mapping array in the connection
  118. * structure.
  119. * @param con A structure representing connection to PostgreSQL server.
  120. * @retval 0 If executed successfully.
  121. * @retval A negative number on error.
  122. */
  123. static int get_oids(db_con_t* con)
  124. {
  125. struct pg_con* pcon;
  126. PGresult* res = NULL;
  127. str sql;
  128. pcon = DB_GET_PAYLOAD(con);
  129. if (build_select_oid_sql(&sql) < 0) goto error;
  130. res = PQexec(pcon->con, sql.s);
  131. pkg_free(sql.s);
  132. if (res == NULL || PQresultStatus(res) != PGRES_TUPLES_OK) goto error;
  133. pcon->oid = pg_new_oid_table(res);
  134. PQclear(res);
  135. if (pcon->oid == NULL) goto error;
  136. return 0;
  137. error:
  138. if (res) PQclear(res);
  139. return -1;
  140. }
  141. /** Free all memory allocated for a pg_con structure.
  142. * This function function frees all memory that is in use by
  143. * a pg_con structure.
  144. * @param con A generic db_con connection structure.
  145. * @param payload PostgreSQL specific payload to be freed.
  146. */
  147. static void pg_con_free(db_con_t* con, struct pg_con* payload)
  148. {
  149. if (!payload) return;
  150. /* Delete the structure only if there are no more references
  151. * to it in the connection pool
  152. */
  153. if (db_pool_remove((db_pool_entry_t*)payload) == 0) return;
  154. db_pool_entry_free(&payload->gen);
  155. pg_destroy_oid_table(payload->oid);
  156. if (payload->con) PQfinish(payload->con);
  157. pkg_free(payload);
  158. }
  159. int pg_con(db_con_t* con)
  160. {
  161. struct pg_con* pcon;
  162. /* First try to lookup the connection in the connection pool and
  163. * re-use it if a match is found
  164. */
  165. pcon = (struct pg_con*)db_pool_get(con->uri);
  166. if (pcon) {
  167. DBG("postgres: Connection to %.*s:%.*s found in connection pool\n",
  168. con->uri->scheme.len, ZSW(con->uri->scheme.s),
  169. con->uri->body.len, ZSW(con->uri->body.s));
  170. goto found;
  171. }
  172. pcon = (struct pg_con*)pkg_malloc(sizeof(struct pg_con));
  173. if (!pcon) {
  174. LOG(L_ERR, "postgres: No memory left\n");
  175. goto error;
  176. }
  177. memset(pcon, '\0', sizeof(struct pg_con));
  178. if (db_pool_entry_init(&pcon->gen, pg_con_free, con->uri) < 0) goto error;
  179. DBG("postgres: Preparing new connection to: %.*s:%.*s\n",
  180. con->uri->scheme.len, ZSW(con->uri->scheme.s),
  181. con->uri->body.len, ZSW(con->uri->body.s));
  182. /* Put the newly created postgres connection into the pool */
  183. db_pool_put((struct db_pool_entry*)pcon);
  184. DBG("postgres: Connection stored in connection pool\n");
  185. found:
  186. /* Attach driver payload to the db_con structure and set connect and
  187. * disconnect functions
  188. */
  189. DB_SET_PAYLOAD(con, pcon);
  190. con->connect = pg_con_connect;
  191. con->disconnect = pg_con_disconnect;
  192. return 0;
  193. error:
  194. if (pcon) {
  195. db_pool_entry_free(&pcon->gen);
  196. pkg_free(pcon);
  197. }
  198. return -1;
  199. }
  200. int pg_con_connect(db_con_t* con)
  201. {
  202. struct pg_con* pcon;
  203. struct pg_uri* puri;
  204. char* port_str;
  205. int ret;
  206. pcon = DB_GET_PAYLOAD(con);
  207. puri = DB_GET_PAYLOAD(con->uri);
  208. /* Do not reconnect already connected connections */
  209. if (pcon->flags & PG_CONNECTED) return 0;
  210. DBG("postgres: Connecting to %.*s:%.*s\n",
  211. con->uri->scheme.len, ZSW(con->uri->scheme.s),
  212. con->uri->body.len, ZSW(con->uri->body.s));
  213. if (puri->port > 0) {
  214. port_str = int2str(puri->port, 0);
  215. } else {
  216. port_str = NULL;
  217. }
  218. if (pcon->con) {
  219. PQfinish(pcon->con);
  220. pcon->con = NULL;
  221. }
  222. pcon->con = PQsetdbLogin(puri->host, port_str,
  223. NULL, NULL, puri->database,
  224. puri->username, puri->password);
  225. if (pcon->con == NULL) {
  226. ERR("postgres: PQsetdbLogin ran out of memory\n");
  227. goto error;
  228. }
  229. if (PQstatus(pcon->con) != CONNECTION_OK) {
  230. ERR("postgres: %s\n", PQerrorMessage(pcon->con));
  231. goto error;
  232. }
  233. /* Override default notice processor */
  234. PQsetNoticeProcessor(pcon->con, notice_processor, 0);
  235. #ifdef HAVE_PGSERVERVERSION
  236. DBG("postgres: Connected. Protocol version=%d, Server version=%d\n",
  237. PQprotocolVersion(pcon->con), PQserverVersion(pcon->con));
  238. #else
  239. DBG("postgres: Connected. Protocol version=%d, Server version=%d\n",
  240. PQprotocolVersion(pcon->con), 0 );
  241. #endif
  242. ret = timestamp_format(pcon->con);
  243. if (ret == 1 || ret == -1) {
  244. /* Assume INT8 representation if detection fails */
  245. pcon->flags |= PG_INT8_TIMESTAMP;
  246. } else {
  247. pcon->flags &= ~PG_INT8_TIMESTAMP;
  248. }
  249. if (get_oids(con) < 0) goto error;
  250. pcon->flags |= PG_CONNECTED;
  251. return 0;
  252. error:
  253. if (pcon->con) PQfinish(pcon->con);
  254. pcon->con = NULL;
  255. return -1;
  256. }
  257. void pg_con_disconnect(db_con_t* con)
  258. {
  259. struct pg_con* pcon;
  260. pcon = DB_GET_PAYLOAD(con);
  261. if ((pcon->flags & PG_CONNECTED) == 0) return;
  262. DBG("postgres: Disconnecting from %.*s:%.*s\n",
  263. con->uri->scheme.len, ZSW(con->uri->scheme.s),
  264. con->uri->body.len, ZSW(con->uri->body.s));
  265. PQfinish(pcon->con);
  266. pcon->con = NULL;
  267. pcon->flags &= ~PG_CONNECTED;
  268. pcon->flags &= ~PG_INT8_TIMESTAMP;
  269. }
  270. /** @} */