Create generic query result mapper and implement it for PostgreSQL.
+ Add generic query result mapper. + Add query result mapper implementation for PostgreSQL.
This commit is contained in:
parent
04b61f9787
commit
e33bc5aaa2
5 changed files with 148 additions and 28 deletions
|
@ -6,6 +6,7 @@ const database = @import("database.zig");
|
||||||
const postgresql = @import("postgresql.zig");
|
const postgresql = @import("postgresql.zig");
|
||||||
const _sql = @import("sql.zig");
|
const _sql = @import("sql.zig");
|
||||||
const repository = @import("repository.zig");
|
const repository = @import("repository.zig");
|
||||||
|
const _result = @import("result.zig");
|
||||||
|
|
||||||
/// Type of an insertable column. Insert shape should be composed of only these.
|
/// Type of an insertable column. Insert shape should be composed of only these.
|
||||||
fn InsertableColumn(comptime ValueType: type) type {
|
fn InsertableColumn(comptime ValueType: type) type {
|
||||||
|
@ -118,6 +119,9 @@ pub fn RepositoryInsert(comptime Model: type, comptime TableShape: type, comptim
|
||||||
|
|
||||||
const Configuration = RepositoryInsertConfiguration(InsertShape);
|
const Configuration = RepositoryInsertConfiguration(InsertShape);
|
||||||
|
|
||||||
|
/// Result mapper type.
|
||||||
|
pub const ResultMapper = _result.ResultMapper(Model, TableShape, repositoryConfig, null, null);
|
||||||
|
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
connector: database.Connector,
|
connector: database.Connector,
|
||||||
connection: *database.Connection = undefined,
|
connection: *database.Connection = undefined,
|
||||||
|
@ -332,7 +336,8 @@ pub fn RepositoryInsert(comptime Model: type, comptime TableShape: type, comptim
|
||||||
defer queryResult.deinit();
|
defer queryResult.deinit();
|
||||||
|
|
||||||
// Map query results.
|
// Map query results.
|
||||||
return postgresql.mapResults(Model, TableShape, repositoryConfig, allocator, queryResult);
|
var postgresqlReader = postgresql.QueryResultReader(TableShape, null).init(queryResult);
|
||||||
|
return try ResultMapper.map(allocator, postgresqlReader.reader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a new repository insert query.
|
/// Initialize a new repository insert query.
|
||||||
|
|
|
@ -7,6 +7,7 @@ const database = @import("database.zig");
|
||||||
const _sql = @import("sql.zig");
|
const _sql = @import("sql.zig");
|
||||||
const _relations = @import("relations.zig");
|
const _relations = @import("relations.zig");
|
||||||
const repository = @import("repository.zig");
|
const repository = @import("repository.zig");
|
||||||
|
const _result = @import("result.zig");
|
||||||
|
|
||||||
/// PostgreSQL query error details.
|
/// PostgreSQL query error details.
|
||||||
pub const PostgresqlError = struct {
|
pub const PostgresqlError = struct {
|
||||||
|
@ -79,32 +80,58 @@ pub fn makeMapper(comptime T: type, result: *pg.Result, allocator: std.mem.Alloc
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic query results mapping.
|
pub fn QueryResultReader(comptime TableShape: type, comptime inlineRelations: ?[]const _relations.ModelRelation) type {
|
||||||
pub fn mapResults(comptime Model: type, comptime TableShape: type,
|
const InstanceInterface = _result.QueryResultReader(TableShape, inlineRelations).Instance;
|
||||||
repositoryConfig: repository.RepositoryConfiguration(Model, TableShape),
|
|
||||||
allocator: std.mem.Allocator, queryResult: *pg.Result) !repository.RepositoryResult(Model)
|
|
||||||
{
|
|
||||||
//TODO make a generic mapper and do it in repository.zig?
|
|
||||||
// Create an arena for mapper data.
|
|
||||||
var mapperArena = std.heap.ArenaAllocator.init(allocator);
|
|
||||||
// Get result mapper.
|
|
||||||
const mapper = try makeMapper(TableShape, queryResult, mapperArena.allocator(), null);
|
|
||||||
|
|
||||||
// Initialize models list.
|
return struct {
|
||||||
var models = std.ArrayList(*Model).init(allocator);
|
const Self = @This();
|
||||||
defer models.deinit();
|
|
||||||
|
|
||||||
// Get all raw models from the result mapper.
|
pub const Instance = struct {
|
||||||
while (try mapper.next()) |rawModel| {
|
/// Main object mapper.
|
||||||
// Parse each raw model from the mapper.
|
mainMapper: pg.Mapper(TableShape) = undefined,
|
||||||
const model = try allocator.create(Model);
|
|
||||||
model.* = try repositoryConfig.fromSql(rawModel);
|
|
||||||
try models.append(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a result with the models.
|
fn next(opaqueSelf: *anyopaque) !?TableShape { //TODO inline relations.
|
||||||
return repository.RepositoryResult(Model).init(allocator,
|
const self: *Instance = @ptrCast(@alignCast(opaqueSelf));
|
||||||
zollections.Collection(Model).init(allocator, try models.toOwnedSlice()),
|
return try self.mainMapper.next();
|
||||||
mapperArena,
|
}
|
||||||
);
|
|
||||||
|
pub fn instance(self: *Instance, allocator: std.mem.Allocator) InstanceInterface {
|
||||||
|
return .{
|
||||||
|
.__interface = .{
|
||||||
|
.instance = self,
|
||||||
|
.next = next,
|
||||||
|
},
|
||||||
|
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
instance: Instance = Instance{},
|
||||||
|
|
||||||
|
/// The PostgreSQL query result.
|
||||||
|
result: *pg.Result,
|
||||||
|
|
||||||
|
fn initInstance(opaqueSelf: *anyopaque, allocator: std.mem.Allocator) !InstanceInterface {
|
||||||
|
const self: *Self = @ptrCast(@alignCast(opaqueSelf));
|
||||||
|
self.instance.mainMapper = try makeMapper(TableShape, self.result, allocator, null);
|
||||||
|
return self.instance.instance(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reader(self: *Self) _result.QueryResultReader(TableShape, inlineRelations) {
|
||||||
|
return .{
|
||||||
|
._interface = .{
|
||||||
|
.instance = self,
|
||||||
|
.init = initInstance,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a PostgreSQL query result reader from the given query result.
|
||||||
|
pub fn init(result: *pg.Result) Self {
|
||||||
|
return .{
|
||||||
|
.result = result,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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 _comptime = @import("comptime.zig");
|
||||||
|
const _result = @import("result.zig");
|
||||||
|
|
||||||
/// Repository query configuration structure.
|
/// Repository query configuration structure.
|
||||||
pub const RepositoryQueryConfiguration = struct {
|
pub const RepositoryQueryConfiguration = struct {
|
||||||
|
@ -19,6 +20,8 @@ pub const RepositoryQueryConfiguration = struct {
|
||||||
|
|
||||||
/// Compiled relations structure.
|
/// Compiled relations structure.
|
||||||
const CompiledRelations = struct {
|
const CompiledRelations = struct {
|
||||||
|
inlineRelations: []relations.ModelRelation,
|
||||||
|
otherRelations: []relations.ModelRelation,
|
||||||
inlineSelect: []const u8,
|
inlineSelect: []const u8,
|
||||||
inlineJoins: []const u8,
|
inlineJoins: []const u8,
|
||||||
};
|
};
|
||||||
|
@ -58,11 +61,15 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
}
|
}
|
||||||
|
|
||||||
break :compile CompiledRelations{
|
break :compile CompiledRelations{
|
||||||
|
.inlineRelations = inlineRelations,
|
||||||
|
.otherRelations = &[0]relations.ModelRelation{},
|
||||||
.inlineSelect = if (inlineSelect.len > 0) ", " ++ _comptime.join(", ", inlineSelect) else "",
|
.inlineSelect = if (inlineSelect.len > 0) ", " ++ _comptime.join(", ", inlineSelect) else "",
|
||||||
.inlineJoins = if (inlineJoins.len > 0) " " ++ _comptime.join(" ", inlineJoins) else "",
|
.inlineJoins = if (inlineJoins.len > 0) " " ++ _comptime.join(" ", inlineJoins) else "",
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
break :compile CompiledRelations{
|
break :compile CompiledRelations{
|
||||||
|
.inlineRelations = &[0]relations.ModelRelation{},
|
||||||
|
.otherRelations = &[0]relations.ModelRelation{},
|
||||||
.inlineSelect = "",
|
.inlineSelect = "",
|
||||||
.inlineJoins = "",
|
.inlineJoins = "",
|
||||||
};
|
};
|
||||||
|
@ -80,6 +87,9 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
/// Result mapper type.
|
||||||
|
pub const ResultMapper = _result.ResultMapper(Model, TableShape, repositoryConfig, compiledRelations.inlineRelations, compiledRelations.otherRelations);
|
||||||
|
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
connector: database.Connector,
|
connector: database.Connector,
|
||||||
connection: *database.Connection = undefined,
|
connection: *database.Connection = undefined,
|
||||||
|
@ -286,7 +296,8 @@ pub fn RepositoryQuery(comptime Model: type, comptime TableShape: type, comptime
|
||||||
defer queryResult.deinit();
|
defer queryResult.deinit();
|
||||||
|
|
||||||
// Map query results.
|
// Map query results.
|
||||||
return postgresql.mapResults(Model, TableShape, repositoryConfig, allocator, queryResult);
|
var postgresqlReader = postgresql.QueryResultReader(TableShape, compiledRelations.inlineRelations).init(queryResult);
|
||||||
|
return try ResultMapper.map(allocator, postgresqlReader.reader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a new repository query.
|
/// Initialize a new repository query.
|
||||||
|
|
72
src/result.zig
Normal file
72
src/result.zig
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const zollections = @import("zollections");
|
||||||
|
const _repository = @import("repository.zig");
|
||||||
|
const _relations = @import("relations.zig");
|
||||||
|
|
||||||
|
/// Generic interface of a query result reader.
|
||||||
|
pub fn QueryResultReader(comptime TableShape: type, comptime inlineRelations: ?[]const _relations.ModelRelation) type {
|
||||||
|
_ = inlineRelations;
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// Generic interface of a query result reader instance.
|
||||||
|
pub const Instance = struct {
|
||||||
|
__interface: struct {
|
||||||
|
instance: *anyopaque,
|
||||||
|
next: *const fn (self: *anyopaque) anyerror!?TableShape, //TODO inline relations.
|
||||||
|
},
|
||||||
|
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
|
pub fn next(self: Instance) !?TableShape {
|
||||||
|
return self.__interface.next(self.__interface.instance);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_interface: struct {
|
||||||
|
instance: *anyopaque,
|
||||||
|
init: *const fn (self: *anyopaque, allocator: std.mem.Allocator) anyerror!Instance,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Initialize a reader instance.
|
||||||
|
pub fn init(self: Self, allocator: std.mem.Allocator) !Instance {
|
||||||
|
return self._interface.init(self._interface.instance, allocator);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map query result to repository model structures, and load the given relations.
|
||||||
|
pub fn ResultMapper(comptime Model: type, comptime TableShape: type, comptime repositoryConfig: _repository.RepositoryConfiguration(Model, TableShape), comptime inlineRelations: ?[]const _relations.ModelRelation, comptime relations: ?[]const _relations.ModelRelation) type {
|
||||||
|
_ = relations;
|
||||||
|
return struct {
|
||||||
|
/// Map the query result to a repository result, with all the required relations.
|
||||||
|
pub fn map(allocator: std.mem.Allocator, queryReader: QueryResultReader(TableShape, inlineRelations)) !_repository.RepositoryResult(Model) {
|
||||||
|
// Create an arena for mapper data.
|
||||||
|
var mapperArena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
|
||||||
|
// Initialize query result reader.
|
||||||
|
const reader = try queryReader.init(mapperArena.allocator());
|
||||||
|
|
||||||
|
// Initialize models list.
|
||||||
|
var models = std.ArrayList(*Model).init(allocator);
|
||||||
|
defer models.deinit();
|
||||||
|
|
||||||
|
// Get all raw models from the result reader.
|
||||||
|
while (try reader.next()) |rawModel| {
|
||||||
|
// Parse each raw model from the reader.
|
||||||
|
const model = try allocator.create(Model);
|
||||||
|
model.* = try repositoryConfig.fromSql(rawModel);
|
||||||
|
//TODO inline relations.
|
||||||
|
try models.append(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO load relations?
|
||||||
|
|
||||||
|
// Return a result with the models.
|
||||||
|
return _repository.RepositoryResult(Model).init(allocator,
|
||||||
|
zollections.Collection(Model).init(allocator, try models.toOwnedSlice()),
|
||||||
|
mapperArena,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ const postgresql = @import("postgresql.zig");
|
||||||
const _sql = @import("sql.zig");
|
const _sql = @import("sql.zig");
|
||||||
const conditions = @import("conditions.zig");
|
const conditions = @import("conditions.zig");
|
||||||
const repository = @import("repository.zig");
|
const repository = @import("repository.zig");
|
||||||
|
const _result = @import("result.zig");
|
||||||
|
|
||||||
/// Repository update query configuration structure.
|
/// Repository update query configuration structure.
|
||||||
pub fn RepositoryUpdateConfiguration(comptime UpdateShape: type) type {
|
pub fn RepositoryUpdateConfiguration(comptime UpdateShape: type) type {
|
||||||
|
@ -57,6 +58,9 @@ pub fn RepositoryUpdate(comptime Model: type, comptime TableShape: type, comptim
|
||||||
|
|
||||||
const Configuration = RepositoryUpdateConfiguration(UpdateShape);
|
const Configuration = RepositoryUpdateConfiguration(UpdateShape);
|
||||||
|
|
||||||
|
/// Result mapper type.
|
||||||
|
pub const ResultMapper = _result.ResultMapper(Model, TableShape, repositoryConfig, null, null);
|
||||||
|
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
connector: database.Connector,
|
connector: database.Connector,
|
||||||
connection: *database.Connection = undefined,
|
connection: *database.Connection = undefined,
|
||||||
|
@ -301,7 +305,8 @@ pub fn RepositoryUpdate(comptime Model: type, comptime TableShape: type, comptim
|
||||||
defer queryResult.deinit();
|
defer queryResult.deinit();
|
||||||
|
|
||||||
// Map query results.
|
// Map query results.
|
||||||
return postgresql.mapResults(Model, TableShape, repositoryConfig, allocator, queryResult);
|
var postgresqlReader = postgresql.QueryResultReader(TableShape, null).init(queryResult);
|
||||||
|
return try ResultMapper.map(allocator, postgresqlReader.reader());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize a new repository update query.
|
/// Initialize a new repository update query.
|
||||||
|
|
Loading…
Reference in a new issue