From babfc3bd36e0cfa50f06648224f33a6a96eb27ea Mon Sep 17 00:00:00 2001 From: bobufa Date: Wed, 27 Jun 2018 16:58:10 +0200 Subject: [PATCH] qlite: add basic (outer) join functionality --- qlite/src/column.vala | 57 ++++++++++++++++++--------------- qlite/src/query_builder.vala | 45 ++++++++++++++++++-------- qlite/src/row.vala | 61 +++++++++++++++++++++++++++--------- qlite/src/table.vala | 10 ++++-- 4 files changed, 118 insertions(+), 55 deletions(-) diff --git a/qlite/src/column.vala b/qlite/src/column.vala index 9c201885..daa6a59f 100644 --- a/qlite/src/column.vala +++ b/qlite/src/column.vala @@ -3,6 +3,8 @@ using Sqlite; namespace Qlite { public abstract class Column { + public const string DEFALT_TABLE_NAME = ""; + public string name { get; private set; } public string? default { get; set; } public int sqlite_type { get; private set; } @@ -12,16 +14,21 @@ public abstract class Column { public virtual bool not_null { get; set; } public long min_version { get; set; default = -1; } public long max_version { get; set; default = long.MAX; } + internal Table table { get; set; } - public abstract T get(Row row); + public abstract T get(Row row, string? table_name = DEFALT_TABLE_NAME); - public virtual bool is_null(Row row) { + public virtual bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { return false; } internal abstract void bind(Statement stmt, int index, T value); public string to_string() { + return table == null ? name : (table.name + "." + name); + } + + public string to_column_definition() { string res = name; switch (sqlite_type) { case INTEGER: @@ -58,12 +65,12 @@ public abstract class Column { base(name, INTEGER); } - public override int get(Row row) { - return (int) row.get_integer(name); + public override int get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return (int) row.get_integer(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } - public override bool is_null(Row row) { - return !row.has_integer(name); + public override bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { + return !row.has_integer(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, int value) { @@ -76,12 +83,12 @@ public abstract class Column { base(name, INTEGER); } - public override long get(Row row) { - return (long) row.get_integer(name); + public override long get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return (long) row.get_integer(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } - public override bool is_null(Row row) { - return !row.has_integer(name); + public override bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { + return !row.has_integer(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, long value) { @@ -94,12 +101,12 @@ public abstract class Column { base(name, FLOAT); } - public override double get(Row row) { - return row.get_real(name); + public override double get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return row.get_real(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } - public override bool is_null(Row row) { - return !row.has_real(name); + public override bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { + return !row.has_real(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, double value) { @@ -112,12 +119,12 @@ public abstract class Column { base(name, TEXT); } - public override string? get(Row row) { - return row.get_text(name); + public override string? get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return row.get_text(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } - public override bool is_null(Row row) { - return get(row) == null; + public override bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { + return get(row, table_name == DEFALT_TABLE_NAME ? table.name : table_name) == null; } internal override void bind(Statement stmt, int index, string? value) { @@ -136,11 +143,11 @@ public abstract class Column { public override bool not_null { get { return true; } set {} } - public override string get(Row row) { - return (!)row.get_text(name); + public override string get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return (!)row.get_text(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name); } - public override bool is_null(Row row) { + public override bool is_null(Row row, string? table_name = DEFALT_TABLE_NAME) { return false; } @@ -154,8 +161,8 @@ public abstract class Column { base(name, TEXT); } - public override bool get(Row row) { - return row.get_text(name) == "1"; + public override bool get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return row.get_text(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name) == "1"; } internal override void bind(Statement stmt, int index, bool value) { @@ -168,8 +175,8 @@ public abstract class Column { base(name, INTEGER); } - public override bool get(Row row) { - return row.get_integer(name) == 1; + public override bool get(Row row, string? table_name = DEFALT_TABLE_NAME) { + return row.get_integer(name, table_name == DEFALT_TABLE_NAME ? table.name : table_name) == 1; } internal override void bind(Statement stmt, int index, bool value) { diff --git a/qlite/src/query_builder.vala b/qlite/src/query_builder.vala index 9ca5bf25..d1254b53 100644 --- a/qlite/src/query_builder.vala +++ b/qlite/src/query_builder.vala @@ -33,12 +33,12 @@ public class QueryBuilder : StatementBuilder { public QueryBuilder select(Column[] columns = {}) { this.columns = columns; - if (columns.length == 0) { + if (columns.length != 0) { for (int i = 0; i < columns.length; i++) { if (column_selector == "*") { - column_selector = columns[0].name; + column_selector = columns[i].to_string(); } else { - column_selector += ", " + columns[i].name; + column_selector += ", " + columns[i].to_string(); } } } else { @@ -65,14 +65,33 @@ public class QueryBuilder : StatementBuilder { return this; } - public QueryBuilder join(string table, string on) { - joins += @"JOIN $table ON $on"; + public QueryBuilder outer_join_with(Table table, Column lhs, Column rhs, string? as = null) { + return outer_join_on(table, @"$lhs = $rhs", as); + } + + public QueryBuilder outer_join_on(Table table, string on, string? as = null) { + if (as == null) as = table.name; + joins += @" LEFT OUTER JOIN $(table.name) AS $as ON $on"; + return this; + } + + public QueryBuilder join_with(Table table, Column lhs, Column rhs, string? as = null) { + return join_on(table, @"$lhs = $rhs", as); + } + + public QueryBuilder join_on(Table table, string on, string? as = null) { + if (as == null) as = table.name; + joins += @" JOIN $(table.name) AS $as ON $on"; + return this; + } + + internal QueryBuilder join_name(string table_name, string on) { + joins += @" JOIN $table_name ON $on"; return this; } public QueryBuilder where(string selection, string[] selection_args = {}) { - if (this.selection != "1") error("selection was already done, but where() was called."); - this.selection = selection; + this.selection = @"($(this.selection)) AND ($selection)"; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } @@ -82,17 +101,17 @@ public class QueryBuilder : StatementBuilder { public QueryBuilder with(Column column, string comp, T value) { if ((column.unique || column.primary_key) && comp == "=") single_result = true; selection_args += new Field(column, value); - selection = @"($selection) AND $table_name.$(column.name) $comp ?"; + selection = @"($selection) AND $column $comp ?"; return this; } public QueryBuilder with_null(Column column) { - selection = @"($selection) AND $table_name.$(column.name) ISNULL"; + selection = @"($selection) AND $column ISNULL"; return this; } public QueryBuilder without_null(Column column) { - selection = @"($selection) AND $table_name.$(column.name) NOT NULL"; + selection = @"($selection) AND $column NOT NULL"; return this; } @@ -155,13 +174,13 @@ public class QueryBuilder : StatementBuilder { } class OrderingTerm { - Column column; + Column? column; string column_name; string dir; public OrderingTerm(Column column, string dir) { this.column = column; - this.column_name = column.name; + this.column_name = column.to_string(); this.dir = dir; } @@ -190,7 +209,7 @@ public class MatchQueryBuilder : QueryBuilder { base(db); if (table.fts_columns == null) error("MATCH query on non FTS table"); from(table); - join(@"_fts_$table_name", @"_fts_$table_name.docid = $table_name.rowid"); + join_name(@"_fts_$table_name", @"_fts_$table_name.docid = $table_name.rowid"); } public MatchQueryBuilder match(Column column, string match) { diff --git a/qlite/src/row.vala b/qlite/src/row.vala index be459719..d3807f41 100644 --- a/qlite/src/row.vala +++ b/qlite/src/row.vala @@ -10,15 +10,21 @@ public class Row { internal Row(Statement stmt) { for (int i = 0; i < stmt.column_count(); i++) { + string column_name; + if (stmt.column_origin_name(i) != null) { + column_name = @"$(stmt.column_table_name(i)).$(stmt.column_origin_name(i))"; + } else { + column_name = stmt.column_name(i); + } switch(stmt.column_type(i)) { case TEXT: - text_map[stmt.column_name(i)] = stmt.column_text(i); + text_map[column_name] = stmt.column_text(i); break; case INTEGER: - int_map[stmt.column_name(i)] = (long) stmt.column_int64(i); + int_map[column_name] = (long) stmt.column_int64(i); break; case FLOAT: - real_map[stmt.column_name(i)] = stmt.column_double(i); + real_map[column_name] = stmt.column_double(i); break; } } @@ -28,27 +34,54 @@ public class Row { return field[this]; } - public string? get_text(string field) { - if (text_map.has_key(field)) { - return text_map[field]; + private string field_name(string field, string? table) { + if (table != null) { + return @"$table.$field"; + } else { + return field; + } + } + + public string? get_text(string field, string? table = null) { + if (text_map.has_key(field_name(field, table))) { + return text_map[field_name(field, table)]; } return null; } - public long get_integer(string field) { - return int_map[field]; + public long get_integer(string field, string? table = null) { + return int_map[field_name(field, table)]; } - public bool has_integer(string field) { - return int_map.has_key(field); + public bool has_integer(string field, string? table = null) { + return int_map.has_key(field_name(field, table)); } - public double get_real(string field, double def = 0) { - return real_map[field] ?? def; + public double get_real(string field, string? table = null, double def = 0) { + return real_map[field_name(field, table)] ?? def; } - public bool has_real(string field) { - return real_map.has_key(field) && real_map[field] != null; + public bool has_real(string field, string? table = null) { + return real_map.has_key(field_name(field, table)) && real_map[field_name(field, table)] != null; + } + + public string to_string() { + string ret = "{"; + + foreach (string key in text_map.keys) { + if (ret.length > 1) ret += ", "; + ret = @"$ret$key: \"$(text_map[key])\""; + } + foreach (string key in int_map.keys) { + if (ret.length > 1) ret += ", "; + ret = @"$ret$key: $(int_map[key])"; + } + foreach (string key in real_map.keys) { + if (ret.length > 1) ret += ", "; + ret = @"$ret$key: $(real_map[key])"; + } + + return ret + "}"; } } diff --git a/qlite/src/table.vala b/qlite/src/table.vala index 8725c4c9..607a396c 100644 --- a/qlite/src/table.vala +++ b/qlite/src/table.vala @@ -19,6 +19,10 @@ public class Table { 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) { @@ -28,7 +32,7 @@ public class Table { string cnames = ""; string cnews = ""; foreach (Column c in columns) { - cs += @", $c"; + cs += @", $(c.to_column_definition())"; cnames += @", $(c.name)"; cnews += @", new.$(c.name)"; } @@ -140,7 +144,7 @@ public class Table { for (int i = 0; i < columns.length; i++) { Column c = columns[i]; if (c.min_version <= version && c.max_version >= version) { - sql += @"$(i > 0 ? "," : "") $c"; + sql += @"$(i > 0 ? "," : "") $(c.to_column_definition())"; } } sql += @"$constraints)"; @@ -163,7 +167,7 @@ public class Table { 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"); + db.exec(@"ALTER TABLE $name ADD COLUMN $(c.to_column_definition())"); } catch (Error e) { error("Qlite Error: Add columns for version"); }