diff options
Diffstat (limited to 'qlite/src/query_builder.vala')
-rw-r--r-- | qlite/src/query_builder.vala | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/qlite/src/query_builder.vala b/qlite/src/query_builder.vala new file mode 100644 index 00000000..0c9f4d98 --- /dev/null +++ b/qlite/src/query_builder.vala @@ -0,0 +1,196 @@ +using Sqlite; + +namespace Qlite { + +public class QueryBuilder : StatementBuilder { + private bool finished; + private bool single_result; + + // SELECT [...] + private string column_selector = "*"; + private Column[] columns; + + // FROM [...] + private Table table; + private string table_name; + + // WHERE [...] + private string selection; + private StatementBuilder.Field[] selection_args; + + // ORDER BY [...] + private OrderingTerm[] order_by_terms; + + // LIMIT [...] + private int limit_val; + + private Row[] result; + + protected QueryBuilder(Database db) { + base(db); + } + + public QueryBuilder select(Column[]? columns = null) { + this.columns = columns; + if (columns != null) { + for (int i = 0; i < columns.length; i++) { + if (column_selector == "*") { + column_selector = columns[0].name; + } else { + column_selector += ", " + columns[i].name; + } + } + } else { + column_selector = "*"; + } + return this; + } + + public QueryBuilder select_string(string column_selector) { + this.columns = null; + this.column_selector = column_selector; + return this; + } + + public QueryBuilder from(Table table) throws DatabaseError { + if (this.table_name != null) throw new DatabaseError.ILLEGAL_QUERY("cannot use from() multiple times."); + this.table = table; + this.table_name = table.name; + return this; + } + + public QueryBuilder from_name(string table) { + this.table_name = table; + 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."); + 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]); + } + } + 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; + } + return this; + } + + public QueryBuilder with_null<T>(Column<T> column) { + selection = @"($selection) AND $(column.name) ISNULL"; + return this; + } + + public QueryBuilder without_null<T>(Column<T> column) { + selection = @"($selection) AND $(column.name) NOT NULL"; + 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)); + return this; + } + + public QueryBuilder order_by_name(string name, string dir) { + add_order_by(new OrderingTerm.by_name(name, dir)); + return this; + } + + public QueryBuilder limit(int limit) { + this.limit_val = limit; + return this; + } + + public int64 count() throws DatabaseError { + this.column_selector = @"COUNT($column_selector) AS count"; + this.single_result = true; + return row().get_integer("count"); + } + + public 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(); + } + + public T get<T>(Column<T> field) throws DatabaseError { + Row row = row(); + if (row != null) { + return row[field]; + } + return null; + } + + public 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" : "")"); + for (int i = 0; i < selection_args.length; i++) { + selection_args[i].bind(stmt, i+1); + } + return stmt; + } + + public Row.RowIterator iterator() throws DatabaseError { + return new Row.RowIterator.from_query_builder(this); + } + + class OrderingTerm { + Column column; + string column_name; + string dir; + + public OrderingTerm(Column column, string dir) { + this.column = column; + this.column_name = column.name; + this.dir = dir; + } + + public OrderingTerm.by_name(string column_name, string dir) { + this.column_name = column_name; + this.dir = dir; + } + + public string to_string() { + return @"$column_name $dir"; + } + + public static string all_to_string(OrderingTerm[] terms) { + if (terms.length == 0) return ""; + string res = "ORDER BY "+terms[0].to_string(); + for (int i = 1; i < terms.length; i++) { + res += @", $(terms[i])"; + } + return res; + } + } +} + +}
\ No newline at end of file |