driver-pgsql.c revision 9fcf7b79236b0045f7709718f7b65ada516565e7
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher/* Copyright (C) 2004 Timo Sirainen */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher#include "ioloop-internal.h" /* kind of dirty, but it should be fine.. */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherextern struct sql_result driver_pgsql_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void queue_send_next(struct pgsql_db *db);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void result_finish(struct pgsql_result *result);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void driver_pgsql_close(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *last_error(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher return "(no error set)";
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Error message should contain trailing \n, we don't want it */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher return len == 0 || msg[len-1] != '\n' ? msg :
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void connect_callback(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher i_info("pgsql: Connected to %s", PQdb(db->pg));
346f41f1ede975cb2db0af570f5b454b9b306704Stephen Gallagher i_error("pgsql: Connect failed to %s: %s",
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher io_add(PQsocket(db->pg), io_dir, connect_callback, db);
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenystatic int driver_pgsql_connect(struct sql_db *_db)
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* don't try reconnecting more than once a second */
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny return db->connected ? 1 : (db->connecting ? 0 : -1);
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny i_fatal("pgsql: PQconnectStart() failed (out of memory)");
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zeleny /* nonblocking connecting begins. */
4a1e58d85409fbb7a12ac244c3dbef8c0c1b15dfMichal Zidekstatic struct sql_db *_driver_pgsql_init(const char *connect_string)
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenystatic void _driver_pgsql_deinit(struct sql_db *_db)
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenydriver_pgsql_get_flags(struct sql_db *db __attr_unused__)
9ab243b369ba317cc964080786dbcdebaf23d6beMichal Zidekstatic void consume_results(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void driver_pgsql_result_free(struct sql_result *_result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)_result->db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* we'll have to read the rest of the results as well */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->io = io_add(PQsocket(db->pg), IO_READ,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (db->queue != NULL && !db->querying && db->connected)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void result_finish(struct pgsql_result *result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher result->callback(&result->api, result->context);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher free_result = db->sync_result != &result->api;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* disconnected */
016e0d7202ff965018e41869c5ab501f86b0d081Jan Zelenystatic void get_result(struct pgsql_result *result)
b1caacb098ae99ad65144120fdec4d0fd98ad9d5Pavel Březina struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void flush_callback(struct pgsql_result *result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* all flushed */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void send_query(struct pgsql_result *result, const char *query)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* write blocks */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->io = io_add(PQsocket(db->pg), IO_WRITE,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void queue_send_next(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void queue_timeout(struct pgsql_db *db)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_queue_query(struct pgsql_result *result, const char *query)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->queue_to = timeout_add(5000, queue_timeout, db);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void do_query(struct pgsql_result *result, const char *query)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->api.db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* only one query at a time */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* try connecting again */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* there's already queries queued, send them first */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void exec_callback(struct sql_result *result,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)result->db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher i_error("pgsql: sql_exec() failed: %s", last_error(db));
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_escape_string(struct sql_db *_db, const char *string)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)_db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* try connecting again */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher len = PQescapeStringConn(db->pg, to, string, len, NULL);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void driver_pgsql_exec(struct sql_db *db, const char *query)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void driver_pgsql_query(struct sql_db *db, const char *query,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher sql_query_callback_t *callback, void *context)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void pgsql_query_s_callback(struct sql_result *result, void *context)
4e2d9fe30bf8b692972a9654c60d2d90ed355815Stephen Gallagherdriver_pgsql_query_s(struct sql_db *_db, const char *query)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)_db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* have to move our existing I/O handler to new I/O loop */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->io = io_add(PQsocket(db->pg), old_io.condition,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher driver_pgsql_query(_db, query, pgsql_query_s_callback, db);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic int driver_pgsql_result_next_row(struct sql_result *_result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)_result->db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* second time we're here */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* no rows returned */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* nonfatal error */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* treat as fatal error */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher unsigned int i;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher result->fields_count = PQnfields(result->pgres);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher result->fields = i_new(const char *, result->fields_count);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < result->fields_count; i++)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher result->fields[i] = PQfname(result->pgres, i);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic unsigned int
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_result_get_fields_count(struct sql_result *_result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic int driver_pgsql_result_find_field(struct sql_result *_result,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher unsigned int i;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < result->fields_count; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (strcmp(result->fields[i], field_name) == 0)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_result_get_field_value(struct sql_result *_result,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher unsigned int idx)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher if (PQgetisnull(result->pgres, result->rownum, idx))
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher return PQgetvalue(result->pgres, result->rownum, idx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherdriver_pgsql_result_find_field_value(struct sql_result *result,
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher idx = driver_pgsql_result_find_field(result, field_name);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher return driver_pgsql_result_get_field_value(result, idx);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *const *
0051296f67bd7d8e2e3094638ddff4e641324d04Michal Zidekdriver_pgsql_result_get_values(struct sql_result *_result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher unsigned int i;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher result->values = i_new(const char *, result->fields_count);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher for (i = 0; i < result->fields_count; i++) {
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher driver_pgsql_result_get_field_value(_result, i);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagherstatic const char *driver_pgsql_result_get_error(struct sql_result *_result)
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_result *result = (struct pgsql_result *)_result;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher struct pgsql_db *db = (struct pgsql_db *)_result->db;
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher msg = PQresultErrorMessage(result->pgres);
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher return "(no error set)";
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher /* Error message should contain trailing \n, we don't want it */
effcbdb12c7ef892f1fd92a745cb33a08ca4ba30Stephen Gallagher db->error = len == 0 || msg[len-1] != '\n' ?
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagherdriver_pgsql_transaction_begin(struct sql_db *db)
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher ctx = i_new(struct pgsql_transaction_context, 1);
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallaghertransaction_commit_callback(struct sql_result *result, void *context)
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher (struct pgsql_transaction_context *)context;
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher ctx->callback(sql_result_get_error(result), ctx->context);
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagherdriver_pgsql_transaction_commit(struct sql_transaction_context *_ctx,
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher sql_commit_callback_t *callback, void *context)
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher /* nothing done */
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagher sql_query(_ctx->db, "COMMIT", transaction_commit_callback, ctx);
d92c50f6d75ae980b0d130134112a33e1584724cStephen Gallagherdriver_pgsql_transaction_commit_s(struct sql_transaction_context *_ctx,
NULL,
void driver_pgsql_init(void);
void driver_pgsql_deinit(void);
void driver_pgsql_init(void)
void driver_pgsql_deinit(void)