Set relations to retrieve by a query at compile time.
+ Add compile-time utility functions to build strings and queries. * Relations to retrieve when querying a model must now be set at comptime.
This commit is contained in:
parent
5a2964622c
commit
04b61f9787
6 changed files with 148 additions and 137 deletions
33
src/comptime.zig
Normal file
33
src/comptime.zig
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
/// Append an element to the given array at comptime.
|
||||||
|
pub fn append(array: anytype, element: anytype) @TypeOf(array ++ .{element}) {
|
||||||
|
return array ++ .{element};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Join strings into one, with the given separator in between.
|
||||||
|
pub fn join(separator: []const u8, slices: []const[]const u8) []const u8 {
|
||||||
|
if (slices.len == 0) return "";
|
||||||
|
|
||||||
|
// Compute total length of the string to make.
|
||||||
|
const totalLen = total: {
|
||||||
|
// Compute separator length.
|
||||||
|
var total = separator.len * (slices.len - 1);
|
||||||
|
// Add length of all slices.
|
||||||
|
for (slices) |slice| total += slice.len;
|
||||||
|
break :total total;
|
||||||
|
};
|
||||||
|
|
||||||
|
var buffer: [totalLen]u8 = undefined;
|
||||||
|
|
||||||
|
// Based on std.mem.joinMaybeZ implementation.
|
||||||
|
@memcpy(buffer[0..slices[0].len], slices[0]);
|
||||||
|
var buf_index: usize = slices[0].len;
|
||||||
|
for (slices[1..]) |slice| {
|
||||||
|
@memcpy(buffer[buf_index .. buf_index + separator.len], separator);
|
||||||
|
buf_index += separator.len;
|
||||||
|
@memcpy(buffer[buf_index .. buf_index + slice.len], slice);
|
||||||
|
buf_index += slice.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &buffer;
|
||||||
|
}
|
155
src/query.zig
155
src/query.zig
|
@ -8,25 +8,71 @@ const _sql = @import("sql.zig");
|
||||||
const _conditions = @import("conditions.zig");
|
const _conditions = @import("conditions.zig");
|
||||||
const relations = @import("relations.zig");
|
const relations = @import("relations.zig");
|
||||||
const repository = @import("repository.zig");
|
const repository = @import("repository.zig");
|
||||||
|
const _comptime = @import("comptime.zig");
|
||||||
const InlineRelationsResult = struct {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Repository query configuration structure.
|
/// Repository query configuration structure.
|
||||||
pub const RepositoryQueryConfiguration = struct {
|
pub const RepositoryQueryConfiguration = struct {
|
||||||
select: ?_sql.RawQuery = null,
|
select: ?_sql.RawQuery = null,
|
||||||
join: ?_sql.RawQuery = null,
|
join: ?_sql.RawQuery = null,
|
||||||
where: ?_sql.RawQuery = null,
|
where: ?_sql.RawQuery = null,
|
||||||
with: ?[]const relations.Eager = null,
|
};
|
||||||
|
|
||||||
|
/// Compiled relations structure.
|
||||||
|
const CompiledRelations = struct {
|
||||||
|
inlineSelect: []const u8,
|
||||||
|
inlineJoins: []const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Repository models query manager.
|
/// Repository models query manager.
|
||||||
/// Manage query string build and its execution.
|
/// Manage query string build and its execution.
|
||||||
pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime repositoryConfig: repository.RepositoryConfiguration(Model, TableShape)) type {
|
pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime repositoryConfig: repository.RepositoryConfiguration(Model, TableShape), comptime with: ?[]const relations.ModelRelation) type {
|
||||||
// Pre-compute SQL buffer size.
|
const compiledRelations = comptime compile: {
|
||||||
|
// Inline relations list.
|
||||||
|
var inlineRelations: []relations.ModelRelation = &[0]relations.ModelRelation{};
|
||||||
|
|
||||||
|
if (with) |_with| {
|
||||||
|
// If there are relations to eager load, prepare their query.
|
||||||
|
|
||||||
|
// Initialize inline select array.
|
||||||
|
var inlineSelect: [][]const u8 = &[0][]const u8{};
|
||||||
|
// Initialize inline joins array.
|
||||||
|
var inlineJoins: [][]const u8 = &[0][]const u8{};
|
||||||
|
|
||||||
|
for (_with) |relation| {
|
||||||
|
// For each relation, determine if it's inline or not.
|
||||||
|
var tt = relation.relation{};
|
||||||
|
const relationInstance = tt.relation();
|
||||||
|
if (relationInstance.inlineMapping()) {
|
||||||
|
// Add the current relation to inline relations.
|
||||||
|
inlineRelations = @ptrCast(@constCast(_comptime.append(inlineRelations, relation)));
|
||||||
|
|
||||||
|
// Build table alias and fields prefix for the relation.
|
||||||
|
const tableAlias = "relations." ++ relation.field;
|
||||||
|
const fieldsPrefix = tableAlias ++ ".";
|
||||||
|
|
||||||
|
// Generate selected columns for the relation.
|
||||||
|
inlineSelect = @ptrCast(@constCast(_comptime.append(inlineSelect, relationInstance.genSelect(tableAlias, fieldsPrefix))));
|
||||||
|
// Generate joined table for the relation.
|
||||||
|
inlineJoins = @ptrCast(@constCast(_comptime.append(inlineJoins, relationInstance.genJoin(tableAlias))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break :compile CompiledRelations{
|
||||||
|
.inlineSelect = if (inlineSelect.len > 0) ", " ++ _comptime.join(", ", inlineSelect) else "",
|
||||||
|
.inlineJoins = if (inlineJoins.len > 0) " " ++ _comptime.join(" ", inlineJoins) else "",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
break :compile CompiledRelations{
|
||||||
|
.inlineSelect = "",
|
||||||
|
.inlineJoins = "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pre-compute SQL buffer.
|
||||||
const fromClause = " FROM \"" ++ repositoryConfig.table ++ "\"";
|
const fromClause = " FROM \"" ++ repositoryConfig.table ++ "\"";
|
||||||
const defaultSelectSql = "\"" ++ repositoryConfig.table ++ "\".*";
|
const defaultSelectSql = "\"" ++ repositoryConfig.table ++ "\".*" ++ compiledRelations.inlineSelect;
|
||||||
|
const defaultJoin = compiledRelations.inlineJoins;
|
||||||
|
|
||||||
// Model key type.
|
// Model key type.
|
||||||
const KeyType = repository.ModelKeyType(Model, TableShape, repositoryConfig);
|
const KeyType = repository.ModelKeyType(Model, TableShape, repositoryConfig);
|
||||||
|
@ -39,9 +85,6 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
connection: *database.Connection = undefined,
|
connection: *database.Connection = undefined,
|
||||||
queryConfig: RepositoryQueryConfiguration,
|
queryConfig: RepositoryQueryConfiguration,
|
||||||
|
|
||||||
/// List of loaded inline relations.
|
|
||||||
inlineRelations: []relations.Eager = undefined,
|
|
||||||
|
|
||||||
query: ?_sql.RawQuery = null,
|
query: ?_sql.RawQuery = null,
|
||||||
sql: ?[]const u8 = null,
|
sql: ?[]const u8 = null,
|
||||||
|
|
||||||
|
@ -177,103 +220,16 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set relations to eager load.
|
|
||||||
pub fn with(self: *Self, relation: relations.ModelRelation) !void {
|
|
||||||
// Take an array of eager relations (which can have subrelations).
|
|
||||||
const allocator = self.arena.allocator();
|
|
||||||
|
|
||||||
// Make a relation instance.
|
|
||||||
const relationInstance = try allocator.create(relation.relation);
|
|
||||||
|
|
||||||
// Add the new relation to a newly allocated array, with one more space.
|
|
||||||
const newPos = if (self.queryConfig.with) |_with| _with.len else 0;
|
|
||||||
var newWith = try allocator.alloc(relations.Eager, newPos + 1);
|
|
||||||
newWith[newPos] = .{
|
|
||||||
.field = relation.field,
|
|
||||||
.relation = relationInstance.*.relation(),
|
|
||||||
.with = &[0]relations.Eager{}, //TODO handle subrelations with dotted syntax
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.queryConfig.with) |_with| {
|
|
||||||
// Copy existing relations.
|
|
||||||
@memcpy(newWith[0..newPos], _with);
|
|
||||||
// Free previous array.
|
|
||||||
allocator.free(_with);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the newly allocated array.
|
|
||||||
self.queryConfig.with = newWith;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build inline relations query part.
|
|
||||||
fn buildInlineRelations(self: *Self) !?struct{
|
|
||||||
select: []const u8,
|
|
||||||
join: _sql.RawQuery,
|
|
||||||
} {
|
|
||||||
if (self.queryConfig.with) |_with| {
|
|
||||||
// Initialize an ArrayList of query parts for relations.
|
|
||||||
var inlineRelations = try std.ArrayList(_sql.RawQuery).initCapacity(self.arena.allocator(), _with.len);
|
|
||||||
defer inlineRelations.deinit();
|
|
||||||
var inlineRelationsSelect = try std.ArrayList([]const u8).initCapacity(self.arena.allocator(), _with.len);
|
|
||||||
defer inlineRelationsSelect.deinit();
|
|
||||||
|
|
||||||
// Initialize an ArrayList to store all loaded inline relations.
|
|
||||||
var loadedRelations = std.ArrayList(relations.Eager).init(self.arena.allocator());
|
|
||||||
defer loadedRelations.deinit();
|
|
||||||
|
|
||||||
for (_with) |_relation| {
|
|
||||||
// Append each inline relation to the ArrayList.
|
|
||||||
if (_relation.relation.inlineMapping()) {
|
|
||||||
try loadedRelations.append(_relation); // Store the loaded inline relation.
|
|
||||||
|
|
||||||
// Get an allocator for local allocations.
|
|
||||||
const localAllocator = self.arena.allocator();
|
|
||||||
|
|
||||||
// Build table alias and fields prefix.
|
|
||||||
const tableAlias = try std.fmt.allocPrint(localAllocator, "relations.{s}", .{_relation.field});
|
|
||||||
defer localAllocator.free(tableAlias);
|
|
||||||
const prefix = try std.fmt.allocPrint(localAllocator, "{s}.", .{tableAlias});
|
|
||||||
defer localAllocator.free(prefix);
|
|
||||||
|
|
||||||
// Alter query to get relation fields.
|
|
||||||
try inlineRelations.append(try _relation.relation.genJoin(self.arena.allocator(), tableAlias));
|
|
||||||
const relationSelect = try _relation.relation.genSelect(localAllocator, tableAlias, prefix);
|
|
||||||
try inlineRelationsSelect.append(relationSelect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.inlineRelations = try loadedRelations.toOwnedSlice();
|
|
||||||
|
|
||||||
// Return the inline relations query part.
|
|
||||||
return .{
|
|
||||||
.select = try std.mem.join(self.arena.allocator(), ", ", inlineRelationsSelect.items),
|
|
||||||
.join = try _sql.RawQuery.fromConcat(self.arena.allocator(), inlineRelations.items),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Nothing.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build SQL query.
|
/// Build SQL query.
|
||||||
pub fn buildSql(self: *Self) !void {
|
pub fn buildSql(self: *Self) !void {
|
||||||
// Build inline relations query part.
|
|
||||||
const inlineRelations = try self.buildInlineRelations();
|
|
||||||
defer if (inlineRelations) |_inlineRelations| self.arena.allocator().free(_inlineRelations.join.sql);
|
|
||||||
defer if (inlineRelations) |_inlineRelations| self.arena.allocator().free(_inlineRelations.join.params);
|
|
||||||
defer if (inlineRelations) |_inlineRelations| self.arena.allocator().free(_inlineRelations.select);
|
|
||||||
|
|
||||||
// Build the full SQL query from all its parts.
|
// Build the full SQL query from all its parts.
|
||||||
const sqlQuery = _sql.RawQuery{
|
const sqlQuery = _sql.RawQuery{
|
||||||
.sql = try std.mem.join(self.arena.allocator(), "", &[_][]const u8{
|
.sql = try std.mem.join(self.arena.allocator(), "", &[_][]const u8{
|
||||||
"SELECT ", if (self.queryConfig.select) |_select| _select.sql else defaultSelectSql,
|
"SELECT ", if (self.queryConfig.select) |_select| _select.sql else defaultSelectSql,
|
||||||
if (inlineRelations) |_| ", " else "",
|
|
||||||
if (inlineRelations) |_inlineRelations| _inlineRelations.select else "",
|
|
||||||
fromClause,
|
fromClause,
|
||||||
|
defaultJoin,
|
||||||
if (self.queryConfig.join) |_| " " else "",
|
if (self.queryConfig.join) |_| " " else "",
|
||||||
if (self.queryConfig.join) |_join| _join.sql else "",
|
if (self.queryConfig.join) |_join| _join.sql else "",
|
||||||
if (inlineRelations) |_| " " else "",
|
|
||||||
if (inlineRelations) |_inlineRelations| _inlineRelations.join.sql else "",
|
|
||||||
if (self.queryConfig.where) |_| " WHERE " else "",
|
if (self.queryConfig.where) |_| " WHERE " else "",
|
||||||
if (self.queryConfig.where) |_where| _where.sql else "",
|
if (self.queryConfig.where) |_where| _where.sql else "",
|
||||||
";",
|
";",
|
||||||
|
@ -281,7 +237,6 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
.params = try std.mem.concat(self.arena.allocator(), _sql.RawQueryParameter, &[_][]const _sql.RawQueryParameter{
|
.params = try std.mem.concat(self.arena.allocator(), _sql.RawQueryParameter, &[_][]const _sql.RawQueryParameter{
|
||||||
if (self.queryConfig.select) |_select| _select.params else &[0]_sql.RawQueryParameter{},
|
if (self.queryConfig.select) |_select| _select.params else &[0]_sql.RawQueryParameter{},
|
||||||
if (self.queryConfig.join) |_join| _join.params else &[0]_sql.RawQueryParameter{},
|
if (self.queryConfig.join) |_join| _join.params else &[0]_sql.RawQueryParameter{},
|
||||||
if (inlineRelations) |_inlineRelations| _inlineRelations.join.params else &[0]_sql.RawQueryParameter{},
|
|
||||||
if (self.queryConfig.where) |_where| _where.params else &[0]_sql.RawQueryParameter{},
|
if (self.queryConfig.where) |_where| _where.params else &[0]_sql.RawQueryParameter{},
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,8 +62,7 @@ pub fn typedMany(
|
||||||
};
|
};
|
||||||
|
|
||||||
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
||||||
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig);
|
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null);
|
||||||
const SelectBuilder = _sql.SelectBuilder(ToTable);
|
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -72,12 +71,12 @@ pub fn typedMany(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genJoin(_: *anyopaque, _: std.mem.Allocator, _: []const u8) !_sql.RawQuery {
|
fn genJoin(_: *anyopaque, comptime _: []const u8) []const u8 {
|
||||||
unreachable; // No possible join in a many relation.
|
unreachable; // No possible join in a many relation.
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genSelect(_: *anyopaque, allocator: std.mem.Allocator, table: []const u8, prefix: []const u8) ![]const u8 {
|
fn genSelect(_: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
||||||
return SelectBuilder.build(allocator, table, prefix);
|
return _sql.SelectBuild(ToTable, table, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildQuery(_: *anyopaque, opaqueModels: []const anyopaque, opaqueQuery: *anyopaque) !void {
|
fn buildQuery(_: *anyopaque, opaqueModels: []const anyopaque, opaqueQuery: *anyopaque) !void {
|
||||||
|
@ -181,8 +180,7 @@ fn typedOne(
|
||||||
comptime config: OneConfiguration) type {
|
comptime config: OneConfiguration) type {
|
||||||
|
|
||||||
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
||||||
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig);
|
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null);
|
||||||
const SelectBuilder = _sql.SelectBuilder(ToTable);
|
|
||||||
|
|
||||||
// Get foreign key from relation config or repository config.
|
// Get foreign key from relation config or repository config.
|
||||||
const foreignKey = switch (config) {
|
const foreignKey = switch (config) {
|
||||||
|
@ -205,32 +203,29 @@ fn typedOne(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genJoin(_: *anyopaque, allocator: std.mem.Allocator, alias: []const u8) !_sql.RawQuery {
|
fn genJoin(_: *anyopaque, comptime alias: []const u8) []const u8 {
|
||||||
return switch (config) {
|
return switch (config) {
|
||||||
.direct => (.{
|
.direct => (
|
||||||
.sql = try std.fmt.allocPrint(allocator, "LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"{s}\" ON " ++
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = \"{s}\"." ++ modelKey, .{alias, alias}),
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = \"" ++ alias ++ "\"." ++ modelKey
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
),
|
||||||
}),
|
|
||||||
|
|
||||||
.reverse => (.{
|
.reverse => (
|
||||||
.sql = try std.fmt.allocPrint(allocator, "LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"{s}\" ON " ++
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"{s}\"." ++ foreignKey, .{alias, alias}),
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"" ++ alias ++ "\"." ++ foreignKey
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
),
|
||||||
}),
|
|
||||||
|
|
||||||
.through => |through| (.{
|
.through => |through| (
|
||||||
.sql = try std.fmt.allocPrint(allocator, "LEFT JOIN \"" ++ through.table ++ "\" AS \"{s}_pivot\" ON " ++
|
"LEFT JOIN \"" ++ through.table ++ "\" AS \"" ++ alias ++ "_pivot\" ON " ++
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = " ++ "\"{s}_pivot\"." ++ through.joinForeignKey ++
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = " ++ "\"" ++ alias ++ "_pivot\"." ++ through.joinForeignKey ++
|
||||||
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"{s}\" ON " ++
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
"\"{s}_pivot\"." ++ through.joinModelKey ++ " = " ++ "\"{s}\"." ++ modelKey, .{alias, alias, alias, alias, alias}),
|
"\"" ++ alias ++ "_pivot\"." ++ through.joinModelKey ++ " = " ++ "\"" ++ alias ++ "\"." ++ modelKey
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn genSelect(_: *anyopaque, allocator: std.mem.Allocator, table: []const u8, prefix: []const u8) ![]const u8 {
|
fn genSelect(_: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
||||||
return SelectBuilder.build(allocator, table, prefix);
|
return _sql.SelectBuild(ToTable, table, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildQuery(_: *anyopaque, opaqueModels: []const anyopaque, opaqueQuery: *anyopaque) !void {
|
fn buildQuery(_: *anyopaque, opaqueModels: []const anyopaque, opaqueQuery: *anyopaque) !void {
|
||||||
|
@ -296,8 +291,8 @@ pub const Relation = struct {
|
||||||
instance: *anyopaque,
|
instance: *anyopaque,
|
||||||
|
|
||||||
inlineMapping: *const fn (self: *anyopaque) bool,
|
inlineMapping: *const fn (self: *anyopaque) bool,
|
||||||
genJoin: *const fn (self: *anyopaque, allocator: std.mem.Allocator, alias: []const u8) anyerror!_sql.RawQuery,
|
genJoin: *const fn (self: *anyopaque, comptime alias: []const u8) []const u8,
|
||||||
genSelect: *const fn (self: *anyopaque, allocator: std.mem.Allocator, table: []const u8, prefix: []const u8) anyerror![]const u8,
|
genSelect: *const fn (self: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8,
|
||||||
buildQuery: *const fn (self: *anyopaque, models: []const anyopaque, query: *anyopaque) anyerror!void,
|
buildQuery: *const fn (self: *anyopaque, models: []const anyopaque, query: *anyopaque) anyerror!void,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -308,13 +303,13 @@ pub const Relation = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In case of inline mapping, generate a JOIN clause to retrieve the associated data.
|
/// In case of inline mapping, generate a JOIN clause to retrieve the associated data.
|
||||||
pub fn genJoin(self: Self, allocator: std.mem.Allocator, alias: []const u8) !_sql.RawQuery {
|
pub fn genJoin(self: Self, comptime alias: []const u8) []const u8 {
|
||||||
return self._interface.genJoin(self._interface.instance, allocator, alias);
|
return self._interface.genJoin(self._interface.instance, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a SELECT clause to retrieve the associated data, with the given table and prefix.
|
/// Generate a SELECT clause to retrieve the associated data, with the given table and prefix.
|
||||||
pub fn genSelect(self: Self, allocator: std.mem.Allocator, table: []const u8, prefix: []const u8) ![]const u8 {
|
pub fn genSelect(self: Self, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
||||||
return self._interface.genSelect(self._interface.instance, allocator, table, prefix);
|
return self._interface.genSelect(self._interface.instance, table, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the query to retrieve relation data.
|
/// Build the query to retrieve relation data.
|
||||||
|
|
|
@ -110,7 +110,7 @@ pub fn Repository(comptime Model: type, comptime TableShape: type, comptime repo
|
||||||
pub const TableType = TableShape;
|
pub const TableType = TableShape;
|
||||||
pub const config = repositoryConfig;
|
pub const config = repositoryConfig;
|
||||||
|
|
||||||
pub const Query: type = query.RepositoryQuery(Model, TableShape, config);
|
pub const Query: type = query.RepositoryQuery(Model, TableShape, config, null);
|
||||||
pub const Insert: type = insert.RepositoryInsert(Model, TableShape, config, config.insertShape);
|
pub const Insert: type = insert.RepositoryInsert(Model, TableShape, config, config.insertShape);
|
||||||
|
|
||||||
/// Type of one model key.
|
/// Type of one model key.
|
||||||
|
@ -152,6 +152,10 @@ pub fn Repository(comptime Model: type, comptime TableShape: type, comptime repo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn QueryWith(comptime with: []const _relations.ModelRelation) type {
|
||||||
|
return query.RepositoryQuery(Model, TableShape, config, with);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn InsertCustom(comptime InsertShape: type) type {
|
pub fn InsertCustom(comptime InsertShape: type) type {
|
||||||
return insert.RepositoryInsert(Model, TableShape, config, InsertShape);
|
return insert.RepositoryInsert(Model, TableShape, config, InsertShape);
|
||||||
}
|
}
|
||||||
|
|
23
src/sql.zig
23
src/sql.zig
|
@ -213,6 +213,29 @@ pub fn SelectBuilder(comptime TableShape: type) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a SELECT query part for a given table, renaming columns with the given prefix, at comptime.
|
||||||
|
pub fn SelectBuild(comptime TableShape: type, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
||||||
|
// Initialize the selected columns string.
|
||||||
|
var columnsSelect: []const u8 = "";
|
||||||
|
|
||||||
|
var first = true;
|
||||||
|
// For each field, generate a format string.
|
||||||
|
for (@typeInfo(TableShape).Struct.fields) |field| {
|
||||||
|
// Add ", " between all selected columns (just not the first one).
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
columnsSelect = @ptrCast(columnsSelect ++ ", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the current field column.
|
||||||
|
columnsSelect = @ptrCast(columnsSelect ++ "\"" ++ table ++ "\".\"" ++ field.name ++ "\" AS \"" ++ prefix ++ field.name ++ "\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return built columns selection.
|
||||||
|
return columnsSelect;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,11 @@ test "belongsTo" {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build a query of submodels.
|
// Build a query of submodels.
|
||||||
var myQuery = repository.MySubmodelRepository.Query.init(std.testing.allocator, poolConnector.connector(), .{});
|
var myQuery = repository.MySubmodelRepository.QueryWith(
|
||||||
defer myQuery.deinit();
|
|
||||||
// Retrieve parents of submodels from relation.
|
// Retrieve parents of submodels from relation.
|
||||||
try myQuery.with(repository.MySubmodelRelations.parent);
|
&[_]zrm.relations.ModelRelation{repository.MySubmodelRelations.parent}
|
||||||
|
).init(std.testing.allocator, poolConnector.connector(), .{});
|
||||||
|
defer myQuery.deinit();
|
||||||
|
|
||||||
try myQuery.buildSql();
|
try myQuery.buildSql();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue