summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Thursfield <sam@afuera.me.uk>2016-06-24 00:04:47 +0100
committerSam Thursfield <sam@afuera.me.uk>2016-06-24 00:07:24 +0100
commit94d8449b1c36a445047015de4997371803c39e44 (patch)
tree9ce5df9f7f4cfe16bdd3f0c5dfe61eb134a42bfd
parent9faae9ce5c852a744644b18b7bcef88b5491c397 (diff)
downloadtracker-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.vala14
-rw-r--r--src/libtracker-sparql-backend/tracker-backend.vala61
-rw-r--r--src/libtracker-sparql/tracker-connection.vala4
-rw-r--r--src/libtracker-sparql/tracker-private-store.vala80
-rw-r--r--tests/libtracker-sparql/tracker-sparql-test.c65
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",