[dovecot-cvs] dovecot/src/lib-dict .cvsignore, NONE, 1.1 Makefile.am, NONE, 1.1 dict-private.h, NONE, 1.1 dict-sql.c, NONE, 1.1 dict-sql.h, NONE, 1.1 dict.c, NONE, 1.1 dict.h, NONE, 1.1

cras at dovecot.org cras at dovecot.org
Sat Dec 10 20:59:05 EET 2005


Update of /var/lib/cvs/dovecot/src/lib-dict
In directory talvi:/tmp/cvs-serv30625/src/lib-dict

Added Files:
	.cvsignore Makefile.am dict-private.h dict-sql.c dict-sql.h 
	dict.c dict.h 
Log Message:
Added dictionary API and implementation for SQL. It's meant for looking and
modifying key=value pairs. In future we may need to use this for many
different things, but for now it's only used by quota-dict plugin.



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

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

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

libdict_a_SOURCES = \
	dict.c \
	dict-sql.c

noinst_HEADERS = \
	dict.h \
	dict-private.h

--- NEW FILE: dict-private.h ---
#ifndef __DICT_PRIVATE_H
#define __DICT_PRIVATE_H

#include "dict.h"

struct dict_vfuncs {
	struct dict *(*init)(struct dict *dict_class, const char *uri);
	void (*deinit)(struct dict *dict);

	char *(*lookup)(struct dict *dict, pool_t pool, const char *key);

	struct dict_iterate_context *
		(*iterate_init)(struct dict *dict, const char *path,
				int recurse);
	int (*iterate)(struct dict_iterate_context *ctx,
		       const char **key_r, const char **value_r);
	void (*iterate_deinit)(struct dict_iterate_context *ctx);

	struct dict_transaction_context *(*transaction_init)(struct dict *dict);
	int (*transaction_commit)(struct dict_transaction_context *ctx);
	void (*transaction_rollback)(struct dict_transaction_context *ctx);

	void (*set)(struct dict_transaction_context *ctx,
		    const char *key, const char *value);
	void (*atomic_inc)(struct dict_transaction_context *ctx,
			   const char *key, long long diff);
};

struct dict {
	const char *name;

	struct dict_vfuncs v;
};

struct dict_iterate_context {
	struct dict *dict;
};

struct dict_transaction_context {
	struct dict *dict;
};

#endif

--- NEW FILE: dict-sql.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "istream.h"
#include "str.h"
#include "strescape.h"
#include "sql-api-private.h"
#include "dict-private.h"
#include "dict-sql.h"

#include <unistd.h>
#include <fcntl.h>

struct sql_dict {
	struct dict dict;

	pool_t pool;
	struct sql_db *db;

	const char *connect_string;
	const char *table, *select_field, *where_field;
};

struct sql_dict_iterate_context {
	struct dict_iterate_context ctx;

	struct sql_result *result;
};

static int sql_dict_read_config(struct sql_dict *dict, const char *path)
{
	struct istream *input;
	const char *line, *value;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd == -1) {
		i_error("open(%s) failed: %m", path);
		return -1;
	}

	input = i_stream_create_file(fd, default_pool, (size_t)-1, FALSE);
	while ((line = i_stream_read_next_line(input)) != NULL) {
		value = strchr(line, '=');
		if (value == NULL)
			continue;

		t_push();
		line = t_strdup_until(line, value);
		value++;

		if (strcmp(line, "connect") == 0)
			dict->connect_string = p_strdup(dict->pool, value);
		else if (strcmp(line, "table") == 0)
			dict->table = p_strdup(dict->pool, value);
		else if (strcmp(line, "select_field") == 0)
			dict->select_field = p_strdup(dict->pool, value);
		else if (strcmp(line, "where_field") == 0)
			dict->where_field = p_strdup(dict->pool, value);

		t_pop();
	}
	i_stream_unref(input);
	(void)close(fd);
	return 0;
}

static struct dict *sql_dict_init(struct dict *dict_class, const char *uri)
{
	struct sql_dict *dict;
	pool_t pool;

	pool = pool_alloconly_create("sql dict", 1024);
	dict = p_new(pool, struct sql_dict, 1);
	dict->pool = pool;
	dict->dict = *dict_class;

	if (sql_dict_read_config(dict, uri) <= 0) {
		pool_unref(pool);
		return NULL;
	}

	t_push();
	dict->db = sql_init(dict_class->name, dict->connect_string);
	t_pop();
	return &dict->dict;
}

static void sql_dict_deinit(struct dict *_dict)
{
	struct sql_dict *dict = (struct sql_dict *)_dict;

	sql_deinit(dict->db);
}

static char *sql_dict_lookup(struct dict *_dict, pool_t pool, const char *key)
{
	struct sql_dict *dict = (struct sql_dict *)_dict;
	struct sql_result *result;
	const char *query;
	char *ret;

	t_push();
	query = t_strdup_printf("SELECT %s FROM %s WHERE %s = '%s'",
				dict->select_field, dict->table,
				dict->where_field, str_escape(key));
	result = sql_query_s(dict->db, query);
	t_pop();

	if (sql_result_next_row(result) <= 0)
		ret = NULL;
	else
                ret = p_strdup(pool, sql_result_get_field_value(result, 0));

	sql_result_free(result);
	return ret;
}

static struct dict_iterate_context *
sql_dict_iterate_init(struct dict *_dict, const char *path, int recurse)
{
	struct sql_dict *dict = (struct sql_dict *)_dict;
        struct sql_dict_iterate_context *ctx;
	string_t *query;

	ctx = i_new(struct sql_dict_iterate_context, 1);
	ctx->ctx.dict = _dict;

	t_push();
	query = t_str_new(256);
	str_printfa(query, "SELECT %s, %s FROM %s WHERE %s LIKE '%s/%%'",
		    dict->where_field, dict->select_field,
		    dict->table, dict->where_field, str_escape(path));
	if (!recurse) {
		str_printfa(query, " AND %s NOT LIKE '%s/%%/%%'",
			    dict->where_field, str_escape(path));
	}
	ctx->result = sql_query_s(dict->db, str_c(query));
	t_pop();

	return &ctx->ctx;
}

static int sql_dict_iterate(struct dict_iterate_context *_ctx,
			    const char **key_r, const char **value_r)
{
	struct sql_dict_iterate_context *ctx =
		(struct sql_dict_iterate_context *)_ctx;
	int ret;

	if ((ret = sql_result_next_row(ctx->result)) <= 0)
		return ret;

	*key_r = sql_result_get_field_value(ctx->result, 0);
	*value_r = sql_result_get_field_value(ctx->result, 1);
	return 1;
}

static void sql_dict_iterate_deinit(struct dict_iterate_context *_ctx)
{
	struct sql_dict_iterate_context *ctx =
		(struct sql_dict_iterate_context *)_ctx;

	sql_result_free(ctx->result);
}

struct sql_dict_transaction_context {
	struct dict_transaction_context ctx;

	struct sql_transaction_context *sql_ctx;
};

static struct dict_transaction_context *
sql_dict_transaction_init(struct dict *_dict)
{
	struct sql_dict *dict = (struct sql_dict *)_dict;
	struct sql_dict_transaction_context *ctx;

	ctx = i_new(struct sql_dict_transaction_context, 1);
	ctx->ctx.dict = _dict;
	ctx->sql_ctx = sql_transaction_begin(dict->db);

	return &ctx->ctx;
}

static int sql_dict_transaction_commit(struct dict_transaction_context *_ctx)
{
	struct sql_dict_transaction_context *ctx =
		(struct sql_dict_transaction_context *)_ctx;
	const char *error;
	int ret;

	ret = sql_transaction_commit_s(ctx->sql_ctx, &error);
	if (ret < 0)
		i_error("sql dict: commit failed: %s", error);
	i_free(ctx);
	return ret;
}

static void sql_dict_transaction_rollback(struct dict_transaction_context *_ctx)
{
	struct sql_dict_transaction_context *ctx =
		(struct sql_dict_transaction_context *)_ctx;

	sql_transaction_rollback(ctx->sql_ctx);
	i_free(ctx);
}

static void sql_dict_set(struct dict_transaction_context *_ctx,
			 const char *key, const char *value)
{
	struct sql_dict_transaction_context *ctx =
		(struct sql_dict_transaction_context *)_ctx;
	struct sql_dict *dict = (struct sql_dict *)_ctx->dict;
	const char *query;

	t_push();
	query = t_strdup_printf("UPDATE %s SET %s = '%s' WHERE %s = '%s'",
				dict->table, dict->select_field, str_escape(value),
				dict->where_field, str_escape(key));
	sql_update(ctx->sql_ctx, query);
	t_pop();
}

static void sql_dict_atomic_inc(struct dict_transaction_context *_ctx,
				const char *key, long long diff)
{
	struct sql_dict_transaction_context *ctx =
		(struct sql_dict_transaction_context *)_ctx;
	struct sql_dict *dict = (struct sql_dict *)_ctx->dict;
	const char *query;

	t_push();
	query = t_strdup_printf("UPDATE %s SET %s = %s + %lld WHERE %s = '%s'",
				dict->table, dict->select_field,
				dict->select_field, diff,
				dict->where_field, str_escape(key));
	sql_update(ctx->sql_ctx, query);
	t_pop();
}

static struct dict sql_dict = {
	MEMBER(name) "sql",

	{
		sql_dict_init,
		sql_dict_deinit,
		sql_dict_lookup,
		sql_dict_iterate_init,
		sql_dict_iterate,
		sql_dict_iterate_deinit,
		sql_dict_transaction_init,
		sql_dict_transaction_commit,
		sql_dict_transaction_rollback,
		sql_dict_set,
		sql_dict_atomic_inc
	}
};

static struct dict *dict_sql_classes;

void dict_sql_register(void)
{
	int i, count;

	/* @UNSAFE */
	for (count = 0; sql_db_drivers[count] != NULL; count++) ;
	dict_sql_classes = i_new(struct dict, count);

	for (i = 0; i < count; i++) {
		dict_sql_classes[i] = sql_dict;
		dict_sql_classes[i].name = sql_db_drivers[i]->name;

		dict_class_register(&dict_sql_classes[i]);
	}
}

void dict_sql_unregister(void)
{
	int i;

	for (i = 0; sql_db_drivers[i] != NULL; i++)
		dict_class_unregister(&dict_sql_classes[i]);
	i_free(dict_sql_classes);
}

--- NEW FILE: dict-sql.h ---
#ifndef __DICT_SQL_H
#define __DICT_SQL_H

void dict_sql_register(void);
void dict_sql_unregister(void);

#endif

--- NEW FILE: dict.c ---
/* Copyright (C) 2005 Timo Sirainen */

#include "lib.h"
#include "array.h"
#include "dict-sql.h"
#include "dict-private.h"

static array_t ARRAY_DEFINE(dict_classes, struct dict *);
static int dict_count = 0;

static void dict_class_register_all(void)
{
	dict_sql_register();
}

static void dict_class_unregister_all(void)
{
	dict_sql_unregister();
}

static struct dict *dict_class_lookup(const char *name)
{
	struct dict *const *dicts;
	unsigned int i, count;

	dicts = array_get(&dict_classes, &count);
	for (i = 0; i < count; i++) {
		if (strcmp(dicts[i]->name, name) == 0)
			return dicts[i];
	}
	return NULL;
}

void dict_class_register(struct dict *dict_class)
{
	if (!array_is_created(&dict_classes))
		ARRAY_CREATE(&dict_classes, default_pool, struct dict *, 8);

	if (dict_class_lookup(dict_class->name) != NULL) {
		i_fatal("dict_class_register(%s): Already registered",
			dict_class->name);
	}
	array_append(&dict_classes, &dict_class, 1);
}

void dict_class_unregister(struct dict *dict_class)
{
	struct dict *const *dicts;
	unsigned int i, count;

	dicts = array_get(&dict_classes, &count);
	for (i = 0; i < count; i++) {
		if (dicts[i] == dict_class) {
			array_delete(&dict_classes, i, 1);
			break;
		}
	}

	i_assert(i < count);

	if (array_count(&dict_classes) == 0)
		array_free(&dict_classes);
}

struct dict *dict_init(const char *uri)
{
	struct dict *dict;
	const char *p;

	if (dict_count++ == 0)
		dict_class_register_all();

	p = strchr(uri, ':');
	if (p == NULL) {
		i_error("URI is missing ':': %s", uri);
		return NULL;
	}

	t_push();
	dict = dict_class_lookup(t_strdup_until(uri, p));
	t_pop();
	if (dict == NULL)
		return NULL;

	return dict->v.init(dict, p+1);
}

void dict_deinit(struct dict *dict)
{
	dict->v.deinit(dict);

	if (--dict_count == 0)
		dict_class_unregister_all();
}

char *dict_lookup(struct dict *dict, pool_t pool, const char *key)
{
	return dict->v.lookup(dict, pool, key);
}

struct dict_iterate_context *
dict_iterate_init(struct dict *dict, const char *path, int recurse)
{
	return dict->v.iterate_init(dict, path, recurse);
}

int dict_iterate(struct dict_iterate_context *ctx,
		 const char **key_r, const char **value_r)
{
	return ctx->dict->v.iterate(ctx, key_r, value_r);
}

void dict_iterate_deinit(struct dict_iterate_context *ctx)
{
	ctx->dict->v.iterate_deinit(ctx);
}

struct dict_transaction_context *dict_transaction_begin(struct dict *dict)
{
	return dict->v.transaction_init(dict);
}

int dict_transaction_commit(struct dict_transaction_context *ctx)
{
	return ctx->dict->v.transaction_commit(ctx);
}

void dict_transaction_rollback(struct dict_transaction_context *ctx)
{
	ctx->dict->v.transaction_rollback(ctx);
}

void dict_set(struct dict_transaction_context *ctx,
	      const char *key, const char *value)
{
	ctx->dict->v.set(ctx, key, value);
}

void dict_atomic_inc(struct dict_transaction_context *ctx,
		     const char *key, long long diff)
{
	ctx->dict->v.atomic_inc(ctx, key, diff);
}

--- NEW FILE: dict.h ---
#ifndef __DICT_H
#define __DICT_H

#define DICT_PATH_PRIVATE "priv/"
#define DICT_PATH_SHARED "shared/"

struct dict;

void dict_class_register(struct dict *dict_class);
void dict_class_unregister(struct dict *dict_class);

/* Open dictionary with given URI (type:data).
   If URI is invalid, returns NULL. */
struct dict *dict_init(const char *uri);
/* Close dictionary. */
void dict_deinit(struct dict *dict);

/* Return value for key, or NULL if not found. */
char *dict_lookup(struct dict *dict, pool_t pool, const char *key);

/* Iterate through all values in a path. If recurse is FALSE, keys in
   the given path are returned, but not their children. */
struct dict_iterate_context *
dict_iterate_init(struct dict *dict, const char *path, int recurse);
/* Returns -1 = error, 0 = finished, 1 = key/value set */
int dict_iterate(struct dict_iterate_context *ctx,
		 const char **key_r, const char **value_r);
void dict_iterate_deinit(struct dict_iterate_context *ctx);

/* Start a new dictionary transaction. */
struct dict_transaction_context *dict_transaction_begin(struct dict *dict);
/* Commit the transaction. Returns 0 if ok, -1 if failed. */
int dict_transaction_commit(struct dict_transaction_context *ctx);
/* Rollback all changes made in transaction. */
void dict_transaction_rollback(struct dict_transaction_context *ctx);

/* Set key=value in dictionary. */
void dict_set(struct dict_transaction_context *ctx,
	      const char *key, const char *value);
/* Increase/decrease a numeric value in dictionary. Note that the value is
   changed when transaction is being committed, so you can't know beforehand
   what the value will become. */
void dict_atomic_inc(struct dict_transaction_context *ctx,
		     const char *key, long long diff);

#endif



More information about the dovecot-cvs mailing list