Easier access to relations types and comptime values and improved definition process.
This commit is contained in:
parent
ce5aeffd2c
commit
97c4c429e1
6 changed files with 325 additions and 357 deletions
|
@ -162,7 +162,7 @@ pub fn makeMapper(comptime T: type, result: *pg.Result, allocator: std.mem.Alloc
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PostgreSQL implementation of the query result reader.
|
/// PostgreSQL implementation of the query result reader.
|
||||||
pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?type, comptime inlineRelations: ?[]const _relations.ModelRelation) type {
|
pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?type, comptime inlineRelations: ?[]const _relations.Relation) type {
|
||||||
const InstanceInterface = _result.QueryResultReader(TableShape, MetadataShape, inlineRelations).Instance;
|
const InstanceInterface = _result.QueryResultReader(TableShape, MetadataShape, inlineRelations).Instance;
|
||||||
|
|
||||||
// Build relations mappers container type.
|
// Build relations mappers container type.
|
||||||
|
@ -173,9 +173,7 @@ pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?typ
|
||||||
|
|
||||||
for (_inlineRelations, &fields) |relation, *field| {
|
for (_inlineRelations, &fields) |relation, *field| {
|
||||||
// Get relation field type (TableShape of the related value).
|
// Get relation field type (TableShape of the related value).
|
||||||
var relationImpl = relation.relation{};
|
const relationFieldType = PgMapper(relation.TableShape);
|
||||||
const relationInstanceType = @TypeOf(relationImpl.relation());
|
|
||||||
const relationFieldType = PgMapper(relationInstanceType.TableShape);
|
|
||||||
|
|
||||||
field.* = .{
|
field.* = .{
|
||||||
.name = relation.field ++ [0:0]u8{},
|
.name = relation.field ++ [0:0]u8{},
|
||||||
|
@ -278,11 +276,8 @@ pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?typ
|
||||||
if (inlineRelations) |_inlineRelations| {
|
if (inlineRelations) |_inlineRelations| {
|
||||||
// Initialize mapper for each relation.
|
// Initialize mapper for each relation.
|
||||||
inline for (_inlineRelations) |relation| {
|
inline for (_inlineRelations) |relation| {
|
||||||
// Get relation field type (TableShape of the related value).
|
|
||||||
comptime var relationImpl = relation.relation{};
|
|
||||||
const relationInstanceType = @TypeOf(relationImpl.relation());
|
|
||||||
@field(self.instance.relationsMappers, relation.field) =
|
@field(self.instance.relationsMappers, relation.field) =
|
||||||
try makeMapper(relationInstanceType.TableShape, self.result, allocator, "relations." ++ relation.field ++ ".");
|
try makeMapper(relation.TableShape, self.result, allocator, "relations." ++ relation.field ++ ".");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,20 +20,20 @@ pub const RepositoryQueryConfiguration = struct {
|
||||||
|
|
||||||
/// Compiled relations structure.
|
/// Compiled relations structure.
|
||||||
const CompiledRelations = struct {
|
const CompiledRelations = struct {
|
||||||
inlineRelations: []relations.ModelRelation,
|
inlineRelations: []relations.Relation,
|
||||||
otherRelations: []relations.ModelRelation,
|
otherRelations: []relations.Relation,
|
||||||
inlineSelect: []const u8,
|
inlineSelect: []const u8,
|
||||||
inlineJoins: []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), comptime with: ?[]const relations.ModelRelation, comptime MetadataShape: ?type) type {
|
pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime repositoryConfig: repository.RepositoryConfiguration(Model, TableShape), comptime with: ?[]const relations.Relation, comptime MetadataShape: ?type) type {
|
||||||
const compiledRelations = comptime compile: {
|
const compiledRelations = comptime compile: {
|
||||||
// Inline relations list.
|
// Inline relations list.
|
||||||
var inlineRelations: []relations.ModelRelation = &[0]relations.ModelRelation{};
|
var inlineRelations: []relations.Relation = &[0]relations.Relation{};
|
||||||
// Other relations list.
|
// Other relations list.
|
||||||
var otherRelations: []relations.ModelRelation = &[0]relations.ModelRelation{};
|
var otherRelations: []relations.Relation = &[0]relations.Relation{};
|
||||||
|
|
||||||
if (with) |_with| {
|
if (with) |_with| {
|
||||||
// If there are relations to eager load, prepare their query.
|
// If there are relations to eager load, prepare their query.
|
||||||
|
@ -45,20 +45,14 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
|
|
||||||
for (_with) |relation| {
|
for (_with) |relation| {
|
||||||
// For each relation, determine if it's inline or not.
|
// For each relation, determine if it's inline or not.
|
||||||
var relationImpl = relation.relation{};
|
if (relation.inlineMapping) {
|
||||||
const relationInstance = relationImpl.relation();
|
|
||||||
if (relationInstance.inlineMapping()) {
|
|
||||||
// Add the current relation to inline relations.
|
// Add the current relation to inline relations.
|
||||||
inlineRelations = @ptrCast(@constCast(_comptime.append(inlineRelations, relation)));
|
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.
|
// Generate selected columns for the relation.
|
||||||
inlineSelect = @ptrCast(@constCast(_comptime.append(inlineSelect, relationInstance.genSelect(tableAlias, fieldsPrefix))));
|
inlineSelect = @ptrCast(@constCast(_comptime.append(inlineSelect, relation.select)));
|
||||||
// Generate joined table for the relation.
|
// Generate joined table for the relation.
|
||||||
inlineJoins = @ptrCast(@constCast(_comptime.append(inlineJoins, relationInstance.genJoin(tableAlias))));
|
inlineJoins = @ptrCast(@constCast(_comptime.append(inlineJoins, relation.join)));
|
||||||
} else {
|
} else {
|
||||||
// Add the current relation to other relations.
|
// Add the current relation to other relations.
|
||||||
otherRelations = @ptrCast(@constCast(_comptime.append(otherRelations, relation)));
|
otherRelations = @ptrCast(@constCast(_comptime.append(otherRelations, relation)));
|
||||||
|
@ -73,8 +67,8 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
break :compile CompiledRelations{
|
break :compile CompiledRelations{
|
||||||
.inlineRelations = &[0]relations.ModelRelation{},
|
.inlineRelations = &[0]relations.Relation{},
|
||||||
.otherRelations = &[0]relations.ModelRelation{},
|
.otherRelations = &[0]relations.Relation{},
|
||||||
.inlineSelect = "",
|
.inlineSelect = "",
|
||||||
.inlineJoins = "",
|
.inlineJoins = "",
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,116 +50,106 @@ pub fn typedMany(
|
||||||
comptime toRepositoryConfig: repository.RepositoryConfiguration(ToModel, ToTable),
|
comptime toRepositoryConfig: repository.RepositoryConfiguration(ToModel, ToTable),
|
||||||
comptime config: ManyConfiguration) type {
|
comptime config: ManyConfiguration) type {
|
||||||
|
|
||||||
// Get foreign key from relation config or repository config.
|
|
||||||
const foreignKey = switch (config) {
|
|
||||||
.direct => |direct| direct.foreignKey,
|
|
||||||
.through => |through| if (through.foreignKey) |_foreignKey| _foreignKey else toRepositoryConfig.key[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get model key from relation config or repository config.
|
|
||||||
const modelKey = switch (config) {
|
|
||||||
.direct => |direct| if (direct.modelKey) |_modelKey| _modelKey else fromRepositoryConfig.key[0],
|
|
||||||
.through => |through| if (through.modelKey) |_modelKey| _modelKey else fromRepositoryConfig.key[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
|
||||||
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null, struct {
|
|
||||||
__zrm_relation_key: FromKeyType,
|
|
||||||
});
|
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
/// Relation implementation.
|
||||||
|
pub fn Implementation(field: []const u8) type {
|
||||||
fn getRepositoryConfiguration(_: *anyopaque) repository.RepositoryConfiguration(ToModel, ToTable) {
|
// Get foreign key from relation config or repository config.
|
||||||
return toRepositoryConfig;
|
const foreignKey = switch (config) {
|
||||||
}
|
.direct => |direct| direct.foreignKey,
|
||||||
|
.through => |through| if (through.foreignKey) |_foreignKey| _foreignKey else toRepositoryConfig.key[0],
|
||||||
fn inlineMapping(_: *anyopaque) bool {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn genJoin(_: *anyopaque, comptime _: []const u8) []const u8 {
|
|
||||||
unreachable; // No possible join in a many relation.
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _genSelect(comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
|
||||||
return _sql.SelectBuild(ToTable, table, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn genSelect(_: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
|
||||||
return _genSelect(table, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn buildQuery(_: *anyopaque, prefix: []const u8, opaqueModels: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
|
||||||
const models: []const *FromModel = @ptrCast(@alignCast(opaqueModels));
|
|
||||||
|
|
||||||
// Initialize the query to build.
|
|
||||||
const query: *QueryType = try allocator.create(QueryType);
|
|
||||||
errdefer allocator.destroy(query);
|
|
||||||
query.* = QueryType.init(allocator, connector, .{});
|
|
||||||
errdefer query.deinit();
|
|
||||||
|
|
||||||
// Build base SELECT.
|
|
||||||
const baseSelect = comptime _genSelect(toRepositoryConfig.table, "");
|
|
||||||
|
|
||||||
// Prepare given models IDs.
|
|
||||||
const modelsIds = try query.arena.allocator().alloc(FromKeyType, models.len);
|
|
||||||
for (models, modelsIds) |model, *modelId| {
|
|
||||||
modelId.* = @field(model, fromRepositoryConfig.key[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config) {
|
|
||||||
.direct => {
|
|
||||||
// Add SELECT.
|
|
||||||
query.select(.{
|
|
||||||
.sql = baseSelect ++ ", \"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\" AS \"__zrm_relation_key\"",
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build WHERE condition.
|
|
||||||
try query.whereIn(FromKeyType, "\"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\"", modelsIds);
|
|
||||||
},
|
|
||||||
.through => |through| {
|
|
||||||
// Add SELECT.
|
|
||||||
query.select(.{
|
|
||||||
.sql = try std.fmt.allocPrint(query.arena.allocator(), baseSelect ++ ", \"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\" AS \"__zrm_relation_key\"", .{prefix}),
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
query.join(.{
|
|
||||||
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ through.table ++ "\" ON AS \"{s}pivot" ++ "\" " ++
|
|
||||||
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = " ++ "\"{s}pivot" ++ "\"." ++ through.joinModelKey, .{prefix, prefix}),
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build WHERE condition.
|
|
||||||
try query.whereIn(FromKeyType, try std.fmt.allocPrint(query.arena.allocator(), "\"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\"", .{prefix}), modelsIds);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return query; // Return built query.
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn relation(self: *Self) Relation(ToModel, ToTable) {
|
|
||||||
return .{
|
|
||||||
._interface = .{
|
|
||||||
.instance = self,
|
|
||||||
|
|
||||||
.getRepositoryConfiguration = getRepositoryConfiguration,
|
|
||||||
.inlineMapping = inlineMapping,
|
|
||||||
.genJoin = genJoin,
|
|
||||||
.genSelect = genSelect,
|
|
||||||
},
|
|
||||||
.QueryType = QueryType,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runtimeRelation(self: *Self) RuntimeRelation {
|
// Get model key from relation config or repository config.
|
||||||
return .{
|
const modelKey = switch (config) {
|
||||||
._interface = .{
|
.direct => |direct| if (direct.modelKey) |_modelKey| _modelKey else fromRepositoryConfig.key[0],
|
||||||
.instance = self,
|
.through => |through| if (through.modelKey) |_modelKey| _modelKey else fromRepositoryConfig.key[0],
|
||||||
.buildQuery = buildQuery,
|
};
|
||||||
},
|
|
||||||
|
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
||||||
|
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null, struct {
|
||||||
|
__zrm_relation_key: FromKeyType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const alias = "relations." ++ field;
|
||||||
|
const prefix = alias ++ ".";
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
fn genSelect() []const u8 {
|
||||||
|
return _sql.SelectBuild(ToTable, alias, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildQuery(opaqueModels: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
||||||
|
const models: []const *FromModel = @ptrCast(@alignCast(opaqueModels));
|
||||||
|
|
||||||
|
// Initialize the query to build.
|
||||||
|
const query: *QueryType = try allocator.create(QueryType);
|
||||||
|
errdefer allocator.destroy(query);
|
||||||
|
query.* = QueryType.init(allocator, connector, .{});
|
||||||
|
errdefer query.deinit();
|
||||||
|
|
||||||
|
// Build base SELECT.
|
||||||
|
const baseSelect = comptime _sql.SelectBuild(ToTable, toRepositoryConfig.table, "");
|
||||||
|
|
||||||
|
// Prepare given models IDs.
|
||||||
|
const modelsIds = try query.arena.allocator().alloc(FromKeyType, models.len);
|
||||||
|
for (models, modelsIds) |model, *modelId| {
|
||||||
|
modelId.* = @field(model, fromRepositoryConfig.key[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config) {
|
||||||
|
.direct => {
|
||||||
|
// Add SELECT.
|
||||||
|
query.select(.{
|
||||||
|
.sql = baseSelect ++ ", \"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\" AS \"__zrm_relation_key\"",
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build WHERE condition.
|
||||||
|
try query.whereIn(FromKeyType, "\"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\"", modelsIds);
|
||||||
|
},
|
||||||
|
.through => |through| {
|
||||||
|
// Add SELECT.
|
||||||
|
query.select(.{
|
||||||
|
.sql = try std.fmt.allocPrint(query.arena.allocator(), baseSelect ++ ", \"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\" AS \"__zrm_relation_key\"", .{prefix}),
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
query.join(.{
|
||||||
|
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ through.table ++ "\" ON AS \"{s}pivot" ++ "\" " ++
|
||||||
|
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = " ++ "\"{s}pivot" ++ "\"." ++ through.joinModelKey, .{prefix, prefix}),
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build WHERE condition.
|
||||||
|
try query.whereIn(FromKeyType, try std.fmt.allocPrint(query.arena.allocator(), "\"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\"", .{prefix}), modelsIds);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return query; // Return built query.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the "many" generic relation.
|
||||||
|
pub fn relation(_: Self) Relation {
|
||||||
|
return .{
|
||||||
|
._interface = .{
|
||||||
|
.repositoryConfiguration = &toRepositoryConfig,
|
||||||
|
|
||||||
|
.buildQuery = buildQuery,
|
||||||
|
},
|
||||||
|
.Model = ToModel,
|
||||||
|
.TableShape = ToTable,
|
||||||
|
.field = field,
|
||||||
|
.alias = alias,
|
||||||
|
.prefix = prefix,
|
||||||
|
.QueryType = QueryType,
|
||||||
|
|
||||||
|
.inlineMapping = false,
|
||||||
|
.join = undefined,
|
||||||
|
.select = genSelect(),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -220,228 +210,198 @@ fn typedOne(
|
||||||
comptime toRepositoryConfig: repository.RepositoryConfiguration(ToModel, ToTable),
|
comptime toRepositoryConfig: repository.RepositoryConfiguration(ToModel, ToTable),
|
||||||
comptime config: OneConfiguration) type {
|
comptime config: OneConfiguration) type {
|
||||||
|
|
||||||
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
|
||||||
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null, struct {
|
|
||||||
__zrm_relation_key: FromKeyType,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get foreign key from relation config or repository config.
|
|
||||||
const foreignKey = switch (config) {
|
|
||||||
.direct => |direct| direct.foreignKey,
|
|
||||||
.reverse => |reverse| reverse.foreignKey,
|
|
||||||
.through => |through| if (through.foreignKey) |_foreignKey| _foreignKey else fromRepositoryConfig.key[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get model key from relation config or repository config.
|
|
||||||
const modelKey = switch (config) {
|
|
||||||
.direct => |direct| if (direct.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
|
||||||
.reverse => |reverse| if (reverse.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
|
||||||
.through => |through| if (through.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
|
||||||
};
|
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
pub fn Implementation(field: []const u8) type {
|
||||||
|
const FromKeyType = std.meta.fields(FromModel)[std.meta.fieldIndex(FromModel, fromRepositoryConfig.key[0]).?].type;
|
||||||
|
const QueryType = _query.RepositoryQuery(ToModel, ToTable, toRepositoryConfig, null, struct {
|
||||||
|
__zrm_relation_key: FromKeyType,
|
||||||
|
});
|
||||||
|
|
||||||
fn getRepositoryConfiguration(_: *anyopaque) repository.RepositoryConfiguration(ToModel, ToTable) {
|
// Get foreign key from relation config or repository config.
|
||||||
return toRepositoryConfig;
|
const foreignKey = switch (config) {
|
||||||
}
|
.direct => |direct| direct.foreignKey,
|
||||||
|
.reverse => |reverse| reverse.foreignKey,
|
||||||
fn inlineMapping(_: *anyopaque) bool {
|
.through => |through| if (through.foreignKey) |_foreignKey| _foreignKey else fromRepositoryConfig.key[0],
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn genJoin(_: *anyopaque, comptime alias: []const u8) []const u8 {
|
|
||||||
return switch (config) {
|
|
||||||
.direct => (
|
|
||||||
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = \"" ++ alias ++ "\"." ++ modelKey
|
|
||||||
),
|
|
||||||
|
|
||||||
.reverse => (
|
|
||||||
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"" ++ alias ++ "\"." ++ foreignKey
|
|
||||||
),
|
|
||||||
|
|
||||||
.through => |through| (
|
|
||||||
"LEFT JOIN \"" ++ through.table ++ "\" AS \"" ++ alias ++ "_pivot\" ON " ++
|
|
||||||
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = " ++ "\"" ++ alias ++ "_pivot\"." ++ through.joinForeignKey ++
|
|
||||||
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
|
||||||
"\"" ++ alias ++ "_pivot\"." ++ through.joinModelKey ++ " = " ++ "\"" ++ alias ++ "\"." ++ modelKey
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
fn _genSelect(comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
// Get model key from relation config or repository config.
|
||||||
return _sql.SelectBuild(ToTable, table, prefix);
|
const modelKey = switch (config) {
|
||||||
}
|
.direct => |direct| if (direct.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
||||||
|
.reverse => |reverse| if (reverse.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
||||||
fn genSelect(_: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
.through => |through| if (through.modelKey) |_modelKey| _modelKey else toRepositoryConfig.key[0],
|
||||||
return _genSelect(table, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn buildQuery(_: *anyopaque, prefix: []const u8, opaqueModels: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
|
||||||
const models: []const *FromModel = @ptrCast(@alignCast(opaqueModels));
|
|
||||||
|
|
||||||
// Initialize the query to build.
|
|
||||||
const query: *QueryType = try allocator.create(QueryType);
|
|
||||||
errdefer allocator.destroy(query);
|
|
||||||
query.* = QueryType.init(allocator, connector, .{});
|
|
||||||
errdefer query.deinit();
|
|
||||||
|
|
||||||
// Build base SELECT.
|
|
||||||
const baseSelect = comptime _genSelect(toRepositoryConfig.table, "");
|
|
||||||
|
|
||||||
// Prepare given models IDs.
|
|
||||||
const modelsIds = try query.arena.allocator().alloc(FromKeyType, models.len);
|
|
||||||
for (models, modelsIds) |model, *modelId| {
|
|
||||||
modelId.* = @field(model, fromRepositoryConfig.key[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config) {
|
|
||||||
.direct => {
|
|
||||||
// Add SELECT.
|
|
||||||
query.select(.{
|
|
||||||
.sql = baseSelect ++ ", \"" ++ fromRepositoryConfig.table ++ "\".\"" ++ fromRepositoryConfig.key[0] ++ "\" AS \"__zrm_relation_key\"",
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
query.join((_sql.RawQuery{
|
|
||||||
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ fromRepositoryConfig.table ++ "\" AS \"{s}related" ++ "\" ON " ++
|
|
||||||
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"{s}related" ++ "\"." ++ foreignKey, .{prefix, prefix}),
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Build WHERE condition.
|
|
||||||
try query.whereIn(FromKeyType, "\"" ++ fromRepositoryConfig.table ++ "\".\"" ++ fromRepositoryConfig.key[0] ++ "\"", modelsIds);
|
|
||||||
},
|
|
||||||
.reverse => {
|
|
||||||
// Add SELECT.
|
|
||||||
query.select(.{
|
|
||||||
.sql = baseSelect ++ ", \"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\" AS \"__zrm_relation_key\"",
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build WHERE condition.
|
|
||||||
try query.whereIn(FromKeyType, "\"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\"", modelsIds);
|
|
||||||
},
|
|
||||||
.through => |through| {
|
|
||||||
// Add SELECT.
|
|
||||||
query.select(.{
|
|
||||||
.sql = try std.fmt.allocPrint(query.arena.allocator(), baseSelect ++ ", \"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\" AS \"__zrm_relation_key\"", .{prefix}),
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
query.join(.{
|
|
||||||
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ through.table ++ "\" AS \"{s}pivot" ++ "\" ON " ++
|
|
||||||
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = " ++ "\"{s}pivot" ++ "\"." ++ through.joinModelKey, .{prefix, prefix}),
|
|
||||||
.params = &[0]_sql.RawQueryParameter{},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Build WHERE condition.
|
|
||||||
try query.whereIn(FromKeyType, try std.fmt.allocPrint(query.arena.allocator(), "\"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\"", .{prefix}), modelsIds);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return built query.
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn relation(self: *Self) Relation(ToModel, ToTable) {
|
|
||||||
return .{
|
|
||||||
._interface = .{
|
|
||||||
.instance = self,
|
|
||||||
|
|
||||||
.getRepositoryConfiguration = getRepositoryConfiguration,
|
|
||||||
.inlineMapping = inlineMapping,
|
|
||||||
.genJoin = genJoin,
|
|
||||||
.genSelect = genSelect,
|
|
||||||
},
|
|
||||||
.QueryType = QueryType,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runtimeRelation(self: *Self) RuntimeRelation {
|
const alias = "relations." ++ field;
|
||||||
return .{
|
const prefix = alias ++ ".";
|
||||||
._interface = .{
|
|
||||||
.instance = self,
|
return struct {
|
||||||
.buildQuery = buildQuery,
|
const Self = @This();
|
||||||
},
|
|
||||||
|
fn genJoin() []const u8 {
|
||||||
|
return switch (config) {
|
||||||
|
.direct => (
|
||||||
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = \"" ++ alias ++ "\"." ++ modelKey
|
||||||
|
),
|
||||||
|
|
||||||
|
.reverse => (
|
||||||
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"" ++ alias ++ "\"." ++ foreignKey
|
||||||
|
),
|
||||||
|
|
||||||
|
.through => |through| (
|
||||||
|
"LEFT JOIN \"" ++ through.table ++ "\" AS \"" ++ alias ++ "_pivot\" ON " ++
|
||||||
|
"\"" ++ fromRepositoryConfig.table ++ "\"." ++ foreignKey ++ " = " ++ "\"" ++ alias ++ "_pivot\"." ++ through.joinForeignKey ++
|
||||||
|
"LEFT JOIN \"" ++ toRepositoryConfig.table ++ "\" AS \"" ++ alias ++ "\" ON " ++
|
||||||
|
"\"" ++ alias ++ "_pivot\"." ++ through.joinModelKey ++ " = " ++ "\"" ++ alias ++ "\"." ++ modelKey
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn genSelect() []const u8 {
|
||||||
|
return _sql.SelectBuild(ToTable, alias, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buildQuery(opaqueModels: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
||||||
|
const models: []const *FromModel = @ptrCast(@alignCast(opaqueModels));
|
||||||
|
|
||||||
|
// Initialize the query to build.
|
||||||
|
const query: *QueryType = try allocator.create(QueryType);
|
||||||
|
errdefer allocator.destroy(query);
|
||||||
|
query.* = QueryType.init(allocator, connector, .{});
|
||||||
|
errdefer query.deinit();
|
||||||
|
|
||||||
|
// Build base SELECT.
|
||||||
|
const baseSelect = comptime _sql.SelectBuild(ToTable, toRepositoryConfig.table, "");
|
||||||
|
|
||||||
|
// Prepare given models IDs.
|
||||||
|
const modelsIds = try query.arena.allocator().alloc(FromKeyType, models.len);
|
||||||
|
for (models, modelsIds) |model, *modelId| {
|
||||||
|
modelId.* = @field(model, fromRepositoryConfig.key[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config) {
|
||||||
|
.direct => {
|
||||||
|
// Add SELECT.
|
||||||
|
query.select(.{
|
||||||
|
.sql = baseSelect ++ ", \"" ++ fromRepositoryConfig.table ++ "\".\"" ++ fromRepositoryConfig.key[0] ++ "\" AS \"__zrm_relation_key\"",
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
query.join((_sql.RawQuery{
|
||||||
|
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ fromRepositoryConfig.table ++ "\" AS \"{s}related" ++ "\" ON " ++
|
||||||
|
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = \"{s}related" ++ "\"." ++ foreignKey, .{prefix, prefix}),
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Build WHERE condition.
|
||||||
|
try query.whereIn(FromKeyType, "\"" ++ fromRepositoryConfig.table ++ "\".\"" ++ fromRepositoryConfig.key[0] ++ "\"", modelsIds);
|
||||||
|
},
|
||||||
|
.reverse => {
|
||||||
|
// Add SELECT.
|
||||||
|
query.select(.{
|
||||||
|
.sql = baseSelect ++ ", \"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\" AS \"__zrm_relation_key\"",
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build WHERE condition.
|
||||||
|
try query.whereIn(FromKeyType, "\"" ++ toRepositoryConfig.table ++ "\".\"" ++ foreignKey ++ "\"", modelsIds);
|
||||||
|
},
|
||||||
|
.through => |through| {
|
||||||
|
// Add SELECT.
|
||||||
|
query.select(.{
|
||||||
|
.sql = try std.fmt.allocPrint(query.arena.allocator(), baseSelect ++ ", \"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\" AS \"__zrm_relation_key\"", .{prefix}),
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
query.join(.{
|
||||||
|
.sql = try std.fmt.allocPrint(query.arena.allocator(), "INNER JOIN \"" ++ through.table ++ "\" AS \"{s}pivot" ++ "\" ON " ++
|
||||||
|
"\"" ++ toRepositoryConfig.table ++ "\"." ++ modelKey ++ " = " ++ "\"{s}pivot" ++ "\"." ++ through.joinModelKey, .{prefix, prefix}),
|
||||||
|
.params = &[0]_sql.RawQueryParameter{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Build WHERE condition.
|
||||||
|
try query.whereIn(FromKeyType, try std.fmt.allocPrint(query.arena.allocator(), "\"{s}pivot" ++ "\".\"" ++ through.joinForeignKey ++ "\"", .{prefix}), modelsIds);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return built query.
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the "one" generic relation.
|
||||||
|
pub fn relation(_: Self) Relation {
|
||||||
|
return .{
|
||||||
|
._interface = .{
|
||||||
|
.repositoryConfiguration = &toRepositoryConfig,
|
||||||
|
|
||||||
|
.buildQuery = buildQuery,
|
||||||
|
},
|
||||||
|
.Model = ToModel,
|
||||||
|
.TableShape = ToTable,
|
||||||
|
.field = field,
|
||||||
|
.alias = alias,
|
||||||
|
.prefix = prefix,
|
||||||
|
.QueryType = QueryType,
|
||||||
|
|
||||||
|
.inlineMapping = true,
|
||||||
|
.join = genJoin(),
|
||||||
|
.select = genSelect(),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic model relation interface.
|
/// Generic model relation interface.
|
||||||
pub fn Relation(comptime ToModel: type, comptime ToTable: type) type {
|
pub const Relation = struct {
|
||||||
return struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Model = ToModel;
|
|
||||||
pub const TableShape = ToTable;
|
|
||||||
|
|
||||||
_interface: struct {
|
|
||||||
instance: *anyopaque,
|
|
||||||
|
|
||||||
getRepositoryConfiguration: *const fn (self: *anyopaque) repository.RepositoryConfiguration(ToModel, ToTable),
|
|
||||||
inlineMapping: *const fn (self: *anyopaque) bool,
|
|
||||||
genJoin: *const fn (self: *anyopaque, comptime alias: []const u8) []const u8,
|
|
||||||
genSelect: *const fn (self: *anyopaque, comptime table: []const u8, comptime prefix: []const u8) []const u8,
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryType: type,
|
|
||||||
|
|
||||||
/// Read the related model repository configuration.
|
|
||||||
pub fn getRepositoryConfiguration(self: Self) repository.RepositoryConfiguration(ToModel, ToTable) {
|
|
||||||
return self._interface.getRepositoryConfiguration(self._interface.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Relation mapping is done inline: this means that it's done at the same time the model is mapped,
|
|
||||||
/// and that the associated data will be retrieved in the main query.
|
|
||||||
pub fn inlineMapping(self: Self) bool {
|
|
||||||
return self._interface.inlineMapping(self._interface.instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In case of inline mapping, generate a JOIN clause to retrieve the associated data.
|
|
||||||
pub fn genJoin(self: Self, comptime alias: []const u8) []const u8 {
|
|
||||||
return self._interface.genJoin(self._interface.instance, alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a SELECT clause to retrieve the associated data, with the given table and prefix.
|
|
||||||
pub fn genSelect(self: Self, comptime table: []const u8, comptime prefix: []const u8) []const u8 {
|
|
||||||
return self._interface.genSelect(self._interface.instance, table, prefix);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generic model runtime relation interface.
|
|
||||||
pub const RuntimeRelation = struct {
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
_interface: struct {
|
_interface: struct {
|
||||||
instance: *anyopaque,
|
repositoryConfiguration: *const anyopaque,
|
||||||
|
|
||||||
buildQuery: *const fn (self: *anyopaque, prefix: []const u8, models: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) anyerror!*anyopaque,
|
buildQuery: *const fn (models: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) anyerror!*anyopaque,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Type of the related model.
|
||||||
|
Model: type,
|
||||||
|
/// Type of the related model table.
|
||||||
|
TableShape: type,
|
||||||
|
/// Field where to put the related model(s).
|
||||||
|
field: []const u8,
|
||||||
|
/// Table alias of the relation.
|
||||||
|
alias: []const u8,
|
||||||
|
/// Prefix of fields of the relation.
|
||||||
|
prefix: []const u8,
|
||||||
|
/// Type of a query of the related models.
|
||||||
|
QueryType: type,
|
||||||
|
|
||||||
|
/// Set if relation mapping is done inline: this means that it's done at the same time the model is mapped,
|
||||||
|
/// and that the associated data will be retrieved in the main query.
|
||||||
|
inlineMapping: bool,
|
||||||
|
/// In case of inline mapping, the JOIN clause to retrieve the associated data.
|
||||||
|
join: []const u8,
|
||||||
|
/// The SELECT clause to retrieve the associated data.
|
||||||
|
select: []const u8,
|
||||||
|
|
||||||
/// Build the query to retrieve relation data.
|
/// Build the query to retrieve relation data.
|
||||||
/// Is always used when inline mapping is not possible, but also when loading relations lazily.
|
/// Is always used when inline mapping is not possible, but also when loading relations lazily.
|
||||||
pub fn buildQuery(self: Self, prefix: []const u8, models: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
pub fn buildQuery(self: Self, models: []const *anyopaque, allocator: std.mem.Allocator, connector: _database.Connector) !*anyopaque {
|
||||||
return self._interface.buildQuery(self._interface.instance, prefix, models, allocator, connector);
|
return self._interface.buildQuery(models, allocator, connector);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
/// Get typed repository configuration for the related model.
|
||||||
/// A model relation object.
|
pub fn repositoryConfiguration(self: Self) repository.RepositoryConfiguration(self.Model, self.TableShape) {
|
||||||
pub const ModelRelation = struct {
|
const repoConfig: *const repository.RepositoryConfiguration(self.Model, self.TableShape)
|
||||||
relation: type,
|
= @ptrCast(@alignCast(self._interface.repositoryConfiguration));
|
||||||
field: []const u8,
|
return repoConfig.*;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// Structure of an eager loaded relation.
|
/// Structure of an eager loaded relation.
|
||||||
pub const Eager = struct {
|
pub const Eager = struct {
|
||||||
/// Model field to fill for the relation.
|
|
||||||
field: []const u8,
|
|
||||||
/// The relation to eager load.
|
/// The relation to eager load.
|
||||||
relation: Relation,
|
relation: Relation,
|
||||||
/// Subrelations to eager load.
|
/// Subrelations to eager load.
|
||||||
|
|
|
@ -78,18 +78,46 @@ pub fn ModelKeyType(comptime Model: type, comptime TableShape: type, comptime co
|
||||||
pub fn RelationsDefinitionType(comptime rawDefinition: anytype) type {
|
pub fn RelationsDefinitionType(comptime rawDefinition: anytype) type {
|
||||||
const rawDefinitionType = @typeInfo(@TypeOf(rawDefinition));
|
const rawDefinitionType = @typeInfo(@TypeOf(rawDefinition));
|
||||||
|
|
||||||
// Build model relations fields.
|
// Build relations fields and implementations fields.
|
||||||
var fields: [rawDefinitionType.Struct.fields.len]std.builtin.Type.StructField = undefined;
|
var fields: [1 + rawDefinitionType.Struct.fields.len]std.builtin.Type.StructField = undefined;
|
||||||
inline for (rawDefinitionType.Struct.fields, &fields) |originalField, *field| {
|
var implementationsFields: [rawDefinitionType.Struct.fields.len]std.builtin.Type.StructField = undefined;
|
||||||
field.* = .{
|
|
||||||
|
inline for (rawDefinitionType.Struct.fields, fields[1..], &implementationsFields) |originalField, *field, *implementationField| {
|
||||||
|
field.* = std.builtin.Type.StructField{
|
||||||
.name = originalField.name,
|
.name = originalField.name,
|
||||||
.type = _relations.ModelRelation,
|
.type = _relations.Relation,
|
||||||
.default_value = null,
|
.default_value = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(_relations.ModelRelation),
|
.alignment = @alignOf(type),
|
||||||
|
};
|
||||||
|
|
||||||
|
const ImplementationType = @field(rawDefinition, originalField.name).Implementation(originalField.name);
|
||||||
|
implementationField.* = std.builtin.Type.StructField{
|
||||||
|
.name = originalField.name,
|
||||||
|
.type = ImplementationType,
|
||||||
|
.default_value = &ImplementationType{},
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = @alignOf(ImplementationType),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add implementations field.
|
||||||
|
const ImplementationsType = @Type(.{
|
||||||
|
.Struct = std.builtin.Type.Struct{
|
||||||
|
.layout = std.builtin.Type.ContainerLayout.auto,
|
||||||
|
.fields = &implementationsFields,
|
||||||
|
.decls = &[_]std.builtin.Type.Declaration{},
|
||||||
|
.is_tuple = false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
fields[0] = std.builtin.Type.StructField{
|
||||||
|
.name = "_implementations",
|
||||||
|
.type = ImplementationsType,
|
||||||
|
.default_value = null,
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = @alignOf(ImplementationsType),
|
||||||
|
};
|
||||||
|
|
||||||
// Return built type.
|
// Return built type.
|
||||||
return @Type(.{
|
return @Type(.{
|
||||||
.Struct = std.builtin.Type.Struct{
|
.Struct = std.builtin.Type.Struct{
|
||||||
|
@ -134,17 +162,16 @@ pub fn Repository(comptime Model: type, comptime TableShape: type, comptime repo
|
||||||
// Initialize final relations definition.
|
// Initialize final relations definition.
|
||||||
var definition: RelationsDefinitionType(rawDefinition) = undefined;
|
var definition: RelationsDefinitionType(rawDefinition) = undefined;
|
||||||
|
|
||||||
|
definition._implementations = .{};
|
||||||
|
|
||||||
// Check that the definition structure only include known fields.
|
// Check that the definition structure only include known fields.
|
||||||
inline for (std.meta.fieldNames(rawDefinitionType)) |fieldName| {
|
inline for (std.meta.fieldNames(rawDefinitionType)) |fieldName| {
|
||||||
if (!@hasField(Model, fieldName)) {
|
if (!@hasField(Model, fieldName)) {
|
||||||
@compileError("No corresponding field for relation " ++ fieldName);
|
@compileError("No corresponding field for relation " ++ fieldName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Alter definition structure to add the field name.
|
// Alter definition structure to set the relation instance.
|
||||||
@field(definition, fieldName) = .{
|
@field(definition, fieldName) = @field(definition._implementations, fieldName).relation();
|
||||||
.relation = @field(rawDefinition, fieldName),
|
|
||||||
.field = fieldName,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return altered definition structure.
|
// Return altered definition structure.
|
||||||
|
@ -152,7 +179,7 @@ pub fn Repository(comptime Model: type, comptime TableShape: type, comptime repo
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn QueryWith(comptime with: []const _relations.ModelRelation) type {
|
pub fn QueryWith(comptime with: []const _relations.Relation) type {
|
||||||
return query.RepositoryQuery(Model, TableShape, config, with, null);
|
return query.RepositoryQuery(Model, TableShape, config, with, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn ModelWithMetadata(comptime Model: type, comptime MetadataShape: ?type) ty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type of a retrieved table data, with its retrieved relations.
|
/// Type of a retrieved table data, with its retrieved relations.
|
||||||
pub fn TableWithRelations(comptime TableShape: type, comptime MetadataShape: ?type, comptime optionalRelations: ?[]const _relations.ModelRelation) type {
|
pub fn TableWithRelations(comptime TableShape: type, comptime MetadataShape: ?type, comptime optionalRelations: ?[]const _relations.Relation) type {
|
||||||
if (optionalRelations) |relations| {
|
if (optionalRelations) |relations| {
|
||||||
const tableType = @typeInfo(TableShape);
|
const tableType = @typeInfo(TableShape);
|
||||||
|
|
||||||
|
@ -29,11 +29,9 @@ pub fn TableWithRelations(comptime TableShape: type, comptime MetadataShape: ?ty
|
||||||
// For each relation, create a new struct field in the table shape.
|
// For each relation, create a new struct field in the table shape.
|
||||||
for (relations, fields[tableType.Struct.fields.len..(tableType.Struct.fields.len+relations.len)]) |relation, *field| {
|
for (relations, fields[tableType.Struct.fields.len..(tableType.Struct.fields.len+relations.len)]) |relation, *field| {
|
||||||
// Get relation field type (optional TableShape of the related value).
|
// Get relation field type (optional TableShape of the related value).
|
||||||
comptime var relationImpl = relation.relation{};
|
|
||||||
const relationInstanceType = @TypeOf(relationImpl.relation());
|
|
||||||
const relationFieldType = @Type(std.builtin.Type{
|
const relationFieldType = @Type(std.builtin.Type{
|
||||||
.Optional = .{
|
.Optional = .{
|
||||||
.child = relationInstanceType.TableShape
|
.child = relation.TableShape
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -74,7 +72,7 @@ pub fn TableWithRelations(comptime TableShape: type, comptime MetadataShape: ?ty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a value of the fully retrieved type to the TableShape type.
|
/// Convert a value of the fully retrieved type to the TableShape type.
|
||||||
pub fn toTableShape(comptime TableShape: type, comptime MetadataShape: ?type, comptime optionalRelations: ?[]const _relations.ModelRelation, value: TableWithRelations(TableShape, MetadataShape, optionalRelations)) TableShape {
|
pub fn toTableShape(comptime TableShape: type, comptime MetadataShape: ?type, comptime optionalRelations: ?[]const _relations.Relation, value: TableWithRelations(TableShape, MetadataShape, optionalRelations)) TableShape {
|
||||||
if (optionalRelations) |_| {
|
if (optionalRelations) |_| {
|
||||||
// Make a structure of TableShape type.
|
// Make a structure of TableShape type.
|
||||||
var tableValue: TableShape = undefined;
|
var tableValue: TableShape = undefined;
|
||||||
|
@ -93,7 +91,7 @@ pub fn toTableShape(comptime TableShape: type, comptime MetadataShape: ?type, co
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic interface of a query result reader.
|
/// Generic interface of a query result reader.
|
||||||
pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?type, comptime inlineRelations: ?[]const _relations.ModelRelation) type {
|
pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?type, comptime inlineRelations: ?[]const _relations.Relation) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
@ -124,7 +122,7 @@ pub fn QueryResultReader(comptime TableShape: type, comptime MetadataShape: ?typ
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map query result to repository model structures, and load the given relations.
|
/// Map query result to repository model structures, and load the given relations.
|
||||||
pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime MetadataShape: ?type, comptime repositoryConfig: _repository.RepositoryConfiguration(Model, TableShape), comptime inlineRelations: ?[]const _relations.ModelRelation, comptime relations: ?[]const _relations.ModelRelation) type {
|
pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime MetadataShape: ?type, comptime repositoryConfig: _repository.RepositoryConfiguration(Model, TableShape), comptime inlineRelations: ?[]const _relations.Relation, comptime relations: ?[]const _relations.Relation) type {
|
||||||
return struct {
|
return struct {
|
||||||
/// Map the query result to a repository result, with all the required relations.
|
/// Map the query result to a repository result, with all the required relations.
|
||||||
pub fn map(comptime withMetadata: bool, allocator: std.mem.Allocator, connector: _database.Connector, queryReader: QueryResultReader(TableShape, MetadataShape, inlineRelations)) !_repository.RepositoryResult(if (withMetadata) ModelWithMetadata(Model, MetadataShape) else Model) {
|
pub fn map(comptime withMetadata: bool, allocator: std.mem.Allocator, connector: _database.Connector, queryReader: QueryResultReader(TableShape, MetadataShape, inlineRelations)) !_repository.RepositoryResult(if (withMetadata) ModelWithMetadata(Model, MetadataShape) else Model) {
|
||||||
|
@ -151,12 +149,10 @@ pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime Me
|
||||||
if (inlineRelations) |_inlineRelations| {
|
if (inlineRelations) |_inlineRelations| {
|
||||||
// If there are loaded inline relations, map them to the result.
|
// If there are loaded inline relations, map them to the result.
|
||||||
inline for (_inlineRelations) |relation| {
|
inline for (_inlineRelations) |relation| {
|
||||||
comptime var relationImpl = relation.relation{};
|
|
||||||
const relationInstance = relationImpl.relation();
|
|
||||||
// Set the read inline relation value.
|
// Set the read inline relation value.
|
||||||
@field(model.*, relation.field) = (
|
@field(model.*, relation.field) = (
|
||||||
if (@field(rawModel, relation.field)) |relationVal|
|
if (@field(rawModel, relation.field)) |relationVal|
|
||||||
try relationInstance.getRepositoryConfiguration().fromSql(relationVal)
|
try relation.repositoryConfiguration().fromSql(relationVal)
|
||||||
else null
|
else null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -172,13 +168,9 @@ pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime Me
|
||||||
|
|
||||||
if (relations) |relationsToLoad| {
|
if (relations) |relationsToLoad| {
|
||||||
inline for (relationsToLoad) |relation| {
|
inline for (relationsToLoad) |relation| {
|
||||||
const comptimeRelation = @constCast(&relation.relation{}).relation();
|
|
||||||
var relationImpl = relation.relation{};
|
|
||||||
const relationInstance = relationImpl.runtimeRelation();
|
|
||||||
|
|
||||||
// Build query for the relation to get.
|
// Build query for the relation to get.
|
||||||
const query: *comptimeRelation.QueryType = @ptrCast(@alignCast(
|
const query: *relation.QueryType = @ptrCast(@alignCast(
|
||||||
try relationInstance.buildQuery("relations." ++ relation.field ++ ".", @ptrCast(models.items), allocator, connector)
|
try relation.buildQuery(@ptrCast(models.items), allocator, connector)
|
||||||
));
|
));
|
||||||
defer {
|
defer {
|
||||||
query.deinit();
|
query.deinit();
|
||||||
|
|
|
@ -34,7 +34,7 @@ test "belongsTo" {
|
||||||
// Build a query of submodels.
|
// Build a query of submodels.
|
||||||
var myQuery = repository.MySubmodelRepository.QueryWith(
|
var myQuery = repository.MySubmodelRepository.QueryWith(
|
||||||
// Retrieve parents of submodels from relation.
|
// Retrieve parents of submodels from relation.
|
||||||
&[_]zrm.relations.ModelRelation{repository.MySubmodelRelations.parent}
|
&[_]zrm.relations.Relation{repository.MySubmodelRelations.parent}
|
||||||
).init(std.testing.allocator, poolConnector.connector(), .{});
|
).init(std.testing.allocator, poolConnector.connector(), .{});
|
||||||
defer myQuery.deinit();
|
defer myQuery.deinit();
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@ test "hasMany" {
|
||||||
|
|
||||||
// Build a query of submodels.
|
// Build a query of submodels.
|
||||||
var myQuery = repository.MyModelRepository.QueryWith(
|
var myQuery = repository.MyModelRepository.QueryWith(
|
||||||
// Retrieve parents of submodels from relation.
|
// Retrieve parents of submodels from relation.
|
||||||
&[_]zrm.relations.ModelRelation{repository.MyModelRelations.submodels}
|
&[_]zrm.relations.Relation{repository.MyModelRelations.submodels}
|
||||||
).init(std.testing.allocator, poolConnector.connector(), .{});
|
).init(std.testing.allocator, poolConnector.connector(), .{});
|
||||||
defer myQuery.deinit();
|
defer myQuery.deinit();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue