diff options
Diffstat (limited to 'src/syncdb')
-rw-r--r-- | src/syncdb/Makefile.am | 62 | ||||
-rw-r--r-- | src/syncdb/plugin_sync_postgres.c | 284 | ||||
-rw-r--r-- | src/syncdb/sync_db_plugin.c | 149 | ||||
-rw-r--r-- | src/syncdb/sync_db_postgres.conf | 7 | ||||
-rw-r--r-- | src/syncdb/test_sync_db.c | 200 | ||||
-rw-r--r-- | src/syncdb/test_sync_db_postgres.conf | 7 |
6 files changed, 709 insertions, 0 deletions
diff --git a/src/syncdb/Makefile.am b/src/syncdb/Makefile.am new file mode 100644 index 0000000..56db964 --- /dev/null +++ b/src/syncdb/Makefile.am @@ -0,0 +1,62 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +plugindir = $(libdir)/taler + +if HAVE_POSTGRESQL +if HAVE_GNUNETPQ +plugin_LTLIBRARIES = \ + libsync_plugin_db_postgres.la +endif +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +lib_LTLIBRARIES = \ + libsyncdb.la + +libsyncdb_la_SOURCES = \ + sync_db_plugin.c + +libsyncdb_la_LIBADD = \ + -lgnunetpq \ + -lpq \ + -lgnunetutil +libsyncdb_la_LDFLAGS = \ + $(POSTGRESQL_LDFLAGS) \ + -version-info 2:0:0 \ + -no-undefined + +libsync_plugin_db_postgres_la_SOURCES = \ + plugin_sync_postgres.c +libsync_plugin_db_postgres_la_LIBADD = \ + $(LTLIBINTL) +libsync_plugin_db_postgres_la_LDFLAGS = \ + $(TALER_PLUGIN_LDFLAGS) \ + -lgnunetpq \ + -lpq \ + -ltalerpq \ + -lgnunetutil $(XLIB) + +check_PROGRAMS = \ + $(TESTS) + +test_sync_db_postgres_SOURCES = \ + test_sync_db.c +test_sync_db_postgres_LDFLAGS = \ + $(top_builddir)/src/util/libsyncutil.la \ + libsyncdb.la \ + -lgnunetutil \ + -lgnunetpq \ + -ltalerutil \ + -ltalerpq \ + -luuid + +TESTS = \ + test_sync_db-postgres + +EXTRA_DIST = \ + test_sync_db_postgres.conf diff --git a/src/syncdb/plugin_sync_postgres.c b/src/syncdb/plugin_sync_postgres.c new file mode 100644 index 0000000..4e63198 --- /dev/null +++ b/src/syncdb/plugin_sync_postgres.c @@ -0,0 +1,284 @@ +/* + This file is part of TALER + (C) 2014--2019 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of ANASTASISABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file sync/plugin_syncdb_postgres.c + * @brief database helper functions for postgres used by the sync + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_pq_lib.h> +#include <taler/taler_pq_lib.h> +#include "sync_database_plugin.h" +#include "sync_database_lib.h" + +/** + * How often do we re-try if we run into a DB serialization error? + */ +#define MAX_RETRIES 3 + + +/** + * Type of the "cls" argument given to each of the functions in + * our API. + */ +struct PostgresClosure +{ + + /** + * Postgres connection handle. + */ + struct GNUNET_PQ_Context *conn; + + /** + * Underlying configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Name of the currently active transaction, NULL if none is active. + */ + const char *transaction_name; + +}; + + +/** + * Drop sync tables + * + * @param cls closure our `struct Plugin` + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +static int +postgres_drop_tables (void *cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_try_execute ("DROP TABLE IF EXISTS backups;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + return GNUNET_PQ_exec_statements (pg->conn, + es); +} + + +/** + * Check that the database connection is still up. + * + * @param pg connection to check + */ +static void +check_connection (void *cls) +{ + struct PostgresClosure *pg = cls; + GNUNET_PQ_reconnect_if_down (pg->conn); +} + + +/** + * Do a pre-flight check that we are not in an uncommitted transaction. + * If we are, try to commit the previous transaction and output a warning. + * Does not return anything, as we will continue regardless of the outcome. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + */ +static void +postgres_preflight (void *cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("COMMIT"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (NULL == pg->transaction_name) + return; /* all good */ + if (GNUNET_OK == + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BUG: Preflight check committed transaction `%s'!\n", + pg->transaction_name); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BUG: Preflight check failed to commit transaction `%s'!\n", + pg->transaction_name); + } + pg->transaction_name = NULL; +} + +/** + * Start a transaction. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param name unique name identifying the transaction (for debugging), + * must point to a constant + * @return #GNUNET_OK on success + */ +static int +begin_transaction (void *cls, + const char *name) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + check_connection (pg); + postgres_preflight (pg); + pg->transaction_name = name; + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + TALER_LOG_ERROR ("Failed to start transaction\n"); + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +/** +* Roll back the current transaction of a database connection. +* +* @param cls the `struct PostgresClosure` with the plugin-specific state +* @return #GNUNET_OK on success +*/ +static void +rollback (void *cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("ROLLBACK"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + TALER_LOG_ERROR ("Failed to rollback transaction\n"); + GNUNET_break (0); + } + pg->transaction_name = NULL; +} + +/** + * Commit the current transaction of a database connection. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @return transaction status code + */ + +static enum SYNC_DB_QueryStatus +commit_transaction (void *cls) +{ + struct PostgresClosure *pg = cls; + enum SYNC_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam no_params[] = { + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "do_commit", + no_params); + pg->transaction_name = NULL; + return qs; +} + + + +/** + * Initialize Postgres database subsystem. + * + * @param cls a configuration instance + * @return NULL on error, otherwise a `struct TALER_SYNCDB_Plugin` + */ +void * +libsync_plugin_db_postgres_init (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + struct PostgresClosure *pg; + struct SYNC_DatabasePlugin *plugin; + struct GNUNET_PQ_ExecuteStatement es[] = { + /* Orders created by the frontend, not signed or given a nonce yet. + The contract terms will change (nonce will be added) when moved to the + contract terms table */ + GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS backups" + "(" + "data BYTEA NOT NULL," + ");"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + struct GNUNET_PQ_PreparedStatement ps[] = { + GNUNET_PQ_make_prepare ("backup_insert", + "INSERT INTO backups " + "(data" + ") VALUES " + "($1);", + 1), + GNUNET_PQ_make_prepare ("do_commit", + "COMMIT", + 0), + GNUNET_PQ_PREPARED_STATEMENT_END + }; + + pg = GNUNET_new (struct PostgresClosure); + pg->cfg = cfg; + pg->conn = GNUNET_PQ_connect_with_cfg (cfg, + "syncdb-postgres", + es, + ps); + if (NULL == pg->conn) + { + GNUNET_free (pg); + return NULL; + } + plugin = GNUNET_new (struct SYNC_DatabasePlugin); + plugin->cls = pg; + plugin->drop_tables = &postgres_drop_tables; + plugin->preflight = &postgres_preflight; + plugin->rollback = &rollback; + plugin->commit = &commit_transaction; + return plugin; +} + + +/** + * Shutdown Postgres database subsystem. + * + * @param cls a `struct SYNC_DB_Plugin` + * @return NULL (always) + */ +void * +libsync_plugin_db_postgres_done (void *cls) +{ + struct SYNC_DatabasePlugin *plugin = cls; + struct PostgresClosure *pg = plugin->cls; + + GNUNET_PQ_disconnect (pg->conn); + GNUNET_free (pg); + GNUNET_free (plugin); + return NULL; +} + +/* end of plugin_syncdb_postgres.c */ diff --git a/src/syncdb/sync_db_plugin.c b/src/syncdb/sync_db_plugin.c new file mode 100644 index 0000000..6b2c6e0 --- /dev/null +++ b/src/syncdb/sync_db_plugin.c @@ -0,0 +1,149 @@ +/* + This file is part of TALER + Copyright (C) 2015, 2016 GNUnet e.V. and INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file merchantdb/merchantdb_plugin.c + * @brief Logic to load database plugin + * @author Christian Grothoff + * @author Sree Harsha Totakura <sreeharsha@totakura.in> + */ +#include "platform.h" +#include "anastasis_database_plugin.h" +#include <ltdl.h> + + +/** + * Initialize the plugin. + * + * @param cfg configuration to use + * @return #GNUNET_OK on success + */ +struct ANASTASIS_DatabasePlugin * +ANASTASIS_DB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + char *plugin_name; + char *lib_name; + struct GNUNET_CONFIGURATION_Handle *cfg_dup; + struct ANASTASIS_DatabasePlugin *plugin; + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, + "anastasis", + "db", + &plugin_name)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "anastasis", + "db"); + return NULL; + } + (void) GNUNET_asprintf (&lib_name, + "libanastasis_plugin_db_%s", + plugin_name); + GNUNET_free (plugin_name); + cfg_dup = GNUNET_CONFIGURATION_dup (cfg); + plugin = GNUNET_PLUGIN_load (lib_name, cfg_dup); + if (NULL != plugin) + plugin->library_name = lib_name; + else + GNUNET_free (lib_name); + GNUNET_CONFIGURATION_destroy (cfg_dup); + return plugin; +} + + +/** + * Shutdown the plugin. + * + * @param plugin the plugin to unload + */ +void +ANASTASIS_DB_plugin_unload (struct ANASTASIS_DatabasePlugin *plugin) +{ + char *lib_name; + + if (NULL == plugin) + return; + lib_name = plugin->library_name; + GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, + plugin)); + GNUNET_free (lib_name); +} + + +/** + * Libtool search path before we started. + */ +static char *old_dlsearchpath; + + +/** + * Setup libtool paths. + */ +void __attribute__ ((constructor)) +plugin_init () +{ + int err; + const char *opath; + char *path; + char *cpath; + + err = lt_dlinit (); + if (err > 0) + { + fprintf (stderr, + _ ("Initialization of plugin mechanism failed: %s!\n"), + lt_dlerror ()); + return; + } + opath = lt_dlgetsearchpath (); + if (NULL != opath) + old_dlsearchpath = GNUNET_strdup (opath); + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR); + if (NULL != path) + { + if (NULL != opath) + { + GNUNET_asprintf (&cpath, "%s:%s", opath, path); + lt_dlsetsearchpath (cpath); + GNUNET_free (path); + GNUNET_free (cpath); + } + else + { + lt_dlsetsearchpath (path); + GNUNET_free (path); + } + } +} + + +/** + * Shutdown libtool. + */ +void __attribute__ ((destructor)) +plugin_fini () +{ + lt_dlsetsearchpath (old_dlsearchpath); + if (NULL != old_dlsearchpath) + { + GNUNET_free (old_dlsearchpath); + old_dlsearchpath = NULL; + } + lt_dlexit (); +} + + +/* end of anastasis_db_plugin.c */ diff --git a/src/syncdb/sync_db_postgres.conf b/src/syncdb/sync_db_postgres.conf new file mode 100644 index 0000000..0460bd2 --- /dev/null +++ b/src/syncdb/sync_db_postgres.conf @@ -0,0 +1,7 @@ +[anastasis] +#The DB plugin to use +DB = postgres + +[anastasisdb-postgres] +#The connection string the plugin has to use for connecting to the database +CONFIG = postgres:///anastasis diff --git a/src/syncdb/test_sync_db.c b/src/syncdb/test_sync_db.c new file mode 100644 index 0000000..e57a548 --- /dev/null +++ b/src/syncdb/test_sync_db.c @@ -0,0 +1,200 @@ +/* + This file is part of + (C) 2014-2017 INRIA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Lesser General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file sync/test_sync_db.c + * @brief testcase for sync postgres db plugin + * @author Marcello Stanisci + * @author Christian Grothoff + */ + +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include "sync_database_plugin.h" +#include "sync_database_lib.h" +#include "sync_error_codes.h" +#include <uuid/uuid.h> + + +#define FAILIF(cond) \ + do { \ + if (! (cond)) { break;} \ + GNUNET_break (0); \ + goto drop; \ + } while (0) + +#define RND_BLK(ptr) \ + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, ptr, sizeof (*ptr)) + +/** + * Global return value for the test. Initially -1, set to 0 upon + * completion. Other values indicate some kind of error. + */ +static int result; + +/** + * Handle to the plugin we are testing. + */ +static struct SYNC_DatabasePlugin *plugin; + +/** + * Payment Secret for the test, set to a random value + */ +static struct SYNC_PaymentSecretP paymentSecretP; + +/** + * User public key, set to a random value + */ +static struct SYNC_AccountPubP accountPubP; + +/** + * Amount which is deposited, set to random value + */ +static struct TALER_Amount amount; + +/** + * How many posts are paid by the payment + */ +static unsigned int post_counter; + +/** + * Recoverydata which is stored into the Database, set to a random value + */ +static void *recovery_data; + +/** + * Recovery_data for the select test + */ +static void *res_recovery_data; + +/** + * Truthdata which is stored into the Database, set to a random value + */ +static void *truth_data; + +/** + * Truth for the select test + */ +static void *truth; + +/** + * Keyshare which is stored into the Database, set to a random value + */ +static void *key_share; + +/** + * Keyshare for the select test + */ +static void *res_key_share; + +/** + * Mime-type of truth + */ +static char *mime_type; + +/** + * Mime-type of truth for the select test + */ +static char *res_mime_type; + +/** + * Version of a Recoverydocument + */ +static uint32_t version; + +/** + * Version of the latest Recoverydocument + */ +static uint32_t res_version; + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure with config + */ +static void +run (void *cls) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + + if (NULL == (plugin = SYNC_DB_plugin_load (cfg))) + { + result = 77; + return; + } + if (GNUNET_OK != plugin->drop_tables (plugin->cls)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Dropping tables failed\n"); + result = 77; + return; + } + SYNC_DB_plugin_unload (plugin); + if (NULL == (plugin = SYNC_DB_plugin_load (cfg))) + { + result = 77; + return; + } + + GNUNET_break (GNUNET_OK == + plugin->drop_tables (plugin->cls)); + SYNC_DB_plugin_unload (plugin); + plugin = NULL; +} + + +int +main (int argc, + char *const argv[]) +{ + const char *plugin_name; + char *config_filename; + char *testname; + struct GNUNET_CONFIGURATION_Handle *cfg; + + result = -1; + if (NULL == (plugin_name = strrchr (argv[0], (int) '-'))) + { + GNUNET_break (0); + return -1; + } + GNUNET_log_setup (argv[0], "DEBUG", NULL); + plugin_name++; + (void) GNUNET_asprintf (&testname, + "%s", + plugin_name); + (void) GNUNET_asprintf (&config_filename, + "test_sync_db_%s.conf", + testname); + cfg = GNUNET_CONFIGURATION_create (); + if (GNUNET_OK != + GNUNET_CONFIGURATION_parse (cfg, + config_filename)) + { + GNUNET_break (0); + GNUNET_free (config_filename); + GNUNET_free (testname); + return 2; + } + GNUNET_SCHEDULER_run (&run, cfg); + GNUNET_CONFIGURATION_destroy (cfg); + GNUNET_free (config_filename); + GNUNET_free (testname); + return result; +} + +/* end of test_sync_db.c */ diff --git a/src/syncdb/test_sync_db_postgres.conf b/src/syncdb/test_sync_db_postgres.conf new file mode 100644 index 0000000..f91dea1 --- /dev/null +++ b/src/syncdb/test_sync_db_postgres.conf @@ -0,0 +1,7 @@ +[anastasis] +#The DB plugin to use +DB = postgres + +[anastasisdb-postgres] +#The connection string the plugin has to use for connecting to the database +CONFIG = postgres:///anastasischeck |