Move query helpers into common code

This commit is contained in:
jaina heartles 2022-10-16 14:46:01 -07:00
parent e90d9daf77
commit 1ba1b18c39
3 changed files with 72 additions and 50 deletions

View file

@ -0,0 +1,16 @@
const std = @import("std");
const util = @import("util");
pub const Direction = enum {
ascending,
descending,
pub const jsonStringify = util.jsonSerializeEnumAsString;
};
pub const PageDirection = enum {
forward,
backward,
pub const jsonStringify = util.jsonSerializeEnumAsString;
};

View file

@ -2,6 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const util = @import("util");
const sql = @import("sql");
const common = @import("./common.zig");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
@ -153,20 +154,8 @@ pub const QueryArgs = struct {
pub const jsonStringify = util.jsonSerializeEnumAsString;
};
pub const Direction = enum {
ascending,
descending,
pub const jsonStringify = util.jsonSerializeEnumAsString;
};
pub const PageDirection = enum {
forward,
backward,
pub const jsonStringify = util.jsonSerializeEnumAsString;
};
pub const Direction = common.Direction;
pub const PageDirection = common.PageDirection;
pub const Prev = std.meta.Child(std.meta.fieldInfo(QueryArgs, .prev).field_type);
pub const OrderVal = std.meta.fieldInfo(Prev, .order_val).field_type;
@ -211,30 +200,6 @@ pub const QueryResult = struct {
next_page: QueryArgs,
};
const QueryBuilder = struct {
array: std.ArrayList(u8),
where_clauses_appended: usize = 0,
pub fn init(alloc: std.mem.Allocator) QueryBuilder {
return QueryBuilder{ .array = std.ArrayList(u8).init(alloc) };
}
pub fn deinit(self: *const QueryBuilder) void {
self.array.deinit();
}
pub fn andWhere(self: *QueryBuilder, clause: []const u8) !void {
if (self.where_clauses_appended == 0) {
try self.array.appendSlice("WHERE ");
} else {
try self.array.appendSlice(" AND ");
}
try self.array.appendSlice(clause);
self.where_clauses_appended += 1;
}
};
const max_max_items = 100;
pub const QueryError = error{
@ -246,7 +211,7 @@ pub const QueryError = error{
// arguments.
// `args.max_items` is only a request, and fewer entries may be returned.
pub fn query(db: anytype, args: QueryArgs, alloc: std.mem.Allocator) !QueryResult {
var builder = QueryBuilder.init(alloc);
var builder = sql.QueryBuilder.init(alloc);
defer builder.deinit();
try builder.array.appendSlice(
@ -266,21 +231,21 @@ pub fn query(db: anytype, args: QueryArgs, alloc: std.mem.Allocator) !QueryResul
if (args.prev) |prev| {
if (prev.order_val != args.order_by) return error.PageArgMismatch;
try builder.andWhere(switch (args.order_by) {
.name => "(name, id)",
.host => "(host, id)",
.created_at => "(created_at, id)",
});
_ = try builder.array.appendSlice(switch (args.direction) {
switch (args.order_by) {
.name => try builder.andWhere("(name, id)"),
.host => try builder.andWhere("(host, id)"),
.created_at => try builder.andWhere("(created_at, id)"),
}
switch (args.direction) {
.ascending => switch (args.page_direction) {
.forward => " > ",
.backward => " < ",
.forward => try builder.andWhere(" > "),
.backward => try builder.andWhere(" < "),
},
.descending => switch (args.page_direction) {
.forward => " < ",
.backward => " > ",
.forward => try builder.andWhere(" < "),
.backward => try builder.andWhere(" > "),
},
});
}
_ = try builder.array.appendSlice("($5, $6)");
}

View file

@ -25,6 +25,47 @@ pub const Engine = enum {
sqlite,
};
/// Helper for building queries at runtime. All constituent parts of the
/// query should be defined at comptime, however the choice of whether
/// or not to include them can occur at runtime.
pub const QueryBuilder = struct {
array: std.ArrayList(u8),
where_clauses_appended: usize = 0,
pub fn init(alloc: std.mem.Allocator) QueryBuilder {
return QueryBuilder{ .array = std.ArrayList(u8).init(alloc) };
}
pub fn deinit(self: *const QueryBuilder) void {
self.array.deinit();
}
/// Add a chunk of sql to the query without processing
pub fn appendSlice(self: *QueryBuilder, comptime sql: []const u8) !void {
try self.array.appendSlice(sql);
}
/// Add a where clause to the query. Clauses are assumed to be components
/// in an overall expression in Conjunctive Normal Form (AND of OR's).
/// https://en.wikipedia.org/wiki/Conjunctive_normal_form
/// All calls to andWhere must be contiguous, that is, they cannot be
/// interspersed with calls to appendSlice
pub fn andWhere(self: *QueryBuilder, comptime clause: []const u8) !void {
if (self.where_clauses_appended == 0) {
try self.array.appendSlice("WHERE ");
} else {
try self.array.appendSlice(" AND ");
}
try self.array.appendSlice(clause);
self.where_clauses_appended += 1;
}
pub fn str(self: *const QueryBuilder) []const u8 {
return self.array.items;
}
};
// TODO: make this suck less
pub const Config = union(Engine) {
postgres: struct {