driver-pgsql.c revision 36c4702131e5a04984ad5d07cf5d8d5c633d43c3
/* Copyright (c) 2004-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "sql-api-private.h"
#ifdef BUILD_PGSQL
#include <stdlib.h>
#include <libpq-fe.h>
struct pgsql_db {
char *connect_string;
char *host;
struct timeout *to_connect;
enum io_condition io_dir;
struct pgsql_result *cur_result;
struct sql_result *sync_result;
char *error;
unsigned int fatal_error:1;
};
struct pgsql_binary_value {
unsigned char *value;
};
struct pgsql_result {
struct sql_result api;
unsigned int fields_count;
const char **fields;
const char **values;
void *context;
unsigned int timeout:1;
};
struct pgsql_transaction_context {
struct sql_transaction_context ctx;
int refcount;
void *context;
const char *error;
unsigned int begin_succeeded:1;
unsigned int begin_failed:1;
unsigned int failed:1;
};
extern const struct sql_db driver_pgsql_db;
extern const struct sql_result driver_pgsql_result;
{
/* switch back to original ioloop in case the caller wants to
}
{
}
}
{
/* running a sync query, stop it */
}
}
{
const char *msg;
return "(no error set)";
/* Error message should contain trailing \n, we don't want it */
}
{
enum io_condition io_dir = 0;
int ret;
;
switch (ret) {
case PGRES_POLLING_READING:
break;
case PGRES_POLLING_WRITING:
break;
case PGRES_POLLING_OK:
break;
case PGRES_POLLING_FAILED:
i_error("pgsql: Connect failed to %s: %s",
return;
}
if (io_dir != 0) {
}
if (io_dir == 0) {
/* driver_pgsql_sync_init() waiting for connection to
finish */
}
}
}
{
i_error("pgsql: Connect failed to %s: Timeout after %u seconds",
}
{
i_fatal("pgsql: PQconnectStart() failed (out of memory)");
i_error("pgsql: Connect failed to %s: %s",
return -1;
}
/* nonblocking connecting begins. */
i_error("pgsql: PQsetnonblocking() failed");
return 0;
}
{
}
{
T_BEGIN {
}
} T_END;
}
{
}
{
if (db->fatal_error)
else
}
{
return;
}
break;
}
else
}
{
bool success;
/* we're coming here from a user's sql_result_free() that's
being called from a callback. we'll do this later,
so ignore. */
return;
}
}
if (success) {
/* we'll have to read the rest of the results as well */
} else {
}
struct pgsql_binary_value *value;
}
}
{
bool free_result = TRUE;
/* if connection to server was lost, we don't yet see that the
connection is bad. we only see the fatal error, so assume it also
means disconnection. */
if (db->fatal_error) {
}
T_BEGIN {
} T_END;
if (free_result)
}
{
return;
}
get_result, result);
return;
}
}
{
int ret;
if (ret > 0) {
return;
}
if (ret < 0) {
} else {
/* all flushed */
}
}
{
i_error("pgsql: Query timed out, aborting");
}
{
int ret;
/* failed to send query */
return;
}
if (ret > 0) {
/* write blocks */
} else {
}
}
static const char *
{
char *to;
#ifdef HAVE_PQESCAPE_STRING_CONN
/* try connecting again */
}
} else
#endif
{
}
return to;
}
void *context ATTR_UNUSED)
{
}
{
struct pgsql_result *result;
}
{
struct pgsql_result *result;
}
{
}
{
bool add_to_connect;
return;
}
/* have to move our existing I/O and timeout handlers to new I/O loop */
} else {
}
if (add_to_connect) {
}
/* wait for connecting to finish */
}
{
}
static struct sql_result *
{
struct sql_result *result;
case SQL_DB_STATE_CONNECTING:
case SQL_DB_STATE_BUSY:
i_unreached();
return &sql_not_connected_result;
case SQL_DB_STATE_IDLE:
break;
}
if (result == &sql_not_connected_result) {
/* we don't end up in pgsql's free function, so sync_result
won't be set to NULL if we don't do it here. */
}
return result;
}
static struct sql_result *
{
struct sql_result *result;
return result;
}
{
/* second time we're here */
return 1;
/* end of this packet. see if there's more. FIXME: this may
block, but the current API doesn't provide a non-blocking
way to do this.. */
return 0;
}
return -1;
}
case PGRES_COMMAND_OK:
/* no rows returned */
return 0;
case PGRES_TUPLES_OK:
case PGRES_EMPTY_QUERY:
case PGRES_NONFATAL_ERROR:
/* nonfatal error */
return -1;
default:
/* treat as fatal error */
return -1;
}
}
{
unsigned int i;
return;
/* @UNSAFE */
for (i = 0; i < result->fields_count; i++)
}
static unsigned int
{
return result->fields_count;
}
static const char *
{
}
const char *field_name)
{
unsigned int i;
for (i = 0; i < result->fields_count; i++) {
return i;
}
return -1;
}
static const char *
unsigned int idx)
{
return NULL;
}
static const unsigned char *
{
const char *value;
struct pgsql_binary_value *binary_value;
*size_r = 0;
return NULL;
}
PQunescapeBytea((const unsigned char *)value,
&binary_value->size);
}
return binary_value->value;
}
static const char *
const char *field_name)
{
int idx;
if (idx < 0)
return NULL;
}
static const char *const *
{
unsigned int i;
}
/* @UNSAFE */
for (i = 0; i < result->fields_count; i++) {
}
}
{
const char *msg;
/* connection error */
} else {
return "(no error set)";
/* Error message should contain trailing \n, we don't want it */
}
}
static struct sql_transaction_context *
{
struct pgsql_transaction_context *ctx;
/* we need to be able to handle multiple open transactions, so at least
for now just keep them in memory until commit time. */
}
static void
{
return;
}
static void
struct pgsql_transaction_context *ctx)
{
if (sql_result_next_row(result) < 0) {
} else {
}
}
static void
struct pgsql_transaction_context *ctx)
{
if (sql_result_next_row(result) < 0)
else
}
static void
struct sql_transaction_query *query)
{
struct pgsql_transaction_context *ctx =
if (sql_result_next_row(result) < 0) {
}
}
static void
{
struct pgsql_transaction_context *ctx =
(struct pgsql_transaction_context *)_ctx;
/* just a single query, send it */
} else {
/* multiple queries, use a transaction */
}
}
}
static void
{
}
static struct sql_result *
{
struct sql_result *result;
struct sql_transaction_query *query;
if (sql_result_next_row(result) < 0) {
return NULL;
}
/* send queries */
if (sql_result_next_row(result) < 0) {
break;
}
struct pgsql_result *pg_result =
(struct pgsql_result *)result;
*query->affected_rows =
}
}
"ROLLBACK" : "COMMIT");
}
static int
const char **error_r)
{
struct pgsql_transaction_context *ctx =
(struct pgsql_transaction_context *)_ctx;
struct sql_result *result;
/* nothing to be done */
/* just a single query, send it */
} else {
/* multiple queries, use a transaction */
}
if (sql_result_next_row(result) < 0)
else if (single_query != NULL &&
struct pgsql_result *pg_result =
(struct pgsql_result *)result;
}
}
}
static void
{
struct pgsql_transaction_context *ctx =
(struct pgsql_transaction_context *)_ctx;
}
static void
unsigned int *affected_rows)
{
struct pgsql_transaction_context *ctx =
(struct pgsql_transaction_context *)_ctx;
}
const struct sql_db driver_pgsql_db = {
.name = "pgsql",
.v = {
}
};
const struct sql_result driver_pgsql_result = {
.v = {
}
};
const char *driver_pgsql_version = DOVECOT_VERSION;
void driver_pgsql_init(void);
void driver_pgsql_deinit(void);
void driver_pgsql_init(void)
{
}
void driver_pgsql_deinit(void)
{
}
#endif