driver-pgsql.c revision 840a3701b7a0f7fadd17738998c33790a8dfad2d
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2004-2007 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "ioloop-internal.h" /* kind of dirty, but it should be fine.. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned char *value;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char **fields;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen const char **values;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ARRAY_DEFINE(binary_values, struct pgsql_binary_value);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic void queue_send_next(struct pgsql_db *db);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic void result_finish(struct pgsql_result *result);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic void driver_pgsql_close(struct pgsql_db *db)
407caeb5d0c8a6b158e2caef48dd909011d40340Timo Sirainenstatic const char *last_error(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *msg;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return "(no error set)";
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen /* Error message should contain trailing \n, we don't want it */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void connect_callback(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainen i_info("pgsql: Connected to %s", PQdb(db->pg));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen io_add(PQsocket(db->pg), io_dir, connect_callback, db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_pgsql_connect(struct sql_db *_db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* don't try reconnecting more than once a second */
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen if (db->connecting || db->last_connect == now)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return db->connected ? 1 : (db->connecting ? 0 : -1);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_fatal("pgsql: PQconnectStart() failed (out of memory)");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* nonblocking connecting begins. */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic struct sql_db *driver_pgsql_init_v(const char *connect_string)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->connect_string = i_strdup(connect_string);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_deinit_v(struct sql_db *_db)
4c9c55e15f35474f53f11659e796c63b1c34e884Timo Sirainendriver_pgsql_get_flags(struct sql_db *db ATTR_UNUSED)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void consume_results(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_result_free(struct sql_result *_result)
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen /* we'll have to read the rest of the results as well */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (array_is_created(&result->binary_values)) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i, count;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen values = array_get_modifiable(&result->binary_values, &count);
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen for (i = 0; i < count; i++)
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (db->queue != NULL && !db->querying && db->connected)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void result_finish(struct pgsql_result *result)
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->callback(&result->api, result->context);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen free_result = db->sync_result != &result->api;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* disconnected */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void get_result(struct pgsql_result *result)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void flush_callback(struct pgsql_result *result)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* all flushed */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void send_query(struct pgsql_result *result, const char *query)
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen /* write blocks */
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainenstatic struct pgsql_queue *queue_unlink_first(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void queue_send_next(struct pgsql_db *db)
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainenstatic void queue_abort_next(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen queue->result->callback(&sql_not_connected_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void queue_drop_timed_out_queries(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->queue->created + QUERY_TIMEOUT_SECS < ioloop_time)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_queue_query(struct pgsql_result *result, const char *query)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->queue_to = timeout_add(5000, queue_timeout, db);
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainenstatic void do_query(struct pgsql_result *result, const char *query)
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* only one query at a time */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* try connecting again */
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen /* there's already queries queued, send them first */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void exec_callback(struct sql_result *result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("pgsql: sql_exec() failed: %s", last_error(db));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_escape_string(struct sql_db *_db, const char *string)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* try connecting again */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen len = PQescapeStringConn(db->pg, to, string, len, NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_exec(struct sql_db *db, const char *query)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic void driver_pgsql_query(struct sql_db *db, const char *query,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query_callback_t *callback, void *context)
36c4702131e5a04984ad5d07cf5d8d5c633d43c3Timo Sirainenstatic void pgsql_query_s_callback(struct sql_result *result, void *context)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic struct sql_result *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_pgsql_query_s(struct sql_db *_db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* have to move our existing I/O handler to new I/O loop */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->io = io_add(PQsocket(db->pg), old_io.condition,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_query(_db, query, pgsql_query_s_callback, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->sync_result == &sql_not_connected_result);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic int driver_pgsql_result_next_row(struct sql_result *_result)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* second time we're here */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* no rows returned */
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen /* nonfatal error */
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen /* treat as fatal error */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* @UNSAFE */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields_count = PQnfields(result->pgres);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields = i_new(const char *, result->fields_count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields[i] = PQfname(result->pgres, i);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic unsigned int
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_fields_count(struct sql_result *_result)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_pgsql_result_find_field(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (strcmp(result->fields[i], field_name) == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_field_value(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return PQgetvalue(result->pgres, result->rownum, idx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const unsigned char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_field_value_binary(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen value = PQgetvalue(result->pgres, result->rownum, idx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!array_is_created(&result->binary_values))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_array_init(&result->binary_values, idx + 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen binary_value = array_idx_modifiable(&result->binary_values, idx);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenstatic const char *
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainendriver_pgsql_result_find_field_value(struct sql_result *result,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen idx = driver_pgsql_result_find_field(result, field_name);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen return driver_pgsql_result_get_field_value(result, idx);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenstatic const char *const *
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainendriver_pgsql_result_get_values(struct sql_result *_result)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen unsigned int i;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen result->values = i_new(const char *, result->fields_count);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen /* @UNSAFE */
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_field_value(_result, i);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *driver_pgsql_result_get_error(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *msg;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return "(no error set)";
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* Error message should contain trailing \n, we don't want it */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_transaction_begin(struct sql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ctx = i_new(struct pgsql_transaction_context, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainentransaction_commit_callback(struct sql_result *result, void *context)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainendriver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen sql_commit_callback_t *callback, void *context)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen /* nothing done */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_query(_ctx->db, "COMMIT", transaction_commit_callback, ctx);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainendriver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen const char **error_r)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainentransaction_update_callback(struct sql_result *result,
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainendriver_pgsql_update(struct sql_transaction_context *_ctx, const char *query)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_query(_ctx->db, "BEGIN", transaction_update_callback, ctx);