diff options
author | Sam Thursfield <sam@afuera.me.uk> | 2016-06-24 00:04:47 +0100 |
---|---|---|
committer | Sam Thursfield <sam@afuera.me.uk> | 2016-06-24 00:07:24 +0100 |
commit | 94d8449b1c36a445047015de4997371803c39e44 (patch) | |
tree | 9ce5df9f7f4cfe16bdd3f0c5dfe61eb134a42bfd | |
parent | 9faae9ce5c852a744644b18b7bcef88b5491c397 (diff) | |
download | tracker-wip/sam/private-store.tar.gz |
libtracker-sparql: Add public API to open a private Tracker storewip/sam/private-store
-rw-r--r-- | src/libtracker-direct/tracker-direct.vala | 14 | ||||
-rw-r--r-- | src/libtracker-sparql-backend/tracker-backend.vala | 61 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-connection.vala | 4 | ||||
-rw-r--r-- | src/libtracker-sparql/tracker-private-store.vala | 80 | ||||
-rw-r--r-- | tests/libtracker-sparql/tracker-sparql-test.c | 65 |
5 files changed, 199 insertions, 25 deletions
diff --git a/src/libtracker-direct/tracker-direct.vala b/src/libtracker-direct/tracker-direct.vala index 1cacf09fd..3f282b44e 100644 --- a/src/libtracker-direct/tracker-direct.vala +++ b/src/libtracker-direct/tracker-direct.vala @@ -21,7 +21,12 @@ public class Tracker.Direct.Connection : Tracker.Sparql.Connection { static int use_count; bool initialized; - public Connection () throws Sparql.Error, IOError, DBusError { + public enum Flags { + NONE=0, + READONLY=1, + } + + public Connection (string? store_path, string[]? ontologies, Flags flags) throws Sparql.Error, IOError, DBusError { DBManager.lock (); try { @@ -40,7 +45,12 @@ public class Tracker.Direct.Connection : Tracker.Sparql.Connection { select_cache_size = int.parse (env_cache_size); } - Data.Manager.init (DBManagerFlags.READONLY, null, null, false, false, select_cache_size, 0, null, null); + DBManagerFlags db_manager_flags = 0; + if ((flags & Flags.READONLY) == Flags.READONLY) { + db_manager_flags |= DBManagerFlags.READONLY; + } + + Data.Manager.init (store_path, ontologies, db_manager_flags, null, false, false, select_cache_size, 0, null, null); } use_count++; diff --git a/src/libtracker-sparql-backend/tracker-backend.vala b/src/libtracker-sparql-backend/tracker-backend.vala index 6f1219db2..d7f5a9f65 100644 --- a/src/libtracker-sparql-backend/tracker-backend.vala +++ b/src/libtracker-sparql-backend/tracker-backend.vala @@ -17,7 +17,7 @@ * Boston, MA 02110-1301, USA. */ -static size_t log_initialized = false; +static size_t log_initialized = 0; static void log_init () { if (GLib.Once.init_enter(&log_initialized)) { @@ -63,7 +63,7 @@ static void log_init () { GLib.Log.set_handler ("Tracker", remove_levels, remove_log_handler); } - GLib.Once.init_leave (&log_initialized, true); + GLib.Once.init_leave (&log_initialized, 1); } } @@ -71,18 +71,22 @@ static void remove_log_handler (string? log_domain, LogLevelFlags log_level, str /* do nothing */ } -class Tracker.Sparql.Backend : Connection { +/* The session-wide backend does reads in-process, but sends writes over D-Bus + * to the session-wide tracker-store daemon. This is the most efficient way to + * provide multiple process with read+write access to the same SQLite database. + */ +class Tracker.Sparql.SessionWideBackend : Connection { bool initialized; Tracker.Sparql.Connection direct = null; Tracker.Sparql.Connection bus = null; - enum Backend { + enum BackendType { AUTO, DIRECT, BUS } GLib.BusType bus_type = BusType.SESSION; - public Backend () throws Sparql.Error, IOError, DBusError, SpawnError { + public SessionWideBackend () throws Sparql.Error, IOError, DBusError, SpawnError { try { // Important to make sure we check the right bus for the store load_env (); @@ -239,28 +243,28 @@ class Tracker.Sparql.Backend : Connection { // Plugin loading functions private void load_plugins () throws GLib.Error { string env_backend = Environment.get_variable ("TRACKER_SPARQL_BACKEND"); - Backend backend = Backend.AUTO; + BackendType backend_type = BackendType.AUTO; if (env_backend != null) { if (env_backend.ascii_casecmp ("direct") == 0) { - backend = Backend.DIRECT; + backend_type = BackendType.DIRECT; debug ("Using backend = 'DIRECT'"); } else if (env_backend.ascii_casecmp ("bus") == 0) { - backend = Backend.BUS; + backend_type = BackendType.BUS; debug ("Using backend = 'BUS'"); } else { warning ("Environment variable TRACKER_SPARQL_BACKEND set to unknown value '%s'", env_backend); } } - if (backend == Backend.AUTO) { + if (backend_type == BackendType.AUTO) { debug ("Using backend = 'AUTO'"); } - switch (backend) { - case Backend.AUTO: + switch (backend_type) { + case BackendType.AUTO: try { - direct = new Tracker.Direct.Connection (); + direct = new Tracker.Direct.Connection (null, null, Tracker.Direct.Connection.Flags.READONLY); } catch (Error e) { debug ("Unable to initialize direct backend: " + e.message); } @@ -268,11 +272,11 @@ class Tracker.Sparql.Backend : Connection { bus = new Tracker.Bus.Connection (); break; - case Backend.DIRECT: - direct = new Tracker.Direct.Connection (); + case BackendType.DIRECT: + direct = new Tracker.Direct.Connection (null, null, Tracker.Direct.Connection.Flags.READONLY); break; - case Backend.BUS: + case BackendType.BUS: bus = new Tracker.Bus.Connection (); break; @@ -294,7 +298,7 @@ class Tracker.Sparql.Backend : Connection { if (result == null) { log_init (); - result = new Tracker.Sparql.Backend (); + result = new Tracker.Sparql.SessionWideBackend (); if (cancellable != null && cancellable.is_cancelled ()) { throw new IOError.CANCELLED ("Operation was cancelled"); @@ -394,17 +398,34 @@ class Tracker.Sparql.Backend : Connection { } public async static Tracker.Sparql.Connection tracker_sparql_connection_get_async (Cancellable? cancellable = null) throws Tracker.Sparql.Error, IOError, DBusError, SpawnError { - return yield Tracker.Sparql.Backend.get_internal_async (cancellable); + return yield Tracker.Sparql.SessionWideBackend.get_internal_async (cancellable); } public static Tracker.Sparql.Connection tracker_sparql_connection_get (Cancellable? cancellable = null) throws Tracker.Sparql.Error, IOError, DBusError, SpawnError { - return Tracker.Sparql.Backend.get_internal (cancellable); + return Tracker.Sparql.SessionWideBackend.get_internal (cancellable); } public async static Tracker.Sparql.Connection tracker_sparql_connection_get_direct_async (Cancellable? cancellable = null) throws Tracker.Sparql.Error, IOError, DBusError, SpawnError { - return yield Tracker.Sparql.Backend.get_internal_async (cancellable); + return yield Tracker.Sparql.SessionWideBackend.get_internal_async (cancellable); } public static Tracker.Sparql.Connection tracker_sparql_connection_get_direct (Cancellable? cancellable = null) throws Tracker.Sparql.Error, IOError, DBusError, SpawnError { - return Tracker.Sparql.Backend.get_internal (cancellable); + return Tracker.Sparql.SessionWideBackend.get_internal (cancellable); +} + +/* The private backend does both reads and writes in-process. This makes sense + * if only one process will be reading and writing. Performance will suffer if + * multiple processes are trying to write to the same SQLite database + * simultaneously. + */ +class Tracker.Sparql.PrivateBackend : Tracker.Direct.Connection { + public PrivateBackend (string store_path, string[]? ontologies) throws Sparql.Error, IOError { + log_init (); + + base (store_path, ontologies, Flags.NONE); + } +} + +public static Tracker.Sparql.Connection tracker_sparql_private_store_open (string store_path, string[]? ontologies) throws Tracker.Sparql.Error, IOError { + return new Tracker.Sparql.PrivateBackend (store_path, ontologies); } diff --git a/src/libtracker-sparql/tracker-connection.vala b/src/libtracker-sparql/tracker-connection.vala index 1e1749e5c..681a4a582 100644 --- a/src/libtracker-sparql/tracker-connection.vala +++ b/src/libtracker-sparql/tracker-connection.vala @@ -19,14 +19,14 @@ /** * SECTION: tracker-sparql-connection - * @short_description: Connecting to the Store + * @short_description: Connecting to the user's session-wide Store * @title: TrackerSparqlConnection * @stability: Stable * @include: tracker-sparql.h * * <para> * #TrackerSparqlConnection is an object which sets up connections to the - * Tracker Store. + * user's session-wide Tracker Store. * </para> */ diff --git a/src/libtracker-sparql/tracker-private-store.vala b/src/libtracker-sparql/tracker-private-store.vala new file mode 100644 index 000000000..7eade57d4 --- /dev/null +++ b/src/libtracker-sparql/tracker-private-store.vala @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016, Sam Thursfield <sam@afuera.me.uk> + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION: tracker-sparql-private-store + * @short_description: Creating a private Store. + * @title: TrackerSparqlPrivateStore + * @stability: Stable + * @include: tracker-sparql.h + * + * <para> + * Use a private Tracker Store if your application wants to keep data + * separate from the user's session-wide Tracker Store. + * + * In general, we encourage storing data in the session-wide Tracker Store, + * so that other applications can make use of it. Use + * tracker_sparql_connection_get() to access the user's session-wide Store. + * There are some reasons why you might want to avoid the user's session-wide + * store, and use a private store instead. + * + * If you are writing automated tests, use a private store to ensure the tests + * can't interfere with the user's real data. + * + * If you want to use a different set of ontologies to those that ship + * by default with Tracker, you'll need to use a private store. It's very hard + * for the session-wide store to deal with changes to the ontologies, so we + * discourage installing new or changed ontologies for the system-wide Tracker + * instance. Using a private store works around this limitation. + * + * With a private Tracker store, all access to the database happens in-process. + * If multiple processes are writing to the same private Tracker store, you + * might performance issues. This is because SQLite needs to lock the entire + * database in order to do a write. + */ + +/** + * TrackerSparqlPrivateStore: + * + * The <structname>TrackerSparqlPrivateStore</structname> object represents a + * connection to a private Tracker store. + */ +public class Tracker.Sparql.PrivateStore : Tracker.Sparql.Connection { + /** + * tracker_sparql_private_store_open: + * @store_path: Location of a new or existing Tracker database. + * @ontologies: An array of paths to ontologies to load, or %NULL to load all + * system-provided ontologies. + * @error: #GError for error reporting. + * + * Opens a private Tracker store. See above for more information on private + * Tracker stores. + * + * All access to a private Tracker store happens in-process. No D-Bus + * communication is involved. For that reason there is no async variant of + * this function. + * + * Returns: a new #TrackerSparqlConnection. Call g_object_unref() on the + * object when no longer used. + * + * Since: 0.10 + */ + /* Implementation of this is in libtracker-sparql-backend/tracker-backend.vala */ + public extern static new Connection open (String store_path, String? ontologies_path) throws Sparql.Error, IOError; +} diff --git a/tests/libtracker-sparql/tracker-sparql-test.c b/tests/libtracker-sparql/tracker-sparql-test.c index 5cd4b7ace..892bc068e 100644 --- a/tests/libtracker-sparql/tracker-sparql-test.c +++ b/tests/libtracker-sparql/tracker-sparql-test.c @@ -191,6 +191,9 @@ test_tracker_sparql_cursor_next_async_cb (GObject *source, } } +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_cursor_next_async_query (gint query) { @@ -215,6 +218,9 @@ test_tracker_sparql_cursor_next_async_query (gint query) GINT_TO_POINTER(query)); } +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_cursor_next_async (void) { @@ -237,6 +243,9 @@ test_tracker_sparql_cursor_next_async (void) #endif /* HAVE_TRACKER_FTS */ +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_connection_locking_sync (void) { @@ -283,9 +292,15 @@ test_tracker_sparql_connection_locking_async_cb (GObject *source, g_assert (connection_waiting == NULL); } +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_connection_locking_async (void) { + /* These could be a connection to the user's real session-wide + * tracker-store, so don't do anything crazy. + */ tracker_sparql_connection_get_async (NULL, test_tracker_sparql_connection_locking_async_cb, c2); tracker_sparql_connection_get_async (NULL, test_tracker_sparql_connection_locking_async_cb, c3); c3 = tracker_sparql_connection_get (NULL, NULL); @@ -320,9 +335,15 @@ test_tracker_sparql_nb237150_cb (GObject *source_object, } } +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_nb237150_subprocess (void) { + /* These could be a connection to the user's real session-wide + * tracker-store, so don't do anything crazy. + */ g_print ("\n"); g_print ("Calling #1 - tracker_sparql_connection_get_async()\n"); tracker_sparql_connection_get_async (NULL, test_tracker_sparql_nb237150_cb, GINT_TO_POINTER(1)); @@ -357,6 +378,9 @@ test_tracker_sparql_nb237150 (void) g_test_trap_assert_stdout ("*Called back ALL*"); } +/* This test could be using a connection to the user's real session-wide + * tracker-store, so it shouldn't do any writes. + */ static void test_tracker_sparql_connection_interleaved (void) { @@ -366,8 +390,11 @@ test_tracker_sparql_connection_interleaved (void) TrackerSparqlCursor *cursor2; TrackerSparqlConnection *connection; - const gchar* query = "select ?u {?u a rdfs:Resource .}"; + const gchar * query = "select ?u {?u a rdfs:Resource .}"; + /* This could be a connection to the user's real session-wide + * tracker-store, so don't do anything crazy. + */ connection = tracker_sparql_connection_get (NULL, &error); g_assert_no_error (error); @@ -389,6 +416,40 @@ test_tracker_sparql_connection_interleaved (void) g_object_unref(cursor1); } +static void test_sparql_connection_private (void) { + GError *error = NULL; + TrackerSparqlConnection *connection; + TrackerSparqlCursor *cursor; + const gchar *store_location; + const gchar *cleanup_command; + const gchar *update = "insert { <http://www.example.com/> a nie:InformationElement; nie:title \"Boris\" . }"; + const gchar *query = "select ?title { <http://www.example.com/> nie:title ?title .}"; + + store_location = g_mkdtemp ("tracker-sparql-test-XXXXXX"); + + connection = tracker_sparql_private_store_open (store_location, NULL, &error); + g_assert_no_error (error); + + /* It's OK to do writes here, we have a private connection. */ + tracker_sparql_connection_update (connection, update, 0, NULL, &error); + g_assert_no_error (error); + + cursor = tracker_sparql_connection_query (connection, query, 0, &error); + g_assert_no_error (error); + + tracker_sparql_cursor_next (cursor, NULL, &error); + g_assert_no_error (error); + + g_assert_cmpstr (tracker_sparql_cursor_get_string (cursor, 0, NULL), ==, "Boris"); + + g_object_unref (connection); + g_object_unref (cursor); + + cleanup_command = g_strdup_printf ("rm -Rf %s/", store_location); + g_spawn_command_line_sync (cleanup_command, NULL, NULL, NULL, NULL); + g_free (cleanup_command); +} + gint main (gint argc, gchar **argv) { @@ -420,6 +481,8 @@ main (gint argc, gchar **argv) test_tracker_sparql_connection_locking_sync); g_test_add_func ("/libtracker-sparql/tracker-sparql/tracker_sparql_connection_locking_async", test_tracker_sparql_connection_locking_async); + g_test_add_func ("/libtracker-sparql/tracker-sparql/tracker_sparql_connection_private", + test_tracker_sparql_connection_private); #if HAVE_TRACKER_FTS g_test_add_func ("/libtracker-sparql/tracker-sparql/tracker_sparql_cursor_next_async", |