driver-pgsql.c revision 7cb128dc4cae2a03a742f63ba7afee23c78e3af0
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2004-2015 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "ioloop.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "sql-api-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#ifdef BUILD_PGSQL
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <stdlib.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include <libpq-fe.h>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct pgsql_db {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct sql_db api;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen pool_t pool;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen char *connect_string;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen char *host;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen PGconn *pg;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen struct io *io;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen struct timeout *to_connect;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen enum io_condition io_dir;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen struct pgsql_result *cur_result;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen struct ioloop *ioloop, *orig_ioloop;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen struct sql_result *sync_result;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen char *error;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen unsigned int fatal_error:1;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct pgsql_binary_value {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned char *value;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen size_t size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct pgsql_result {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct sql_result api;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen PGresult *pgres;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct timeout *to;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen unsigned int rownum, rows;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int fields_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **fields;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **values;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen ARRAY(struct pgsql_binary_value) binary_values;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen sql_query_callback_t *callback;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen void *context;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen unsigned int timeout:1;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen};
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainenstruct pgsql_transaction_context {
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen struct sql_transaction_context ctx;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen int refcount;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen sql_commit_callback_t *callback;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen void *context;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen pool_t query_pool;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *error;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int begin_succeeded:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int begin_failed:1;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen unsigned int failed:1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenextern const struct sql_db driver_pgsql_db;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainenextern const struct sql_result driver_pgsql_result;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void result_finish(struct pgsql_result *result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic const char *pgsql_prefix(struct pgsql_db *db)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return db->host == NULL ? "pgsql" :
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen t_strdup_printf("pgsql(%s)", db->host);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstatic void driver_pgsql_set_state(struct pgsql_db *db, enum sql_db_state state)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(state == SQL_DB_STATE_BUSY || db->cur_result == NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* switch back to original ioloop in case the caller wants to
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen add/remove timeouts */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (db->ioloop != NULL)
659fe5d24825b160cae512538088020d97a60239Timo Sirainen io_loop_set_current(db->orig_ioloop);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen sql_db_set_state(&db->api, state);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (db->ioloop != NULL)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen io_loop_set_current(db->ioloop);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen}
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenstatic void driver_pgsql_stop_io(struct pgsql_db *db)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (db->io != NULL) {
659fe5d24825b160cae512538088020d97a60239Timo Sirainen io_remove(&db->io);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen db->io_dir = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen }
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen}
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenstatic void driver_pgsql_close(struct pgsql_db *db)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen db->io_dir = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen db->fatal_error = FALSE;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_stop_io(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen PQfinish(db->pg);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen db->pg = NULL;
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->to_connect != NULL)
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen timeout_remove(&db->to_connect);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_DISCONNECTED);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (db->ioloop != NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* running a sync query, stop it */
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen io_loop_stop(db->ioloop);
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen }
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen}
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic const char *last_error(struct pgsql_db *db)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen const char *msg;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen size_t len;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen msg = PQerrorMessage(db->pg);
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen if (msg == NULL)
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen return "(no error set)";
96c253a039f102fa78a313ee05200ab3970112dcTimo Sirainen
96c253a039f102fa78a313ee05200ab3970112dcTimo Sirainen /* Error message should contain trailing \n, we don't want it */
96c253a039f102fa78a313ee05200ab3970112dcTimo Sirainen len = strlen(msg);
96c253a039f102fa78a313ee05200ab3970112dcTimo Sirainen return len == 0 || msg[len-1] != '\n' ? msg :
96c253a039f102fa78a313ee05200ab3970112dcTimo Sirainen t_strndup(msg, len-1);
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen}
c3412ddeb9abc13f99d3caf50faf76cd99f7e9d2Timo Sirainen
c3412ddeb9abc13f99d3caf50faf76cd99f7e9d2Timo Sirainenstatic void connect_callback(struct pgsql_db *db)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen enum io_condition io_dir = 0;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen int ret;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen driver_pgsql_stop_io(db);
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
dc049c5e83d947aaf1b97c26ae819cc9577e0475Timo Sirainen while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen ;
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen switch (ret) {
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen case PGRES_POLLING_READING:
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen io_dir = IO_READ;
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen break;
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen case PGRES_POLLING_WRITING:
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen io_dir = IO_WRITE;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen break;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case PGRES_POLLING_OK:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen break;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case PGRES_POLLING_FAILED:
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen i_error("%s: Connect failed to database %s: %s",
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen pgsql_prefix(db), PQdb(db->pg), last_error(db));
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen driver_pgsql_close(db);
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen return;
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen }
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if (io_dir != 0) {
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen db->io = io_add(PQsocket(db->pg), io_dir, connect_callback, db);
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen db->io_dir = io_dir;
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen }
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if (io_dir == 0) {
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen if (db->to_connect != NULL)
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen timeout_remove(&db->to_connect);
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->ioloop != NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* driver_pgsql_sync_init() waiting for connection to
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen finish */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen io_loop_stop(db->ioloop);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainenstatic void driver_pgsql_connect_timeout(struct pgsql_db *db)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen unsigned int secs = ioloop_time - db->api.last_connect_try;
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen i_error("%s: Connect failed: Timeout after %u seconds",
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen pgsql_prefix(db), secs);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen driver_pgsql_close(db);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int driver_pgsql_connect(struct sql_db *_db)
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen{
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_DISCONNECTED);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->pg = PQconnectStart(db->connect_string);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->pg == NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_fatal("%s: PQconnectStart() failed (out of memory)",
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen pgsql_prefix(db));
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_error("%s: Connect failed to database %s: %s",
36e2fa21c22452470c1509cc63de20f7415c7b5eTimo Sirainen pgsql_prefix(db), PQdb(db->pg), last_error(db));
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_close(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return -1;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* nonblocking connecting begins. */
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen if (PQsetnonblocking(db->pg, 1) < 0)
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen i_error("%s: PQsetnonblocking() failed", pgsql_prefix(db));
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen i_assert(db->to_connect == NULL);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen driver_pgsql_connect_timeout, db);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE, connect_callback, db);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen db->io_dir = IO_WRITE;
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_CONNECTING);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return 0;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainenstatic void driver_pgsql_disconnect(struct sql_db *_db)
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen if (db->cur_result != NULL && db->cur_result->to != NULL)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen result_finish(db->cur_result);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen _db->no_reconnect = TRUE;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen driver_pgsql_close(db);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen _db->no_reconnect = FALSE;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen}
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainenstatic struct sql_db *driver_pgsql_init_v(const char *connect_string)
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen{
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen struct pgsql_db *db;
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen db = i_new(struct pgsql_db, 1);
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen db->connect_string = i_strdup(connect_string);
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen db->api = driver_pgsql_db;
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen T_BEGIN {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen const char *const *arg = t_strsplit(connect_string, " ");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen for (; *arg != NULL; arg++) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (strncmp(*arg, "host=", 5) == 0)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->host = i_strdup(*arg + 5);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen } T_END;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return &db->api;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainenstatic void driver_pgsql_deinit_v(struct sql_db *_db)
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen{
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->cur_result != NULL && db->cur_result->to != NULL)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result_finish(db->cur_result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen _db->no_reconnect = TRUE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_close(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(db->host);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(db->error);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(db->connect_string);
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen array_free(&_db->module_contexts);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainenstatic void driver_pgsql_set_idle(struct pgsql_db *db)
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen{
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_BUSY);
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen if (db->fatal_error)
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen driver_pgsql_close(db);
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen else
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_IDLE);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
7f773564b94e6054a40d3785cb63c29f1e4d4deeTimo Sirainenstatic void consume_results(struct pgsql_db *db)
7f773564b94e6054a40d3785cb63c29f1e4d4deeTimo Sirainen{
484efa22e65c509f787dbbc892351146c726c257Timo Sirainen PGresult *pgres;
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen driver_pgsql_stop_io(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen while (PQconsumeInput(db->pg)) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (PQisBusy(db->pg)) {
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen consume_results, db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->io_dir = IO_READ;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen return;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen }
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen pgres = PQgetResult(db->pg);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (pgres == NULL)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen break;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen PQclear(pgres);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen }
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen driver_pgsql_close(db);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen else
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen driver_pgsql_set_idle(db);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen}
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenstatic void driver_pgsql_result_free(struct sql_result *_result)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen{
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen bool success;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (result->api.callback) {
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen /* we're coming here from a user's sql_result_free() that's
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen being called from a callback. we'll do this later,
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen so ignore. */
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen return;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen }
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen i_assert(db->cur_result == result);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen i_assert(result->callback == NULL);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (_result == db->sync_result)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen db->sync_result = NULL;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen db->cur_result = NULL;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen success = result->pgres != NULL && !db->fatal_error;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (result->pgres != NULL) {
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen PQclear(result->pgres);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen result->pgres = NULL;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen }
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (success) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen /* we'll have to read the rest of the results as well */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen i_assert(db->io == NULL);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen consume_results(db);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen } else {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen driver_pgsql_set_idle(db);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen }
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (array_is_created(&result->binary_values)) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct pgsql_binary_value *value;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen array_foreach_modifiable(&result->binary_values, value)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen PQfreemem(value->value);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen array_free(&result->binary_values);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(result->fields);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(result->values);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_free(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void result_finish(struct pgsql_result *result)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen bool free_result = TRUE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(db->io == NULL);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen timeout_remove(&result->to);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* if connection to server was lost, we don't yet see that the
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen connection is bad. we only see the fatal error, so assume it also
8af07808ba203f8709e2ff9eaf2291e1c4a4d53dTimo Sirainen means disconnection. */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (PQstatus(db->pg) == CONNECTION_BAD || result->pgres == NULL ||
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen PQresultStatus(result->pgres) == PGRES_FATAL_ERROR)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->fatal_error = TRUE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->fatal_error) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.failed = TRUE;
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen result->api.failed_try_retry = TRUE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.callback = TRUE;
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen T_BEGIN {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->callback(&result->api, result->context);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen } T_END;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.callback = FALSE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->callback = NULL;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen free_result = db->sync_result != &result->api;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->ioloop != NULL)
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen io_loop_stop(db->ioloop);
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen
b9ac6179d3aee0d1641a4ee1d78da28628929c61Timo Sirainen if (free_result)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_result_unref(&result->api);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenstatic void get_result(struct pgsql_result *result)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen{
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen driver_pgsql_stop_io(db);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (!PQconsumeInput(db->pg)) {
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen result_finish(result);
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen return;
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen }
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen if (PQisBusy(db->pg)) {
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_READ,
33d63688ed8b26dc333e3c2edbfb2fe6e412604dTimo Sirainen get_result, result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->io_dir = IO_READ;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->pgres = PQgetResult(db->pg);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result_finish(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainenstatic void flush_callback(struct pgsql_result *result)
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen int ret;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_stop_io(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ret = PQflush(db->pg);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (ret > 0) {
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen flush_callback, result);
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen db->io_dir = IO_WRITE;
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen return;
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen }
88553367d677170a4b703b9d52aac9eabf91c656Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (ret < 0) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result_finish(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen } else {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* all flushed */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen get_result(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void query_timeout(struct pgsql_result *result)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_stop_io(db);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_error("%s: Query timed out, aborting", pgsql_prefix(db));
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen result->timeout = TRUE;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen result_finish(result);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic void do_query(struct pgsql_result *result, const char *query)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)result->api.db;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen int ret;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_assert(SQL_DB_IS_READY(&db->api));
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_assert(db->cur_result == NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen i_assert(db->io == NULL);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen driver_pgsql_set_state(db, SQL_DB_STATE_BUSY);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen db->cur_result = result;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen result->to = timeout_add(SQL_QUERY_TIMEOUT_SECS * 1000,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen query_timeout, result);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (!PQsendQuery(db->pg, query) ||
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen (ret = PQflush(db->pg)) < 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* failed to send query */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen result_finish(result);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen return;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen if (ret > 0) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen /* write blocks */
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen db->io = io_add(PQsocket(db->pg), IO_WRITE,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen flush_callback, result);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen db->io_dir = IO_WRITE;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen } else {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen get_result(result);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen }
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainenstatic const char *
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainendriver_pgsql_escape_string(struct sql_db *_db, const char *string)
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen{
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen size_t len = strlen(string);
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen char *to;
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen#ifdef HAVE_PQESCAPE_STRING_CONN
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen if (db->api.state == SQL_DB_STATE_DISCONNECTED) {
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen /* try connecting again */
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen (void)sql_connect(&db->api);
ab3c1eab9ca13916358a9e8b12df8212fefb7dbfTimo Sirainen }
99be58a447b69d62cbd9e764000a06226b9c9c89Timo Sirainen if (db->api.state != SQL_DB_STATE_DISCONNECTED) {
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen int error;
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen to = t_buffer_get(len * 2 + 1);
bbc92cfb17eb71d2ee9463c9cfd70dfea9a36bb6Timo Sirainen len = PQescapeStringConn(db->pg, to, string, len, &error);
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen } else
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen#endif
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen {
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen to = t_buffer_get(len * 2 + 1);
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen len = PQescapeString(to, string, len);
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen }
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen t_buffer_alloc(len + 1);
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen return to;
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen}
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainenstatic void exec_callback(struct sql_result *_result,
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen void *context ATTR_UNUSED)
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen{
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen i_error("%s: sql_exec() failed: %s", pgsql_prefix(db), last_error(db));
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen}
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void driver_pgsql_exec(struct sql_db *db, const char *query)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_result *result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result = i_new(struct pgsql_result, 1);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api = driver_pgsql_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.db = db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.refcount = 1;
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen result->callback = exec_callback;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen do_query(result, query);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void driver_pgsql_query(struct sql_db *db, const char *query,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen sql_query_callback_t *callback, void *context)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_result *result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen result = i_new(struct pgsql_result, 1);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api = driver_pgsql_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.db = db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->api.refcount = 1;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->callback = callback;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->context = context;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen do_query(result, query);
3f190f4cbb9233a3a6830956cb5c7ae56a577b79Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainenstatic void pgsql_query_s_callback(struct sql_result *result, void *context)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = context;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen db->sync_result = result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void driver_pgsql_sync_init(struct pgsql_db *db)
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen bool add_to_connect;
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen db->orig_ioloop = current_ioloop;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (db->io == NULL) {
659fe5d24825b160cae512538088020d97a60239Timo Sirainen db->ioloop = io_loop_create();
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(db->api.state == SQL_DB_STATE_CONNECTING);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* have to move our existing I/O and timeout handlers to new I/O loop */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen io_remove(&db->io);
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen if (db->to_connect != NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen timeout_remove(&db->to_connect);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen add_to_connect = TRUE;
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen } else {
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen add_to_connect = FALSE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->ioloop = io_loop_create();
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (add_to_connect) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->to_connect = timeout_add(SQL_CONNECT_TIMEOUT_SECS * 1000,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_connect_timeout, db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen db->io = io_add(PQsocket(db->pg), db->io_dir, connect_callback, db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* wait for connecting to finish */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen io_loop_run(db->ioloop);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainen
ff640b54224881abbc21141f217c881d6ba5cd28Timo Sirainenstatic void driver_pgsql_sync_deinit(struct pgsql_db *db)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen io_loop_destroy(&db->ioloop);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic struct sql_result *
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainendriver_pgsql_sync_query(struct pgsql_db *db, const char *query)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen struct sql_result *result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(db->sync_result == NULL);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen switch (db->api.state) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case SQL_DB_STATE_CONNECTING:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case SQL_DB_STATE_BUSY:
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen i_unreached();
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case SQL_DB_STATE_DISCONNECTED:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_not_connected_result.refcount++;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return &sql_not_connected_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen case SQL_DB_STATE_IDLE:
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen break;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_query(&db->api, query, pgsql_query_s_callback, db);
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen if (db->sync_result == NULL)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen io_loop_run(db->ioloop);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen i_assert(db->io == NULL);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result = db->sync_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (result == &sql_not_connected_result) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen /* we don't end up in pgsql's free function, so sync_result
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen won't be set to NULL if we don't do it here. */
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen db->sync_result = NULL;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen } else if (result == NULL) {
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen result = &sql_not_connected_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result->refcount++;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen }
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen i_assert(db->io == NULL);
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen return result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic struct sql_result *
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainendriver_pgsql_query_s(struct sql_db *_db, const char *query)
a205d315b0978985ba77d871f44e4a98273612e6Timo Sirainen{
a205d315b0978985ba77d871f44e4a98273612e6Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct sql_result *result;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen driver_pgsql_sync_init(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result = driver_pgsql_sync_query(db, query);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_sync_deinit(db);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
01cbf4ac5d44137ab434791be7f838d98d0fcf3bTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int driver_pgsql_result_next_row(struct sql_result *_result)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (result->rows != 0) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* second time we're here */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (++result->rownum < result->rows)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return 1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* end of this packet. see if there's more. FIXME: this may
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen block, but the current API doesn't provide a non-blocking
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen way to do this.. */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen PQclear(result->pgres);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen result->pgres = PQgetResult(db->pg);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (result->pgres == NULL)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return 0;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (result->pgres == NULL) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen _result->failed = TRUE;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen return -1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen switch (PQresultStatus(result->pgres)) {
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen case PGRES_COMMAND_OK:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* no rows returned */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen return 0;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen case PGRES_TUPLES_OK:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen result->rows = PQntuples(result->pgres);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return result->rows > 0;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen case PGRES_EMPTY_QUERY:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen case PGRES_NONFATAL_ERROR:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* nonfatal error */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen _result->failed = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return -1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen default:
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen /* treat as fatal error */
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen _result->failed = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen db->fatal_error = TRUE;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen return -1;
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen{
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen unsigned int i;
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen
cece2b9cd692c06025cc0a7a0ff54d996a8c90efTimo Sirainen if (result->fields != NULL)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return;
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen /* @UNSAFE */
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen result->fields_count = PQnfields(result->pgres);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen result->fields = i_new(const char *, result->fields_count);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen for (i = 0; i < result->fields_count; i++)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen result->fields[i] = PQfname(result->pgres, i);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic unsigned int
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainendriver_pgsql_result_get_fields_count(struct sql_result *_result)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_result_fetch_fields(result);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return result->fields_count;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic const char *
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainendriver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen{
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_result_fetch_fields(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_assert(idx < result->fields_count);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return result->fields[idx];
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic int driver_pgsql_result_find_field(struct sql_result *_result,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen const char *field_name)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
d252f81a2ff1bdd5439f9d2b3df715b70a4bcd3dTimo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
3343a61404603b21c246783a7963b77833095f31Timo Sirainen unsigned int i;
3343a61404603b21c246783a7963b77833095f31Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_result_fetch_fields(result);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen for (i = 0; i < result->fields_count; i++) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (strcmp(result->fields[i], field_name) == 0)
f1b7a02a05fbca580934c7312aae63ea9542aa79Timo Sirainen return i;
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen }
f1b7a02a05fbca580934c7312aae63ea9542aa79Timo Sirainen return -1;
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen}
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic const char *
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainendriver_pgsql_result_get_field_value(struct sql_result *_result,
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen unsigned int idx)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen{
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen
f1b7a02a05fbca580934c7312aae63ea9542aa79Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx))
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return NULL;
f1b7a02a05fbca580934c7312aae63ea9542aa79Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen return PQgetvalue(result->pgres, result->rownum, idx);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainenstatic const unsigned char *
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainendriver_pgsql_result_get_field_value_binary(struct sql_result *_result,
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen unsigned int idx, size_t *size_r)
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen{
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
755fe6da51ab7f54aa1d86913cb344bffef60e79Timo Sirainen const char *value;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen struct pgsql_binary_value *binary_value;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen if (PQgetisnull(result->pgres, result->rownum, idx)) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *size_r = 0;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return NULL;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen value = PQgetvalue(result->pgres, result->rownum, idx);
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (!array_is_created(&result->binary_values))
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_array_init(&result->binary_values, idx + 1);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen binary_value = array_idx_modifiable(&result->binary_values, idx);
d7abb52419f9aaf5de040778de3ea238b2431234Timo Sirainen if (binary_value->value == NULL) {
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen binary_value->value =
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen PQunescapeBytea((const unsigned char *)value,
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen &binary_value->size);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen }
9a06cabdfdf4d5e2f19a07e506c3c7d08a7e7038Timo Sirainen
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen *size_r = binary_value->size;
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen return binary_value->value;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen}
6fd7c571326c61949ffc0f3ea565c3df04104235Timo Sirainen
6fd7c571326c61949ffc0f3ea565c3df04104235Timo Sirainenstatic const char *
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainendriver_pgsql_result_find_field_value(struct sql_result *result,
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen const char *field_name)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen{
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen int idx;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen idx = driver_pgsql_result_find_field(result, field_name);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen if (idx < 0)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen return NULL;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen return driver_pgsql_result_get_field_value(result, idx);
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen}
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainenstatic const char *const *
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainendriver_pgsql_result_get_values(struct sql_result *_result)
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen{
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
7f3be7d885c75cdd77f536929a45bc9764595960Timo Sirainen unsigned int i;
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen if (result->values == NULL) {
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen driver_pgsql_result_fetch_fields(result);
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen result->values = i_new(const char *, result->fields_count);
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen }
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen
71a74e26cf070a205d31cf6c6fae003f90027b63Timo Sirainen /* @UNSAFE */
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen for (i = 0; i < result->fields_count; i++) {
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen result->values[i] =
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen driver_pgsql_result_get_field_value(_result, i);
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen }
6843896c40bee4f9b6680ca7ced598c446e9f999Timo Sirainen
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen return result->values;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainenstatic const char *driver_pgsql_result_get_error(struct sql_result *_result)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_result *result = (struct pgsql_result *)_result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_result->db;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen const char *msg;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen size_t len;
b2ecd50bb98c44816cb07c17aa17fae2b425f941Timo Sirainen
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen i_free_and_null(db->error);
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen if (result->timeout) {
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen db->error = i_strdup("Query timed out");
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen } else if (result->pgres == NULL) {
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen /* connection error */
907723f35f4d3dfc774ca42d00a8a7b8ef90dd5dTimo Sirainen db->error = i_strdup(last_error(db));
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen } else {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen msg = PQresultErrorMessage(result->pgres);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (msg == NULL)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return "(no error set)";
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* Error message should contain trailing \n, we don't want it */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen len = strlen(msg);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen db->error = len == 0 || msg[len-1] != '\n' ?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen i_strdup(msg) : i_strndup(msg, len-1);
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen }
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen return db->error;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic struct sql_transaction_context *
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainendriver_pgsql_transaction_begin(struct sql_db *db)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen{
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct pgsql_transaction_context *ctx;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen ctx = i_new(struct pgsql_transaction_context, 1);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen ctx->ctx.db = db;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen ctx->refcount = 1;
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen /* we need to be able to handle multiple open transactions, so at least
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen for now just keep them in memory until commit time. */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen ctx->query_pool = pool_alloconly_create("pgsql transaction", 1024);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen return &ctx->ctx;
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic void
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainendriver_pgsql_transaction_unref(struct pgsql_transaction_context *ctx)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen{
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen i_assert(ctx->refcount > 0);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (--ctx->refcount > 0)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen pool_unref(&ctx->query_pool);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen i_free(ctx);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic void
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainentransaction_begin_callback(struct sql_result *result,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct pgsql_transaction_context *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen if (sql_result_next_row(result) < 0) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen ctx->begin_failed = TRUE;
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen ctx->failed = TRUE;
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen ctx->error = sql_result_get_error(result);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen } else {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen ctx->begin_succeeded = TRUE;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen }
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_transaction_unref(ctx);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void
3cfff0ca01961d885bdbd6ef08d761880116af07Timo Sirainentransaction_commit_callback(struct sql_result *result,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_transaction_context *ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (sql_result_next_row(result) < 0)
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen ctx->callback(sql_result_get_error(result), ctx->context);
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen else
33ae95df45c9b5ec51332a6b39eb5322038686b9Timo Sirainen ctx->callback(NULL, ctx->context);
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainen driver_pgsql_transaction_unref(ctx);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic void
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainentransaction_update_callback(struct sql_result *result,
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct sql_transaction_query *query)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_transaction_context *ctx =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (struct pgsql_transaction_context *)query->trans;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (sql_result_next_row(result) < 0) {
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen ctx->failed = TRUE;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ctx->error = sql_result_get_error(result);
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen } else if (query->affected_rows != NULL) {
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen struct pgsql_result *pg_result = (struct pgsql_result *)result;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen *query->affected_rows = atoi(PQcmdTuples(pg_result->pgres));
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen }
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen driver_pgsql_transaction_unref(ctx);
02b79f9c2636da1829eee5b92753602bba8b67edTimo Sirainen}
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainenstatic void
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainendriver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
e68309fcfa2eaa88217fd51e7b4900fc9c20ef5dTimo Sirainen sql_commit_callback_t *callback, void *context)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_transaction_context *ctx =
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (struct pgsql_transaction_context *)_ctx;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen ctx->callback = callback;
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen ctx->context = context;
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (ctx->failed || _ctx->head == NULL) {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen callback(ctx->failed ? ctx->error : NULL, context);
60d3fa9883237e896a8704275b6116fa46f7ffdaTimo Sirainen driver_pgsql_transaction_unref(ctx);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen } else if (_ctx->head->next == NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* just a single query, send it */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_query(_ctx->db, _ctx->head->query,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen transaction_commit_callback, ctx);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen } else {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* multiple queries, use a transaction */
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ctx->refcount++;
f16c114c20bbd7d292d93415d1e56c8dd6abd3e7Timo Sirainen sql_query(_ctx->db, "BEGIN", transaction_begin_callback, ctx);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen while (_ctx->head != NULL) {
a205d315b0978985ba77d871f44e4a98273612e6Timo Sirainen ctx->refcount++;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_query(_ctx->db, _ctx->head->query,
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen transaction_update_callback, _ctx->head);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen _ctx->head = _ctx->head->next;
e5c08648676d1989f6e70b95e5990c26b3e8b96bTimo Sirainen }
99be58a447b69d62cbd9e764000a06226b9c9c89Timo Sirainen sql_query(_ctx->db, "COMMIT", transaction_commit_callback, ctx);
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen }
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen}
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainen
313fe89df4d91cd0cd7f3558dc6d7fd21ad39eeeTimo Sirainenstatic void
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainencommit_multi_fail(struct pgsql_transaction_context *ctx,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct sql_result *result, const char *query)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen ctx->failed = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx->error = t_strdup_printf("%s (query: %s)",
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_result_get_error(result), query);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_result_unref(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen}
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainenstatic struct sql_result *
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainendriver_pgsql_transaction_commit_multi(struct pgsql_transaction_context *ctx)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)ctx->ctx.db;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct sql_result *result;
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen struct sql_transaction_query *query;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen result = driver_pgsql_sync_query(db, "BEGIN");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (sql_result_next_row(result) < 0) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen commit_multi_fail(ctx, result, "BEGIN");
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return NULL;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen sql_result_unref(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen /* send queries */
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen for (query = ctx->ctx.head; query != NULL; query = query->next) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result = driver_pgsql_sync_query(db, query->query);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (sql_result_next_row(result) < 0) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen commit_multi_fail(ctx, result, query->query);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen }
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen if (query->affected_rows != NULL) {
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen struct pgsql_result *pg_result =
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen (struct pgsql_result *)result;
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen *query->affected_rows =
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen atoi(PQcmdTuples(pg_result->pgres));
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen }
292a66475ffe1037c2535063614f8beb71d266bfTimo Sirainen sql_result_unref(result);
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen return driver_pgsql_sync_query(db, ctx->failed ?
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen "ROLLBACK" : "COMMIT");
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen}
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen
178511b57faa7c3f8203dd8b7e4059d00cbfc23aTimo Sirainenstatic void
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainendriver_pgsql_try_commit_s(struct pgsql_transaction_context *ctx,
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen const char **error_r)
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen{
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct sql_transaction_context *_ctx = &ctx->ctx;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct sql_transaction_query *single_query = NULL;
fcfd317f7eb1f0216764c75c5fab3555020552d4Timo Sirainen struct sql_result *result;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (_ctx->head->next == NULL) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* just a single query, send it */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen single_query = _ctx->head;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen result = sql_query_s(_ctx->db, single_query->query);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen } else {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen /* multiple queries, use a transaction */
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen driver_pgsql_sync_init(db);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen result = driver_pgsql_transaction_commit_multi(ctx);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_sync_deinit(db);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen }
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (ctx->failed) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen i_assert(ctx->error != NULL);
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen *error_r = ctx->error;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen } else if (result != NULL) {
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen if (sql_result_next_row(result) < 0)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen *error_r = sql_result_get_error(result);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen else if (single_query != NULL &&
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen single_query->affected_rows != NULL) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_result *pg_result =
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (struct pgsql_result *)result;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *single_query->affected_rows =
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen atoi(PQcmdTuples(pg_result->pgres));
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen if (result != NULL)
26687b22a471df9c28ce7bf233e7128e9d85f64cTimo Sirainen sql_result_unref(result);
f0569d9fbb25c8437760be69f194595a841ad711Timo Sirainen}
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainen
a393d9d6dabdc46cf724f8cb004a652b4036d53dTimo Sirainenstatic int
325f4573edfa5b751832ac01023f3e81be992bf0Timo Sirainendriver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen const char **error_r)
9e59a1f3f095b3099478562cf3f3970a24736970Timo Sirainen{
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen struct pgsql_transaction_context *ctx =
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen (struct pgsql_transaction_context *)_ctx;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen struct pgsql_db *db = (struct pgsql_db *)_ctx->db;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *error_r = NULL;
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen if (_ctx->head != NULL) {
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen driver_pgsql_try_commit_s(ctx, error_r);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (_ctx->db->state == SQL_DB_STATE_DISCONNECTED) {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen *error_r = t_strdup(*error_r);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_info("%s: Disconnected from database, "
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen "retrying commit", pgsql_prefix(db));
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen if (sql_connect(_ctx->db) >= 0) {
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen ctx->failed = FALSE;
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen *error_r = NULL;
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen driver_pgsql_try_commit_s(ctx, error_r);
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen }
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen }
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen }
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_assert(ctx->refcount == 1);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_transaction_unref(ctx);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen return *error_r == NULL ? 0 : -1;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic void
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainendriver_pgsql_transaction_rollback(struct sql_transaction_context *_ctx)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen struct pgsql_transaction_context *ctx =
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen (struct pgsql_transaction_context *)_ctx;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen i_assert(ctx->refcount == 1);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen driver_pgsql_transaction_unref(ctx);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenstatic void
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainendriver_pgsql_update(struct sql_transaction_context *_ctx, const char *query,
345212e8f61ebf14ff4f80df26df9e655eb5121eTimo Sirainen unsigned int *affected_rows)
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen{
abe8230dd1dd37d7ccf0163100e934bb5e658c20Timo Sirainen struct pgsql_transaction_context *ctx =
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen (struct pgsql_transaction_context *)_ctx;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen sql_transaction_add_query(_ctx, ctx->query_pool, query, affected_rows);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenconst struct sql_db driver_pgsql_db = {
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen .name = "pgsql",
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen .flags = SQL_DB_FLAG_POOLED,
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen .v = {
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen driver_pgsql_init_v,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen driver_pgsql_deinit_v,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_connect,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_disconnect,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_escape_string,
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen driver_pgsql_exec,
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen driver_pgsql_query,
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen driver_pgsql_query_s,
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen driver_pgsql_transaction_begin,
db7c9201c88e3d9bee10485194ee5b0c67249916Timo Sirainen driver_pgsql_transaction_commit,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_transaction_commit_s,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_transaction_rollback,
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen
dda2c506c8fc8ac2f88272de4523ded42baa0aa0Timo Sirainen driver_pgsql_update
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen }
9aa52288a4b53186d81b0ec9afa7d9e0a8ee8753Timo Sirainen};
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainenconst struct sql_result driver_pgsql_result = {
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen .v = {
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_free,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_next_row,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_fields_count,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_field_name,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_find_field,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_field_value,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_field_value_binary,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_find_field_value,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_values,
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen driver_pgsql_result_get_error
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen }
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen};
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenconst char *driver_pgsql_version = DOVECOT_ABI_VERSION;
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenvoid driver_pgsql_init(void);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenvoid driver_pgsql_deinit(void);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenvoid driver_pgsql_init(void)
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen{
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen sql_driver_register(&driver_pgsql_db);
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen}
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainen
f1901fd21906911f7be075c965ac882f6a87b4c3Timo Sirainenvoid driver_pgsql_deinit(void)
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen{
0b17b95357d5d73d941f1eb4ca9fc543bc510e42Timo Sirainen sql_driver_unregister(&driver_pgsql_db);
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen}
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen#endif
b3febb0933fdce10394d25093e23ce0a5aadddd3Timo Sirainen