driver-pgsql.c revision 9349a0afffad990e45d3ad33081e1d2d9e68a753
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (c) 2004-2010 Dovecot authors, see the included COPYING file */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "lib.h"
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen#include "array.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "ioloop.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include "sql-api-private.h"
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen#ifdef BUILD_PGSQL
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <stdlib.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#include <libpq-fe.h>
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstruct pgsql_db {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct sql_db api;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen pool_t pool;
22627da0fb77c1d0d9a8e8bc485ef5540b6f2e69Timo Sirainen char *connect_string;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen char *host;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PGconn *pg;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct io *io;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct timeout *to_connect;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen enum io_condition io_dir;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_result *cur_result;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen struct ioloop *ioloop, *orig_ioloop;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct sql_result *sync_result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen char *error;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int fatal_error:1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen};
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenstruct pgsql_binary_value {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen unsigned char *value;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen size_t size;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen};
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstruct pgsql_result {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct sql_result api;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PGresult *pgres;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct timeout *to;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int rownum, rows;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int fields_count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char **fields;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char **values;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen ARRAY_DEFINE(binary_values, struct pgsql_binary_value);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen sql_query_callback_t *callback;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen void *context;
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int timeout:1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen};
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstruct pgsql_transaction_context {
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct sql_transaction_context ctx;
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen int refcount;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_commit_callback_t *callback;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen void *context;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen pool_t query_pool;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const char *error;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen unsigned int begin_succeeded:1;
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen unsigned int begin_failed:1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen unsigned int failed:1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen};
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainenextern const struct sql_db driver_pgsql_db;
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainenextern const struct sql_result driver_pgsql_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainenstatic void result_finish(struct pgsql_result *result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainenstatic void driver_pgsql_set_state(struct pgsql_db *db, enum sql_db_state state)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen{
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen /* switch back to original ioloop in case the caller wants to
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen add/remove timeouts */
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen if (db->ioloop != NULL)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen current_ioloop = db->orig_ioloop;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen sql_db_set_state(&db->api, state);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen if (db->ioloop != NULL)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen current_ioloop = db->ioloop;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen}
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainenstatic void driver_pgsql_stop_io(struct pgsql_db *db)
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen{
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (db->io != NULL) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen io_remove(&db->io);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io_dir = 0;
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen }
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen}
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_close(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->io_dir = 0;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->fatal_error = FALSE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PQfinish(db->pg);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->pg = NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen driver_pgsql_stop_io(db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->to_connect != NULL)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen timeout_remove(&db->to_connect);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_DISCONNECTED);
a54fa00087ba926a3d966a8449d8d7579e89911cTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->ioloop != NULL) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* running a sync query, stop it */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_stop(db->ioloop);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *last_error(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *msg;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen size_t len;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen msg = PQerrorMessage(db->pg);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (msg == NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return "(no error set)";
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* Error message should contain trailing \n, we don't want it */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen len = strlen(msg);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return len == 0 || msg[len-1] != '\n' ? msg :
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen t_strndup(msg, len-1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void connect_callback(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen enum io_condition io_dir = 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen driver_pgsql_stop_io(db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen switch (ret) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_POLLING_READING:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen io_dir = IO_READ;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen break;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_POLLING_WRITING:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen io_dir = IO_WRITE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen break;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_POLLING_OK:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen break;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_POLLING_FAILED:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_error("pgsql: Connect failed to %s: %s",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PQdb(db->pg), last_error(db));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_close(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (io_dir != 0) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io = io_add(PQsocket(db->pg), io_dir, connect_callback, db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->io_dir = io_dir;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (io_dir == 0) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_info("pgsql: Connected to %s", PQdb(db->pg));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->to_connect != NULL)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen timeout_remove(&db->to_connect);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->ioloop != NULL) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* driver_pgsql_sync_init() waiting for connection to
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen finish */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_stop(db->ioloop);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_connect_timeout(struct pgsql_db *db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen const char *dbname = PQdb(db->pg);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen unsigned int secs = ioloop_time - db->api.last_connect_try;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("pgsql: Connect failed to %s: Timeout after %u seconds",
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen dbname != NULL ? dbname : db->host, secs);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_close(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainenstatic int driver_pgsql_connect(struct sql_db *_db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->pg = PQconnectStart(db->connect_string);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (db->pg == NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_fatal("pgsql: PQconnectStart() failed (out of memory)");
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_error("pgsql: Connect failed to %s: %s",
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PQdb(db->pg), last_error(db));
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_close(db);
a0b89f3b1df99b3a32f44623f13ad1893118825bTimo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* nonblocking connecting begins. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (PQsetnonblocking(db->pg, 1) < 0)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("pgsql: PQsetnonblocking() failed");
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen i_assert(db->to_connect == NULL);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_connect_timeout, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE, connect_callback, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->io_dir = IO_WRITE;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_CONNECTING);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return 0;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_disconnect(struct sql_db *_db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainen if (db->cur_result != NULL && db->cur_result->to != NULL)
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainen result_finish(db->cur_result);
9349a0afffad990e45d3ad33081e1d2d9e68a753Timo Sirainen
4c9c55e15f35474f53f11659e796c63b1c34e884Timo Sirainen _db->no_reconnect = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_close(db);
4c9c55e15f35474f53f11659e796c63b1c34e884Timo Sirainen _db->no_reconnect = FALSE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic struct sql_db *driver_pgsql_init_v(const char *connect_string)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db = i_new(struct pgsql_db, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->connect_string = i_strdup(connect_string);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->api = driver_pgsql_db;
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen T_BEGIN {
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen const char *const *arg = t_strsplit(connect_string, " ");
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen for (; *arg != NULL; arg++) {
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen if (strncmp(*arg, "host=", 5) == 0)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen db->host = i_strdup(*arg + 5);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen }
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen } T_END;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return &db->api;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a94936bafd127680184da114c6a177b37ff656e5Timo Sirainenstatic void driver_pgsql_deinit_v(struct sql_db *_db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->cur_result != NULL && db->cur_result->to != NULL)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result_finish(db->cur_result);
d16b506f5540e3407d256bda35624b38a5ecf88fTimo Sirainen
4c9c55e15f35474f53f11659e796c63b1c34e884Timo Sirainen _db->no_reconnect = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_close(db);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen i_free(db->host);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen i_free(db->error);
22627da0fb77c1d0d9a8e8bc485ef5540b6f2e69Timo Sirainen i_free(db->connect_string);
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen array_free(&_db->module_contexts);
3e28b527dd6048a40684afd29cff0ee008fc0014Timo Sirainen i_free(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_set_idle(struct pgsql_db *db)
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_BUSY);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->fatal_error)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_close(db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen else
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen}
ccffb125d94adff0ad776de5a96e22f864d6fb0aTimo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void consume_results(struct pgsql_db *db)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen PGresult *pgres;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen driver_pgsql_stop_io(db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen while (PQconsumeInput(db->pg)) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (PQisBusy(db->pg)) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen consume_results, db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io_dir = IO_READ;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen }
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen pgres = PQgetResult(db->pg);
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen if (pgres == NULL)
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen break;
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen PQclear(pgres);
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_close(db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen else
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_set_idle(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void driver_pgsql_result_free(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen bool success;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (result->api.callback) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* we're coming here from a user's sql_result_free() that's
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen being called from a callback. we'll do this later,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen so ignore. */
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->cur_result == result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(result->callback == NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen if (_result == db->sync_result)
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen db->sync_result = NULL;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->cur_result = NULL;
a1aaf11831cab8346d6d0dc702e37b3f1d95eb43Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen success = result->pgres != NULL && !db->fatal_error;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (result->pgres != NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen PQclear(result->pgres);
c44f402f17f9a58ead24ac0083945cae86fb172bTimo Sirainen result->pgres = NULL;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (success) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* we'll have to read the rest of the results as well */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(db->io == NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen consume_results(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_set_idle(db);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (array_is_created(&result->binary_values)) {
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen struct pgsql_binary_value *value;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen array_foreach_modifiable(&result->binary_values, value)
e20e638805c4bd54e039891a3e92760b1dfa189aTimo Sirainen PQfreemem(value->value);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen array_free(&result->binary_values);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(result->fields);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(result->values);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_free(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void result_finish(struct pgsql_result *result)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen bool free_result = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen i_assert(db->io == NULL);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen timeout_remove(&result->to);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen /* if connection to server was lost, we don't yet see that the
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen connection is bad. we only see the fatal error, so assume it also
e958a3c4573058f17999f0083a34080ca35e34d8Timo Sirainen means disconnection. */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD || result->pgres == NULL ||
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen PQresultStatus(result->pgres) == PGRES_FATAL_ERROR)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->fatal_error = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->fatal_error) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->api.failed = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->api.failed_try_retry = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->api.callback = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen T_BEGIN {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->callback(&result->api, result->context);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } T_END;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->api.callback = FALSE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->callback = NULL;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen free_result = db->sync_result != &result->api;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->ioloop != NULL)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_stop(db->ioloop);
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen
68f0dfb4b2815ecbc1bd8d8a68adcfd577ec55aeTimo Sirainen if (free_result)
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen sql_result_unref(&result->api);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void get_result(struct pgsql_result *result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen driver_pgsql_stop_io(db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (!PQconsumeInput(db->pg)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result_finish(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (PQisBusy(db->pg)) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen get_result, result);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io_dir = IO_READ;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->pgres = PQgetResult(db->pg);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result_finish(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
2cfe9983ce7a6280636ee12beccc2e865111967bTimo Sirainenstatic void flush_callback(struct pgsql_result *result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen driver_pgsql_stop_io(db);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen ret = PQflush(db->pg);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen if (ret > 0) {
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen flush_callback, result);
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen db->io_dir = IO_WRITE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret < 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result_finish(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* all flushed */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen get_result(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void query_timeout(struct pgsql_result *result)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("pgsql: Query timed out, aborting");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->timeout = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result_finish(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void do_query(struct pgsql_result *result, const char *query)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int ret;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(SQL_DB_IS_READY(&db->api));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->cur_result == NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(db->io == NULL);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->cur_result = result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result->to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen query_timeout, result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (!PQsendQuery(db->pg, query) ||
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen (ret = PQflush(db->pg)) < 0) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* failed to send query */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result_finish(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_BUSY);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (ret > 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* write blocks */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen flush_callback, result);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen db->io_dir = IO_WRITE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen } else {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen get_result(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
13a8c553f293349248b161ff851743498916e26eTimo Sirainenstatic const char *
13a8c553f293349248b161ff851743498916e26eTimo Sirainendriver_pgsql_escape_string(struct sql_db *_db, const char *string)
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen{
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen size_t len = strlen(string);
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen char *to;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen#ifdef HAVE_PQESCAPE_STRING_CONN
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->api.state == SQL_DB_STATE_DISCONNECTED) {
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen /* try connecting again */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen (void)sql_connect(&db->api);
9fcf7b79236b0045f7709718f7b65ada516565e7Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->api.state != SQL_DB_STATE_DISCONNECTED) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen to = t_buffer_get(len * 2 + 1);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen len = PQescapeStringConn(db->pg, to, string, len, NULL);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen#endif
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen to = t_buffer_get(len * 2 + 1);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen len = PQescapeString(to, string, len);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen t_buffer_alloc(len + 1);
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen return to;
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen}
24ce0c343cefe54af841871fa39dbc3464028b06Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void exec_callback(struct sql_result *_result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen void *context ATTR_UNUSED)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_error("pgsql: sql_exec() failed: %s", last_error(db));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_exec(struct sql_db *db, const char *query)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result = i_new(struct pgsql_result, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->api = driver_pgsql_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->api.db = db;
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen result->api.refcount = 1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->callback = exec_callback;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen do_query(result, query);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_query(struct sql_db *db, const char *query,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query_callback_t *callback, void *context)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result = i_new(struct pgsql_result, 1);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->api = driver_pgsql_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->api.db = db;
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen result->api.refcount = 1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->callback = callback;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->context = context;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen do_query(result, query);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void pgsql_query_s_callback(struct sql_result *result, void *context)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_db *db = context;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen db->sync_result = result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_sync_init(struct pgsql_db *db)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen db->orig_ioloop = current_ioloop;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->io == NULL) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->ioloop = io_loop_create();
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return;
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen }
5fe06fea9fee0f5e4e9cb49f6866877223f78b85Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_CONNECTING);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen /* have to move our existing I/O and timeout handlers to new I/O loop */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_remove(&db->io);
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen if (db->to_connect != NULL)
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen timeout_remove(&db->to_connect);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->ioloop = io_loop_create();
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
ab1b9a793d57a60c230a41f65f1a25d52c026233Timo Sirainen driver_pgsql_connect_timeout, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->io = io_add(PQsocket(db->pg), db->io_dir, connect_callback, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* wait for connecting to finish */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_run(db->ioloop);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void driver_pgsql_sync_deinit(struct pgsql_db *db)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_destroy(&db->ioloop);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic struct sql_result *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_pgsql_sync_query(struct pgsql_db *db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_result *result;
840a3701b7a0f7fadd17738998c33790a8dfad2dTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(db->sync_result == NULL);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen switch (db->api.state) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen case SQL_DB_STATE_CONNECTING:
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen case SQL_DB_STATE_BUSY:
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_unreached();
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen case SQL_DB_STATE_DISCONNECTED:
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_not_connected_result.refcount++;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return &sql_not_connected_result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen case SQL_DB_STATE_IDLE:
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen break;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_query(&db->api, query, pgsql_query_s_callback, db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (db->sync_result == NULL)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen io_loop_run(db->ioloop);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
f6e301cb2060c4367d8145e2bf5d553ba87ceb34Timo Sirainen i_assert(db->io == NULL);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen result = db->sync_result;
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen if (result == &sql_not_connected_result) {
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen /* we don't end up in pgsql's free function, so sync_result
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen won't be set to NULL if we don't do it here. */
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen db->sync_result = NULL;
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen i_assert(db->io == NULL);
06f537a8e0b399222cc2a7755015ef3963525fd2Timo Sirainen return result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic struct sql_result *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_pgsql_query_s(struct sql_db *_db, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_result *result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_sync_init(db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result = driver_pgsql_sync_query(db, query);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_sync_deinit(db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_pgsql_result_next_row(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (result->rows != 0) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* second time we're here */
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen if (++result->rownum < result->rows)
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen return 1;
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen /* end of this packet. see if there's more. FIXME: this may
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen block, but the current API doesn't provide a non-blocking
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen way to do this.. */
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen PQclear(result->pgres);
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen result->pgres = PQgetResult(db->pg);
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen if (result->pgres == NULL)
26cdaf7097427fa90343260fa236af12ab93cca3Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (result->pgres == NULL) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen _result->failed = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen switch (PQresultStatus(result->pgres)) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_COMMAND_OK:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* no rows returned */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_TUPLES_OK:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->rows = PQntuples(result->pgres);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return result->rows > 0;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_EMPTY_QUERY:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen case PGRES_NONFATAL_ERROR:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* nonfatal error */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen _result->failed = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen default:
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* treat as fatal error */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen _result->failed = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->fatal_error = TRUE;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (result->fields != NULL)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* @UNSAFE */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields_count = PQnfields(result->pgres);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields = i_new(const char *, result->fields_count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < result->fields_count; i++)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->fields[i] = PQfname(result->pgres, i);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic unsigned int
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_fields_count(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_result_fetch_fields(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return result->fields_count;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_result_fetch_fields(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen i_assert(idx < result->fields_count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return result->fields[idx];
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic int driver_pgsql_result_find_field(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *field_name)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_result_fetch_fields(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < result->fields_count; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (strcmp(result->fields[i], field_name) == 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return -1;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_field_value(struct sql_result *_result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int idx)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx))
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return PQgetvalue(result->pgres, result->rownum, idx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainenstatic const unsigned char *
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainendriver_pgsql_result_get_field_value_binary(struct sql_result *_result,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen unsigned int idx, size_t *size_r)
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen{
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen const char *value;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen struct pgsql_binary_value *binary_value;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx)) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen *size_r = 0;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen return NULL;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen value = PQgetvalue(result->pgres, result->rownum, idx);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (!array_is_created(&result->binary_values))
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen i_array_init(&result->binary_values, idx + 1);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen binary_value = array_idx_modifiable(&result->binary_values, idx);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen if (binary_value->value == NULL) {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen binary_value->value =
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen PQunescapeBytea((const unsigned char *)value,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen &binary_value->size);
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen *size_r = binary_value->size;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen return binary_value->value;
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen}
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_find_field_value(struct sql_result *result,
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *field_name)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen int idx;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen idx = driver_pgsql_result_find_field(result, field_name);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (idx < 0)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return NULL;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return driver_pgsql_result_get_field_value(result, idx);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *const *
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainendriver_pgsql_result_get_values(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen unsigned int i;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen if (result->values == NULL) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_result_fetch_fields(result);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->values = i_new(const char *, result->fields_count);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen /* @UNSAFE */
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen for (i = 0; i < result->fields_count; i++) {
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen result->values[i] =
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen driver_pgsql_result_get_field_value(_result, i);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen return result->values;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainenstatic const char *driver_pgsql_result_get_error(struct sql_result *_result)
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen{
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen const char *msg;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen size_t len;
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen i_free_and_null(db->error);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (result->timeout) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen db->error = i_strdup("Query timed out");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else if (result->pgres == NULL) {
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen /* connection error */
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen db->error = i_strdup(last_error(db));
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen } else {
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen msg = PQresultErrorMessage(result->pgres);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen if (msg == NULL)
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen return "(no error set)";
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen /* Error message should contain trailing \n, we don't want it */
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen len = strlen(msg);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen db->error = len == 0 || msg[len-1] != '\n' ?
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen i_strdup(msg) : i_strndup(msg, len-1);
9672bb2a11c37c275d695451accd824da5c9e485Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return db->error;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic struct sql_transaction_context *
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_pgsql_transaction_begin(struct sql_db *db)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_transaction_context *ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx = i_new(struct pgsql_transaction_context, 1);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->ctx.db = db;
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen ctx->refcount = 1;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* we need to be able to handle multiple open transactions, so at least
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen for now just keep them in memory until commit time. */
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ctx->query_pool = pool_alloconly_create("pgsql transaction", 1024);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return &ctx->ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainenstatic void
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainendriver_pgsql_transaction_unref(struct pgsql_transaction_context *ctx)
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen{
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen i_assert(ctx->refcount > 0);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen if (--ctx->refcount > 0)
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen return;
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen pool_unref(&ctx->query_pool);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen i_free(ctx);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen}
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainentransaction_begin_callback(struct sql_result *result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_transaction_context *ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen if (sql_result_next_row(result) < 0) {
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen ctx->begin_failed = TRUE;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen ctx->failed = TRUE;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen ctx->error = sql_result_get_error(result);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen } else {
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen ctx->begin_succeeded = TRUE;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen }
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen driver_pgsql_transaction_unref(ctx);
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainenstatic void
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainentransaction_commit_callback(struct sql_result *result,
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen struct pgsql_transaction_context *ctx)
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen if (sql_result_next_row(result) < 0)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen else
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen ctx->callback(NULL, ctx->context);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen driver_pgsql_transaction_unref(ctx);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen}
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainenstatic void
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainentransaction_update_callback(struct sql_result *result,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_transaction_query *query)
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_transaction_context *ctx =
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen (struct pgsql_transaction_context *)query->trans;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen if (sql_result_next_row(result) < 0) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ctx->failed = TRUE;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ctx->error = sql_result_get_error(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else if (query->affected_rows != NULL) {
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen struct pgsql_result *pg_result = (struct pgsql_result *)result;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen *query->affected_rows = atoi(PQcmdTuples(pg_result->pgres));
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen driver_pgsql_transaction_unref(ctx);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen sql_commit_callback_t *callback, void *context)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_transaction_context *ctx =
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (struct pgsql_transaction_context *)_ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen ctx->callback = callback;
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen ctx->context = context;
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (ctx->failed || _ctx->head == NULL) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen callback(ctx->failed ? ctx->error : NULL, context);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen driver_pgsql_transaction_unref(ctx);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else if (_ctx->head->next == NULL) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* just a single query, send it */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query(_ctx->db, _ctx->head->query,
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen transaction_commit_callback, ctx);
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen } else {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* multiple queries, use a transaction */
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ctx->refcount++;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen sql_query(_ctx->db, "BEGIN", transaction_begin_callback, ctx);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen while (_ctx->head != NULL) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen ctx->refcount++;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_query(_ctx->db, _ctx->head->query,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen transaction_update_callback, _ctx->head);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen _ctx->head = _ctx->head->next;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen sql_query(_ctx->db, "COMMIT", transaction_commit_callback, ctx);
28cddf411c475eb8bb84b4023398bb12346ce5adTimo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic void
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainencommit_multi_fail(struct pgsql_transaction_context *ctx,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_result *result, const char *query)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ctx->failed = TRUE;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen ctx->error = t_strdup_printf("%s (query: %s)",
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_result_get_error(result), query);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_result_unref(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainenstatic struct sql_result *
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainendriver_pgsql_transaction_commit_multi(struct pgsql_transaction_context *ctx)
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen{
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)ctx->ctx.db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_result *result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_transaction_query *query;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result = driver_pgsql_sync_query(db, "BEGIN");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (sql_result_next_row(result) < 0) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen commit_multi_fail(ctx, result, "BEGIN");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return NULL;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_result_unref(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen /* send queries */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen for (query = ctx->ctx.head; query != NULL; query = query->next) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result = driver_pgsql_sync_query(db, query->query);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (sql_result_next_row(result) < 0) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen commit_multi_fail(ctx, result, query->query);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen break;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (query->affected_rows != NULL) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_result *pg_result =
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen (struct pgsql_result *)result;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen *query->affected_rows =
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen atoi(PQcmdTuples(pg_result->pgres));
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_result_unref(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen }
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen return driver_pgsql_sync_query(db, ctx->failed ?
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen "ROLLBACK" : "COMMIT");
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen}
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic int
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen const char **error_r)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_transaction_context *ctx =
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (struct pgsql_transaction_context *)_ctx;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen struct sql_transaction_query *single_query = NULL;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct sql_result *result;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen *error_r = NULL;
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (ctx->failed || _ctx->head == NULL) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* nothing to be done */
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen result = NULL;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else if (_ctx->head->next == NULL) {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* just a single query, send it */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen single_query = _ctx->head;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result = sql_query_s(_ctx->db, single_query->query);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen } else {
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen /* multiple queries, use a transaction */
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_sync_init(db);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen result = driver_pgsql_transaction_commit_multi(ctx);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_sync_deinit(db);
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen if (ctx->failed) {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen i_assert(ctx->error != NULL);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen *error_r = ctx->error;
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen } else if (result != NULL) {
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen if (sql_result_next_row(result) < 0)
f4c0b1874b0533bcf2df1d28d584ff02cfdae3faTimo Sirainen *error_r = sql_result_get_error(result);
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen else if (single_query != NULL &&
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen single_query->affected_rows != NULL) {
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen struct pgsql_result *pg_result =
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen (struct pgsql_result *)result;
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen *single_query->affected_rows =
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen atoi(PQcmdTuples(pg_result->pgres));
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen }
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen }
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen if (result != NULL)
3656c91dcb8336814bebd4500e81c3dde25233e6Timo Sirainen sql_result_unref(result);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen i_assert(ctx->refcount == 1);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen driver_pgsql_transaction_unref(ctx);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen return *error_r == NULL ? 0 : -1;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainendriver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_transaction_context *ctx =
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (struct pgsql_transaction_context *)_ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
892b3cbf0eba9ba455448adcf71864a409345c6dTimo Sirainen i_assert(ctx->refcount == 1);
9c2b0eb659540b9db8dd3a8a6a2515921fbe8eebTimo Sirainen driver_pgsql_transaction_unref(ctx);
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen}
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainenstatic void
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainendriver_pgsql_update(struct sql_transaction_context *_ctx, const char *query,
bfdf0fd7b6186f64cbdcbf1cb2bf9c42a9007b77Timo Sirainen unsigned int *affected_rows)
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen{
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen struct pgsql_transaction_context *ctx =
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen (struct pgsql_transaction_context *)_ctx;
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen sql_transaction_add_query(_ctx, ctx->query_pool, query, affected_rows);
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen}
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainenconst struct sql_db driver_pgsql_db = {
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen .name = "pgsql",
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen .flags = SQL_DB_FLAG_POOLED,
dc9de21d4375faeedbe5b7e941502ac578650da9Timo Sirainen
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .v = {
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_init_v,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_deinit_v,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_connect,
6b2738c39a868ff9291867138c55029fc40cf105Timo Sirainen driver_pgsql_disconnect,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_escape_string,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_exec,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_query,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_query_s,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_transaction_begin,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_transaction_commit,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_transaction_commit_s,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_transaction_rollback,
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen driver_pgsql_update
0ae010139a1bb3b29fbf117c5da1a6a6c6b7b5a0Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen};
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
0db5b158a00c08955bdacc99b1e2cd1ec07f4311Timo Sirainenconst struct sql_result driver_pgsql_result = {
7bafda1813454621e03615e83d55bccfa7cc56bdTimo Sirainen .v = {
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_free,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_next_row,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_fields_count,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_field_name,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_find_field,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_field_value,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_field_value_binary,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_find_field_value,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_values,
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen driver_pgsql_result_get_error
1ac19c5c2b66a12f5598792aad15114ee3eb62e2Timo Sirainen }
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen};
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenvoid driver_pgsql_init(void);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenvoid driver_pgsql_deinit(void);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenvoid driver_pgsql_init(void)
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen{
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen sql_driver_register(&driver_pgsql_db);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen}
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainenvoid driver_pgsql_deinit(void)
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen{
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen sql_driver_unregister(&driver_pgsql_db);
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen}
0371406d952fe51367c7be91703e5634b7d9d225Timo Sirainen
6c2c5f20760b06bfb4a40b0ee2ef5ab016bc41f0Timo Sirainen#endif