zrm/src/conditions.zig

181 lines
7 KiB
Zig

const std = @import("std");
const _sql = @import("sql.zig");
const ZrmError = @import("errors.zig").ZrmError;
const Static = @This();
/// Create a value condition on a column.
pub fn value(comptime ValueType: type, allocator: std.mem.Allocator, comptime _column: []const u8, comptime operator: []const u8, _value: ValueType) !_sql.RawQuery {
// Initialize the SQL condition string.
var comptimeSql: [_column.len + 1 + operator.len + 1 + 1]u8 = undefined;
@memcpy(comptimeSql[0.._column.len], _column);
@memcpy(comptimeSql[_column.len.._column.len + 1], " ");
@memcpy(comptimeSql[_column.len + 1..(_column.len + 1 + operator.len)], operator);
@memcpy(comptimeSql[_column.len + 1 + operator.len..], " ?");
// Initialize SQL buffer and set its value to comptime-generated SQL.
const sqlBuf = try allocator.alloc(u8, comptimeSql.len);
std.mem.copyForwards(u8, sqlBuf, &comptimeSql);
// Initialize parameters array.
const params = try allocator.alloc(_sql.RawQueryParameter, 1);
params[0] = try _sql.RawQueryParameter.fromValue(_value);
// Return the built SQL condition.
return .{
.sql = sqlBuf,
.params = params,
};
}
/// Create a column condition on a column.
pub fn column(allocator: std.mem.Allocator, comptime _column: []const u8, comptime operator: []const u8, comptime valueColumn: []const u8) !_sql.RawQuery {
// Initialize the SQL condition string.
var comptimeSql: [_column.len + 1 + operator.len + 1 + valueColumn.len]u8 = undefined;
@memcpy(comptimeSql[0.._column.len], _column);
@memcpy(comptimeSql[_column.len.._column.len + 1], " ");
@memcpy(comptimeSql[_column.len + 1..(_column.len + 1 + operator.len)], operator);
@memcpy(comptimeSql[_column.len + 1 + operator.len.._column.len + 1 + operator.len + 1], " ");
@memcpy(comptimeSql[_column.len + 1 + operator.len + 1..], valueColumn);
// Initialize SQL buffer and set its value to comptime-generated SQL.
const sqlBuf = try allocator.alloc(u8, comptimeSql.len);
std.mem.copyForwards(u8, sqlBuf, &comptimeSql);
// Return the built SQL condition.
return .{
.sql = sqlBuf,
.params = &[0]_sql.RawQueryParameter{},
};
}
/// Create an IN condition on a column.
pub fn in(comptime ValueType: type, allocator: std.mem.Allocator, _column: []const u8, _value: []const ValueType) !_sql.RawQuery {
// Generate parameters SQL.
const parametersSql = try _sql.generateParametersSql(allocator, _value.len);
// Get all query parameters from given values.
var valueParameters: []_sql.RawQueryParameter = try allocator.alloc(_sql.RawQueryParameter, _value.len);
for (0.._value.len) |i| {
// Convert every given value to a query parameter.
valueParameters[i] = try _sql.RawQueryParameter.fromValue(_value[i]);
}
// Initialize the SQL condition string.
var sqlBuf: []u8 = try allocator.alloc(u8, _column.len + 1 + 2 + 1 + 1 + parametersSql.len + 1);
std.mem.copyForwards(u8, sqlBuf[0.._column.len], _column);
std.mem.copyForwards(u8, sqlBuf[_column.len.._column.len + 1 + 2 + 1 + 1], " IN (");
std.mem.copyForwards(u8, sqlBuf[_column.len + 1 + 2 + 1 + 1.._column.len + 1 + 2 + 1 + 1 + parametersSql.len], parametersSql);
std.mem.copyForwards(u8, sqlBuf[_column.len + 1 + 2 + 1 + 1 + parametersSql.len..], ")");
// Return the built SQL condition.
return .{
.sql = sqlBuf,
.params = valueParameters,
};
}
/// Generic conditions combiner generator.
fn conditionsCombiner(comptime keyword: []const u8, allocator: std.mem.Allocator, subconditions: []const _sql.RawQuery) !_sql.RawQuery {
if (subconditions.len == 0) {
// At least one condition is required.
return ZrmError.AtLeastOneConditionRequired;
}
// Full keyword constant.
const fullKeyword = " " ++ keyword ++ " ";
// Compute size of the SQL to generate, and the count of query parameters in total.
var sqlSize: usize = 1 + 1; // parentheses.
var queryParametersCount: usize = 0;
for (subconditions) |subcondition| {
sqlSize += subcondition.sql.len;
queryParametersCount += subcondition.params.len;
}
// There are n-1 keywords.
sqlSize += (subconditions.len - 1) * fullKeyword.len;
// Initialize the SQL condition string.
var sqlBuf = try allocator.alloc(u8, sqlSize);
// Initialize the query parameters array.
var parameters = try allocator.alloc(_sql.RawQueryParameter, queryParametersCount);
var sqlBufCursor: usize = 0; var parametersCursor: usize = 0;
// Add first parenthesis.
sqlBuf[sqlBufCursor] = '('; sqlBufCursor += 1;
// Add all subconditions.
for (0..subconditions.len) |i| {
// Add each subcondition to SQL.
const subcondition = subconditions[i];
std.mem.copyForwards(u8, sqlBuf[sqlBufCursor..sqlBufCursor + subcondition.sql.len], subcondition.sql);
sqlBufCursor += subcondition.sql.len;
if (i < subconditions.len - 1) {
// Append the keyword, if required.
@memcpy(sqlBuf[sqlBufCursor..sqlBufCursor + fullKeyword.len], fullKeyword);
sqlBufCursor += fullKeyword.len;
}
// Add query parameters to the array.
std.mem.copyForwards(_sql.RawQueryParameter, parameters[parametersCursor..parametersCursor+subcondition.params.len], subcondition.params);
parametersCursor += subcondition.params.len;
}
// Add last parenthesis.
sqlBuf[sqlBufCursor] = ')'; sqlBufCursor += 1;
// Return built SQL params.
return .{
.sql = sqlBuf,
.params = parameters,
};
}
/// Create an AND condition between multiple sub-conditions.
pub fn @"and"(allocator: std.mem.Allocator, subconditions: []const _sql.RawQuery) !_sql.RawQuery {
return conditionsCombiner("AND", allocator, subconditions);
}
/// Create an OR condition between multiple sub-conditions.
pub fn @"or"(allocator: std.mem.Allocator, subconditions: []const _sql.RawQuery) !_sql.RawQuery {
return conditionsCombiner("OR", allocator, subconditions);
}
/// A conditions builder.
pub const Builder = struct {
const Self = @This();
allocator: std.mem.Allocator,
/// Create a value condition on a column.
pub fn value(self: Self, comptime ValueType: type, comptime _column: []const u8, comptime operator: []const u8, _value: ValueType) !_sql.RawQuery {
return Static.value(ValueType, self.allocator, _column, operator, _value);
}
/// Create a column condition on a column.
pub fn column(self: Self, comptime _column: []const u8, comptime operator: []const u8, comptime valueColumn: []const u8) !_sql.RawQuery {
return Static.column(self.allocator, _column, operator, valueColumn);
}
/// Create an IN condition on a column.
pub fn in(self: Self, comptime ValueType: type, _column: []const u8, _value: []const ValueType) !_sql.RawQuery {
return Static.in(ValueType, self.allocator, _column, _value);
}
/// Create an AND condition between multiple sub-conditions.
pub fn @"and"(self: Self, subconditions: []const _sql.RawQuery) !_sql.RawQuery {
return Static.@"and"(self.allocator, subconditions);
}
/// Create an OR condition between multiple sub-conditions.
pub fn @"or"(self: Self, subconditions: []const _sql.RawQuery) !_sql.RawQuery {
return Static.@"or"(self.allocator, subconditions);
}
/// Initialize a new conditions builder with the given allocator.
pub fn init(allocator: std.mem.Allocator) Self {
return .{
.allocator = allocator,
};
}
};