diff --git a/qlite/src/column.vala b/qlite/src/column.vala index 048446fb..9c201885 100644 --- a/qlite/src/column.vala +++ b/qlite/src/column.vala @@ -4,12 +4,12 @@ namespace Qlite { public abstract class Column { public string name { get; private set; } - public string default { get; set; } + public string? default { get; set; } public int sqlite_type { get; private set; } public bool primary_key { get; set; } public bool auto_increment { get; set; } public bool unique { get; set; } - public bool not_null { get; set; } + public virtual bool not_null { get; set; } public long min_version { get; set; default = -1; } public long max_version { get; set; default = long.MAX; } @@ -43,7 +43,7 @@ public abstract class Column { } if (not_null) res += " NOT NULL"; if (unique) res += " UNIQUE"; - if (default != null) res += @" DEFAULT $default"; + if (default != null) res += @" DEFAULT $((!) default)"; return res; } @@ -122,13 +122,33 @@ public abstract class Column { internal override void bind(Statement stmt, int index, string? value) { if (value != null) { - stmt.bind_text(index, value); + stmt.bind_text(index, (!) value); } else { stmt.bind_null(index); } } } + public class NonNullText : Column { + public NonNullText(string name) { + base(name, TEXT); + } + + public override bool not_null { get { return true; } set {} } + + public override string get(Row row) { + return (!)row.get_text(name); + } + + public override bool is_null(Row row) { + return false; + } + + internal override void bind(Statement stmt, int index, string value) { + stmt.bind_text(index, (!) value); + } + } + public class BoolText : Column { public BoolText(string name) { base(name, TEXT); diff --git a/qlite/src/database.vala b/qlite/src/database.vala index 89eda204..0427df16 100644 --- a/qlite/src/database.vala +++ b/qlite/src/database.vala @@ -17,11 +17,11 @@ public class Database { private string file_name; private Sqlite.Database db; private long expected_version; - private Table[] tables; + private Table[]? tables; - private Column meta_name = new Column.Text("name") { primary_key = true }; + private Column meta_name = new Column.Text("name") { primary_key = true }; private Column meta_int_val = new Column.Long("int_val"); - private Column meta_text_val = new Column.Text("text_val"); + private Column meta_text_val = new Column.Text("text_val"); private Table meta_table; public bool debug = false; diff --git a/qlite/src/delete_builder.vala b/qlite/src/delete_builder.vala index 8e4afb06..5483b652 100644 --- a/qlite/src/delete_builder.vala +++ b/qlite/src/delete_builder.vala @@ -5,12 +5,12 @@ namespace Qlite { public class DeleteBuilder : StatementBuilder { // DELETE FROM [...] - private Table table; + private Table? table; private string table_name; // WHERE [...] - private string selection; - private StatementBuilder.Field[] selection_args; + private string selection = "1"; + private StatementBuilder.AbstractField[] selection_args = {}; internal DeleteBuilder(Database db) { base(db); @@ -29,35 +29,22 @@ public class DeleteBuilder : StatementBuilder { } public DeleteBuilder where(string selection, string[]? selection_args = null) throws DatabaseError { - if (this.selection != null) throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); + if (this.selection != "1") throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); this.selection = selection; - if (selection_args != null) { - this.selection_args = new StatementBuilder.Field[selection_args.length]; - for (int i = 0; i < selection_args.length; i++) { - this.selection_args[i] = new StatementBuilder.StringField(selection_args[i]); - } + foreach (string arg in selection_args) { + this.selection_args += new StatementBuilder.StringField(arg); } return this; } public DeleteBuilder with(Column column, string comp, T value) { - if (selection == null) { - selection = @"$(column.name) $comp ?"; - selection_args = { new StatementBuilder.Field(column, value) }; - } else { - selection = @"($selection) AND $(column.name) $comp ?"; - StatementBuilder.Field[] selection_args_new = new StatementBuilder.Field[selection_args.length+1]; - for (int i = 0; i < selection_args.length; i++) { - selection_args_new[i] = selection_args[i]; - } - selection_args_new[selection_args.length] = new Field(column, value); - selection_args = selection_args_new; - } + selection_args += new Field(column, value); + selection = @"($selection) AND $(column.name) $comp ?"; return this; } internal override Statement prepare() throws DatabaseError { - Statement stmt = db.prepare(@"DELETE FROM $table_name $(selection != null ? @"WHERE $selection": "")"); + Statement stmt = db.prepare(@"DELETE FROM $table_name WHERE $selection"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } diff --git a/qlite/src/insert_builder.vala b/qlite/src/insert_builder.vala index 51030294..91388f69 100644 --- a/qlite/src/insert_builder.vala +++ b/qlite/src/insert_builder.vala @@ -6,14 +6,14 @@ public class InsertBuilder : StatementBuilder { // INSERT [OR ...] private bool replace_val; - private string or_val; + private string? or_val; // INTO [...] private Table table; private string table_name; // VALUES [...] - private StatementBuilder.Field[] fields; + private StatementBuilder.AbstractField[] fields = {}; internal InsertBuilder(Database db) { base(db); @@ -41,31 +41,13 @@ public class InsertBuilder : StatementBuilder { } public InsertBuilder value(Column column, T value) { - if (fields == null) { - fields = { new StatementBuilder.Field(column, value) }; - } else { - StatementBuilder.Field[] fields_new = new StatementBuilder.Field[fields.length+1]; - for (int i = 0; i < fields.length; i++) { - fields_new[i] = fields[i]; - } - fields_new[fields.length] = new Field(column, value); - fields = fields_new; - } + fields += new Field(column, value); return this; } public InsertBuilder value_null(Column column) throws DatabaseError { if (column.not_null) throw new DatabaseError.ILLEGAL_QUERY(@"Can't set non-null column $(column.name) to null"); - if (fields == null) { - fields = { new NullField(column) }; - } else { - StatementBuilder.Field[] fields_new = new StatementBuilder.Field[fields.length+1]; - for (int i = 0; i < fields.length; i++) { - fields_new[i] = fields[i]; - } - fields_new[fields.length] = new NullField(column); - fields = fields_new; - } + fields += new NullField(column); return this; } @@ -77,11 +59,11 @@ public class InsertBuilder : StatementBuilder { value_qs += ", "; fields_text += ", "; } - fields_text += fields[i].column.name; + fields_text += ((!)fields[i].column).name; value_qs += "?"; } string sql = replace_val ? "REPLACE" : "INSERT"; - if (!replace_val && or_val != null) sql += @" OR $or_val"; + if (!replace_val && or_val != null) sql += @" OR $((!)or_val)"; sql += @" INTO $table_name ( $fields_text ) VALUES ($value_qs)"; Statement stmt = db.prepare(sql); for (int i = 0; i < fields.length; i++) { diff --git a/qlite/src/query_builder.vala b/qlite/src/query_builder.vala index 06232fc1..f46fe98c 100644 --- a/qlite/src/query_builder.vala +++ b/qlite/src/query_builder.vala @@ -7,18 +7,18 @@ public class QueryBuilder : StatementBuilder { // SELECT [...] private string column_selector = "*"; - private Column[] columns; + private Column[] columns = {}; // FROM [...] - private Table table; - private string table_name; + private Table? table; + private string? table_name; // WHERE [...] - private string selection; - private StatementBuilder.Field[] selection_args; + private string selection = "1"; + private StatementBuilder.AbstractField[] selection_args = {}; // ORDER BY [...] - private OrderingTerm[] order_by_terms; + private OrderingTerm[]? order_by_terms = {}; // LIMIT [...] private int limit_val; @@ -27,9 +27,9 @@ public class QueryBuilder : StatementBuilder { base(db); } - public QueryBuilder select(Column[]? columns = null) { + public QueryBuilder select(Column[] columns = {}) { this.columns = columns; - if (columns != null) { + if (columns.length == 0) { for (int i = 0; i < columns.length; i++) { if (column_selector == "*") { column_selector = columns[0].name; @@ -44,7 +44,7 @@ public class QueryBuilder : StatementBuilder { } public QueryBuilder select_string(string column_selector) { - this.columns = null; + this.columns = {}; this.column_selector = column_selector; return this; } @@ -61,32 +61,19 @@ public class QueryBuilder : StatementBuilder { return this; } - public QueryBuilder where(string selection, string[]? selection_args = null) throws DatabaseError { - if (this.selection != null) throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); + public QueryBuilder where(string selection, string[] selection_args = {}) throws DatabaseError { + if (this.selection != "1") throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); this.selection = selection; - if (selection_args != null) { - this.selection_args = new StatementBuilder.Field[selection_args.length]; - for (int i = 0; i < selection_args.length; i++) { - this.selection_args[i] = new StatementBuilder.StringField(selection_args[i]); - } + foreach (string arg in selection_args) { + this.selection_args += new StatementBuilder.StringField(arg); } return this; } public QueryBuilder with(Column column, string comp, T value) { if ((column.unique || column.primary_key) && comp == "=") single_result = true; - if (selection == null) { - selection = @"$(column.name) $comp ?"; - selection_args = { new StatementBuilder.Field(column, value) }; - } else { - selection = @"($selection) AND $(column.name) $comp ?"; - StatementBuilder.Field[] selection_args_new = new StatementBuilder.Field[selection_args.length+1]; - for (int i = 0; i < selection_args.length; i++) { - selection_args_new[i] = selection_args[i]; - } - selection_args_new[selection_args.length] = new Field(column, value); - selection_args = selection_args_new; - } + selection_args += new Field(column, value); + selection = @"($selection) AND $(column.name) $comp ?"; return this; } @@ -100,26 +87,13 @@ public class QueryBuilder : StatementBuilder { return this; } - private void add_order_by(OrderingTerm term) { - if (order_by_terms == null) { - order_by_terms = { term }; - } else { - OrderingTerm[] order_by_terms_new = new OrderingTerm[order_by_terms.length+1]; - for (int i = 0; i < order_by_terms.length; i++) { - order_by_terms_new[i] = order_by_terms[i]; - } - order_by_terms_new[order_by_terms.length] = term; - order_by_terms = order_by_terms_new; - } - } - public QueryBuilder order_by(Column column, string dir = "ASC") { - add_order_by(new OrderingTerm(column, dir)); + order_by_terms += new OrderingTerm(column, dir); return this; } public QueryBuilder order_by_name(string name, string dir) { - add_order_by(new OrderingTerm.by_name(name, dir)); + order_by_terms += new OrderingTerm.by_name(name, dir); return this; } @@ -131,12 +105,12 @@ 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"); } 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(); + return iterator().get_next(); } public RowOption row() throws DatabaseError { @@ -148,7 +122,7 @@ public class QueryBuilder : StatementBuilder { } internal override Statement prepare() throws DatabaseError { - Statement stmt = db.prepare(@"SELECT $column_selector FROM $table_name $(selection != null ? @"WHERE $selection" : "") $(order_by_terms != null ? OrderingTerm.all_to_string(order_by_terms) : "") $(limit_val > 0 ? @" LIMIT $limit_val" : "")"); + Statement stmt = db.prepare(@"SELECT $column_selector $(table_name == null ? "" : @"FROM $((!) table_name)") WHERE $selection $(OrderingTerm.all_to_string(order_by_terms)) $(limit_val > 0 ? @" LIMIT $limit_val" : "")"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } @@ -179,8 +153,8 @@ public class QueryBuilder : StatementBuilder { return @"$column_name $dir"; } - public static string all_to_string(OrderingTerm[] terms) { - if (terms.length == 0) return ""; + public static string all_to_string(OrderingTerm[]? terms) { + if (terms == null || terms.length == 0) return ""; string res = "ORDER BY "+terms[0].to_string(); for (int i = 1; i < terms.length; i++) { res += @", $(terms[i])"; diff --git a/qlite/src/row.vala b/qlite/src/row.vala index 96762be3..8854656f 100644 --- a/qlite/src/row.vala +++ b/qlite/src/row.vala @@ -4,7 +4,7 @@ using Sqlite; namespace Qlite { public class Row { - private Map text_map = new HashMap(); + private Map text_map = new HashMap(); private Map int_map = new HashMap(); private Map real_map = new HashMap(); @@ -43,8 +43,8 @@ public class Row { return int_map.has_key(field); } - public double get_real(string field) { - return real_map[field]; + public double get_real(string field, double def = 0) { + return real_map[field] ?? def; } public bool has_real(string field) { @@ -71,11 +71,21 @@ public class RowIterator { } } - public Row? next_value() throws DatabaseError { + public bool next() { int r = stmt.step(); - if (r == Sqlite.ROW) return new Row(stmt); - if (r == Sqlite.DONE) return null; - throw new DatabaseError.EXEC_ERROR(@"SQLite error: $(db.errcode()) - $(db.errmsg())"); + if (r == Sqlite.ROW) return true; + if (r == Sqlite.DONE) return false; + print(@"SQLite error: $(db.errcode()) - $(db.errmsg())\n"); + return false; + } + + public Row get() { + return new Row(stmt); + } + + public Row? get_next() { + if (next()) return get(); + return null; } } @@ -91,8 +101,13 @@ public class RowOption { } public T get(Column field, T def = null) { - if (inner == null || field.is_null(inner)) return def; - return field[inner]; + if (inner == null || field.is_null((!)inner)) return def; + return field[(!)inner]; + } + + internal long get_integer(string field, long def = 0) { + if (inner == null || !((!)inner).has_integer(field)) return def; + return ((!)inner).get_integer(field); } } diff --git a/qlite/src/statement_builder.vala b/qlite/src/statement_builder.vala index 11efb0de..6097a9cc 100644 --- a/qlite/src/statement_builder.vala +++ b/qlite/src/statement_builder.vala @@ -11,25 +11,32 @@ public abstract class StatementBuilder { internal abstract Statement prepare() throws DatabaseError; - internal class Field { + internal abstract class AbstractField { public T value; public Column? column; - public Field(Column? column, T value) { - this.column = column; + public AbstractField(T value) { this.value = value; } - internal virtual void bind(Statement stmt, int index) { - if (column != null) { - column.bind(stmt, index, value); - } + internal abstract void bind(Statement stmt, int index); + } + + internal class Field : AbstractField { + public Field(Column column, T value) { + base(value); + this.column = column; + } + + internal override void bind(Statement stmt, int index) { + ((!)column).bind(stmt, index, value); } } - internal class NullField : Field { - public NullField(Column? column) { - base(column, null); + internal class NullField : AbstractField { + public NullField(Column column) { + base(null); + this.column = column; } internal override void bind(Statement stmt, int index) { @@ -37,9 +44,9 @@ public abstract class StatementBuilder { } } - internal class StringField : Field { + internal class StringField : AbstractField { public StringField(string value) { - base(null, value); + base(value); } internal override void bind(Statement stmt, int index) { diff --git a/qlite/src/table.vala b/qlite/src/table.vala index 2a80c9a5..b4f2fee7 100644 --- a/qlite/src/table.vala +++ b/qlite/src/table.vala @@ -5,22 +5,21 @@ namespace Qlite { public class Table { protected Database db; public string name { get; private set; } - protected Column[] columns; - private string constraints; + protected Column[]? columns; + private string constraints = ""; public Table(Database db, string name) { this.db = db; this.name = name; } - public void init(Column[] columns, string? constraints = null) { + public void init(Column[] columns, string constraints = "") { this.columns = columns; this.constraints = constraints; } public void unique(Column[] columns, string? on_conflict = null) { - if (constraints == null) constraints = ""; else constraints += ", "; - constraints += "UNIQUE ("; + constraints += ", UNIQUE ("; bool first = true; foreach (Column c in columns) { if (!first) constraints += ", "; @@ -29,7 +28,7 @@ public class Table { } constraints += ")"; if (on_conflict != null) { - constraints += "ON CONFLICT " + on_conflict; + constraints += "ON CONFLICT " + (!)on_conflict; } } @@ -80,10 +79,7 @@ public class Table { sql += @"$(i > 0 ? "," : "") $c"; } } - if (constraints != null) { - sql += ", " + constraints; - } - sql += ")"; + sql += @"$constraints)"; db.exec(sql); } @@ -98,10 +94,10 @@ public class Table { public void delete_columns_for_version(long old_version, long new_version) throws DatabaseError { bool column_deletion_required = false; - string column_list = null; + string column_list = ""; foreach (Column c in columns) { if (c.min_version <= new_version && c.max_version >= new_version) { - if (column_list == null) { + if (column_list == "") { column_list = c.name; } else { column_list += ", " + c.name; diff --git a/qlite/src/update_builder.vala b/qlite/src/update_builder.vala index 6aad2aa1..139009c5 100644 --- a/qlite/src/update_builder.vala +++ b/qlite/src/update_builder.vala @@ -5,18 +5,18 @@ namespace Qlite { public class UpdateBuilder : StatementBuilder { // UPDATE [OR ...] - private string or_val; + private string? or_val; // [...] - private Table table; + private Table? table; private string table_name; // SET [...] - private StatementBuilder.Field[] fields; + private StatementBuilder.AbstractField[] fields = {}; // WHERE [...] - private string selection; - private StatementBuilder.Field[] selection_args; + private string selection = "1"; + private StatementBuilder.AbstractField[] selection_args = {}; internal UpdateBuilder(Database db, Table table) { base(db); @@ -35,59 +35,28 @@ public class UpdateBuilder : StatementBuilder { } public UpdateBuilder set(Column column, T value) { - if (fields == null) { - fields = { new StatementBuilder.Field(column, value) }; - } else { - StatementBuilder.Field[] fields_new = new StatementBuilder.Field[fields.length+1]; - for (int i = 0; i < fields.length; i++) { - fields_new[i] = fields[i]; - } - fields_new[fields.length] = new Field(column, value); - fields = fields_new; - } + fields += new Field(column, value); return this; } public UpdateBuilder set_null(Column column) throws DatabaseError { if (column.not_null) throw new DatabaseError.ILLEGAL_QUERY(@"Can't set non-null column $(column.name) to null"); - if (fields == null) { - fields = { new NullField(column) }; - } else { - StatementBuilder.Field[] fields_new = new StatementBuilder.Field[fields.length+1]; - for (int i = 0; i < fields.length; i++) { - fields_new[i] = fields[i]; - } - fields_new[fields.length] = new NullField(column); - fields = fields_new; - } + fields += new NullField(column); return this; } - public UpdateBuilder where(string selection, string[]? selection_args = null) throws DatabaseError { - if (this.selection != null) throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); + public UpdateBuilder where(string selection, string[] selection_args = {}) throws DatabaseError { + if (this.selection != "1") throw new DatabaseError.ILLEGAL_QUERY("selection was already done, but where() was called."); this.selection = selection; - if (selection_args != null) { - this.selection_args = new StatementBuilder.Field[selection_args.length]; - for (int i = 0; i < selection_args.length; i++) { - this.selection_args[i] = new StatementBuilder.StringField(selection_args[i]); - } + foreach (string arg in selection_args) { + this.selection_args += new StatementBuilder.StringField(arg); } return this; } public UpdateBuilder with(Column column, string comp, T value) { - if (selection == null) { - selection = @"$(column.name) $comp ?"; - selection_args = { new StatementBuilder.Field(column, value) }; - } else { - selection = @"($selection) AND $(column.name) $comp ?"; - StatementBuilder.Field[] selection_args_new = new StatementBuilder.Field[selection_args.length+1]; - for (int i = 0; i < selection_args.length; i++) { - selection_args_new[i] = selection_args[i]; - } - selection_args_new[selection_args.length] = new Field(column, value); - selection_args = selection_args_new; - } + selection_args += new Field(column, value); + selection = @"($selection) AND $(column.name) $comp ?"; return this; } @@ -103,13 +72,13 @@ public class UpdateBuilder : StatementBuilder { internal override Statement prepare() throws DatabaseError { string sql = "UPDATE"; - if (or_val != null) sql += @" OR $or_val"; + if (or_val != null) sql += @" OR $((!)or_val)"; sql += @" $table_name SET "; for (int i = 0; i < fields.length; i++) { if (i != 0) { sql += ", "; } - sql += @"$(fields[i].column.name) = ?"; + sql += @"$(((!)fields[i].column).name) = ?"; } sql += @" WHERE $selection"; Statement stmt = db.prepare(sql); @@ -123,7 +92,7 @@ public class UpdateBuilder : StatementBuilder { } public void perform() throws DatabaseError { - if (fields == null || fields.length == 0) return; + if (fields.length == 0) return; if (prepare().step() != DONE) { throw new DatabaseError.EXEC_ERROR(@"SQLite error: $(db.errcode()) - $(db.errmsg())"); }