driver-pgsql.c revision d1a0845aed2bbbe9435e96bd10bd774ed194ca4b
45312f52ff3a3d4c137447be4c7556500c2f8bf2Timo Sirainen/* Copyright (c) 2004-2015 Dovecot authors, see the included COPYING file */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "lib.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "array.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "ioloop.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "hex-binary.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "str.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "time-util.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include "sql-api-private.h"
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#ifdef BUILD_PGSQL
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#include <libpq-fe.h>
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#define PGSQL_DNS_WARN_MSECS 500
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstruct pgsql_db {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_db api;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pool_t pool;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen char *connect_string;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen char *host;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PGconn *pg;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct io *io;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct timeout *to_connect;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen enum io_condition io_dir;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *cur_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct ioloop *ioloop, *orig_ioloop;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result *sync_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen bool (*next_callback)(void *);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen void *next_context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen char *error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *connect_state;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int fatal_error:1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstruct pgsql_binary_value {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned char *value;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen size_t size;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstruct pgsql_result {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result api;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PGresult *pgres;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct timeout *to;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int rownum, rows;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int fields_count;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char **fields;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char **values;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ARRAY(struct pgsql_binary_value) binary_values;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query_callback_t *callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen void *context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int timeout:1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstruct pgsql_transaction_context {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_context ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int refcount;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_commit_callback_t *callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen void *context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pool_t query_pool;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int failed:1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenextern const struct sql_db driver_pgsql_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenextern const struct sql_result driver_pgsql_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void result_finish(struct pgsql_result *result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainentransaction_update_callback(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_query *query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *pgsql_prefix(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return db->host == NULL ? "pgsql" :
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen t_strdup_printf("pgsql(%s)", db->host);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_set_state(struct pgsql_db *db, enum sql_db_state state)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(state == SQL_DB_STATE_BUSY || db->cur_result == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* switch back to original ioloop in case the caller wants to
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen add/remove timeouts */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->ioloop != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_set_current(db->orig_ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_db_set_state(&db->api, state);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->ioloop != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_set_current(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic bool driver_pgsql_next_callback(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen bool (*next_callback)(void *) = db->next_callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen void *next_context = db->next_context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (next_callback == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_callback = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_context = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return next_callback(next_context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_stop_io(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->io != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_remove(&db->io);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_close(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = 0;
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen db->fatal_error = FALSE;
ef5fb27361cc5e15766e85e28355750ff04b13c9Timo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQfinish(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->pg = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->to_connect != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen timeout_remove(&db->to_connect);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_DISCONNECTED);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->ioloop != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* running a sync query, stop it */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_stop(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_next_callback(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *last_error(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *msg;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen size_t len;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen msg = PQerrorMessage(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (msg == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return "(no error set)";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* Error message should contain trailing \n, we don't want it */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen len = strlen(msg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return len == 0 || msg[len-1] != '\n' ? msg :
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen t_strndup(msg, len-1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void connect_callback(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen enum io_condition io_dir = 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int ret;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen switch (ret) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_POLLING_READING:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->connect_state = "wait for input";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_dir = IO_READ;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_POLLING_WRITING:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->connect_state = "wait for output";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_dir = IO_WRITE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_POLLING_OK:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_POLLING_FAILED:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: Connect failed to database %s: %s (state: %s)",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgsql_prefix(db), PQdb(db->pg), last_error(db), db->connect_state);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (io_dir != 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), io_dir, connect_callback, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = io_dir;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (io_dir == 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->connect_state = "connected";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->to_connect != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen timeout_remove(&db->to_connect);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->ioloop != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* driver_pgsql_sync_init() waiting for connection to
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen finish */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_stop(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_connect_timeout(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int secs = ioloop_time - db->api.last_connect_try;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: Connect failed: Timeout after %u seconds (state: %s)",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgsql_prefix(db), secs, db->connect_state);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic int driver_pgsql_connect(struct sql_db *_db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct timeval tv_start;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int msecs;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_time_refresh();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen tv_start = ioloop_timeval;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->pg = PQconnectStart(db->connect_string);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->pg == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_fatal("%s: PQconnectStart() failed (out of memory)",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgsql_prefix(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: Connect failed to database %s: %s",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgsql_prefix(db), PQdb(db->pg), last_error(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* PQconnectStart() blocks on host name resolving. Log a warning if
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen it takes too long. Also don't include time spent on that in the
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen connect timeout (by refreshing ioloop time). */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_time_refresh();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen msecs = timeval_diff_msecs(&ioloop_timeval, &tv_start);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (msecs > PGSQL_DNS_WARN_MSECS) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_warning("%s: DNS lookup took %d.%03d s",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgsql_prefix(db), msecs/1000, msecs % 1000);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* nonblocking connecting begins. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQsetnonblocking(db->pg, 1) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: PQsetnonblocking() failed", pgsql_prefix(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->to_connect == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_connect_timeout, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->connect_state = "connecting";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE, connect_callback, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = IO_WRITE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_CONNECTING);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_disconnect(struct sql_db *_db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->cur_result != NULL && db->cur_result->to != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(db->cur_result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen _db->no_reconnect = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen _db->no_reconnect = FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic struct sql_db *driver_pgsql_init_v(const char *connect_string)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db = i_new(struct pgsql_db, 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->connect_string = i_strdup(connect_string);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->api = driver_pgsql_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen T_BEGIN {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *const *arg = t_strsplit(connect_string, " ");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for (; *arg != NULL; arg++) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (strncmp(*arg, "host=", 5) == 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->host = i_strdup(*arg + 5);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } T_END;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return &db->api;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_deinit_v(struct sql_db *_db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_disconnect(_db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(db->host);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(db->error);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(db->connect_string);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen array_free(&_db->module_contexts);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_set_idle(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->api.state == SQL_DB_STATE_BUSY);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->fatal_error)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen else if (!driver_pgsql_next_callback(db))
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void consume_results(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PGresult *pgres;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen while (PQconsumeInput(db->pg)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQisBusy(db->pg)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen consume_results, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = IO_READ;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pgres = PQgetResult(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (pgres == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQclear(pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_close(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen else
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_idle(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_result_free(struct sql_result *_result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen bool success;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(!result->api.callback);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->cur_result == result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(result->callback == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (_result == db->sync_result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->sync_result = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->cur_result = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen success = result->pgres != NULL && !db->fatal_error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result->pgres != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQclear(result->pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->pgres = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (success) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* we'll have to read the rest of the results as well */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->io == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen consume_results(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_idle(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (array_is_created(&result->binary_values)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_binary_value *value;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen array_foreach_modifiable(&result->binary_values, value)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQfreemem(value->value);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen array_free(&result->binary_values);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(result->fields);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(result->values);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void result_finish(struct pgsql_result *result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen bool free_result = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->io == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen timeout_remove(&result->to);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* if connection to server was lost, we don't yet see that the
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen connection is bad. we only see the fatal error, so assume it also
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen means disconnection. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD || result->pgres == NULL ||
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQresultStatus(result->pgres) == PGRES_FATAL_ERROR)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->fatal_error = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->fatal_error) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.failed = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.failed_try_retry = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.callback = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen T_BEGIN {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->callback(&result->api, result->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } T_END;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.callback = FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen free_result = db->sync_result != &result->api;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->ioloop != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_stop(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(!free_result || result->api.refcount > 0);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->callback = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (free_result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_unref(&result->api);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void get_result(struct pgsql_result *result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (!PQconsumeInput(db->pg)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQisBusy(db->pg)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen get_result, result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = IO_READ;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->pgres = PQgetResult(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void flush_callback(struct pgsql_result *result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int ret;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ret = PQflush(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ret > 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen flush_callback, result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = IO_WRITE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ret < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* all flushed */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen get_result(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void query_timeout(struct pgsql_result *result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_stop_io(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: Query timed out, aborting", pgsql_prefix(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->timeout = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void do_query(struct pgsql_result *result, const char *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int ret;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(SQL_DB_IS_READY(&db->api));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->cur_result == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->io == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_BUSY);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->cur_result = result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen query_timeout, result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (!PQsendQuery(db->pg, query) ||
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (ret = PQflush(db->pg)) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* failed to send query */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result_finish(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ret > 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* write blocks */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen flush_callback, result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io_dir = IO_WRITE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen get_result(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_escape_string(struct sql_db *_db, const char *string)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen size_t len = strlen(string);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen char *to;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#ifdef HAVE_PQESCAPE_STRING_CONN
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->api.state == SQL_DB_STATE_DISCONNECTED) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* try connecting again */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (void)sql_connect(&db->api);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->api.state != SQL_DB_STATE_DISCONNECTED) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen to = t_buffer_get(len * 2 + 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen len = PQescapeStringConn(db->pg, to, string, len, &error);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#endif
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen to = t_buffer_get(len * 2 + 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen len = PQescapeString(to, string, len);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen t_buffer_alloc(len + 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return to;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void exec_callback(struct sql_result *_result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen void *context ATTR_UNUSED)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_error("%s: sql_exec() failed: %s", pgsql_prefix(db), last_error(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_exec(struct sql_db *db, const char *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = i_new(struct pgsql_result, 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api = driver_pgsql_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.db = db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.refcount = 1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->callback = exec_callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen do_query(result, query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_query(struct sql_db *db, const char *query,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query_callback_t *callback, void *context)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = i_new(struct pgsql_result, 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api = driver_pgsql_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.db = db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->api.refcount = 1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->callback = callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->context = context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen do_query(result, query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void pgsql_query_s_callback(struct sql_result *result, void *context)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->sync_result = result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_sync_init(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen bool add_to_connect;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->orig_ioloop = current_ioloop;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->io == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->ioloop = io_loop_create();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->api.state == SQL_DB_STATE_CONNECTING);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* have to move our existing I/O and timeout handlers to new I/O loop */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_remove(&db->io);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->to_connect != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen timeout_remove(&db->to_connect);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen add_to_connect = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen add_to_connect = FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->ioloop = io_loop_create();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (add_to_connect) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_connect_timeout, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->io = io_add(PQsocket(db->pg), db->io_dir, connect_callback, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* wait for connecting to finish */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_run(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_sync_deinit(struct pgsql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_destroy(&db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic struct sql_result *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_sync_query(struct pgsql_db *db, const char *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result *result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->sync_result == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen switch (db->api.state) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case SQL_DB_STATE_CONNECTING:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case SQL_DB_STATE_BUSY:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_unreached();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case SQL_DB_STATE_DISCONNECTED:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_not_connected_result.refcount++;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return &sql_not_connected_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case SQL_DB_STATE_IDLE:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_query(&db->api, query, pgsql_query_s_callback, db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (db->sync_result == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen io_loop_run(db->ioloop);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->io == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = db->sync_result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen if (result == &sql_not_connected_result) {
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen /* we don't end up in pgsql's free function, so sync_result
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen won't be set to NULL if we don't do it here. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->sync_result = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else if (result == NULL) {
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen result = &sql_not_connected_result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen result->refcount++;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen }
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen i_assert(db->io == NULL);
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen return result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen}
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainenstatic struct sql_result *
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainendriver_pgsql_query_s(struct sql_db *_db, const char *query)
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen{
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen struct sql_result *result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen driver_pgsql_sync_init(db);
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen result = driver_pgsql_sync_query(db, query);
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen driver_pgsql_sync_deinit(db);
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen return result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen}
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainenstatic int driver_pgsql_result_next_row(struct sql_result *_result)
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen{
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen if (result->rows != 0) {
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen /* second time we're here */
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen if (++result->rownum < result->rows)
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen return 1;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen /* end of this packet. see if there's more. FIXME: this may
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen block, but the current API doesn't provide a non-blocking
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen way to do this.. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQclear(result->pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->pgres = PQgetResult(db->pg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result->pgres == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result->pgres == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen _result->failed = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen switch (PQresultStatus(result->pgres)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_COMMAND_OK:
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen /* no rows returned */
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen return 0;
39413f6b07f7e4f4c1aeeecab73a2c454c84e308Timo Sirainen case PGRES_TUPLES_OK:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->rows = PQntuples(result->pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return result->rows > 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_EMPTY_QUERY:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen case PGRES_NONFATAL_ERROR:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* nonfatal error */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen _result->failed = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen default:
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* treat as fatal error */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen _result->failed = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->fatal_error = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int i;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result->fields != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* @UNSAFE */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->fields_count = PQnfields(result->pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->fields = i_new(const char *, result->fields_count);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for (i = 0; i < result->fields_count; i++)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->fields[i] = PQfname(result->pgres, i);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic unsigned int
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_get_fields_count(struct sql_result *_result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_fetch_fields(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return result->fields_count;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_fetch_fields(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(idx < result->fields_count);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return result->fields[idx];
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic int driver_pgsql_result_find_field(struct sql_result *_result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *field_name)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int i;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_fetch_fields(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for (i = 0; i < result->fields_count; i++) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (strcmp(result->fields[i], field_name) == 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return i;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_get_field_value(struct sql_result *_result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int idx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx))
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return PQgetvalue(result->pgres, result->rownum, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const unsigned char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_get_field_value_binary(struct sql_result *_result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen unsigned int idx, size_t *size_r)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *value;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_binary_value *binary_value;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *size_r = 0;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen value = PQgetvalue(result->pgres, result->rownum, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (!array_is_created(&result->binary_values))
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_array_init(&result->binary_values, idx + 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen binary_value = array_idx_modifiable(&result->binary_values, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (binary_value->value == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen binary_value->value =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen PQunescapeBytea((const unsigned char *)value,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen &binary_value->size);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *size_r = binary_value->size;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return binary_value->value;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_find_field_value(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *field_name)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen int idx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen idx = driver_pgsql_result_find_field(result, field_name);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (idx < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return driver_pgsql_result_get_field_value(result, idx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *const *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_result_get_values(struct sql_result *_result)
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen{
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen unsigned int i;
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen if (result->values == NULL) {
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen driver_pgsql_result_fetch_fields(result);
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen result->values = i_new(const char *, result->fields_count);
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* @UNSAFE */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for (i = 0; i < result->fields_count; i++) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result->values[i] =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_field_value(_result, i);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return result->values;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *driver_pgsql_result_get_error(struct sql_result *_result)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char *msg;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen size_t len;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free_and_null(db->error);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result->timeout) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->error = i_strdup("Query timed out");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else if (result->pgres == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* connection error */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->error = i_strdup(last_error(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen msg = PQresultErrorMessage(result->pgres);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (msg == NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return "(no error set)";
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* Error message should contain trailing \n, we don't want it */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen len = strlen(msg);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->error = len == 0 || msg[len-1] != '\n' ?
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_strdup(msg) : i_strndup(msg, len-1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return db->error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic struct sql_transaction_context *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_begin(struct sql_db *db)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx = i_new(struct pgsql_transaction_context, 1);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->ctx.db = db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* we need to be able to handle multiple open transactions, so at least
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for now just keep them in memory until commit time. */
b6dff2ba7a4640c1c4fa8fcad5602d236c31a2e4Timo Sirainen ctx->query_pool = pool_alloconly_create("pgsql transaction", 1024);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return &ctx->ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_free(struct pgsql_transaction_context *ctx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen pool_unref(&ctx->query_pool);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainentransaction_commit_callback(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen else
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(NULL, ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic bool transaction_send_next(void *context)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx = context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(!ctx->failed);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ctx->ctx.db->state == SQL_DB_STATE_BUSY) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* kludgy.. */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->ctx.db->state = SQL_DB_STATE_IDLE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else if (!SQL_DB_IS_READY(ctx->ctx.db)) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback("Not connected", ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ctx->ctx.head != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query(ctx->ctx.db, ctx->ctx.head->query,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen transaction_update_callback, ctx->ctx.head);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->ctx.head = ctx->ctx.head->next;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query(ctx->ctx.db, "COMMIT",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen transaction_commit_callback, ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainentransaction_begin_callback(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(result->db == ctx->ctx.db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->next_callback == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_callback = transaction_send_next;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_context = ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainentransaction_update_callback(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_query *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)query->trans;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (query->affected_rows != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *pg_result = (struct pgsql_result *)result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (str_to_uint(PQcmdTuples(pg_result->pgres),
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen query->affected_rows) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_unreached();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(db->next_callback == NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_callback = transaction_send_next;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen db->next_context = ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainentransaction_trans_query_callback(struct sql_result *result,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_query *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)query->trans;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (query->affected_rows != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *pg_result = (struct pgsql_result *)result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (str_to_uint(PQcmdTuples(pg_result->pgres),
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen query->affected_rows) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_unreached();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback(NULL, ctx->context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_commit_callback_t *callback, void *context)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)_ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->callback = callback;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->context = context;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ctx->failed || _ctx->head == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen callback(ctx->failed ? ctx->error : NULL, context);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else if (_ctx->head->next == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* just a single query, send it */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query(_ctx->db, _ctx->head->query,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen transaction_trans_query_callback, _ctx->head);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* multiple queries, use a transaction */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(_ctx->db->v.query == driver_pgsql_query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_query(_ctx->db, "BEGIN", transaction_begin_callback, ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainencommit_multi_fail(struct pgsql_transaction_context *ctx,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result *result, const char *query)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->failed = TRUE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->error = t_strdup_printf("%s (query: %s)",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_get_error(result), query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_unref(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic struct sql_result *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_commit_multi(struct pgsql_transaction_context *ctx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)ctx->ctx.db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result *result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_query *query;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = driver_pgsql_sync_query(db, "BEGIN");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen commit_multi_fail(ctx, result, "BEGIN");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_unref(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* send queries */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen for (query = ctx->ctx.head; query != NULL; query = query->next) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = driver_pgsql_sync_query(db, query->query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen commit_multi_fail(ctx, result, query->query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen break;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (query->affected_rows != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *pg_result =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_result *)result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (str_to_uint(PQcmdTuples(pg_result->pgres),
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen query->affected_rows) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_unreached();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_unref(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return driver_pgsql_sync_query(db, ctx->failed ?
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen "ROLLBACK" : "COMMIT");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_try_commit_s(struct pgsql_transaction_context *ctx,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char **error_r)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_context *_ctx = &ctx->ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_transaction_query *single_query = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct sql_result *result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (_ctx->head->next == NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* just a single query, send it */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen single_query = _ctx->head;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = sql_query_s(_ctx->db, single_query->query);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen /* multiple queries, use a transaction */
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_sync_init(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen result = driver_pgsql_transaction_commit_multi(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_sync_deinit(db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (ctx->failed) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_assert(ctx->error != NULL);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *error_r = ctx->error;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen } else if (result != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_result_next_row(result) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *error_r = sql_result_get_error(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen else if (single_query != NULL &&
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen single_query->affected_rows != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_result *pg_result =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_result *)result;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (str_to_uint(PQcmdTuples(pg_result->pgres),
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen single_query->affected_rows) < 0)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_unreached();
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (result != NULL)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_result_unref(result);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic int
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const char **error_r)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)_ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *error_r = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (_ctx->head != NULL) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_try_commit_s(ctx, error_r);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (_ctx->db->state == SQL_DB_STATE_DISCONNECTED) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *error_r = t_strdup(*error_r);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen i_info("%s: Disconnected from database, "
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen "retrying commit", pgsql_prefix(db));
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen if (sql_connect(_ctx->db) >= 0) {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen ctx->failed = FALSE;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen *error_r = NULL;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_try_commit_s(ctx, error_r);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return *error_r == NULL ? 0 : -1;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)_ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_free(ctx);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic void
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_update(struct sql_transaction_context *_ctx, const char *query,
49f8bdc504635afec3016a6973aac06d6ff8403fTimo Sirainen unsigned int *affected_rows)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen struct pgsql_transaction_context *ctx =
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen (struct pgsql_transaction_context *)_ctx;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_transaction_add_query(_ctx, ctx->query_pool, query, affected_rows);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenstatic const char *
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainendriver_pgsql_escape_blob(struct sql_db *_db ATTR_UNUSED,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen const unsigned char *data, size_t size)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen string_t *str = t_str_new(128);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen str_append(str, "E'\\x");
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen binary_to_hex_append(str, data, size);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen str_append_c(str, '\'');
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen return str_c(str);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenconst struct sql_db driver_pgsql_db = {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen .name = "pgsql",
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen .flags = SQL_DB_FLAG_POOLED,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen .v = {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_init_v,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_deinit_v,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_connect,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_disconnect,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_escape_string,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_exec,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_query,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_query_s,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_begin,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_commit,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_commit_s,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_transaction_rollback,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_update,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_escape_blob
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenconst struct sql_result driver_pgsql_result = {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen .v = {
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_free,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_next_row,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_fields_count,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_field_name,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_find_field,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_field_value,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_field_value_binary,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_find_field_value,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_values,
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen driver_pgsql_result_get_error
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen }
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen};
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenconst char *driver_pgsql_version = DOVECOT_ABI_VERSION;
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenvoid driver_pgsql_init(void);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenvoid driver_pgsql_deinit(void);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenvoid driver_pgsql_init(void)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_driver_register(&driver_pgsql_db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainenvoid driver_pgsql_deinit(void)
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen{
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen sql_driver_unregister(&driver_pgsql_db);
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen}
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen#endif
a914bff43644dd9b3977244203839ca74161e40cTimo Sirainen