2017-03-02 14:37:32 +00:00
|
|
|
using Sqlite;
|
|
|
|
|
|
|
|
namespace Qlite {
|
|
|
|
|
|
|
|
public class Database {
|
|
|
|
private string file_name;
|
|
|
|
private Sqlite.Database db;
|
|
|
|
private long expected_version;
|
2017-04-16 13:11:00 +00:00
|
|
|
private Table[]? tables;
|
2017-03-02 14:37:32 +00:00
|
|
|
|
2017-04-16 13:11:00 +00:00
|
|
|
private Column<string?> meta_name = new Column.Text("name") { primary_key = true };
|
2017-03-02 14:37:32 +00:00
|
|
|
private Column<long> meta_int_val = new Column.Long("int_val");
|
2017-04-16 13:11:00 +00:00
|
|
|
private Column<string?> meta_text_val = new Column.Text("text_val");
|
2017-03-02 14:37:32 +00:00
|
|
|
private Table meta_table;
|
|
|
|
|
|
|
|
public bool debug = false;
|
|
|
|
|
|
|
|
public Database(string file_name, long expected_version) {
|
|
|
|
this.file_name = file_name;
|
|
|
|
this.expected_version = expected_version;
|
|
|
|
meta_table = new Table(this, "_meta");
|
|
|
|
meta_table.init({meta_name, meta_int_val, meta_text_val});
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public void init(Table[] tables) {
|
2017-03-02 14:37:32 +00:00
|
|
|
Sqlite.config(Config.SERIALIZED);
|
|
|
|
int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000);
|
|
|
|
if (ec != Sqlite.OK) {
|
2017-10-28 21:48:07 +00:00
|
|
|
error(@"SQLite error: %d - %s", db.errcode(), db.errmsg());
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
this.tables = tables;
|
2017-08-25 22:05:36 +00:00
|
|
|
if (debug) db.trace((message) => print(@"Qlite trace: $message\n"));
|
2020-03-10 22:53:11 +00:00
|
|
|
start_migration();
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public void ensure_init() {
|
|
|
|
if (tables == null) error(@"Database $file_name was not initialized, call init()");
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
private void start_migration() {
|
2019-12-15 18:03:02 +00:00
|
|
|
try {
|
|
|
|
exec("BEGIN TRANSACTION");
|
|
|
|
} catch (Error e) {
|
|
|
|
error("SQLite error: %d - %s", db.errcode(), db.errmsg());
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
meta_table.create_table_at_version(expected_version);
|
|
|
|
long old_version = 0;
|
2017-10-28 21:48:07 +00:00
|
|
|
old_version = meta_table.row_with(meta_name, "version")[meta_int_val, -1];
|
2017-03-12 18:33:31 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
foreach (Table t in tables) {
|
|
|
|
t.add_columns_for_version(old_version, expected_version);
|
|
|
|
}
|
|
|
|
migrate(old_version);
|
|
|
|
foreach (Table t in tables) {
|
|
|
|
t.delete_columns_for_version(old_version, expected_version);
|
|
|
|
}
|
|
|
|
if (old_version == -1) {
|
|
|
|
meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform();
|
|
|
|
} else {
|
|
|
|
meta_table.update().with(meta_name, "=", "version").set(meta_int_val, expected_version).perform();
|
|
|
|
}
|
|
|
|
}
|
2017-04-23 08:23:11 +00:00
|
|
|
foreach (Table t in tables) {
|
|
|
|
t.post();
|
|
|
|
}
|
2019-12-15 18:03:02 +00:00
|
|
|
try {
|
|
|
|
exec("END TRANSACTION");
|
|
|
|
} catch (Error e) {
|
|
|
|
error("SQLite error: %d - %s", db.errcode(), db.errmsg());
|
|
|
|
}
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
internal int errcode() {
|
|
|
|
return db.errcode();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal string errmsg() {
|
|
|
|
return db.errmsg();
|
|
|
|
}
|
|
|
|
|
|
|
|
internal int64 last_insert_rowid() {
|
|
|
|
return db.last_insert_rowid();
|
|
|
|
}
|
|
|
|
|
|
|
|
// To be implemented by actual implementation if required
|
|
|
|
// new table columns are added, outdated columns are still present and will be removed afterwards
|
2017-10-28 21:48:07 +00:00
|
|
|
public virtual void migrate(long old_version) {
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public QueryBuilder select(Column[]? columns = null) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
return new QueryBuilder(this).select(columns);
|
|
|
|
}
|
|
|
|
|
2018-06-17 23:47:43 +00:00
|
|
|
internal MatchQueryBuilder match_query(Table table) {
|
|
|
|
ensure_init();
|
|
|
|
return new MatchQueryBuilder(this, table);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public InsertBuilder insert() {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
return new InsertBuilder(this);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public UpdateBuilder update(Table table) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
return new UpdateBuilder(this, table);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public UpsertBuilder upsert(Table table) {
|
2017-08-25 22:05:36 +00:00
|
|
|
ensure_init();
|
|
|
|
return new UpsertBuilder(this, table);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public UpdateBuilder update_named(string table) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
return new UpdateBuilder.for_name(this, table);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public DeleteBuilder delete() {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
return new DeleteBuilder(this);
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public RowIterator query_sql(string sql, string[]? args = null) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
2017-03-12 18:33:31 +00:00
|
|
|
return new RowIterator(this, sql, args);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
internal Statement prepare(string sql) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
Sqlite.Statement statement;
|
|
|
|
if (db.prepare_v2(sql, sql.length, out statement) != OK) {
|
2020-06-04 23:28:31 +00:00
|
|
|
error("SQLite error: %d - %s: %s", db.errcode(), db.errmsg(), sql);
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
return statement;
|
|
|
|
}
|
|
|
|
|
2017-10-29 14:15:28 +00:00
|
|
|
public void exec(string sql) throws Error {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
if (db.exec(sql) != OK) {
|
2017-11-01 17:49:53 +00:00
|
|
|
throw new Error(-1, 0, "SQLite error: %d - %s", db.errcode(), db.errmsg());
|
2017-03-02 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
public bool is_known_column(string table, string field) {
|
2017-03-02 14:37:32 +00:00
|
|
|
ensure_init();
|
|
|
|
foreach (Table t in tables) {
|
|
|
|
if (t.is_known_column(field)) return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-28 21:48:07 +00:00
|
|
|
}
|