rygel-media-export-db-container.vala \
rygel-media-export-db-object-factory.vala \
rygel-media-export-media-cache.vala \
+ rygel-media-export-media-cache-upgrader.vala \
rygel-media-export-metadata-extractor.vala \
rygel-media-export-null-container.vala \
rygel-media-export-dummy-container.vala \
--- /dev/null
+/*
+ * Copyright (C) 2010 Jens Georg <mail@jensge.org>.
+ *
+ * Author: Jens Georg <mail@jensge.org>
+ *
+ * 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 Gee;
+
+internal class Rygel.MediaExport.MediaCacheUpgrader {
+ private unowned Database database;
+ private const string UPDATE_V3_V4_STRING_2 =
+ "UPDATE meta_data SET object_fk = " +
+ "(SELECT upnp_id FROM Object WHERE metadata_fk = meta_data.id)";
+
+ private const string UPDATE_V3_V4_STRING_3 =
+ "ALTER TABLE Object ADD timestamp INTEGER";
+
+ private const string UPDATE_V3_V4_STRING_4 =
+ "UPDATE Object SET timestamp = 0";
+
+ public MediaCacheUpgrader (Database database) {
+ this.database = database;
+ }
+
+ public bool needs_upgrade (out int current_version) throws Error {
+ // cannot capture out parameters in closure
+ int current_version_temp = 0;
+
+ this.database.exec ("SELECT version FROM schema_info",
+ null,
+ (statement) => {
+ current_version_temp = statement.column_int (0);
+
+ return false;
+ });
+ current_version = current_version_temp;
+
+ return current_version < MediaCache.schema_version.to_int ();
+ }
+
+ public void fix_schema () throws Error {
+ bool schema_ok = true;
+
+ database.exec ("SELECT count(*) FROM sqlite_master WHERE sql " +
+ "LIKE 'CREATE TABLE Meta_Data%object_fk TEXT " +
+ "UNIQUE%'",
+ null,
+ (statement) => {
+ schema_ok = statement.column_int (0) == 1;
+
+ return false;
+ });
+ if (!schema_ok) {
+ try {
+ message ("Found faulty schema, forcing full reindex");
+ database.begin ();
+ database.exec ("DELETE FROM Object WHERE upnp_id IN (" +
+ "SELECT DISTINCT object_fk FROM meta_data)");
+ database.exec ("DROP TABLE Meta_Data");
+ database.exec (MediaCache.CREATE_META_DATA_TABLE_STRING);
+ database.commit ();
+ } catch (Error error) {
+ database.rollback ();
+ warning ("Failed to force reindex to fix database: " +
+ error.message);
+ }
+ }
+ }
+
+ public void upgrade (int old_version) {
+ debug ("Older schema detected. Upgrading...");
+ int current_version = MediaCache.schema_version.to_int ();
+ while (old_version < current_version) {
+ if (this.database != null) {
+ switch (old_version) {
+ case 3:
+ update_v3_v4 ();
+ break;
+ case 4:
+ update_v4_v5 ();
+ break;
+ case 5:
+ update_v5_v6 ();
+ break;
+ default:
+ warning ("Cannot upgrade");
+ database = null;
+ break;
+ }
+ old_version++;
+ }
+ }
+ }
+
+ private void update_v3_v4 () {
+ try {
+ database.begin ();
+ database.exec ("ALTER TABLE Meta_Data RENAME TO _Meta_Data");
+ database.exec (MediaCache.CREATE_META_DATA_TABLE_STRING);
+ database.exec ("INSERT INTO meta_data (size, mime_type, " +
+ "duration, width, height, class, author, album, " +
+ "date, bitrate, sample_freq, bits_per_sample, " +
+ "channels, track, color_depth, object_fk) SELECT " +
+ "size, mime_type, duration, width, height, class, " +
+ "author, album, date, bitrate, sample_freq, " +
+ "bits_per_sample, channels, track, color_depth, " +
+ "o.upnp_id FROM _Meta_Data JOIN object o " +
+ "ON id = o.metadata_fk");
+ database.exec ("DROP TABLE _Meta_Data");
+ database.exec (UPDATE_V3_V4_STRING_3);
+ database.exec (UPDATE_V3_V4_STRING_4);
+ database.exec (MediaCache.CREATE_TRIGGER_STRING);
+ database.exec ("UPDATE schema_info SET version = '4'");
+ database.commit ();
+ } catch (DatabaseError error) {
+ database.rollback ();
+ warning ("Database upgrade failed: %s", error.message);
+ database = null;
+ }
+ }
+
+ private void update_v4_v5 () {
+ Gee.Queue<string> queue = new LinkedList<string> ();
+ try {
+ database.begin ();
+ database.exec ("DROP TRIGGER IF EXISTS trgr_delete_children");
+ database.exec (MediaCache.CREATE_CLOSURE_TABLE);
+ // this is to have the database generate the closure table
+ database.exec ("ALTER TABLE Object RENAME TO _Object");
+ database.exec ("CREATE TABLE Object AS SELECT * FROM _Object");
+ database.exec ("DELETE FROM Object");
+ database.exec (MediaCache.CREATE_CLOSURE_TRIGGER_STRING);
+ database.exec ("INSERT INTO _Object (upnp_id, type_fk, title, " +
+ "timestamp) VALUES ('0', 0, 'Root', 0)");
+ database.exec ("INSERT INTO Object (upnp_id, type_fk, title, " +
+ "timestamp) VALUES ('0', 0, 'Root', 0)");
+
+ queue.offer ("0");
+ while (!queue.is_empty) {
+ GLib.Value[] args = { queue.poll () };
+ database.exec ("SELECT upnp_id FROM _Object WHERE parent = ?",
+ args,
+ (statement) => {
+ queue.offer (statement.column_text (0));
+
+ return true;
+ });
+
+ database.exec ("INSERT INTO Object SELECT * FROM _OBJECT " +
+ "WHERE parent = ?",
+ args);
+ }
+ database.exec ("DROP TABLE Object");
+ database.exec ("ALTER TABLE _Object RENAME TO Object");
+ // the triggers created above have been dropped automatically
+ // so we need to recreate them
+ database.exec (MediaCache.CREATE_CLOSURE_TRIGGER_STRING);
+ database.exec (MediaCache.CREATE_INDICES_STRING);
+ database.exec ("UPDATE schema_info SET version = '5'");
+ database.commit ();
+ database.exec ("VACUUM");
+ database.analyze ();
+ } catch (DatabaseError err) {
+ database.rollback ();
+ warning ("Database upgrade failed: %s", err.message);
+ database = null;
+ }
+ }
+
+ private void update_v5_v6 () {
+ try {
+ database.begin ();
+ database.exec ("DROP TABLE object_type");
+ database.exec ("ALTER TABLE Object ADD COLUMN uri TEXT");
+ database.exec ("UPDATE Object SET uri = (SELECT uri " +
+ "FROM uri WHERE Uri.object_fk == Object.upnp_id LIMIT 1)");
+ database.exec ("DROP TRIGGER IF EXISTS trgr_delete_uris");
+ database.exec ("DROP INDEX IF EXISTS idx_uri_fk");
+ database.exec ("DROP TABLE Uri");
+ database.exec ("UPDATE schema_info SET version = '6'");
+ database.commit ();
+ database.exec ("VACUUM");
+ database.analyze ();
+ } catch (DatabaseError error) {
+ database.rollback ();
+ warning ("Database upgrade failed: %s", error.message);
+ database = null;
+ }
+ }
+
+
+}
public class Rygel.MediaExport.MediaCache : Object {
private Database db;
private DBObjectFactory factory;
- private const string schema_version = "6";
- private const string CREATE_META_DATA_TABLE_STRING =
+ internal const string schema_version = "6";
+ internal const string CREATE_META_DATA_TABLE_STRING =
"CREATE TABLE meta_data (size INTEGER NOT NULL, " +
"mime_type TEXT NOT NULL, " +
"duration INTEGER, " +
"o.title ASC " +
"LIMIT ?,?";
-
private const string CHILDREN_COUNT_STRING =
"SELECT COUNT(upnp_id) FROM Object WHERE Object.parent = ?";
private const string GET_CHILD_ID_STRING =
"SELECT upnp_id FROM OBJECT WHERE parent = ?";
- private const string UPDATE_V3_V4_STRING_2 =
- "UPDATE meta_data SET object_fk = " +
- "(SELECT upnp_id FROM Object WHERE metadata_fk = meta_data.id)";
-
- private const string UPDATE_V3_V4_STRING_3 =
- "ALTER TABLE Object ADD timestamp INTEGER";
-
- private const string UPDATE_V3_V4_STRING_4 =
- "UPDATE Object SET timestamp = 0";
-
private const string GET_META_DATA_COLUMN_STRING =
"SELECT DISTINCT %s FROM meta_data AS m %s " +
"ORDER BY %s LIMIT ?,?";
private void open_db (string name) throws Error {
this.db = new Database (name);
int old_version = -1;
+ int current_version = schema_version.to_int ();
try {
- this.db.exec ("SELECT version FROM schema_info",
- null,
- (statement) => {
- old_version = statement.column_int (0);
-
- return false;
- });
- int current_version = schema_version.to_int ();
- if (old_version == current_version) {
- bool schema_ok = true;
-
- debug ("Media DB schema has current version");
- debug ("Checking for consistent schema...");
- db.exec ("SELECT count(*) FROM sqlite_master WHERE sql " +
- "LIKE 'CREATE TABLE Meta_Data%object_fk TEXT " +
- "UNIQUE%'",
- null,
- (statement) => {
- schema_ok = statement.column_int (0) == 1;
-
- return false;
- });
- if (!schema_ok) {
- try {
- message ("Found faulty schema, forcing full reindex");
- db.begin ();
- db.exec ("DELETE FROM Object WHERE upnp_id IN (" +
- "SELECT DISTINCT object_fk FROM meta_data)");
- db.exec ("DROP TABLE Meta_Data");
- db.exec (CREATE_META_DATA_TABLE_STRING);
- db.commit ();
- } catch (Error error) {
- db.rollback ();
- warning ("Failed to force reindex to fix database: " +
- error.message);
- }
- }
+ var upgrader = new MediaCacheUpgrader (this.db);
+ if (upgrader.needs_upgrade (out old_version)) {
+ upgrader.upgrade (old_version);
+ } else if (old_version == current_version) {
+ upgrader.fix_schema ();
} else {
- if (old_version < current_version) {
- debug ("Older schema detected. Upgrading...");
- while (old_version < current_version) {
- if (this.db != null) {
- switch (old_version) {
- case 3:
- update_v3_v4 ();
- break;
- case 4:
- update_v4_v5 ();
- break;
- case 5:
- update_v5_v6 ();
- break;
- default:
- warning ("Cannot upgrade");
- db = null;
- break;
- }
- old_version++;
- }
- }
- } else {
- warning ("The version \"%d\" of the detected database" +
- " is newer than our supported version \"%d\"",
- old_version,
- current_version);
- this.db = null;
+ warning ("The version \"%d\" of the detected database" +
+ " is newer than our supported version \"%d\"",
+ old_version,
+ current_version);
+ this.db = null;
- throw new MediaDBError.GENERAL_ERROR ("Database format" +
+ throw new MediaDBError.GENERAL_ERROR ("Database format" +
" not supported");
- }
}
} catch (DatabaseError error) {
debug ("Could not find schema version;" +
}
}
- private void update_v3_v4 () {
- try {
- db.begin ();
- db.exec ("ALTER TABLE Meta_Data RENAME TO _Meta_Data");
- db.exec (CREATE_META_DATA_TABLE_STRING);
- db.exec ("INSERT INTO meta_data (size, mime_type, duration, " +
- "width, height, class, author, album, date, " +
- "bitrate, sample_freq, bits_per_sample, channels, " +
- "track, color_depth, object_fk) SELECT size, " +
- "mime_type, duration, width, height, class, author, " +
- "album, date, bitrate, sample_freq, bits_per_sample, " +
- "channels, track, color_depth, o.upnp_id FROM " +
- "_Meta_Data JOIN object o ON id = o.metadata_fk");
- db.exec ("DROP TABLE _Meta_Data");
- db.exec (UPDATE_V3_V4_STRING_3);
- db.exec (UPDATE_V3_V4_STRING_4);
- db.exec (CREATE_TRIGGER_STRING);
- db.exec ("UPDATE schema_info SET version = '4'");
- db.commit ();
- } catch (DatabaseError error) {
- db.rollback ();
- warning ("Database upgrade failed: %s", error.message);
- db = null;
- }
- }
-
- private void update_v4_v5 () {
- Gee.Queue<string> queue = new LinkedList<string> ();
- try {
- db.begin ();
- db.exec ("DROP TRIGGER IF EXISTS trgr_delete_children");
- db.exec (CREATE_CLOSURE_TABLE);
- // this is to have the database generate the closure table
- db.exec ("ALTER TABLE Object RENAME TO _Object");
- db.exec ("CREATE TABLE Object AS SELECT * FROM _Object");
- db.exec ("DELETE FROM Object");
- db.exec (CREATE_CLOSURE_TRIGGER_STRING);
- db.exec ("INSERT INTO _Object (upnp_id, type_fk, title, " +
- "timestamp) VALUES ('0', 0, 'Root', 0)");
- db.exec ("INSERT INTO Object (upnp_id, type_fk, title, " +
- "timestamp) VALUES ('0', 0, 'Root', 0)");
-
- queue.offer ("0");
- while (!queue.is_empty) {
- GLib.Value[] args = { queue.poll () };
- db.exec ("SELECT upnp_id FROM _Object WHERE parent = ?",
- args,
- (statement) => {
- queue.offer (statement.column_text (0));
-
- return true;
- });
-
- db.exec ("INSERT INTO Object SELECT * FROM _OBJECT " +
- "WHERE parent = ?",
- args);
- }
- db.exec ("DROP TABLE Object");
- db.exec ("ALTER TABLE _Object RENAME TO Object");
- // the triggers created above have been dropped automatically
- // so we need to recreate them
- db.exec (CREATE_CLOSURE_TRIGGER_STRING);
- db.exec (CREATE_INDICES_STRING);
- db.exec ("UPDATE schema_info SET version = '5'");
- db.commit ();
- db.exec ("VACUUM");
- db.analyze ();
- } catch (DatabaseError err) {
- db.rollback ();
- warning ("Database upgrade failed: %s", err.message);
- db = null;
- }
- }
-
- private void update_v5_v6 () {
- try {
- db.begin ();
- db.exec ("DROP TABLE object_type");
- db.exec ("ALTER TABLE Object ADD COLUMN uri TEXT");
- db.exec ("UPDATE Object SET uri = (SELECT uri " +
- "FROM uri WHERE Uri.object_fk == Object.upnp_id LIMIT 1)");
- db.exec ("DROP TRIGGER IF EXISTS trgr_delete_uris");
- db.exec ("DROP INDEX IF EXISTS idx_uri_fk");
- db.exec ("DROP TABLE Uri");
- db.exec ("UPDATE schema_info SET version = '6'");
- db.commit ();
- db.exec ("VACUUM");
- db.analyze ();
- } catch (DatabaseError error) {
- db.rollback ();
- warning ("Database upgrade failed: %s", error.message);
- db = null;
- }
- }
-
private void update_object_internal (MediaObject object) throws Error {
GLib.Value[] values = { object.title,
(int64) object.modified,