From 86c1d0890a3bbf0e34af08e6526692793a38f352 Mon Sep 17 00:00:00 2001 From: Jens Georg Date: Fri, 17 Apr 2015 00:15:57 +0200 Subject: build,db,media-export: Move database stuff Move database handling code to own library to prepare inclusion of new SQLite3 based plugins and avoid code duplication. --- common.am | 13 ++ configure.ac | 32 ++- src/Makefile.am | 2 +- src/librygel-db/Makefile.am | 40 ++++ src/librygel-db/collate.c | 49 +++++ src/librygel-db/database-cursor.vala | 147 ++++++++++++++ src/librygel-db/database.vala | 219 +++++++++++++++++++++ src/librygel-db/filelist.am | 8 + src/librygel-db/rygel-db-2.6.deps | 1 + src/librygel-db/sqlite-wrapper.vala | 80 ++++++++ src/plugins/media-export/Makefile.am | 11 +- .../media-export/rygel-media-export-collate.c | 49 ----- .../rygel-media-export-database-cursor.vala | 147 -------------- .../media-export/rygel-media-export-database.vala | 219 --------------------- .../rygel-media-export-db-container.vala | 1 + .../media-export/rygel-media-export-harvester.vala | 2 +- .../rygel-media-export-harvesting-task.vala | 2 +- .../rygel-media-export-media-cache-upgrader.vala | 32 +-- .../rygel-media-export-media-cache.vala | 7 +- .../rygel-media-export-root-container.vala | 6 +- .../rygel-media-export-sqlite-wrapper.vala | 80 -------- 21 files changed, 612 insertions(+), 535 deletions(-) create mode 100644 src/librygel-db/Makefile.am create mode 100644 src/librygel-db/collate.c create mode 100644 src/librygel-db/database-cursor.vala create mode 100644 src/librygel-db/database.vala create mode 100644 src/librygel-db/filelist.am create mode 100644 src/librygel-db/rygel-db-2.6.deps create mode 100644 src/librygel-db/sqlite-wrapper.vala delete mode 100644 src/plugins/media-export/rygel-media-export-collate.c delete mode 100644 src/plugins/media-export/rygel-media-export-database-cursor.vala delete mode 100644 src/plugins/media-export/rygel-media-export-database.vala delete mode 100644 src/plugins/media-export/rygel-media-export-sqlite-wrapper.vala diff --git a/common.am b/common.am index 74c69942..285f06c5 100644 --- a/common.am +++ b/common.am @@ -47,6 +47,19 @@ RYGEL_COMMON_LIBRYGEL_CORE_CFLAGS = \ RYGEL_COMMON_LIBRYGEL_CORE_LIBS = \ $(top_builddir)/src/librygel-core/librygel-core-2.6.la +RYGEL_COMMON_LIBRYGEL_DB_VALAFLAGS = \ + --vapidir=$(top_srcdir)/src/librygel-db \ + --pkg rygel-db-2.6 \ + $(RYGEL_COMMON_LIBRYGEL_CORE_VALAFLAGS) + +RYGEL_COMMON_LIBRYGEL_DB_CFLAGS = \ + $(RYGEL_COMMON_DEFINES_CFLAGS) \ + -I$(top_builddir)/src/librygel-db \ + -I$(top_srcdir)/src/librygel-db + +RYGEL_COMMON_LIBRYGEL_DB_LIBS = \ + $(RYGEL_COMMON_LIBRYGEL_CORE_LIBS) \ + $(top_builddir)/src/librygel-db/librygel-db-2.6.la RYGEL_COMMON_LIBRYGEL_RENDERER_VALAFLAGS = \ --vapidir=$(top_srcdir)/src/librygel-renderer \ diff --git a/configure.ac b/configure.ac index ea839eb5..270113e5 100644 --- a/configure.ac +++ b/configure.ac @@ -87,6 +87,27 @@ AC_SUBST([LIBRYGEL_CORE_DEPS_VALAFLAGS]) RYGEL_COMMON_MODULES="$RYGEL_BASE_MODULES gupnp-av-1.0 >= $GUPNP_AV_REQUIRED" RYGEL_COMMON_MODULES_VALAFLAGS="$RYGEL_BASE_MODULES_VALAFLAGS --pkg gupnp-av-1.0" +######################################################## +## Db +######################################################## +RYGEL_CHECK_PACKAGES([sqlite3]) +PKG_CHECK_MODULES([LIBRYGEL_DB_DEPS], + [$RYGEL_COMMON_MODULES + gio-2.0 >= $GIO_REQUIRED + sqlite3 >= $LIBSQLITE3_REQUIRED]) +LIBRYGEL_DB_DEPS_VALAFLAGS="$RYGEL_COMMON_MODULES_VALAFLAGS --pkg sqlite3 --pkg gio-2.0" +AC_CHECK_HEADER([unistr.h], + AC_CHECK_LIB([unistring], + [u8_strcoll], + [have_unistring=yes],[have_unistring=no])) +AS_IF([test "x$have_unistring" = "xyes"], + [ + AC_DEFINE([HAVE_UNISTRING], [1], [Use libunistring for collation]) + LIBRYGEL_DB_DEPS_LIBS="$LIBRYGEL_DB_DEPS_LIBS -lunistring" + ]) +AC_SUBST([LIBRYGEL_DB_DEPS_VALAFLAGS]) + + ######################################################## ## Server ######################################################## @@ -369,15 +390,7 @@ AS_IF([test "x$enable_coverage" != "xno"], dnl Check additional requirements for MediaExport plugins if test "x$enable_media_export_plugin" = "xyes"; then - RYGEL_CHECK_PACKAGES([sqlite3 gstreamer-tag-1.0 gstreamer-app-1.0]) - AC_CHECK_HEADER([unistr.h], - AC_CHECK_LIB([unistring], - [u8_strcoll], - [have_unistring=yes],[have_unistring=no])) - if test "x$have_unistring" = "xyes"; then - AC_DEFINE([HAVE_UNISTRING],[1],[Use libunistring for collation]) - RYGEL_PLUGIN_MEDIA_EXPORT_DEPS_LIBS="$RYGEL_PLUGIN_MEDIA_EXPORT_DEPS_LIBS -lunistring" - fi + RYGEL_CHECK_PACKAGES([gstreamer-tag-1.0 gstreamer-app-1.0]) fi @@ -489,6 +502,7 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/librygel-core/Makefile +src/librygel-db/Makefile src/librygel-renderer/Makefile src/librygel-renderer-gst/Makefile src/librygel-server/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 31b44eb8..8e6937bb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = librygel-core librygel-renderer librygel-server librygel-ruih +SUBDIRS = librygel-core librygel-renderer librygel-server librygel-ruih librygel-db if HAVE_GSTREAMER SUBDIRS += librygel-renderer-gst diff --git a/src/librygel-db/Makefile.am b/src/librygel-db/Makefile.am new file mode 100644 index 00000000..8914ded2 --- /dev/null +++ b/src/librygel-db/Makefile.am @@ -0,0 +1,40 @@ +include $(top_srcdir)/common.am +include $(srcdir)/filelist.am + +librygel_db_includedir=$(includedir)/rygel-2.6 +librygel_db_include_HEADERS = rygel-db.h + +lib_LTLIBRARIES = librygel-db-2.6.la + +LIBRYGEL_DB_VERSION=2:4:0 + +librygel_db_2_6_la_SOURCES = \ + $(LIBRYGEL_DB_VAPI_SOURCE_FILES) \ + $(LIBRYGEL_DB_NONVAPI_SOURCE_FILES) + +librygel_db_2_6_la_VALAFLAGS = \ + -H rygel-db.h --library=rygel-db-2.6 \ + $(LIBRYGEL_DB_DEPS_VALAFLAGS) \ + $(RYGEL_COMMON_LIBRYGEL_CORE_VALAFLAGS) \ + $(RYGEL_COMMON_VALAFLAGS) + +librygel_db_2_6_la_CFLAGS = \ + $(LIBRYGEL_DB_DEPS_CFLAGS) \ + $(RYGEL_COMMON_LIBRYGEL_CORE_CFLAGS) \ + -DENGINE_DIR='"$(enginedir)"' \ + -DG_LOG_DOMAIN=\"RygelDb\" +librygel_db_2_6_la_LIBADD = \ + $(LIBRYGEL_DB_DEPS_LIBS) \ + $(RYGEL_COMMON_LIBRYGEL_CORE_LIBS) + +librygel_db_2_6_la_LDFLAGS = \ + $(RYGEL_DATA_DIR_DEFINES) \ + -version-info $(LIBRYGEL_DB_VERSION) + +vapidir = $(VAPIDIR) +vapi_DATA = rygel-db-2.6.vapi rygel-db-2.6.deps + +rygel-db.h rygel-db-2.6.vapi: librygel_db_2_6_la_vala.stamp + +DISTCLEANFILES = $(nodist_typelib_DATA) +EXTRA_DIST = rygel-db.h rygel-db-2.6.vapi rygel-db-2.6.deps diff --git a/src/librygel-db/collate.c b/src/librygel-db/collate.c new file mode 100644 index 00000000..f547d1e1 --- /dev/null +++ b/src/librygel-db/collate.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 Jens Georg . + * + * Author: Jens Georg + * + * This file is part of Rygel. + * + * Rygel 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 of the License, or + * (at your option) any later version. + * + * Rygel 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#ifdef HAVE_UNISTRING +# include +#endif + +gint rygel_database_utf8_collate_str (const char *a, gsize alen, + const char *b, gsize blen) +{ + char *a_str, *b_str; + gint result; + + /* Make sure the passed strings are null terminated */ + a_str = g_strndup (a, alen); + b_str = g_strndup (b, blen); + +#ifdef HAVE_UNISTRING + result = u8_strcoll (a_str, b_str); +#else + return g_utf8_collate (a_str, b_str); +#endif + + g_free (a_str); + g_free (b_str); + + return result; +} diff --git a/src/librygel-db/database-cursor.vala b/src/librygel-db/database-cursor.vala new file mode 100644 index 00000000..763f1d89 --- /dev/null +++ b/src/librygel-db/database-cursor.vala @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2011 Jens Georg . + * + * Author: Jens Georg + * + * This file is part of Rygel. + * + * Rygel 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 of the License, or + * (at your option) any later version. + * + * Rygel 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Sqlite; + +public class Rygel.Database.Cursor : SqliteWrapper { + private Statement statement; + private int current_state = -1; + private bool dirty = true; + + /** + * Prepare a SQLite statement from a SQL string + * + * This function uses the type of the GValue passed in values to determine + * which _bind function to use. + * + * Supported types are: int, long, int64, uint64, string and pointer. + * @note the only pointer supported is the null pointer as provided by + * Database.@null. This is a special value to bind a column to NULL + * + * @param db SQLite database this cursor belongs to + * @param sql statement to execute + * @param values array of values to bind to the SQL statement or null if + * none + */ + public Cursor (Sqlite.Database db, + string sql, + GLib.Value[]? arguments) throws DatabaseError { + base.wrap (db); + + this.throw_if_code_is_error (db.prepare_v2 (sql, + -1, + out this.statement, + null)); + if (arguments == null) { + return; + } + + for (var i = 1; i <= arguments.length; ++i) { + unowned GLib.Value current_value = arguments[i - 1]; + + if (current_value.holds (typeof (int))) { + statement.bind_int (i, current_value.get_int ()); + } else if (current_value.holds (typeof (int64))) { + statement.bind_int64 (i, current_value.get_int64 ()); + } else if (current_value.holds (typeof (uint64))) { + statement.bind_int64 (i, (int64) current_value.get_uint64 ()); + } else if (current_value.holds (typeof (long))) { + statement.bind_int64 (i, current_value.get_long ()); + } else if (current_value.holds (typeof (uint))) { + statement.bind_int64 (i, current_value.get_uint ()); + } else if (current_value.holds (typeof (string))) { + statement.bind_text (i, current_value.get_string ()); + } else if (current_value.holds (typeof (void *))) { + if (current_value.peek_pointer () == null) { + statement.bind_null (i); + } else { + assert_not_reached (); + } + } else { + var type = current_value.type (); + warning (_("Unsupported type %s"), type.name ()); + assert_not_reached (); + } + + this.throw_if_db_has_error (); + } + } + + /** + * Check if the cursor has more rows left + * + * @return true if more rows left, false otherwise + */ + public bool has_next () throws DatabaseError { + if (this.dirty) { + this.current_state = this.statement.step (); + this.dirty = false; + } + + this.throw_if_code_is_error (this.current_state); + + return this.current_state == Sqlite.ROW || this.current_state == -1; + } + + /** + * Get the next row of this cursor. + * + * This function uses pointers instead of unowned because var doesn't work + * with unowned. + * + * @return a pointer to the current row + */ + public Statement* next () throws DatabaseError { + this.has_next (); + this.throw_if_code_is_error (this.current_state); + this.dirty = true; + + return this.statement; + } + + // convenience functions for "foreach" + + /** + * Return an iterator to the cursor to use with foreach + * + * @return an iterator wrapping the cursor + */ + public Iterator iterator () { + return new Iterator (this); + } + + public class Iterator { + public Cursor cursor; + + public Iterator (Cursor cursor) { + this.cursor = cursor; + } + + public bool next () throws DatabaseError { + return this.cursor.has_next (); + } + + public unowned Statement @get () throws DatabaseError { + return this.cursor.next (); + } + } +} diff --git a/src/librygel-db/database.vala b/src/librygel-db/database.vala new file mode 100644 index 00000000..25b3f974 --- /dev/null +++ b/src/librygel-db/database.vala @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2009,2011 Jens Georg . + * + * Author: Jens Georg + * + * This file is part of Rygel. + * + * Rygel 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 of the License, or + * (at your option) any later version. + * + * Rygel 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Sqlite; + +public errordomain Rygel.Database.DatabaseError { + IO_ERROR, + SQLITE_ERROR +} + +namespace Rygel.Database { + extern static int utf8_collate_str (uint8[] a, uint8[] b); + + /** + * Special GValue to pass to exec or exec_cursor to bind a column to + * NULL + */ + public static GLib.Value @null () { + GLib.Value v = GLib.Value (typeof (void *)); + v.set_pointer (null); + + return v; + } +} + +/** + * This class is a thin wrapper around SQLite's database object. + * + * It adds statement preparation based on GValue and a cancellable exec + * function. + */ +public class Rygel.Database.Database : SqliteWrapper { + + /** + * Function to implement the custom SQL function 'contains' + */ + private static void utf8_contains (Sqlite.Context context, + Sqlite.Value[] args) + requires (args.length == 2) { + if (args[0].to_text () == null || + args[1].to_text () == null) { + context.result_int (0); + + return; + } + + var pattern = Regex.escape_string (args[1].to_text ()); + if (Regex.match_simple (pattern, + args[0].to_text (), + RegexCompileFlags.CASELESS)) { + context.result_int (1); + } else { + context.result_int (0); + } + } + + /** + * Function to implement the custom SQLite collation 'CASEFOLD'. + * + * Uses utf8 case-fold to compare the strings. + */ + private static int utf8_collate (int alen, void* a, int blen, void* b) { + // unowned to prevent array copy + unowned uint8[] _a = (uint8[]) a; + _a.length = alen; + + unowned uint8[] _b = (uint8[]) b; + _b.length = blen; + + return utf8_collate_str (_a, _b); + } + + /** + * Open a database in the user's cache directory as defined by XDG + * + * @param name of the database, used to build full path + * (/rygel/.db) + */ + public Database (string name) throws DatabaseError { + string db_file; + + if (name != ":memory:") { + var dirname = Path.build_filename ( + Environment.get_user_cache_dir (), + "rygel"); + DirUtils.create_with_parents (dirname, 0750); + db_file = Path.build_filename (dirname, "%s.db".printf (name)); + } else { + db_file = name; + } + + base (db_file); + + debug ("Using database file %s", db_file); + + this.exec ("PRAGMA synchronous = OFF"); + this.exec ("PRAGMA temp_store = MEMORY"); + this.exec ("PRAGMA count_changes = OFF"); + + this.db.create_function ("contains", + 2, + Sqlite.UTF8, + null, + Database.utf8_contains, + null, + null); + + this.db.create_collation ("CASEFOLD", + Sqlite.UTF8, + Database.utf8_collate); + } + + /** + * SQL query function. + * + * Use for all queries that return a result set. + * + * @param sql The SQL query to run. + * @param args Values to bind in the SQL query or null. + * @throws DatabaseError if the underlying SQLite operation fails. + */ + public Cursor exec_cursor (string sql, + GLib.Value[]? arguments = null) + throws DatabaseError { + return new Cursor (this.db, sql, arguments); + } + + /** + * Simple SQL query execution function. + * + * Use for all queries that don't return anything. + * + * @param sql The SQL query to run. + * @param args Values to bind in the SQL query or null. + * @throws DatabaseError if the underlying SQLite operation fails. + */ + public void exec (string sql, + GLib.Value[]? arguments = null) + throws DatabaseError { + if (arguments == null) { + this.throw_if_code_is_error (this.db.exec (sql)); + + return; + } + + var cursor = this.exec_cursor (sql, arguments); + while (cursor.has_next ()) { + cursor.next (); + } + } + + /** + * Execute a SQL query that returns a single number. + * + * @param sql The SQL query to run. + * @param args Values to bind in the SQL query or null. + * @return The contents of the first row's column as an int. + * @throws DatabaseError if the underlying SQLite operation fails. + */ + public int query_value (string sql, + GLib.Value[]? args = null) + throws DatabaseError { + var cursor = this.exec_cursor (sql, args); + var statement = cursor.next (); + return statement->column_int (0); + } + + /** + * Analyze triggers of database + */ + public void analyze () { + this.db.exec ("ANALYZE"); + } + + /** + * Start a transaction + */ + public void begin () throws DatabaseError { + this.exec ("BEGIN"); + } + + /** + * Commit a transaction + */ + public void commit () throws DatabaseError { + this.exec ("COMMIT"); + } + + /** + * Rollback a transaction + */ + public void rollback () { + try { + this.exec ("ROLLBACK"); + } catch (DatabaseError error) { + critical (_("Failed to roll back transaction: %s"), + error.message); + } + } +} diff --git a/src/librygel-db/filelist.am b/src/librygel-db/filelist.am new file mode 100644 index 00000000..3feb5763 --- /dev/null +++ b/src/librygel-db/filelist.am @@ -0,0 +1,8 @@ + +LIBRYGEL_DB_VAPI_SOURCE_FILES = \ + database-cursor.vala \ + database.vala \ + sqlite-wrapper.vala + +LIBRYGEL_DB_NONVAPI_SOURCE_FILES = \ + collate.c diff --git a/src/librygel-db/rygel-db-2.6.deps b/src/librygel-db/rygel-db-2.6.deps new file mode 100644 index 00000000..8b2f60c6 --- /dev/null +++ b/src/librygel-db/rygel-db-2.6.deps @@ -0,0 +1 @@ +sqlite3 diff --git a/src/librygel-db/sqlite-wrapper.vala b/src/librygel-db/sqlite-wrapper.vala new file mode 100644 index 00000000..a1a01c99 --- /dev/null +++ b/src/librygel-db/sqlite-wrapper.vala @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 Jens Georg . + * + * Author: Jens Georg + * + * This file is part of Rygel. + * + * Rygel 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 of the License, or + * (at your option) any later version. + * + * Rygel 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Sqlite; + +public class Rygel.Database.SqliteWrapper : Object { + private Sqlite.Database database = null; + private Sqlite.Database *reference = null; + + /** + * Property to access the wrapped database + */ + protected unowned Sqlite.Database db { + get { return reference; } + } + + /** + * Wrap an existing SQLite Database object. + * + * The SqliteWrapper doesn't take ownership of the passed db + */ + public SqliteWrapper.wrap (Sqlite.Database db) { + this.reference = db; + } + + /** + * Create or open a new SQLite database in path. + * + * @note: Path may also be ":memory:" for temporary databases + */ + public SqliteWrapper (string path) throws DatabaseError { + Sqlite.Database.open (path, out this.database); + this.reference = this.database; + this.throw_if_db_has_error (); + } + + /** + * Convert a SQLite return code to a DatabaseError + */ + protected void throw_if_code_is_error (int sqlite_error) + throws DatabaseError { + switch (sqlite_error) { + case Sqlite.OK: + case Sqlite.DONE: + case Sqlite.ROW: + return; + default: + throw new DatabaseError.SQLITE_ERROR + ("SQLite error %d: %s", + sqlite_error, + this.reference->errmsg ()); + } + } + + /** + * Check if the last operation on the database was an error + */ + protected void throw_if_db_has_error () throws DatabaseError { + this.throw_if_code_is_error (this.reference->errcode ()); + } +} diff --git a/src/plugins/media-export/Makefile.am b/src/plugins/media-export/Makefile.am index ee19f169..2bbf458b 100644 --- a/src/plugins/media-export/Makefile.am +++ b/src/plugins/media-export/Makefile.am @@ -33,9 +33,6 @@ plugin_DATA = media-export.plugin librygel_media_export_la_SOURCES = \ rygel-media-export-plugin.vala \ - rygel-media-export-database.vala \ - rygel-media-export-database-cursor.vala \ - rygel-media-export-sqlite-wrapper.vala \ rygel-media-export-db-container.vala \ rygel-media-export-sql-factory.vala \ rygel-media-export-media-cache.vala \ @@ -63,8 +60,7 @@ librygel_media_export_la_SOURCES = \ rygel-media-export-photo-item.vala \ rygel-media-export-playlist-item.vala \ rygel-media-export-trackable-db-container.vala \ - rygel-media-export-updatable-object.vala \ - rygel-media-export-collate.c + rygel-media-export-updatable-object.vala librygel_media_export_la_VALAFLAGS = \ --enable-experimental \ @@ -75,16 +71,19 @@ librygel_media_export_la_VALAFLAGS = \ --pkg gio-unix-2.0 \ $(RYGEL_PLUGIN_MEDIA_EXPORT_DEPS_VALAFLAGS) \ $(RYGEL_COMMON_LIBRYGEL_SERVER_VALAFLAGS) \ + $(RYGEL_COMMON_LIBRYGEL_DB_VALAFLAGS) \ $(RYGEL_COMMON_VALAFLAGS) librygel_media_export_la_CFLAGS = \ $(RYGEL_PLUGIN_MEDIA_EXPORT_DEPS_CFLAGS) \ $(RYGEL_COMMON_LIBRYGEL_SERVER_CFLAGS) \ + $(RYGEL_COMMON_LIBRYGEL_DB_CFLAGS) \ -DMX_EXTRACT_PATH='"$(MX_EXTRACT_PATH)"' \ -DG_LOG_DOMAIN='"MediaExport"' librygel_media_export_la_LIBADD = \ $(RYGEL_PLUGIN_MEDIA_EXPORT_DEPS_LIBS) \ - $(RYGEL_COMMON_LIBRYGEL_SERVER_LIBS) + $(RYGEL_COMMON_LIBRYGEL_SERVER_LIBS) \ + $(RYGEL_COMMON_LIBRYGEL_DB_LIBS) librygel_media_export_la_LDFLAGS = \ $(RYGEL_PLUGIN_LINKER_FLAGS) diff --git a/src/plugins/media-export/rygel-media-export-collate.c b/src/plugins/media-export/rygel-media-export-collate.c deleted file mode 100644 index aa2553fb..00000000 --- a/src/plugins/media-export/rygel-media-export-collate.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2012 Jens Georg . - * - * Author: Jens Georg - * - * This file is part of Rygel. - * - * Rygel 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 of the License, or - * (at your option) any later version. - * - * Rygel 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 program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -#ifdef HAVE_UNISTRING -# include -#endif - -gint rygel_media_export_utf8_collate_str (const char *a, gsize alen, - const char *b, gsize blen) -{ - char *a_str, *b_str; - gint result; - - /* Make sure the passed strings are null terminated */ - a_str = g_strndup (a, alen); - b_str = g_strndup (b, blen); - -#ifdef HAVE_UNISTRING - result = u8_strcoll (a_str, b_str); -#else - return g_utf8_collate (a_str, b_str); -#endif - - g_free (a_str); - g_free (b_str); - - return result; -} diff --git a/src/plugins/media-export/rygel-media-export-database-cursor.vala b/src/plugins/media-export/rygel-media-export-database-cursor.vala deleted file mode 100644 index 55a538ba..00000000 --- a/src/plugins/media-export/rygel-media-export-database-cursor.vala +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2011 Jens Georg . - * - * Author: Jens Georg - * - * This file is part of Rygel. - * - * Rygel 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 of the License, or - * (at your option) any later version. - * - * Rygel 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 program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using Sqlite; - -internal class Rygel.MediaExport.DatabaseCursor : SqliteWrapper { - private Statement statement; - private int current_state = -1; - private bool dirty = true; - - /** - * Prepare a SQLite statement from a SQL string - * - * This function uses the type of the GValue passed in values to determine - * which _bind function to use. - * - * Supported types are: int, long, int64, uint64, string and pointer. - * @note the only pointer supported is the null pointer as provided by - * Database.@null. This is a special value to bind a column to NULL - * - * @param db SQLite database this cursor belongs to - * @param sql statement to execute - * @param values array of values to bind to the SQL statement or null if - * none - */ - public DatabaseCursor (Sqlite.Database db, - string sql, - GLib.Value[]? arguments) throws DatabaseError { - base.wrap (db); - - this.throw_if_code_is_error (db.prepare_v2 (sql, - -1, - out this.statement, - null)); - if (arguments == null) { - return; - } - - for (var i = 1; i <= arguments.length; ++i) { - unowned GLib.Value current_value = arguments[i - 1]; - - if (current_value.holds (typeof (int))) { - statement.bind_int (i, current_value.get_int ()); - } else if (current_value.holds (typeof (int64))) { - statement.bind_int64 (i, current_value.get_int64 ()); - } else if (current_value.holds (typeof (uint64))) { - statement.bind_int64 (i, (int64) current_value.get_uint64 ()); - } else if (current_value.holds (typeof (long))) { - statement.bind_int64 (i, current_value.get_long ()); - } else if (current_value.holds (typeof (uint))) { - statement.bind_int64 (i, current_value.get_uint ()); - } else if (current_value.holds (typeof (string))) { - statement.bind_text (i, current_value.get_string ()); - } else if (current_value.holds (typeof (void *))) { - if (current_value.peek_pointer () == null) { - statement.bind_null (i); - } else { - assert_not_reached (); - } - } else { - var type = current_value.type (); - warning (_("Unsupported type %s"), type.name ()); - assert_not_reached (); - } - - this.throw_if_db_has_error (); - } - } - - /** - * Check if the cursor has more rows left - * - * @return true if more rows left, false otherwise - */ - public bool has_next () throws DatabaseError { - if (this.dirty) { - this.current_state = this.statement.step (); - this.dirty = false; - } - - this.throw_if_code_is_error (this.current_state); - - return this.current_state == Sqlite.ROW || this.current_state == -1; - } - - /** - * Get the next row of this cursor. - * - * This function uses pointers instead of unowned because var doesn't work - * with unowned. - * - * @return a pointer to the current row - */ - public Statement* next () throws DatabaseError { - this.has_next (); - this.throw_if_code_is_error (this.current_state); - this.dirty = true; - - return this.statement; - } - - // convenience functions for "foreach" - - /** - * Return an iterator to the cursor to use with foreach - * - * @return an iterator wrapping the cursor - */ - public Iterator iterator () { - return new Iterator (this); - } - - public class Iterator { - public DatabaseCursor cursor; - - public Iterator (DatabaseCursor cursor) { - this.cursor = cursor; - } - - public bool next () throws DatabaseError { - return this.cursor.has_next (); - } - - public unowned Statement @get () throws DatabaseError { - return this.cursor.next (); - } - } -} diff --git a/src/plugins/media-export/rygel-media-export-database.vala b/src/plugins/media-export/rygel-media-export-database.vala deleted file mode 100644 index f6550599..00000000 --- a/src/plugins/media-export/rygel-media-export-database.vala +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (C) 2009,2011 Jens Georg . - * - * Author: Jens Georg - * - * This file is part of Rygel. - * - * Rygel 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 of the License, or - * (at your option) any later version. - * - * Rygel 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 program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using Sqlite; - -public errordomain Rygel.MediaExport.DatabaseError { - IO_ERROR, - SQLITE_ERROR -} - -namespace Rygel.MediaExport { - extern static int utf8_collate_str (uint8[] a, uint8[] b); -} - -/** - * This class is a thin wrapper around SQLite's database object. - * - * It adds statement preparation based on GValue and a cancellable exec - * function. - */ -internal class Rygel.MediaExport.Database : SqliteWrapper { - - /** - * Function to implement the custom SQL function 'contains' - */ - private static void utf8_contains (Sqlite.Context context, - Sqlite.Value[] args) - requires (args.length == 2) { - if (args[0].to_text () == null || - args[1].to_text () == null) { - context.result_int (0); - - return; - } - - var pattern = Regex.escape_string (args[1].to_text ()); - if (Regex.match_simple (pattern, - args[0].to_text (), - RegexCompileFlags.CASELESS)) { - context.result_int (1); - } else { - context.result_int (0); - } - } - - /** - * Function to implement the custom SQLite collation 'CASEFOLD'. - * - * Uses utf8 case-fold to compare the strings. - */ - private static int utf8_collate (int alen, void* a, int blen, void* b) { - // unowned to prevent array copy - unowned uint8[] _a = (uint8[]) a; - _a.length = alen; - - unowned uint8[] _b = (uint8[]) b; - _b.length = blen; - - return utf8_collate_str (_a, _b); - } - - /** - * Open a database in the user's cache directory as defined by XDG - * - * @param name of the database, used to build full path - * (/rygel/.db) - */ - public Database (string name) throws DatabaseError { - string db_file; - - if (name != ":memory:") { - var dirname = Path.build_filename ( - Environment.get_user_cache_dir (), - "rygel"); - DirUtils.create_with_parents (dirname, 0750); - db_file = Path.build_filename (dirname, "%s.db".printf (name)); - } else { - db_file = name; - } - - base (db_file); - - debug ("Using database file %s", db_file); - - this.exec ("PRAGMA synchronous = OFF"); - this.exec ("PRAGMA temp_store = MEMORY"); - this.exec ("PRAGMA count_changes = OFF"); - - this.db.create_function ("contains", - 2, - Sqlite.UTF8, - null, - Database.utf8_contains, - null, - null); - - this.db.create_collation ("CASEFOLD", - Sqlite.UTF8, - Database.utf8_collate); - } - - /** - * SQL query function. - * - * Use for all queries that return a result set. - * - * @param sql The SQL query to run. - * @param args Values to bind in the SQL query or null. - * @throws DatabaseError if the underlying SQLite operation fails. - */ - public DatabaseCursor exec_cursor (string sql, - GLib.Value[]? arguments = null) - throws DatabaseError { - return new DatabaseCursor (this.db, sql, arguments); - } - - /** - * Simple SQL query execution function. - * - * Use for all queries that don't return anything. - * - * @param sql The SQL query to run. - * @param args Values to bind in the SQL query or null. - * @throws DatabaseError if the underlying SQLite operation fails. - */ - public void exec (string sql, - GLib.Value[]? arguments = null) - throws DatabaseError { - if (arguments == null) { - this.throw_if_code_is_error (this.db.exec (sql)); - - return; - } - - var cursor = this.exec_cursor (sql, arguments); - while (cursor.has_next ()) { - cursor.next (); - } - } - - /** - * Execute a SQL query that returns a single number. - * - * @param sql The SQL query to run. - * @param args Values to bind in the SQL query or null. - * @return The contents of the first row's column as an int. - * @throws DatabaseError if the underlying SQLite operation fails. - */ - public int query_value (string sql, - GLib.Value[]? args = null) - throws DatabaseError { - var cursor = this.exec_cursor (sql, args); - var statement = cursor.next (); - return statement->column_int (0); - } - - /** - * Analyze triggers of database - */ - public void analyze () { - this.db.exec ("ANALYZE"); - } - - /** - * Special GValue to pass to exec or exec_cursor to bind a column to - * NULL - */ - public static GLib.Value @null () { - GLib.Value v = GLib.Value (typeof (void *)); - v.set_pointer (null); - - return v; - } - - /** - * Start a transaction - */ - public void begin () throws DatabaseError { - this.exec ("BEGIN"); - } - - /** - * Commit a transaction - */ - public void commit () throws DatabaseError { - this.exec ("COMMIT"); - } - - /** - * Rollback a transaction - */ - public void rollback () { - try { - this.exec ("ROLLBACK"); - } catch (DatabaseError error) { - critical (_("Failed to roll back transaction: %s"), - error.message); - } - } -} diff --git a/src/plugins/media-export/rygel-media-export-db-container.vala b/src/plugins/media-export/rygel-media-export-db-container.vala index c9e2a1dd..fdd1e7e2 100644 --- a/src/plugins/media-export/rygel-media-export-db-container.vala +++ b/src/plugins/media-export/rygel-media-export-db-container.vala @@ -21,6 +21,7 @@ */ using GUPnP; using Gee; +using Rygel.Database; public class Rygel.MediaExport.DBContainer : MediaContainer, SearchableContainer { diff --git a/src/plugins/media-export/rygel-media-export-harvester.vala b/src/plugins/media-export/rygel-media-export-harvester.vala index d1e077c0..b0c8007d 100644 --- a/src/plugins/media-export/rygel-media-export-harvester.vala +++ b/src/plugins/media-export/rygel-media-export-harvester.vala @@ -188,7 +188,7 @@ internal class Rygel.MediaExport.Harvester : GLib.Object { var parent_container = cache.get_object (id) as MediaContainer; this.schedule (file, parent_container); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { warning (_("Error fetching object '%s' from database: %s"), id, error.message); diff --git a/src/plugins/media-export/rygel-media-export-harvesting-task.vala b/src/plugins/media-export/rygel-media-export-harvesting-task.vala index 69bd1038..15b932b6 100644 --- a/src/plugins/media-export/rygel-media-export-harvesting-task.vala +++ b/src/plugins/media-export/rygel-media-export-harvesting-task.vala @@ -261,7 +261,7 @@ public class Rygel.MediaExport.HarvestingTask : Rygel.StateMachine, foreach (var child in container.children) { this.cache.remove_by_id (child); } - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { warning (_("Failed to get children of container %s: %s"), container.id, error.message); diff --git a/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala b/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala index 633bb9b2..6a4b21af 100644 --- a/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala +++ b/src/plugins/media-export/rygel-media-export-media-cache-upgrader.vala @@ -22,7 +22,7 @@ using Gee; internal class Rygel.MediaExport.MediaCacheUpgrader { - private unowned Database database; + private unowned Database.Database database; private unowned SQLFactory sql; private const string UPDATE_V3_V4_STRING_2 = @@ -35,7 +35,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { private const string UPDATE_V3_V4_STRING_4 = "UPDATE Object SET timestamp = 0"; - public MediaCacheUpgrader (Database database, SQLFactory sql) { + public MediaCacheUpgrader (Database.Database database, SQLFactory sql) { this.database = database; this.sql = sql; } @@ -137,7 +137,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { } } - private void force_reindex () throws DatabaseError { + private void force_reindex () throws Database.DatabaseError { database.exec ("UPDATE Object SET timestamp = 0"); } @@ -161,7 +161,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.exec (this.sql.make (SQLString.TRIGGER_COMMON)); database.exec ("UPDATE schema_info SET version = '4'"); database.commit (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -209,7 +209,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError err) { + } catch (Database.DatabaseError err) { database.rollback (); warning ("Database upgrade failed: %s", err.message); database = null; @@ -230,7 +230,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -246,7 +246,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -263,7 +263,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -286,7 +286,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { this.database.exec ("UPDATE schema_info SET version = '9'"); this.database.commit (); this.database.exec ("VACUUM"); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -355,7 +355,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -376,7 +376,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -419,7 +419,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -482,7 +482,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { this.database.commit (); this.database.exec ("VACUUM"); this.database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { this.database.rollback (); warning ("Database upgrade failed: %s", error.message); this.database = null; @@ -501,7 +501,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { this.database.commit (); this.database.exec ("VACUUM"); this.database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { this.database.rollback (); warning ("Database upgrade failed: %s", error.message); this.database = null; @@ -517,7 +517,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; @@ -536,7 +536,7 @@ internal class Rygel.MediaExport.MediaCacheUpgrader { database.commit (); database.exec ("VACUUM"); database.analyze (); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { database.rollback (); warning ("Database upgrade failed: %s", error.message); database = null; diff --git a/src/plugins/media-export/rygel-media-export-media-cache.vala b/src/plugins/media-export/rygel-media-export-media-cache.vala index 332f34dc..9c8ff1da 100644 --- a/src/plugins/media-export/rygel-media-export-media-cache.vala +++ b/src/plugins/media-export/rygel-media-export-media-cache.vala @@ -27,6 +27,7 @@ using Gee; using GUPnP; using Sqlite; +using Rygel.Database; public errordomain Rygel.MediaExport.MediaCacheError { SQLITE_ERROR, @@ -52,7 +53,7 @@ internal struct Rygel.MediaExport.ExistsCacheEntry { */ public class Rygel.MediaExport.MediaCache : Object { // Private members - private Database db; + private Database.Database db; private ObjectFactory factory; private SQLFactory sql; private HashMap exists_cache; @@ -617,7 +618,7 @@ public class Rygel.MediaExport.MediaCache : Object { } private void open_db (string name) throws Error { - this.db = new Database (name); + this.db = new Database.Database (name); int old_version = -1; int current_version = int.parse (SQLFactory.SCHEMA_VERSION); @@ -1170,7 +1171,7 @@ public class Rygel.MediaExport.MediaCache : Object { return operator.to_string (); } - private DatabaseCursor exec_cursor (SQLString id, + private Database.Cursor exec_cursor (SQLString id, GLib.Value[]? values = null) throws DatabaseError { return this.db.exec_cursor (this.sql.make (id), values); diff --git a/src/plugins/media-export/rygel-media-export-root-container.vala b/src/plugins/media-export/rygel-media-export-root-container.vala index 06529414..08e9a171 100644 --- a/src/plugins/media-export/rygel-media-export-root-container.vala +++ b/src/plugins/media-export/rygel-media-export-root-container.vala @@ -408,7 +408,7 @@ public class Rygel.MediaExport.RootContainer : TrackableDbContainer { ArrayList ids; try { ids = media_db.get_child_ids (FILESYSTEM_FOLDER_ID); - } catch (DatabaseError e) { + } catch (Database.DatabaseError e) { ids = new ArrayList (); } @@ -436,7 +436,7 @@ public class Rygel.MediaExport.RootContainer : TrackableDbContainer { try { // FIXME: I think this needs to emit objDel events... this.media_db.remove_by_id (id); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { warning (_("Failed to remove entry: %s"), error.message); } } @@ -503,7 +503,7 @@ public class Rygel.MediaExport.RootContainer : TrackableDbContainer { this.harvester.cancel (file); try { this.media_db.remove_by_id (MediaCache.get_id (file)); - } catch (DatabaseError error) { + } catch (Database.DatabaseError error) { warning (_("Failed to remove entry: %s"), error.message); } } diff --git a/src/plugins/media-export/rygel-media-export-sqlite-wrapper.vala b/src/plugins/media-export/rygel-media-export-sqlite-wrapper.vala deleted file mode 100644 index 84a288bb..00000000 --- a/src/plugins/media-export/rygel-media-export-sqlite-wrapper.vala +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2011 Jens Georg . - * - * Author: Jens Georg - * - * This file is part of Rygel. - * - * Rygel 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 of the License, or - * (at your option) any later version. - * - * Rygel 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 program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using Sqlite; - -internal class Rygel.MediaExport.SqliteWrapper : Object { - private Sqlite.Database database = null; - private Sqlite.Database *reference = null; - - /** - * Property to access the wrapped database - */ - protected unowned Sqlite.Database db { - get { return reference; } - } - - /** - * Wrap an existing SQLite Database object. - * - * The SqliteWrapper doesn't take ownership of the passed db - */ - public SqliteWrapper.wrap (Sqlite.Database db) { - this.reference = db; - } - - /** - * Create or open a new SQLite database in path. - * - * @note: Path may also be ":memory:" for temporary databases - */ - public SqliteWrapper (string path) throws DatabaseError { - Sqlite.Database.open (path, out this.database); - this.reference = this.database; - this.throw_if_db_has_error (); - } - - /** - * Convert a SQLite return code to a DatabaseError - */ - protected void throw_if_code_is_error (int sqlite_error) - throws DatabaseError { - switch (sqlite_error) { - case Sqlite.OK: - case Sqlite.DONE: - case Sqlite.ROW: - return; - default: - throw new DatabaseError.SQLITE_ERROR - ("SQLite error %d: %s", - sqlite_error, - this.reference->errmsg ()); - } - } - - /** - * Check if the last operation on the database was an error - */ - protected void throw_if_db_has_error () throws DatabaseError { - this.throw_if_code_is_error (this.reference->errcode ()); - } -} -- cgit v1.2.1