From 98ef191bfb3cc5694b9d1001e46a9b00abbd7195 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 23 Aug 2026 19:32:25 +0200 Subject: Move qlite into libdino --- libdino/src/qlite/table.vala | 218 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 libdino/src/qlite/table.vala (limited to 'libdino/src/qlite/table.vala') diff --git a/libdino/src/qlite/table.vala b/libdino/src/qlite/table.vala new file mode 100644 index 00000000..3b75e22e --- /dev/null +++ b/libdino/src/qlite/table.vala @@ -0,0 +1,218 @@ +using Sqlite; + +namespace Dino.Qlite { + +public class Table { + protected Database db; + public string name { get; private set; } + protected Column[]? columns; + private string constraints = ""; + private string[] post_statements = {}; + private string[] create_statements = {}; + internal Column[]? fts_columns; + + public Table(Database db, string name) { + this.db = db; + this.name = name; + } + + public void init(Column[] columns, string constraints = "") { + this.columns = columns; + this.constraints = constraints; + + foreach(Column c in columns) { + c.table = this; + } + } + + public void fts(Column[] columns) { + if (fts_columns != null) error("Only one FTS index may be used per table."); + fts_columns = columns; + string cs = ""; + string cnames = ""; + string cnews = ""; + foreach (Column c in columns) { + cs += @", $(c.to_column_definition())"; + cnames += @", $(c.name)"; + cnews += @", new.$(c.name)"; + } + add_create_statement(@"CREATE VIRTUAL TABLE IF NOT EXISTS _fts_$name USING fts4(tokenize=unicode61, content=\"$name\"$cs)"); + add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bu_$(name) BEFORE UPDATE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); + add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bd_$(name) BEFORE DELETE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); + add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_au_$(name) AFTER UPDATE ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); + add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_ai_$(name) AFTER INSERT ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); + } + + public void fts_rebuild() { + if (fts_columns == null) error("FTS not available on this table."); + try { + db.exec(@"INSERT INTO _fts_$name(_fts_$name) VALUES('rebuild');"); + } catch (Error e) { + critical(@"Qlite Error: Rebuilding FTS index: $(e.message)"); + } + } + + public void unique(Column[] columns, string? on_conflict = null) { + constraints += ", UNIQUE ("; + bool first = true; + foreach (Column c in columns) { + if (!first) constraints += ", "; + constraints += c.name; + first = false; + } + constraints += ")"; + if (on_conflict != null) { + constraints += " ON CONFLICT " + (!)on_conflict; + } + } + + public void add_post_statement(string stmt) { + post_statements += stmt; + } + + public void add_create_statement(string stmt) { + create_statements += stmt; + } + + public void index(string index_name, Column[] columns, bool unique = false) { + string stmt = @"CREATE $(unique ? "UNIQUE" : "") INDEX IF NOT EXISTS $index_name ON $name ("; + bool first = true; + foreach (Column c in columns) { + if (!first) stmt += ", "; + stmt += c.name; + first = false; + } + stmt += ")"; + add_post_statement(stmt); + } + + private void ensure_init() { + if (columns == null) error("Table %s was not initialized, call init()", name); + } + + public QueryBuilder select(Column[]? columns = null) { + ensure_init(); + return db.select(columns).from(this); + } + + private MatchQueryBuilder match_query() { + ensure_init(); + return db.match_query(this); + } + + public MatchQueryBuilder match(Column column, string query) { + return match_query().match(column, query); + } + + public InsertBuilder insert() { + ensure_init(); + return db.insert().into(this); + } + + public UpdateBuilder update() { + ensure_init(); + return db.update(this); + } + + public UpsertBuilder upsert() { + ensure_init(); + return db.upsert(this); + } + + public DeleteBuilder delete() { + ensure_init(); + return db.delete().from(this); + } + + public RowOption row_with(Column column, T value) { + ensure_init(); + if (!column.unique && !column.primary_key) error("%s is not suited to identify a row, but used with row_with()", column.name); + return select().with(column, "=", value).row(); + } + + public bool is_known_column(string column) { + ensure_init(); + foreach (Column c in columns) { + if (c.name == column) return true; + } + return false; + } + + public void create_table_at_version(long version) { + ensure_init(); + string sql = @"CREATE TABLE IF NOT EXISTS $name ("; + bool first = true; + for (int i = 0; i < columns.length; i++) { + Column c = columns[i]; + if (c.min_version <= version && c.max_version >= version) { + sql += @"$(!first ? "," : "") $(c.to_column_definition())"; + first = false; + } + } + sql += @"$constraints)"; + try { + db.exec(sql); + } catch (Error e) { + error(@"Qlite Error: Create table at version: $(e.message)"); + } + foreach (string stmt in create_statements) { + try { + db.exec(stmt); + } catch (Error e) { + error(@"Qlite Error: Create table at version: $(e.message)"); + } + } + } + + public void add_columns_for_version(long old_version, long new_version) { + ensure_init(); + foreach (Column c in columns) { + if (c.min_version <= new_version && c.max_version >= new_version && c.min_version > old_version) { + try { + db.exec(@"ALTER TABLE $name ADD COLUMN $(c.to_column_definition())"); + } catch (Error e) { + critical(@"Qlite Error: Add columns for version: $(e.message)"); + } + } + } + } + + public void delete_columns_for_version(long old_version, long new_version) { + bool column_deletion_required = false; + string column_list = ""; + foreach (Column c in columns) { + if (c.min_version <= new_version && c.max_version >= new_version) { + if (column_list == "") { + 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) { + try { + 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"); + } catch (Error e) { + error(@"Qlite Error: Delete columns for version change: $(e.message)"); + } + } + } + + internal void post() { + foreach (string stmt in post_statements) { + try { + db.exec(stmt); + } catch (Error e) { + error(@"Qlite Error: Post: $(e.message)"); + } + } + } +} + +} -- cgit v1.3