dovecot-2.2: lib-sql: Added support for Cassandra CQL as lib-sql...

dovecot at dovecot.org dovecot at dovecot.org
Mon May 11 18:57:40 UTC 2015


details:   http://hg.dovecot.org/dovecot-2.2/rev/3725c601dbaf
changeset: 18640:3725c601dbaf
user:      Timo Sirainen <tss at iki.fi>
date:      Mon May 11 21:55:42 2015 +0300
description:
lib-sql: Added support for Cassandra CQL as lib-sql backend.
Implemented using DataStax's cpp-driver.

Many things are still unimplemented. Column name specific functionality
isn't even  supported by the Cassandra library. So this can currently mainly
be used as one of the dict backends for some simple functionality.

diffstat:

 configure.ac                   |   39 +-
 src/lib-sql/Makefile.am        |   18 +-
 src/lib-sql/driver-cassandra.c |  961 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1012 insertions(+), 6 deletions(-)

diffs (truncated from 1110 to 300 lines):

diff -r 7abdcf42b63b -r 3725c601dbaf configure.ac
--- a/configure.ac	Mon May 11 21:48:45 2015 +0300
+++ b/configure.ac	Mon May 11 21:55:42 2015 +0300
@@ -153,6 +153,11 @@
   TEST_WITH(sqlite, $withval),
   want_sqlite=no)
 
+AC_ARG_WITH(cassandra,
+AS_HELP_STRING([--with-cassandra], [Build with Cassandra driver support]),
+  TEST_WITH(cassandra, $withval),
+  want_cassandra=no)
+
 AC_ARG_WITH(lucene,
 AS_HELP_STRING([--with-lucene], [Build with CLucene full text search support]),
   TEST_WITH(lucene, $withval),
@@ -2319,14 +2324,32 @@
 	  fi
 	])
 fi
+
+if test $want_cassandra != no; then
+	AC_CHECK_LIB(cassandra, cass_session_new, [
+		AC_CHECK_HEADER(cassandra.h, [
+			CASSANDRA_LIBS="$CASSANDRA_LIBS -lcassandra"
+
+			AC_DEFINE(HAVE_CASSANDRA,, Build with Cassandra support)
+			found_sql_drivers="$found_sql_drivers cassandra"
+		], [
+		  if test $want_cassandra = yes; then
+		    AC_ERROR([Can't build with Cassandra support: cassandra.h not found])
+		  fi
+		])
+	], [
+	  if test $want_cassandra = yes; then
+	    AC_ERROR([Can't build with Cassandra support: libcassandra not found])
+	  fi
+	])
+fi
 	
-SQL_CFLAGS="$MYSQL_CFLAGS $PGSQL_CFLAGS $SQLITE_CFLAGS"
+SQL_CFLAGS="$MYSQL_CFLAGS $PGSQL_CFLAGS $SQLITE_CFLAGS $CASSANDRA_CFLAGS"
 if test "$want_sql" != "plugin"; then
-	SQL_LIBS="$MYSQL_LIBS $PGSQL_LIBS $SQLITE_LIBS"
+	SQL_LIBS="$MYSQL_LIBS $PGSQL_LIBS $SQLITE_LIBS $CASSANDRA_LIBS"
 else
 	AC_DEFINE(SQL_DRIVER_PLUGINS,, Build SQL drivers as plugins)
 fi
-
 sql_drivers=
 not_sql_drivers=
 
@@ -2444,6 +2467,8 @@
 AC_SUBST(PGSQL_LIBS)
 AC_SUBST(SQLITE_CFLAGS)
 AC_SUBST(SQLITE_LIBS)
+AC_SUBST(CASSANDRA_CFLAGS)
+AC_SUBST(CASSANDRA_LIBS)
 
 AC_SUBST(DICT_LIBS)
 AC_SUBST(CDB_LIBS)
@@ -2587,6 +2612,7 @@
 build_pgsql=no
 build_mysql=no
 build_sqlite=no
+build_cassandra=no
 for driver in $sql_drivers; do
   if test "$driver" = "pgsql"; then
     AC_DEFINE(BUILD_PGSQL,, Built-in PostgreSQL support)
@@ -2597,6 +2623,9 @@
   elif test "$driver" = "sqlite"; then
     AC_DEFINE(BUILD_SQLITE,, Built-in SQLite support)
     build_sqlite=yes
+  elif test "$driver" = "cassandra"; then
+    AC_DEFINE(BUILD_CASSANDRA,, Built-in Cassandra support)
+    build_cassandra=yes
   fi
 done
 if test $build_pgsql = no; then
@@ -2608,11 +2637,15 @@
 if test $build_sqlite = no; then
   not_sql_drivers="$not_sql_drivers sqlite"
 fi
+if test $build_cassandra = no; then
+  not_sql_drivers="$not_sql_drivers cassandra"
+fi
 
 AC_SUBST(sql_drivers)
 AM_CONDITIONAL(BUILD_PGSQL, test "$build_pgsql" = "yes")
 AM_CONDITIONAL(BUILD_MYSQL, test "$build_mysql" = "yes")
 AM_CONDITIONAL(BUILD_SQLITE, test "$build_sqlite" = "yes")
+AM_CONDITIONAL(BUILD_CASSANDRA, test "$build_cassandra" = "yes")
 AM_CONDITIONAL(SQL_PLUGINS, test "$want_sql" = "plugin")
 
 dnl **
diff -r 7abdcf42b63b -r 3725c601dbaf src/lib-sql/Makefile.am
--- a/src/lib-sql/Makefile.am	Mon May 11 21:48:45 2015 +0300
+++ b/src/lib-sql/Makefile.am	Mon May 11 21:55:42 2015 +0300
@@ -18,11 +18,16 @@
 SQLITE_LIB = libdriver_sqlite.la
 SQL_DRIVER_PLUGINS += sqlite
 endif
+if BUILD_CASSANDRA
+SQLITE_LIB = libdriver_cassandra.la
+SQL_DRIVER_PLUGINS += cassandra
+endif
 
 sql_module_LTLIBRARIES = \
 	$(MYSQL_LIB) \
 	$(PGSQL_LIB) \
-	$(SQLITE_LIB)
+	$(SQLITE_LIB) \
+	$(CASSANDRA_LIB)
 
 sql_moduledir = $(moduledir)
 endif
@@ -41,7 +46,8 @@
 driver_sources = \
 	driver-mysql.c \
 	driver-pgsql.c \
-	driver-sqlite.c
+	driver-sqlite.c \
+	driver-cassandra.c
 endif
 
 libsql_la_SOURCES = \
@@ -69,12 +75,18 @@
 libdriver_sqlite_la_CPPFLAGS = -I$(top_srcdir)/src/lib $(SQLITE_CFLAGS)
 libdriver_sqlite_la_SOURCES = driver-sqlite.c
 
+libdriver_cassandra_la_LDFLAGS = -module -avoid-version
+libdriver_cassandra_la_LIBADD = $(CASSANDRA_LIBS)
+libdriver_cassandra_la_CPPFLAGS = -I$(top_srcdir)/src/lib $(CASSANDRA_CFLAGS)
+libdriver_cassandra_la_SOURCES = driver-cassandra.c
+
 sql_libs =
 else
 sql_libs = \
 	$(MYSQL_LIBS) \
 	$(PGSQL_LIBS) \
-	$(SQLITE_LIBS)
+	$(SQLITE_LIBS) \
+	$(CASSANDRA_LIBS)
 endif
 
 pkglib_LTLIBRARIES = libdovecot-sql.la
diff -r 7abdcf42b63b -r 3725c601dbaf src/lib-sql/driver-cassandra.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-sql/driver-cassandra.c	Mon May 11 21:55:42 2015 +0300
@@ -0,0 +1,961 @@
+/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "ioloop.h"
+#include "write-full.h"
+#include "sql-api-private.h"
+
+#ifdef BUILD_CASSANDRA
+#include <unistd.h>
+#include <cassandra.h>
+
+#define IS_CONNECTED(db) \
+	((db)->api.state != SQL_DB_STATE_DISCONNECTED && \
+	 (db)->api.state != SQL_DB_STATE_CONNECTING)
+
+typedef void driver_cassandra_callback_t(CassFuture *future, void *context);
+
+struct cassandra_callback {
+	unsigned int id;
+	CassFuture *future;
+	struct cassandra_db *db;
+	driver_cassandra_callback_t *callback;
+	void *context;
+};
+
+struct cassandra_db {
+	struct sql_db api;
+
+	char *hosts, *keyspace;
+	CassConsistency consistency;
+
+	CassCluster *cluster;
+	CassSession *session;
+
+	int fd_pipe[2];
+	struct io *io_pipe;
+	ARRAY(struct cassandra_callback *) callbacks;
+	unsigned int callback_ids;
+
+	struct cassandra_result *cur_result;
+	struct ioloop *ioloop, *orig_ioloop;
+	struct sql_result *sync_result;
+
+	char *error;
+};
+
+struct cassandra_result {
+	struct sql_result api;
+	CassStatement *statement;
+	const CassResult *result;
+	CassIterator *iterator;
+	char *query;
+	char *error;
+
+	pool_t row_pool;
+	ARRAY_TYPE(const_string) fields;
+
+	sql_query_callback_t *callback;
+	void *context;
+
+	unsigned int finished:1;
+};
+
+struct cassandra_transaction_context {
+	struct sql_transaction_context ctx;
+	int refcount;
+
+	sql_commit_callback_t *callback;
+	void *context;
+
+	pool_t query_pool;
+	const char *error;
+
+	unsigned int begin_succeeded:1;
+	unsigned int begin_failed:1;
+	unsigned int failed:1;
+};
+
+extern const struct sql_db driver_cassandra_db;
+extern const struct sql_result driver_cassandra_result;
+
+static struct {
+	CassConsistency consistency;
+	const char *name;
+} cass_consistency_names[] = {
+	{ CASS_CONSISTENCY_ANY, "any" },
+	{ CASS_CONSISTENCY_ONE, "one" },
+	{ CASS_CONSISTENCY_TWO, "two" },
+	{ CASS_CONSISTENCY_THREE, "three" },
+	{ CASS_CONSISTENCY_QUORUM, "" },
+	{ CASS_CONSISTENCY_ALL, "all" },
+	{ CASS_CONSISTENCY_QUORUM, "" },
+	{ CASS_CONSISTENCY_ALL, "all" },
+	{ CASS_CONSISTENCY_LOCAL_QUORUM, "local-quorum" },
+	{ CASS_CONSISTENCY_EACH_QUORUM, "each-quorum" },
+	{ CASS_CONSISTENCY_SERIAL, "serial" },
+	{ CASS_CONSISTENCY_LOCAL_SERIAL, "local-serial" },
+	{ CASS_CONSISTENCY_LOCAL_ONE, "local-one" }
+};
+
+static void result_finish(struct cassandra_result *result);
+
+static int consistency_parse(const char *str, CassConsistency *consistency_r)
+{
+	unsigned int i;
+
+	for (i = 0; i < N_ELEMENTS(cass_consistency_names); i++) {
+		if (strcmp(cass_consistency_names[i].name, str) == 0) {
+			*consistency_r = cass_consistency_names[i].consistency;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static void driver_cassandra_set_state(struct cassandra_db *db, enum sql_db_state state)
+{
+	i_assert(state == SQL_DB_STATE_BUSY || db->cur_result == NULL);
+
+	/* switch back to original ioloop in case the caller wants to
+	   add/remove timeouts */
+	if (db->ioloop != NULL)
+		io_loop_set_current(db->orig_ioloop);
+	sql_db_set_state(&db->api, state);
+	if (db->ioloop != NULL)
+		io_loop_set_current(db->ioloop);
+}
+
+static void driver_cassandra_close(struct cassandra_db *db)
+{
+	if (db->io_pipe != NULL)
+		io_remove(&db->io_pipe);
+	if (db->fd_pipe[0] != -1) {
+		i_close_fd(&db->fd_pipe[0]);
+		i_close_fd(&db->fd_pipe[1]);
+	}
+	driver_cassandra_set_state(db, SQL_DB_STATE_DISCONNECTED);
+
+	if (db->ioloop != NULL) {
+		/* running a sync query, stop it */
+		io_loop_stop(db->ioloop);
+	}
+}
+
+static void driver_cassandra_log_error(CassFuture *future, const char *str)
+{
+	const char *message;
+	size_t size;
+


More information about the dovecot-cvs mailing list