Qlite: Return OptionalRow instead of Row?, add ability to remove columns on version upgrade
This commit is contained in:
parent
4f34e43116
commit
c2643a45b0
|
@ -121,7 +121,7 @@ public class Database : Qlite.Database {
|
||||||
public AvatarTable avatar { get; private set; }
|
public AvatarTable avatar { get; private set; }
|
||||||
public EntityFeatureTable entity_feature { get; private set; }
|
public EntityFeatureTable entity_feature { get; private set; }
|
||||||
|
|
||||||
public Database(string fileName) {
|
public Database(string fileName) throws DatabaseError {
|
||||||
base(fileName, VERSION);
|
base(fileName, VERSION);
|
||||||
account = new AccountTable(this);
|
account = new AccountTable(this);
|
||||||
jid = new JidTable(this);
|
jid = new JidTable(this);
|
||||||
|
@ -174,7 +174,7 @@ public class Database : Qlite.Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account? get_account_by_id(int id) {
|
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) {
|
if (row != null) {
|
||||||
return get_account_from_row(row);
|
return get_account_from_row(row);
|
||||||
}
|
}
|
||||||
|
@ -300,7 +300,7 @@ public class Database : Qlite.Database {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message? get_message_by_id(int id) {
|
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) {
|
if (row != null) {
|
||||||
return get_message_from_row(row);
|
return get_message_from_row(row);
|
||||||
}
|
}
|
||||||
|
@ -427,7 +427,7 @@ public class Database : Qlite.Database {
|
||||||
|
|
||||||
|
|
||||||
private int get_jid_id(Jid jid_obj) {
|
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);
|
return row != null ? row[jid.id] : add_jid(jid_obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ public class AccountSettingWidget : Plugins.AccountSettingsWidget, Box {
|
||||||
public void set_account(Account account) {
|
public void set_account(Account account) {
|
||||||
this.account = account;
|
this.account = account;
|
||||||
try {
|
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) {
|
if (row == null) {
|
||||||
fingerprint.set_markup(@"Own fingerprint\n<span font='8'>Will be generated on first connect</span>");
|
fingerprint.set_markup(@"Own fingerprint\n<span font='8'>Will be generated on first connect</span>");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class Manager : StreamInteractionModule, Object {
|
||||||
private void on_store_created(Account account, Store store) {
|
private void on_store_created(Account account, Store store) {
|
||||||
Qlite.Row? row = null;
|
Qlite.Row? row = null;
|
||||||
try {
|
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) {
|
} catch (Error e) {
|
||||||
// Ignore error
|
// Ignore error
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,15 +51,19 @@ public class Database {
|
||||||
meta_table.create_table_at_version(expected_version);
|
meta_table.create_table_at_version(expected_version);
|
||||||
long old_version = 0;
|
long old_version = 0;
|
||||||
try {
|
try {
|
||||||
Row? row = meta_table.row_with(meta_name, "version");
|
old_version = meta_table.row_with(meta_name, "version")[meta_int_val, -1];
|
||||||
old_version = row == null ? -1 : (long) row[meta_int_val];
|
|
||||||
} catch (DatabaseError e) {
|
} catch (DatabaseError e) {
|
||||||
old_version = -1;
|
old_version = -1;
|
||||||
}
|
}
|
||||||
foreach (Table t in tables) {
|
if (old_version == -1) {
|
||||||
t.create_table_at_version(old_version);
|
foreach (Table t in tables) {
|
||||||
}
|
t.create_table_at_version(expected_version);
|
||||||
if (expected_version != old_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) {
|
foreach (Table t in tables) {
|
||||||
t.add_columns_for_version(old_version, expected_version);
|
t.add_columns_for_version(old_version, expected_version);
|
||||||
}
|
}
|
||||||
|
@ -117,9 +121,9 @@ public class Database {
|
||||||
return new DeleteBuilder(this);
|
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();
|
ensure_init();
|
||||||
return new Row.RowIterator(this, sql, args);
|
return new RowIterator(this, sql, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Statement prepare(string sql) throws DatabaseError {
|
public Statement prepare(string sql) throws DatabaseError {
|
||||||
|
|
|
@ -131,20 +131,20 @@ public class QueryBuilder : StatementBuilder {
|
||||||
public int64 count() throws DatabaseError {
|
public int64 count() throws DatabaseError {
|
||||||
this.column_selector = @"COUNT($column_selector) AS count";
|
this.column_selector = @"COUNT($column_selector) AS count";
|
||||||
this.single_result = true;
|
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.");
|
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();
|
return iterator().next_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RowOption row() throws DatabaseError {
|
||||||
|
return new RowOption(row_());
|
||||||
|
}
|
||||||
|
|
||||||
public T get<T>(Column<T> field) throws DatabaseError {
|
public T get<T>(Column<T> field) throws DatabaseError {
|
||||||
Row row = row();
|
return row()[field];
|
||||||
if (row != null) {
|
|
||||||
return row[field];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Statement prepare() throws DatabaseError {
|
public override Statement prepare() throws DatabaseError {
|
||||||
|
@ -155,8 +155,8 @@ public class QueryBuilder : StatementBuilder {
|
||||||
return stmt;
|
return stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Row.RowIterator iterator() throws DatabaseError {
|
public RowIterator iterator() throws DatabaseError {
|
||||||
return new Row.RowIterator.from_query_builder(this);
|
return new RowIterator.from_query_builder(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
class OrderingTerm {
|
class OrderingTerm {
|
||||||
|
|
|
@ -50,29 +50,46 @@ public class Row {
|
||||||
public bool has_real(string field) {
|
public bool has_real(string field) {
|
||||||
return real_map.has_key(field) && real_map[field] != null;
|
return real_map.has_key(field) && real_map[field] != null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class RowIterator {
|
public class RowIterator {
|
||||||
private Statement stmt;
|
private Statement stmt;
|
||||||
|
|
||||||
public RowIterator.from_query_builder(QueryBuilder query) throws DatabaseError {
|
public RowIterator.from_query_builder(QueryBuilder query) throws DatabaseError {
|
||||||
this.stmt = query.prepare();
|
this.stmt = query.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RowIterator(Database db, string sql, string[]? args = null) throws DatabaseError {
|
public RowIterator(Database db, string sql, string[]? args = null) throws DatabaseError {
|
||||||
this.stmt = db.prepare(sql);
|
this.stmt = db.prepare(sql);
|
||||||
if (args != null) {
|
if (args != null) {
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
stmt.bind_text(i, sql, sql.length);
|
stmt.bind_text(i, sql, sql.length);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Row? next_value() {
|
public Row? next_value() {
|
||||||
if (stmt.step() == Sqlite.ROW) {
|
if (stmt.step() == Sqlite.ROW) {
|
||||||
return new Row(stmt);
|
return new Row(stmt);
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
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<T>(Column<T> field, T def = null) {
|
||||||
|
if (inner == null || field.is_null(inner)) return def;
|
||||||
|
return field[inner];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class Table {
|
||||||
if (constraints == null) constraints = ""; else constraints += ", ";
|
if (constraints == null) constraints = ""; else constraints += ", ";
|
||||||
constraints += "UNIQUE (";
|
constraints += "UNIQUE (";
|
||||||
bool first = true;
|
bool first = true;
|
||||||
foreach(Column c in columns) {
|
foreach (Column c in columns) {
|
||||||
if (!first) constraints += ", ";
|
if (!first) constraints += ", ";
|
||||||
constraints += c.name;
|
constraints += c.name;
|
||||||
first = false;
|
first = false;
|
||||||
|
@ -57,7 +57,7 @@ public class Table {
|
||||||
return db.delete().from(this);
|
return db.delete().from(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Row? row_with<T>(Column<T> column, T value) throws DatabaseError {
|
public RowOption row_with<T>(Column<T> column, T value) throws DatabaseError {
|
||||||
ensure_init();
|
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()");
|
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();
|
return select().with(column, "=", value).row();
|
||||||
|
@ -74,7 +74,7 @@ public class Table {
|
||||||
public void create_table_at_version(long version) throws DatabaseError {
|
public void create_table_at_version(long version) throws DatabaseError {
|
||||||
ensure_init();
|
ensure_init();
|
||||||
string sql = @"CREATE TABLE IF NOT EXISTS $name (";
|
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];
|
Column c = columns[i];
|
||||||
if (c.min_version <= version && c.max_version >= version) {
|
if (c.min_version <= version && c.max_version >= version) {
|
||||||
sql += @"$(i > 0 ? "," : "") $c";
|
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 {
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue