From c2643a45b0dc05c4fd82ec7d32577700dae7450e Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 12 Mar 2017 19:33:31 +0100 Subject: [PATCH] Qlite: Return OptionalRow instead of Row?, add ability to remove columns on version upgrade --- libdino/src/service/database.vala | 8 +-- .../omemo/src/account_settings_widget.vala | 2 +- plugins/omemo/src/manager.vala | 2 +- qlite/src/database.vala | 20 +++++--- qlite/src/query_builder.vala | 18 +++---- qlite/src/row.vala | 49 +++++++++++++------ qlite/src/table.vala | 27 ++++++++-- 7 files changed, 83 insertions(+), 43 deletions(-) diff --git a/libdino/src/service/database.vala b/libdino/src/service/database.vala index b50581f8..885a1a8f 100644 --- a/libdino/src/service/database.vala +++ b/libdino/src/service/database.vala @@ -121,7 +121,7 @@ public class Database : Qlite.Database { public AvatarTable avatar { get; private set; } public EntityFeatureTable entity_feature { get; private set; } - public Database(string fileName) { + public Database(string fileName) throws DatabaseError { base(fileName, VERSION); account = new AccountTable(this); jid = new JidTable(this); @@ -174,7 +174,7 @@ public class Database : Qlite.Database { } private Account? get_account_by_id(int id) { - Row? row = account.row_with(account.id, id); + Row? row = account.row_with(account.id, id).inner; if (row != null) { return get_account_from_row(row); } @@ -300,7 +300,7 @@ public class Database : Qlite.Database { } public Message? get_message_by_id(int id) { - Row? row = message.row_with(message.id, id); + Row? row = message.row_with(message.id, id).inner; if (row != null) { return get_message_from_row(row); } @@ -427,7 +427,7 @@ public class Database : Qlite.Database { private int get_jid_id(Jid jid_obj) { - Row? row = jid.row_with(jid.bare_jid, jid_obj.bare_jid.to_string()); + Row? row = jid.row_with(jid.bare_jid, jid_obj.bare_jid.to_string()).inner; return row != null ? row[jid.id] : add_jid(jid_obj); } diff --git a/plugins/omemo/src/account_settings_widget.vala b/plugins/omemo/src/account_settings_widget.vala index 87ea0e37..b922c9a6 100644 --- a/plugins/omemo/src/account_settings_widget.vala +++ b/plugins/omemo/src/account_settings_widget.vala @@ -30,7 +30,7 @@ public class AccountSettingWidget : Plugins.AccountSettingsWidget, Box { public void set_account(Account account) { this.account = account; try { - Qlite.Row? row = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id); + Qlite.Row? row = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id).inner; if (row == null) { fingerprint.set_markup(@"Own fingerprint\nWill be generated on first connect"); } else { diff --git a/plugins/omemo/src/manager.vala b/plugins/omemo/src/manager.vala index e5db631e..b2226e2a 100644 --- a/plugins/omemo/src/manager.vala +++ b/plugins/omemo/src/manager.vala @@ -99,7 +99,7 @@ public class Manager : StreamInteractionModule, Object { private void on_store_created(Account account, Store store) { Qlite.Row? row = null; try { - row = db.identity.row_with(db.identity.account_id, account.id); + row = db.identity.row_with(db.identity.account_id, account.id).inner; } catch (Error e) { // Ignore error } diff --git a/qlite/src/database.vala b/qlite/src/database.vala index 6d26b2b4..9fea7708 100644 --- a/qlite/src/database.vala +++ b/qlite/src/database.vala @@ -51,15 +51,19 @@ public class Database { meta_table.create_table_at_version(expected_version); long old_version = 0; try { - Row? row = meta_table.row_with(meta_name, "version"); - old_version = row == null ? -1 : (long) row[meta_int_val]; + old_version = meta_table.row_with(meta_name, "version")[meta_int_val, -1]; } catch (DatabaseError e) { old_version = -1; } - foreach (Table t in tables) { - t.create_table_at_version(old_version); - } - if (expected_version != old_version) { + if (old_version == -1) { + foreach (Table t in tables) { + t.create_table_at_version(expected_version); + } + meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform(); + } else if (expected_version != old_version) { + foreach (Table t in tables) { + t.create_table_at_version(old_version); + } foreach (Table t in tables) { t.add_columns_for_version(old_version, expected_version); } @@ -117,9 +121,9 @@ public class Database { return new DeleteBuilder(this); } - public Row.RowIterator query_sql(string sql, string[]? args = null) throws DatabaseError { + public RowIterator query_sql(string sql, string[]? args = null) throws DatabaseError { ensure_init(); - return new Row.RowIterator(this, sql, args); + return new RowIterator(this, sql, args); } public Statement prepare(string sql) throws DatabaseError { diff --git a/qlite/src/query_builder.vala b/qlite/src/query_builder.vala index 65cbb8f6..37f9b261 100644 --- a/qlite/src/query_builder.vala +++ b/qlite/src/query_builder.vala @@ -131,20 +131,20 @@ public class QueryBuilder : StatementBuilder { public int64 count() throws DatabaseError { this.column_selector = @"COUNT($column_selector) AS count"; this.single_result = true; - return row().get_integer("count"); + return row_().get_integer("count"); } - public Row? row() throws DatabaseError { + private Row? row_() throws DatabaseError { if (!single_result) throw new DatabaseError.NON_UNIQUE("query is not suited to return a single row, but row() was called."); return iterator().next_value(); } + public RowOption row() throws DatabaseError { + return new RowOption(row_()); + } + public T get(Column field) throws DatabaseError { - Row row = row(); - if (row != null) { - return row[field]; - } - return null; + return row()[field]; } public override Statement prepare() throws DatabaseError { @@ -155,8 +155,8 @@ public class QueryBuilder : StatementBuilder { return stmt; } - public Row.RowIterator iterator() throws DatabaseError { - return new Row.RowIterator.from_query_builder(this); + public RowIterator iterator() throws DatabaseError { + return new RowIterator.from_query_builder(this); } class OrderingTerm { diff --git a/qlite/src/row.vala b/qlite/src/row.vala index ff98405f..de10751f 100644 --- a/qlite/src/row.vala +++ b/qlite/src/row.vala @@ -50,29 +50,46 @@ public class Row { public bool has_real(string field) { return real_map.has_key(field) && real_map[field] != null; } +} - public class RowIterator { - private Statement stmt; +public class RowIterator { + private Statement stmt; - public RowIterator.from_query_builder(QueryBuilder query) throws DatabaseError { - this.stmt = query.prepare(); - } + public RowIterator.from_query_builder(QueryBuilder query) throws DatabaseError { + this.stmt = query.prepare(); + } - public RowIterator(Database db, string sql, string[]? args = null) throws DatabaseError { - this.stmt = db.prepare(sql); - if (args != null) { - for (int i = 0; i < args.length; i++) { - stmt.bind_text(i, sql, sql.length); - } + public RowIterator(Database db, string sql, string[]? args = null) throws DatabaseError { + this.stmt = db.prepare(sql); + if (args != null) { + for (int i = 0; i < args.length; i++) { + stmt.bind_text(i, sql, sql.length); } } + } - public Row? next_value() { - if (stmt.step() == Sqlite.ROW) { - return new Row(stmt); - } - return null; + public Row? next_value() { + if (stmt.step() == Sqlite.ROW) { + return new Row(stmt); } + return null; + } +} + +public class RowOption { + public Row? inner { get; private set; } + + public RowOption(Row? row) { + this.inner = row; + } + + public bool is_present() { + return inner != null; + } + + public T get(Column field, T def = null) { + if (inner == null || field.is_null(inner)) return def; + return field[inner]; } } diff --git a/qlite/src/table.vala b/qlite/src/table.vala index 7396136e..357e18d4 100644 --- a/qlite/src/table.vala +++ b/qlite/src/table.vala @@ -22,7 +22,7 @@ public class Table { if (constraints == null) constraints = ""; else constraints += ", "; constraints += "UNIQUE ("; bool first = true; - foreach(Column c in columns) { + foreach (Column c in columns) { if (!first) constraints += ", "; constraints += c.name; first = false; @@ -57,7 +57,7 @@ public class Table { return db.delete().from(this); } - public Row? row_with(Column column, T value) throws DatabaseError { + public RowOption row_with(Column column, T value) throws DatabaseError { ensure_init(); if (!column.unique && !column.primary_key) throw new DatabaseError.NON_UNIQUE(@"$(column.name) is not suited to identify a row, but used with row_with()"); return select().with(column, "=", value).row(); @@ -74,7 +74,7 @@ public class Table { public void create_table_at_version(long version) throws DatabaseError { ensure_init(); string sql = @"CREATE TABLE IF NOT EXISTS $name ("; - for(int i = 0; i < columns.length; i++) { + for (int i = 0; i < columns.length; i++) { Column c = columns[i]; if (c.min_version <= version && c.max_version >= version) { sql += @"$(i > 0 ? "," : "") $c"; @@ -97,7 +97,26 @@ public class Table { } public void delete_columns_for_version(long old_version, long new_version) throws DatabaseError { - // TODO: Rename old table, create table at new_version, transfer data + bool column_deletion_required = false; + string column_list = null; + foreach (Column c in columns) { + if (c.min_version <= new_version && c.max_version >= new_version) { + if (column_list == null) { + column_list = c.name; + } else { + column_list += ", " + c.name; + } + } + if (!(c.min_version <= new_version && c.max_version >= new_version) && c.min_version <= old_version && c.max_version >= old_version) { + column_deletion_required = true; + } + } + if (column_deletion_required) { + db.exec(@"ALTER TABLE $name RENAME TO _$(name)_$old_version"); + create_table_at_version(new_version); + db.exec(@"INSERT INTO $name ($column_list) SELECT $column_list FROM _$(name)_$old_version"); + db.exec(@"DROP TABLE _$(name)_$old_version"); + } } }