qlite: cleanup, fix nullity issues

This commit is contained in:
Marvin W 2017-04-16 15:11:00 +02:00
parent c6ff3387fa
commit 765c2605cd
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
9 changed files with 131 additions and 181 deletions

View File

@ -4,12 +4,12 @@ namespace Qlite {
public abstract class Column<T> {
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<T> {
}
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<T> {
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<string> {
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<bool> {
public BoolText(string name) {
base(name, TEXT);

View File

@ -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<string> meta_name = new Column.Text("name") { primary_key = true };
private Column<string?> meta_name = new Column.Text("name") { primary_key = true };
private Column<long> meta_int_val = new Column.Long("int_val");
private Column<string> meta_text_val = new Column.Text("text_val");
private Column<string?> meta_text_val = new Column.Text("text_val");
private Table meta_table;
public bool debug = false;

View File

@ -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<T>(Column<T> column, string comp, T value) {
if (selection == null) {
selection = @"$(column.name) $comp ?";
selection_args = { new StatementBuilder.Field<T>(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<T>(column, value);
selection_args = selection_args_new;
}
selection_args += new Field<T>(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);
}

View File

@ -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<T>(Column<T> column, T value) {
if (fields == null) {
fields = { new StatementBuilder.Field<T>(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<T>(column, value);
fields = fields_new;
}
fields += new Field<T>(column, value);
return this;
}
public InsertBuilder value_null<T>(Column<T> 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<T>(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<T>(column);
fields = fields_new;
}
fields += new NullField<T>(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++) {

View File

@ -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<T>(Column<T> 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<T>(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<T>(column, value);
selection_args = selection_args_new;
}
selection_args += new Field<T>(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])";

View File

@ -4,7 +4,7 @@ using Sqlite;
namespace Qlite {
public class Row {
private Map<string, string> text_map = new HashMap<string, string>();
private Map<string, string?> text_map = new HashMap<string, string?>();
private Map<string, long> int_map = new HashMap<string, long>();
private Map<string, double?> real_map = new HashMap<string, double?>();
@ -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<T>(Column<T> 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);
}
}

View File

@ -11,25 +11,32 @@ public abstract class StatementBuilder {
internal abstract Statement prepare() throws DatabaseError;
internal class Field<T> {
internal abstract class AbstractField<T> {
public T value;
public Column<T>? column;
public Field(Column<T>? 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<T> : AbstractField<T> {
public Field(Column<T> column, T value) {
base(value);
this.column = column;
}
internal override void bind(Statement stmt, int index) {
((!)column).bind(stmt, index, value);
}
}
internal class NullField<T> : Field<T> {
public NullField(Column<T>? column) {
base(column, null);
internal class NullField<T> : AbstractField<T> {
public NullField(Column<T> 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<string> {
internal class StringField : AbstractField<string> {
public StringField(string value) {
base(null, value);
base(value);
}
internal override void bind(Statement stmt, int index) {

View File

@ -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;

View File

@ -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<T>(Column<T> column, T value) {
if (fields == null) {
fields = { new StatementBuilder.Field<T>(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<T>(column, value);
fields = fields_new;
}
fields += new Field<T>(column, value);
return this;
}
public UpdateBuilder set_null<T>(Column<T> 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<T>(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<T>(column);
fields = fields_new;
}
fields += new NullField<T>(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<T>(Column<T> column, string comp, T value) {
if (selection == null) {
selection = @"$(column.name) $comp ?";
selection_args = { new StatementBuilder.Field<T>(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<T>(column, value);
selection_args = selection_args_new;
}
selection_args += new Field<T>(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())");
}