Move query helpers into common code
This commit is contained in:
parent
e90d9daf77
commit
1ba1b18c39
3 changed files with 72 additions and 50 deletions
16
src/api/services/common.zig
Normal file
16
src/api/services/common.zig
Normal 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;
|
||||
};
|
|
@ -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)");
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue