driver-pgsql.c revision c44f402f17f9a58ead24ac0083945cae86fb172b
/* Copyright (c) 2004-2010 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "ioloop-internal.h" /* kind of dirty, but it should be fine.. */
#include "sql-api-private.h"
#ifdef BUILD_PGSQL
#include <stdlib.h>
#include <time.h>
#include <libpq-fe.h>
#define QUERY_TIMEOUT_SECS 6
struct pgsql_db {
char *connect_string;
enum io_condition io_dir;
struct sql_result *sync_result;
char *error;
unsigned int connecting:1;
unsigned int connected:1;
unsigned int querying:1;
unsigned int query_finished:1;
};
struct pgsql_binary_value {
unsigned char *value;
};
struct pgsql_result {
struct sql_result api;
char *query;
unsigned int fields_count;
const char **fields;
const char **values;
void *context;
unsigned int retry_query:1;
};
struct pgsql_queue {
struct pgsql_queue *next;
char *query;
struct pgsql_result *result;
};
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;
};
struct pgsql_query_list {
struct pgsql_query_list *next;
struct pgsql_transaction_context *ctx;
const char *query;
unsigned int *affected_rows;
};
extern struct sql_db driver_pgsql_db;
extern struct sql_result driver_pgsql_result;
static void
bool retry_query);
{
/* The fd may be closed before call to PQfinish() already,
so use io_remove_closed(). */
}
}
{
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;
}
}
}
{
/* don't try reconnecting more than once a second */
i_fatal("pgsql: PQconnectStart() failed (out of memory)");
i_error("pgsql: Connect failed to %s: %s",
return -1;
} else {
/* nonblocking connecting begins. */
i_error("pgsql: PQsetnonblocking() failed");
return 0;
}
}
{
}
{
}
}
static enum sql_db_flags
{
return 0;
}
{
return;
break;
}
else
}
{
return;
/* we'll have to read the rest of the results as well */
} else {
}
struct pgsql_binary_value *value;
}
}
{
bool disconnected;
/* 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. */
/* retry the query */
T_BEGIN {
} T_END;
}
if (disconnected) {
/* disconnected */
}
if (retry) {
/* retry the query */
}
}
if (free_result)
}
{
return;
}
get_result, result);
}
return;
}
}
{
int ret;
if (ret > 0)
return;
if (ret < 0) {
} else {
/* all flushed */
}
}
{
int ret;
return;
}
if (ret < 0) {
return;
}
if (ret > 0) {
/* write blocks */
} else {
}
}
{
struct pgsql_queue *queue;
return queue;
}
{
struct pgsql_queue *queue;
}
{
struct pgsql_queue *queue;
}
{
}
{
return;
return;
}
}
static void
{
struct pgsql_queue *queue;
}
{
/* only one query at a time */
return;
}
/* try connecting again */
return;
}
else {
/* there's already queries queued, send them first */
}
}
void *context ATTR_UNUSED)
{
}
static const char *
{
char *to;
#ifdef HAVE_PQESCAPE_STRING_CONN
/* try connecting again */
}
#else
#endif
return to;
}
bool retry_query)
{
struct pgsql_result *result;
if (retry_query) {
}
}
{
}
static void
bool retry_query)
{
struct pgsql_result *result;
if (retry_query) {
}
}
{
}
{
}
static struct sql_result *
{
struct sql_result *result;
/* we're creating a new ioloop, make sure the timeout gets
added there. */
}
else {
/* have to move our existing I/O handler to new I/O loop */
}
if (!db->query_finished) {
else
}
} else {
}
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;
}
{
/* 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 pgsql_query_list *list)
{
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 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 */
}
/* flush the previous queries */
}
if (ctx->begin_failed) {
} else {
}
}
if (sql_result_next_row(result) < 0)
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;
struct pgsql_query_list *list;
else
}
struct sql_db driver_pgsql_db = {
"pgsql",
.v = {
}
};
struct sql_result driver_pgsql_result = {
.v = {
}
};
void driver_pgsql_init(void);
void driver_pgsql_deinit(void);
void driver_pgsql_init(void)
{
}
void driver_pgsql_deinit(void)
{
}
#endif