[dovecot-cvs] dovecot/src/lib-sql .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 driver-mysql.c, NONE, 1.1 driver-pgsql.c, NONE, 1.1 sql-api-private.h, NONE, 1.1 sql-api.c, NONE, 1.1 sql-api.h, NONE, 1.1

cras at dovecot.org cras at dovecot.org
Sat Oct 16 02:12:54 EEST 2004


Update of /var/lib/cvs/dovecot/src/lib-sql
In directory talvi:/tmp/cvs-serv14242/src/lib-sql

Added Files:
	.cvsignore Makefile.am driver-mysql.c driver-pgsql.c 
	sql-api-private.h sql-api.c sql-api.h 
Log Message:
Created generic asynchronous SQL API and implemented MySQL and PostgreSQL
drivers. MySQL is implemented synchronously because it's API doesn't provide
async way to do it.

Replaced pgsql and mysql userdb/passdb with generic sql userdb/passdb.



--- NEW FILE: .cvsignore ---
*.la
*.lo
*.o
.deps
.libs
Makefile
Makefile.in
so_locations

--- NEW FILE: Makefile.am ---
noinst_LIBRARIES = libsql.a

INCLUDES = \
	-I$(top_srcdir)/src/lib \
	$(SQL_CFLAGS)

libsql_a_SOURCES = \
	driver-mysql.c \
	driver-pgsql.c \
	sql-api.c

noinst_HEADERS = \
	sql-api.h \
	sql-api-private.h

--- NEW FILE: driver-mysql.c ---
/* Copyright (C) 2003-2004 Timo Sirainen, Alex Howansky */

#include "lib.h"
#include "sql-api-private.h"

#ifdef HAVE_MYSQL
#include <stdlib.h>
#include <time.h>
#include <mysql.h>
#include <errmsg.h>

struct mysql_db {
	struct sql_db api;

	pool_t pool;
	const char *host, *user, *password, *dbname, *unix_socket;
	const char *ssl_cert, *ssl_key, *ssl_ca, *ssl_ca_path, *ssl_cipher;
	unsigned int port, client_flags;
	MYSQL *mysql;

	time_t last_connect;

	unsigned int connected:1;
	unsigned int ssl:1;
};

struct mysql_result {
	struct sql_result api;
	MYSQL_RES *result;
        MYSQL_ROW row;

	MYSQL_FIELD *fields;
	unsigned int fields_count;
};

extern struct sql_result driver_mysql_result;
extern struct sql_result driver_mysql_error_result;

static int driver_mysql_connect(struct mysql_db *db)
{
	const char *unix_socket, *host;
	time_t now;

	if (db->connected)
		return TRUE;

	/* don't try reconnecting more than once a second */
	now = time(NULL);
	if (db->last_connect == now)
		return FALSE;
	db->last_connect = now;

	if (*db->host == '/') {
		unix_socket = db->host;
		host = NULL;
	} else {
		unix_socket = NULL;
		host = db->host;
	}

	if (mysql_real_connect(db->mysql, host, db->user, db->password,
			       db->dbname, db->port, unix_socket,
			       db->client_flags) == NULL) {
		i_error("mysql: Connect failed to %s: %s",
			db->dbname, mysql_error(db->mysql));
		return FALSE;
	} else {
		i_info("mysql: Connected to %s%s", db->dbname,
		       db->ssl ? " using SSL" : "");
		db->connected = TRUE;
		return TRUE;
	}
}

static void driver_mysql_parse_connect_string(struct mysql_db *db,
					      const char *connect_string)
{
	const char *const *args, *name, *value;
	const char **field;

	db->ssl_cipher = "HIGH";

	t_push();
	args = t_strsplit_spaces(connect_string, " ");
	for (; *args != NULL; args++) {
		value = strchr(*args, '=');
		if (value == NULL) {
			i_fatal("mysql: Missing value in connect string: %s",
				*args);
		}
		name = t_strdup_until(*args, value);
		value++;

		field = NULL;
		if (strcmp(name, "host") == 0 || strcmp(name, "hostaddr") == 0)
			field = &db->host;
		else if (strcmp(name, "user") == 0)
			field = &db->user;
		else if (strcmp(name, "password") == 0)
			field = &db->password;
		else if (strcmp(name, "dbname") == 0)
			field = &db->dbname;
		else if (strcmp(name, "port") == 0)
			db->port = atoi(value);
		else if (strcmp(name, "client_flags") == 0)
			db->client_flags = atoi(value);
		else if (strcmp(name, "ssl_cert") == 0)
			field = &db->ssl_cert;
		else if (strcmp(name, "ssl_key") == 0)
			field = &db->ssl_key;
		else if (strcmp(name, "ssl_ca") == 0)
			field = &db->ssl_ca;
		else if (strcmp(name, "ssl_ca_path") == 0)
			field = &db->ssl_ca_path;
		else if (strcmp(name, "ssl_cipher") == 0)
			field = &db->ssl_cipher;
		else
			i_fatal("mysql: Unknown connect string: %s", name);

		if (field != NULL)
			*field = p_strdup(db->pool, value);
	}
	t_pop();
}

static struct sql_db *driver_mysql_init(const char *connect_string)
{
	struct mysql_db *db;
	pool_t pool;

	pool = pool_alloconly_create("mysql driver", 256);

	db = p_new(pool, struct mysql_db, 1);
	db->pool = pool;
	db->api = driver_mysql_db;
	db->mysql = mysql_init(NULL);
	if (db->mysql == NULL)
		i_fatal("mysql_init() failed");

	driver_mysql_parse_connect_string(db, connect_string);
	if (db->ssl_ca != NULL || db->ssl_ca_path != NULL) {
#ifdef HAVE_MYSQL_SSL
		mysql_ssl_set(db->mysql, db->ssl_key, db->ssl_cert,
			      db->ssl_ca, db->ssl_ca_path
#ifdef HAVE_MYSQL_SSL_CIPHER
			      , db->ssl_cipher
#endif
			     );
		db->ssl = TRUE;
#else
		i_fatal("mysql: SSL support not compiled in "
			"(remove ssl_ca and ssl_ca_path settings)");
#endif
	}
	(void)driver_mysql_connect(db);
	return &db->api;
}

static void driver_mysql_deinit(struct sql_db *_db)
{
	struct mysql_db *db = (struct mysql_db *)_db;

	mysql_close(db->mysql);
	pool_unref(db->pool);
}

static int driver_mysql_do_query(struct mysql_db *db, const char *query)
{
	int i;

	for (i = 0; i < 2; i++) {
		if (!driver_mysql_connect(db))
			return 0;

		if (mysql_query(db->mysql, query) == 0)
			return 1;

		/* failed */
		switch (mysql_errno(db->mysql)) {
		case CR_SERVER_GONE_ERROR:
		case CR_SERVER_LOST:
			/* connection lost - try immediate reconnect */
			db->connected = FALSE;
			break;
		default:
			return -1;
		}
	}

	/* connected -> lost it -> connected -> lost again */
	return 0;
}

static void driver_mysql_exec(struct sql_db *_db, const char *query)
{
	struct mysql_db *db = (struct mysql_db *)_db;

	(void)driver_mysql_do_query(db, query);
}

static void driver_mysql_query(struct sql_db *_db, const char *query,
			       sql_query_callback_t *callback, void *context)
{
	struct mysql_db *db = (struct mysql_db *)_db;
	struct sql_result error_result;
	struct mysql_result result;

	switch (driver_mysql_do_query(db, query)) {
	case 0:
		/* not connected */
		callback(&sql_not_connected_result, context);
		return;

	case 1:
		/* query ok */
		memset(&result, 0, sizeof(result));
		result.api = driver_mysql_result;
		result.result = mysql_store_result(db->mysql);
		if (result.result == NULL)
			break;

		callback(&result.api, context);
		return;
	case -1:
		/* error */
		break;
	}

	/* error */
	error_result = driver_mysql_error_result;
	error_result.db = _db;
	callback(&error_result, context);
}

static int driver_mysql_result_next_row(struct sql_result *_result)
{
	struct mysql_result *result = (struct mysql_result *)_result;

	result->row = mysql_fetch_row(result->result);
	return result->row != NULL;
}

static void driver_mysql_result_fetch_fields(struct mysql_result *result)
{
	if (result->fields != NULL)
		return;

	result->fields_count = mysql_num_fields(result->result);
	result->fields = mysql_fetch_fields(result->result);
}

static unsigned int
driver_mysql_result_get_fields_count(struct sql_result *_result)
{
	struct mysql_result *result = (struct mysql_result *)_result;

        driver_mysql_result_fetch_fields(result);
	return result->fields_count;
}

static const char *
driver_mysql_result_get_field_name(struct sql_result *_result, unsigned int idx)
{
	struct mysql_result *result = (struct mysql_result *)_result;

	driver_mysql_result_fetch_fields(result);
	i_assert(idx < result->fields_count);
	return result->fields[idx].name;
}

static int driver_mysql_result_find_field(struct sql_result *_result,
					  const char *field_name)
{
	struct mysql_result *result = (struct mysql_result *)_result;
	unsigned int i;

	driver_mysql_result_fetch_fields(result);
	for (i = 0; i < result->fields_count; i++) {
		if (strcmp(result->fields[i].name, field_name) == 0)
			return i;
	}
	return -1;
}

static const char *
driver_mysql_result_get_field_value(struct sql_result *_result,
				    unsigned int idx)
{
	struct mysql_result *result = (struct mysql_result *)_result;

	return (const char *)result->row[idx];
}

static const char *
driver_mysql_result_find_field_value(struct sql_result *result,
				     const char *field_name)
{
	int idx;

	idx = driver_mysql_result_find_field(result, field_name);
	if (idx < 0)
		return NULL;
	return driver_mysql_result_get_field_value(result, idx);
}

static const char *const *
driver_mysql_result_get_values(struct sql_result *_result)
{
	struct mysql_result *result = (struct mysql_result *)_result;

	return (const char *const *)result->row;
}

static const char *driver_mysql_result_get_error(struct sql_result *result)
{
	struct mysql_db *db = (struct mysql_db *)result->db;

	return mysql_error(db->mysql);
}

struct sql_db driver_mysql_db = {
	driver_mysql_init,
	driver_mysql_deinit,
	driver_mysql_exec,
	driver_mysql_query
};

struct sql_result driver_mysql_result = {
	NULL,

	driver_mysql_result_next_row,
	driver_mysql_result_get_fields_count,
	driver_mysql_result_get_field_name,
	driver_mysql_result_find_field,
	driver_mysql_result_get_field_value,
	driver_mysql_result_find_field_value,
	driver_mysql_result_get_values,
	driver_mysql_result_get_error
};

static int
driver_mysql_result_error_next_row(struct sql_result *result __attr_unused__)
{
	return -1;
}

struct sql_result driver_mysql_error_result = {
	NULL,

	driver_mysql_result_error_next_row,
	NULL, NULL, NULL, NULL, NULL, NULL,
	driver_mysql_result_get_error
};
#endif

--- NEW FILE: driver-pgsql.c ---
/* Copyright (C) 2004 Timo Sirainen */

#include "lib.h"
#include "ioloop.h"
#include "sql-api-private.h"

#ifdef HAVE_PGSQL
#include <stdlib.h>
#include <time.h>
#include <libpq-fe.h>

struct pgsql_db {
	struct sql_db api;

	pool_t pool;
	const char *connect_string;
	PGconn *pg;

	struct io *io;
	enum io_condition io_dir;

	struct pgsql_queue *queue, **queue_tail;
	struct timeout *queue_to;

	time_t last_connect;
	unsigned int connecting:1;
	unsigned int connected:1;
	unsigned int querying:1;
};

struct pgsql_result {
	struct sql_result api;
	PGresult *pgres;

	unsigned int rownum, rows;
	unsigned int fields_count;
	const char **fields;
	const char **values;

	sql_query_callback_t *callback;
	void *context;
};

struct pgsql_queue {
	struct pgsql_queue *next;

	time_t created;
	char *query;
	struct pgsql_result *result;
};

extern struct sql_result driver_pgsql_result;

static void queue_send_next(struct pgsql_db *db);

static void driver_pgsql_close(struct pgsql_db *db)
{
	if (db->io != NULL) {
		io_remove(db->io);
		db->io = NULL;
	}
	db->io_dir = 0;

	PQfinish(db->pg);
	db->pg = NULL;

	db->connecting = FALSE;
	db->connected = FALSE;
        db->querying = FALSE;
}

static const char *last_error(struct pgsql_db *db)
{
	const char *msg;
	size_t len;

	msg = PQerrorMessage(db->pg);
	if (msg == NULL)
		return "(no error set)";

	/* Error message should contain trailing \n, we don't want it */
	len = strlen(msg);
	return len == 0 || msg[len-1] != '\n' ? msg :
		t_strndup(msg, len-1);
}

static void connect_callback(void *context)
{
	struct pgsql_db *db = context;
	enum io_condition io_dir = 0;
	int ret;

	while ((ret = PQconnectPoll(db->pg)) == PGRES_POLLING_ACTIVE)
		;

	switch (ret) {
	case PGRES_POLLING_READING:
		io_dir = IO_READ;
		break;
	case PGRES_POLLING_WRITING:
		io_dir = IO_WRITE;
		break;
	case PGRES_POLLING_OK:
		i_info("pgsql: Connected to %s", PQdb(db->pg));
		db->connecting = FALSE;
		db->connected = TRUE;
		break;
	case PGRES_POLLING_FAILED:
		i_error("pgsql: Connect failed to %s: %s",
			PQdb(db->pg), last_error(db));
		driver_pgsql_close(db);
		return;
	}

	if (db->io_dir != io_dir) {
		if (db->io != NULL)
			io_remove(db->io);
		db->io = io_dir == 0 ? NULL :
			io_add(PQsocket(db->pg), io_dir, connect_callback, db);
		db->io_dir = io_dir;
	}
}

static void driver_pgsql_connect(struct pgsql_db *db)
{
	time_t now;

	/* don't try reconnecting more than once a second */
	now = time(NULL);
	if (db->connecting || db->last_connect == now)
		return;
	db->last_connect = now;

	db->pg = PQconnectStart(db->connect_string);
	if (db->pg == NULL)
		i_fatal("pgsql: PQconnectStart() failed (out of memory)");

	if (PQstatus(db->pg) == CONNECTION_BAD) {
		i_error("pgsql: Connect failed to %s: %s",
			PQdb(db->pg), last_error(db));
		driver_pgsql_close(db);
	} else {
		/* nonblocking connecting begins. */
		db->io = io_add(PQsocket(db->pg), IO_WRITE,
				connect_callback, db);
		db->io_dir = IO_WRITE;
		db->connecting = TRUE;
	}
}

static struct sql_db *driver_pgsql_init(const char *connect_string)
{
	struct pgsql_db *db;

	db = i_new(struct pgsql_db, 1);
	db->connect_string = i_strdup(connect_string);
	db->api = driver_pgsql_db;
	db->queue_tail = &db->queue;

	(void)driver_pgsql_connect(db);
	return &db->api;
}

static void driver_pgsql_deinit(struct sql_db *_db)
{
	struct pgsql_db *db = (struct pgsql_db *)_db;

        driver_pgsql_close(db);
	i_free(db);
}

static void consume_results(void *context)
{
	struct pgsql_db *db = context;

	do {
		if (!PQconsumeInput(db->pg)) {
			db->connected = FALSE;
			break;

		}
		if (PQisBusy(db->pg))
			return;

	} while (PQgetResult(db->pg) != NULL);

	io_remove(db->io);
	db->io = NULL;

	db->querying = FALSE;
	if (db->queue != NULL && db->connected)
		queue_send_next(db);
}

static void result_finish(struct pgsql_result *result)
{
	struct pgsql_db *db = (struct pgsql_db *)result->api.db;

	if (result->callback != NULL)
		result->callback(&result->api, result->context);
	if (result->pgres != NULL) {
		PQclear(result->pgres);

		/* we'll have to read the rest of the results as well */
		i_assert(db->io == NULL);
		db->io = io_add(PQsocket(db->pg), IO_READ,
				consume_results, db);
		consume_results(db);
	} else {
		db->querying = FALSE;
	}

	i_free(result->fields);
	i_free(result->values);
	i_free(result);

	if (db->queue != NULL && !db->querying && db->connected)
		queue_send_next(db);
}

static void get_result(void *context)
{
        struct pgsql_result *result = context;
        struct pgsql_db *db = (struct pgsql_db *)result->api.db;

	if (!PQconsumeInput(db->pg)) {
		db->connected = FALSE;
		result_finish(result);
		return;
	}

	if (PQisBusy(db->pg)) {
		if (db->io == NULL) {
 			db->io = io_add(PQsocket(db->pg), IO_READ,
					get_result, result);
		}
		return;
	}

	if (db->io != NULL) {
		io_remove(db->io);
		db->io = NULL;
	}

	result->pgres = PQgetResult(db->pg);
	result_finish(result);
}

static void flush_callback(void *context)
{
	struct pgsql_result *result = context;
        struct pgsql_db *db = (struct pgsql_db *)result->api.db;
	int ret;

	ret = PQflush(db->pg);
	if (ret > 0)
		return;

	io_remove(db->io);
        db->io = NULL;

	if (ret < 0) {
		db->connected = FALSE;
		result_finish(result);
	} else {
		/* all flushed */
		get_result(result);
	}
}

static void send_query(struct pgsql_result *result, const char *query)
{
        struct pgsql_db *db = (struct pgsql_db *)result->api.db;
	int ret;

	i_assert(db->io == NULL);
	i_assert(!db->querying);
	i_assert(db->connected);

	if (!PQsendQuery(db->pg, query)) {
		db->connected = FALSE;
		result_finish(result);
		return;
	}

	ret = PQflush(db->pg);
	if (ret < 0) {
		db->connected = FALSE;
		result_finish(result);
		return;
	}

	db->querying = TRUE;
	if (ret > 0) {
		/* write blocks */
		db->io = io_add(PQsocket(db->pg), IO_WRITE,
				flush_callback, result);
	} else {
		get_result(result);
	}
}

static void queue_send_next(struct pgsql_db *db)
{
	struct pgsql_queue *queue;

	queue = db->queue;
	db->queue = queue->next;

	send_query(queue->result, queue->query);

	i_free(queue->query);
	i_free(queue);
}

static void queue_timeout(void *context)
{
	struct pgsql_db *db = context;

	if (db->querying)
		return;

	if (!db->connected) {
		driver_pgsql_connect(db);
		return;
	}

	if (db->queue != NULL)
		queue_send_next(db);

	if (db->queue == NULL) {
		timeout_remove(db->queue_to);
                db->queue_to = NULL;
	}
}

static void
driver_pgsql_queue_query(struct pgsql_result *result, const char *query)
{
        struct pgsql_db *db = (struct pgsql_db *)result->api.db;
	struct pgsql_queue *queue;

	queue = i_new(struct pgsql_queue, 1);
	queue->created = time(NULL);
	queue->query = i_strdup(query);
	queue->result = result;

	*db->queue_tail = queue;

	if (db->queue_to == NULL)
		db->queue_to = timeout_add(5000, queue_timeout, db);
}

static void do_query(struct pgsql_result *result, const char *query)
{
        struct pgsql_db *db = (struct pgsql_db *)result->api.db;

	if (db->querying) {
		/* only one query at a time */
		driver_pgsql_queue_query(result, query);
		return;
	}

	if (!db->connected) {
		/* try connecting again */
		driver_pgsql_connect(db);
		driver_pgsql_queue_query(result, query);
		return;
	}

	if (db->queue == NULL)
		send_query(result, query);
	else {
		/* there's already queries queued, send them first */
		driver_pgsql_queue_query(result, query);
		queue_send_next(db);
	}
}

static void exec_callback(struct sql_result *result,
			  void *context __attr_unused__)
{
        struct pgsql_db *db = (struct pgsql_db *)result->db;

	i_error("pgsql: sql_exec() failed: %s", last_error(db));
}

static void driver_pgsql_exec(struct sql_db *db, const char *query)
{
	struct pgsql_result *result;

	result = i_new(struct pgsql_result, 1);
	result->api = driver_pgsql_result;
	result->api.db = db;
	result->callback = exec_callback;

	do_query(result, query);
}

static void driver_pgsql_query(struct sql_db *db, const char *query,
			       sql_query_callback_t *callback, void *context)
{
	struct pgsql_result *result;

	result = i_new(struct pgsql_result, 1);
	result->api = driver_pgsql_result;
	result->api.db = db;
	result->callback = callback;
	result->context = context;

	do_query(result, query);
}

static int driver_pgsql_result_next_row(struct sql_result *_result)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;
	struct pgsql_db *db = (struct pgsql_db *)_result->db;

	if (result->rows != 0) {
		/* second time we're here */
		return ++result->rownum < result->rows;
	}

	if (result->pgres == NULL)
		return -1;

	switch (PQresultStatus(result->pgres)) {
	case PGRES_COMMAND_OK:
		/* no rows returned */
		return 0;
	case PGRES_TUPLES_OK:
		result->rows = PQntuples(result->pgres);
		return result->rows > 0;
	case PGRES_EMPTY_QUERY:
	case PGRES_NONFATAL_ERROR:
		/* nonfatal error */
		return -1;
	default:
		/* treat as fatal error */
		db->connected = FALSE;
		return -1;
	}
}

static void driver_pgsql_result_fetch_fields(struct pgsql_result *result)
{
	unsigned int i;

	if (result->fields != NULL)
		return;

	/* @UNSAFE */
	result->fields_count = PQnfields(result->pgres);
	result->fields = i_new(const char *, result->fields_count);
	for (i = 0; i < result->fields_count; i++)
		result->fields[i] = PQfname(result->pgres, i);
}

static unsigned int
driver_pgsql_result_get_fields_count(struct sql_result *_result)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;

        driver_pgsql_result_fetch_fields(result);
	return result->fields_count;
}

static const char *
driver_pgsql_result_get_field_name(struct sql_result *_result, unsigned int idx)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;

	driver_pgsql_result_fetch_fields(result);
	i_assert(idx < result->fields_count);
	return result->fields[idx];
}

static int driver_pgsql_result_find_field(struct sql_result *_result,
					  const char *field_name)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;
	unsigned int i;

	driver_pgsql_result_fetch_fields(result);
	for (i = 0; i < result->fields_count; i++) {
		if (strcmp(result->fields[i], field_name) == 0)
			return i;
	}
	return -1;
}

static const char *
driver_pgsql_result_get_field_value(struct sql_result *_result,
				    unsigned int idx)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;

	if (PQgetisnull(result->pgres, result->rownum, idx))
		return NULL;

	return PQgetvalue(result->pgres, result->rownum, idx);
}

static const char *
driver_pgsql_result_find_field_value(struct sql_result *result,
				     const char *field_name)
{
	int idx;

	idx = driver_pgsql_result_find_field(result, field_name);
	if (idx < 0)
		return NULL;
	return driver_pgsql_result_get_field_value(result, idx);
}

static const char *const *
driver_pgsql_result_get_values(struct sql_result *_result)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;
	unsigned int i;

	if (result->values == NULL) {
		driver_pgsql_result_fetch_fields(result);
		result->values = i_new(const char *, result->fields_count);
	}

	/* @UNSAFE */
	for (i = 0; i < result->fields_count; i++) {
		result->values[i] =
                        driver_pgsql_result_get_field_value(_result, i);
	}

	return result->values;
}

static const char *driver_pgsql_result_get_error(struct sql_result *_result)
{
	struct pgsql_result *result = (struct pgsql_result *)_result;
	const char *msg;
	size_t len;

	msg = PQresultErrorMessage(result->pgres);
	if (msg == NULL)
		return "(no error set)";

	/* Error message should contain trailing \n, we don't want it */
	len = strlen(msg);
	return len == 0 || msg[len-1] != '\n' ? msg :
		t_strndup(msg, len-1);
}

struct sql_db driver_pgsql_db = {
	driver_pgsql_init,
	driver_pgsql_deinit,
	driver_pgsql_exec,
	driver_pgsql_query
};

struct sql_result driver_pgsql_result = {
	NULL,

	driver_pgsql_result_next_row,
	driver_pgsql_result_get_fields_count,
	driver_pgsql_result_get_field_name,
	driver_pgsql_result_find_field,
	driver_pgsql_result_get_field_value,
	driver_pgsql_result_find_field_value,
	driver_pgsql_result_get_values,
	driver_pgsql_result_get_error
};

#endif

--- NEW FILE: sql-api-private.h ---
#ifndef __SQL_API_PRIVATE_H
#define __SQL_API_PRIVATE_H

#include "sql-api.h"

struct sql_db {
	struct sql_db *(*init)(const char *connect_string);
	void (*deinit)(struct sql_db *db);

	void (*exec)(struct sql_db *db, const char *query);
	void (*query)(struct sql_db *db, const char *query,
		      sql_query_callback_t *callback, void *context);
};

struct sql_result {
	struct sql_db *db;

	int (*next_row)(struct sql_result *result);

	unsigned int (*get_fields_count)(struct sql_result *result);
	const char *(*get_field_name)(struct sql_result *result,
				      unsigned int idx);
	int (*find_field)(struct sql_result *result, const char *field_name);

	const char *(*get_field_value)(struct sql_result *result,
				       unsigned int idx);
	const char *(*find_field_value)(struct sql_result *result,
					const char *field_name);
	const char *const *(*get_values)(struct sql_result *result);

	const char *(*get_error)(struct sql_result *result);
};

extern struct sql_db driver_mysql_db;
extern struct sql_db driver_pgsql_db;

extern struct sql_result sql_not_connected_result;

#endif

--- NEW FILE: sql-api.c ---
/* Copyright (c) 2004 Timo Sirainen */

#include "lib.h"
#include "sql-api-private.h"

struct sql_db *sql_init(const char *db_driver, const char *connect_string)
{
#ifdef HAVE_PGSQL
	if (strcmp(db_driver, "pgsql") == 0)
		return driver_pgsql_db.init(connect_string);
#endif
#ifdef HAVE_MYSQL
	if (strcmp(db_driver, "mysql") == 0)
		return driver_mysql_db.init(connect_string);
#endif

	i_fatal("Unknown database driver '%s'", db_driver);
}

void sql_deinit(struct sql_db *db)
{
	db->deinit(db);
}

void sql_exec(struct sql_db *db, const char *query)
{
	db->exec(db, query);
}

void sql_query(struct sql_db *db, const char *query,
	       sql_query_callback_t *callback, void *context)
{
	db->query(db, query, callback, context);
}

int sql_result_next_row(struct sql_result *result)
{
	return result->next_row(result);
}

unsigned int sql_result_get_fields_count(struct sql_result *result)
{
	return result->get_fields_count(result);
}

const char *sql_result_get_field_name(struct sql_result *result,
				      unsigned int idx)
{
	return result->get_field_name(result, idx);
}

int sql_result_find_field(struct sql_result *result, const char *field_name)
{
	return result->find_field(result, field_name);
}

const char *sql_result_get_field_value(struct sql_result *result,
				       unsigned int idx)
{
	return result->get_field_value(result, idx);
}

const char *sql_result_find_field_value(struct sql_result *result,
					const char *field_name)
{
	return result->find_field_value(result, field_name);
}

const char *const *sql_result_get_values(struct sql_result *result)
{
	return result->get_values(result);
}

const char *sql_result_get_error(struct sql_result *result)
{
	return result->get_error(result);
}

static int
sql_result_not_connected_next_row(struct sql_result *result __attr_unused__)
{
	return -1;
}

static const char *
sql_result_not_connected_get_error(struct sql_result *result __attr_unused__)
{
	return "Not connected to database";
}

struct sql_result sql_not_connected_result = {
	NULL,

	sql_result_not_connected_next_row,
	NULL, NULL, NULL, NULL, NULL, NULL,
	sql_result_not_connected_get_error
};

--- NEW FILE: sql-api.h ---
#ifndef __SQL_API_H
#define __SQL_API_H

/* This SQL API is designed to work asynchronously. The underlying drivers
   however may not. */

struct sql_db;
struct sql_result;

typedef void sql_query_callback_t(struct sql_result *result, void *context);

/* Initialize database connections. db_driver is the database driver name,
   eg. "mysql" or "pgsql". connect_string is driver-specific. */
struct sql_db *sql_init(const char *db_driver, const char *connect_string);
void sql_deinit(struct sql_db *db);

/* Execute SQL query without waiting for results. */
void sql_exec(struct sql_db *db, const char *query);
/* Execute SQL query and return result in callback. */
void sql_query(struct sql_db *db, const char *query,
	       sql_query_callback_t *callback, void *context);

/* Go to next row, returns 1 if ok, 0 if this was the last row or -1 if error
   occured. This needs to be the first call for result. */
int sql_result_next_row(struct sql_result *result);

/* Return number of fields in result. */
unsigned int sql_result_get_fields_count(struct sql_result *result);
/* Return name of the given field index. */
const char *sql_result_get_field_name(struct sql_result *result,
				      unsigned int idx);
/* Return field index for given name, or -1 if not found. */
int sql_result_find_field(struct sql_result *result, const char *field_name);

/* Returns value of given field as string. */
const char *sql_result_get_field_value(struct sql_result *result,
				       unsigned int idx);
const char *sql_result_find_field_value(struct sql_result *result,
					const char *field_name);
/* Return all values of current row. */
const char *const *sql_result_get_values(struct sql_result *result);

/* Return last error message in result. */
const char *sql_result_get_error(struct sql_result *result);

#endif



More information about the dovecot-cvs mailing list