2022-07-15 00:58:08 +00:00
|
|
|
const std = @import("std");
|
2022-09-11 11:38:46 +00:00
|
|
|
const util = @import("util");
|
2022-07-15 00:58:08 +00:00
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
const postgres = @import("./engines/postgres.zig");
|
|
|
|
const sqlite = @import("./engines/sqlite.zig");
|
|
|
|
const common = @import("./engines/common.zig");
|
2022-09-11 08:55:20 +00:00
|
|
|
const Allocator = std.mem.Allocator;
|
2022-07-16 19:00:33 +00:00
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
const errors = @import("./errors.zig").library_errors;
|
|
|
|
|
|
|
|
pub const OpenError = errors.OpenError;
|
|
|
|
pub const QueryError = errors.QueryError;
|
|
|
|
pub const RowError = errors.RowError;
|
|
|
|
pub const QueryRowError = errors.QueryRowError;
|
|
|
|
pub const BeginError = errors.BeginError;
|
|
|
|
pub const CommitError = errors.CommitError;
|
|
|
|
|
2022-09-15 01:12:07 +00:00
|
|
|
pub const QueryOptions = common.QueryOptions;
|
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
pub const Engine = enum {
|
2022-09-11 08:55:20 +00:00
|
|
|
postgres,
|
|
|
|
sqlite,
|
2022-07-15 00:58:08 +00:00
|
|
|
};
|
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
pub const Config = union(Engine) {
|
2022-09-11 08:55:20 +00:00
|
|
|
postgres: struct {
|
2022-09-15 01:12:07 +00:00
|
|
|
pg_conn_str: [:0]const u8,
|
2022-09-11 08:55:20 +00:00
|
|
|
},
|
|
|
|
sqlite: struct {
|
2022-09-15 01:12:07 +00:00
|
|
|
sqlite_file_path: [:0]const u8,
|
2022-09-11 08:55:20 +00:00
|
|
|
},
|
2022-07-15 00:58:08 +00:00
|
|
|
};
|
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
const RawResults = union(Engine) {
|
2022-09-11 08:55:20 +00:00
|
|
|
postgres: postgres.Results,
|
|
|
|
sqlite: sqlite.Results,
|
2022-07-15 00:58:08 +00:00
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
fn finish(self: RawResults) void {
|
2022-09-11 08:55:20 +00:00
|
|
|
switch (self) {
|
|
|
|
.postgres => |pg| pg.finish(),
|
|
|
|
.sqlite => |lite| lite.finish(),
|
|
|
|
}
|
2022-07-16 19:00:33 +00:00
|
|
|
}
|
2022-09-29 21:52:01 +00:00
|
|
|
|
|
|
|
fn columnCount(self: RawResults) u15 {
|
|
|
|
return switch (self) {
|
|
|
|
.postgres => |pg| pg.columnCount(),
|
|
|
|
.sqlite => |lite| lite.columnCount(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
fn columnIndex(self: RawResults, name: []const u8) QueryError!u15 {
|
2022-09-29 21:52:01 +00:00
|
|
|
return try switch (self) {
|
2022-10-01 09:05:33 +00:00
|
|
|
.postgres => |pg| pg.columnIndex(name),
|
|
|
|
.sqlite => |lite| lite.columnIndex(name),
|
2022-09-29 21:52:01 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
fn row(self: *RawResults) RowError!?Row {
|
2022-09-29 21:52:01 +00:00
|
|
|
return switch (self.*) {
|
|
|
|
.postgres => |*pg| if (try pg.row()) |r| Row{ .postgres = r } else null,
|
|
|
|
.sqlite => |*lite| if (try lite.row()) |r| Row{ .sqlite = r } else null,
|
|
|
|
};
|
|
|
|
}
|
2022-09-11 11:38:46 +00:00
|
|
|
};
|
2022-07-16 19:00:33 +00:00
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
// Represents a set of results.
|
|
|
|
// row() must be called until it returns null, or the query may not complete
|
|
|
|
// Must be deallocated by a call to finish()
|
2022-09-29 21:52:01 +00:00
|
|
|
pub fn Results(comptime T: type) type {
|
|
|
|
// would normally make this a declaration of the struct, but it causes the compiler to crash
|
|
|
|
const fields = std.meta.fields(T);
|
2022-09-11 11:38:46 +00:00
|
|
|
return struct {
|
|
|
|
const Self = @This();
|
2022-07-15 00:58:08 +00:00
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
underlying: RawResults,
|
2022-09-29 21:52:01 +00:00
|
|
|
column_indices: [fields.len]u15,
|
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
fn from(underlying: RawResults) QueryError!Self {
|
|
|
|
if (std.debug.runtime_safety and std.meta.trait.isTuple(T) and fields.len != underlying.columnCount()) {
|
|
|
|
std.log.err("Expected {} columns in result, got {}", .{ fields.len, underlying.columnCount() });
|
|
|
|
return error.ColumnMismatch;
|
|
|
|
}
|
|
|
|
|
2022-09-29 21:52:01 +00:00
|
|
|
return Self{ .underlying = underlying, .column_indices = blk: {
|
|
|
|
var indices: [fields.len]u15 = undefined;
|
|
|
|
inline for (fields) |f, i| {
|
2022-10-01 09:05:33 +00:00
|
|
|
indices[i] = if (!std.meta.trait.isTuple(T)) try underlying.columnIndex(f.name) else i;
|
2022-09-29 21:52:01 +00:00
|
|
|
}
|
|
|
|
break :blk indices;
|
|
|
|
} };
|
|
|
|
}
|
2022-09-11 11:38:46 +00:00
|
|
|
|
|
|
|
pub fn finish(self: Self) void {
|
|
|
|
self.underlying.finish();
|
|
|
|
}
|
|
|
|
|
2022-09-29 21:52:01 +00:00
|
|
|
// Returns the next row of results, or null if there are no more rows.
|
|
|
|
// Caller owns all memory allocated. The entire object can be deallocated with a
|
|
|
|
// call to util.deepFree
|
2022-10-01 09:05:33 +00:00
|
|
|
pub fn row(self: *Self, alloc: ?Allocator) RowError!?T {
|
2022-09-29 21:52:01 +00:00
|
|
|
if (try self.underlying.row()) |row_val| {
|
|
|
|
var result: T = undefined;
|
|
|
|
var fields_allocated: usize = 0;
|
|
|
|
errdefer inline for (fields) |f, i| {
|
|
|
|
// Iteration bounds must be defined at comptime (inline for) but the number of fields we could
|
|
|
|
// successfully allocate is defined at runtime. So we iterate over the entire field array and
|
|
|
|
// conditionally deallocate fields in the loop.
|
|
|
|
if (i < fields_allocated) util.deepFree(alloc, @field(result, f.name));
|
|
|
|
};
|
|
|
|
|
|
|
|
inline for (fields) |f, i| {
|
|
|
|
@field(result, f.name) = try row_val.get(f.field_type, self.column_indices[i], alloc);
|
|
|
|
fields_allocated += 1;
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
2022-09-29 21:52:01 +00:00
|
|
|
return result;
|
|
|
|
} else return null;
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2022-07-24 04:14:46 +00:00
|
|
|
|
2022-09-11 08:55:20 +00:00
|
|
|
// Row is invalidated by the next call to result.row()
|
2022-09-25 08:10:30 +00:00
|
|
|
const Row = union(Engine) {
|
2022-09-11 08:55:20 +00:00
|
|
|
postgres: postgres.Row,
|
|
|
|
sqlite: sqlite.Row,
|
|
|
|
|
|
|
|
// Returns a value of type T from the zero-indexed column given by idx.
|
|
|
|
// Not all types require an allocator to be present. If an allocator is needed but
|
|
|
|
// not required, it will return error.AllocatorRequired.
|
|
|
|
// The caller is responsible for deallocating T, if relevant.
|
2022-10-01 09:05:33 +00:00
|
|
|
fn get(self: Row, comptime T: type, idx: u15, alloc: ?Allocator) !T {
|
2022-09-11 08:55:20 +00:00
|
|
|
return switch (self) {
|
|
|
|
.postgres => |pg| pg.get(T, idx, alloc),
|
|
|
|
.sqlite => |lite| lite.get(T, idx, alloc),
|
2022-07-15 00:58:08 +00:00
|
|
|
};
|
|
|
|
}
|
2022-09-11 08:55:20 +00:00
|
|
|
};
|
2022-07-15 00:58:08 +00:00
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
const QueryHelper = union(Engine) {
|
2022-09-11 08:55:20 +00:00
|
|
|
postgres: postgres.Db,
|
|
|
|
sqlite: sqlite.Db,
|
2022-10-01 09:05:33 +00:00
|
|
|
|
|
|
|
// internal helper fn
|
|
|
|
fn queryInternal(
|
|
|
|
self: QueryHelper,
|
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
opt: QueryOptions,
|
|
|
|
) !RawResults {
|
|
|
|
return switch (self) {
|
|
|
|
.postgres => |pg| RawResults{ .postgres = try pg.exec(sql, args, opt.prep_allocator) },
|
|
|
|
.sqlite => |lite| RawResults{ .sqlite = try lite.exec(sql, args, opt) },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn queryWithOptions(
|
|
|
|
self: QueryHelper,
|
|
|
|
comptime RowType: type,
|
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
options: QueryOptions,
|
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
return Results(RowType).from(try self.queryInternal(sql, args, options));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Executes a query and returns the result set
|
|
|
|
fn query(
|
|
|
|
self: QueryHelper,
|
|
|
|
comptime RowType: type,
|
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
return self.queryWithOptions(RowType, sql, args, .{ .prep_allocator = alloc });
|
|
|
|
}
|
|
|
|
|
|
|
|
// Executes a query without returning results
|
|
|
|
fn exec(
|
|
|
|
self: QueryHelper,
|
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
|
|
|
) QueryError!void {
|
|
|
|
try self.queryRow(void, sql, args, alloc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Runs a query and returns a single row
|
|
|
|
fn queryRow(
|
|
|
|
self: QueryHelper,
|
|
|
|
comptime RowType: type,
|
|
|
|
q: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-02 05:18:24 +00:00
|
|
|
) QueryRowError!RowType {
|
2022-10-01 09:05:33 +00:00
|
|
|
var results = try self.query(RowType, q, args, alloc);
|
|
|
|
defer results.finish();
|
|
|
|
|
2022-10-02 05:18:24 +00:00
|
|
|
const row = (try results.row(alloc)) orelse return error.NoRows;
|
2022-10-01 09:05:33 +00:00
|
|
|
errdefer util.deepFree(alloc, row);
|
|
|
|
|
2022-10-02 05:18:24 +00:00
|
|
|
// execute query to completion
|
2022-10-01 09:05:33 +00:00
|
|
|
var more_rows = false;
|
|
|
|
while (try results.row(alloc)) |r| {
|
|
|
|
util.deepFree(alloc, r);
|
|
|
|
more_rows = true;
|
|
|
|
}
|
|
|
|
if (more_rows) return error.TooManyRows;
|
|
|
|
|
|
|
|
return row;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inserts a single value into a table
|
|
|
|
fn insert(
|
|
|
|
self: QueryHelper,
|
|
|
|
comptime table: []const u8,
|
|
|
|
value: anytype,
|
2022-10-02 05:18:24 +00:00
|
|
|
alloc: ?std.mem.Allocator,
|
2022-10-01 09:05:33 +00:00
|
|
|
) !void {
|
|
|
|
const ValueType = comptime @TypeOf(value);
|
|
|
|
|
|
|
|
const fields = std.meta.fields(ValueType);
|
|
|
|
comptime var types: [fields.len]type = undefined;
|
|
|
|
comptime var table_spec: []const u8 = table ++ "(";
|
|
|
|
comptime var value_spec: []const u8 = "(";
|
|
|
|
inline for (fields) |field, i| {
|
|
|
|
types[i] = field.field_type;
|
|
|
|
table_spec = comptime (table_spec ++ field.name ++ ",");
|
|
|
|
value_spec = comptime value_spec ++ std.fmt.comptimePrint("${},", .{i + 1});
|
|
|
|
}
|
|
|
|
table_spec = comptime table_spec[0 .. table_spec.len - 1] ++ ")";
|
|
|
|
value_spec = comptime value_spec[0 .. value_spec.len - 1] ++ ")";
|
|
|
|
const q = comptime std.fmt.comptimePrint(
|
|
|
|
"INSERT INTO {s} VALUES {s}",
|
|
|
|
.{ table_spec, value_spec },
|
|
|
|
);
|
|
|
|
|
|
|
|
var args_tuple: std.meta.Tuple(&types) = undefined;
|
|
|
|
inline for (fields) |field, i| {
|
|
|
|
args_tuple[i] = @field(value, field.name);
|
|
|
|
}
|
2022-10-02 05:18:24 +00:00
|
|
|
try self.exec(q, args_tuple, alloc);
|
2022-10-01 09:05:33 +00:00
|
|
|
}
|
2022-09-11 11:38:46 +00:00
|
|
|
};
|
2022-07-15 00:58:08 +00:00
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
pub const ConstraintMode = enum {
|
|
|
|
deferred,
|
|
|
|
immediate,
|
|
|
|
};
|
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
pub const Db = struct {
|
2022-09-25 08:10:30 +00:00
|
|
|
tx_open: bool = false,
|
2022-10-01 09:05:33 +00:00
|
|
|
engine: QueryHelper,
|
2022-09-25 08:10:30 +00:00
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
pub fn open(cfg: Config) OpenError!Db {
|
2022-09-11 08:55:20 +00:00
|
|
|
return switch (cfg) {
|
|
|
|
.postgres => |postgres_cfg| Db{
|
2022-10-01 09:05:33 +00:00
|
|
|
.engine = .{
|
2022-09-15 01:12:07 +00:00
|
|
|
.postgres = try postgres.Db.open(postgres_cfg.pg_conn_str),
|
2022-09-11 11:38:46 +00:00
|
|
|
},
|
2022-09-08 01:55:55 +00:00
|
|
|
},
|
2022-09-11 08:55:20 +00:00
|
|
|
.sqlite => |lite_cfg| Db{
|
2022-10-01 09:05:33 +00:00
|
|
|
.engine = .{
|
2022-09-15 01:12:07 +00:00
|
|
|
.sqlite = try sqlite.Db.open(lite_cfg.sqlite_file_path),
|
2022-09-11 11:38:46 +00:00
|
|
|
},
|
2022-09-08 01:55:55 +00:00
|
|
|
},
|
2022-09-11 08:55:20 +00:00
|
|
|
};
|
|
|
|
}
|
2022-09-11 11:38:46 +00:00
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
pub fn close(self: *Db) void {
|
2022-10-01 09:05:33 +00:00
|
|
|
switch (self.engine) {
|
2022-09-11 08:55:20 +00:00
|
|
|
.postgres => |pg| pg.close(),
|
|
|
|
.sqlite => |lite| lite.close(),
|
2022-09-08 01:55:55 +00:00
|
|
|
}
|
2022-07-15 00:58:08 +00:00
|
|
|
}
|
2022-07-22 04:18:20 +00:00
|
|
|
|
2022-09-15 01:12:07 +00:00
|
|
|
pub fn queryWithOptions(
|
2022-09-25 08:10:30 +00:00
|
|
|
self: *Db,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-09-15 01:12:07 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
opt: QueryOptions,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
|
|
|
return self.engine.queryWithOptions(RowType, sql, args, opt);
|
2022-09-15 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
pub fn query(
|
2022-09-25 08:10:30 +00:00
|
|
|
self: *Db,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-09-11 11:38:46 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
|
|
|
return self.engine.query(RowType, sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn exec(
|
2022-09-25 08:10:30 +00:00
|
|
|
self: *Db,
|
2022-09-11 11:38:46 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!void {
|
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
|
|
|
return self.engine.exec(sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn queryRow(
|
2022-09-25 08:10:30 +00:00
|
|
|
self: *Db,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-09-11 11:38:46 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-02 05:18:24 +00:00
|
|
|
) QueryRowError!RowType {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
|
|
|
return self.engine.queryRow(RowType, sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert(
|
2022-09-25 08:10:30 +00:00
|
|
|
self: *Db,
|
2022-09-11 11:38:46 +00:00
|
|
|
comptime table: []const u8,
|
|
|
|
value: anytype,
|
2022-10-02 05:18:24 +00:00
|
|
|
alloc: ?std.mem.Allocator,
|
2022-09-11 11:38:46 +00:00
|
|
|
) !void {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
2022-10-02 05:18:24 +00:00
|
|
|
return self.engine.insert(table, value, alloc);
|
2022-09-25 08:10:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn sqlEngine(self: *Db) Engine {
|
2022-10-01 09:05:33 +00:00
|
|
|
return self.engine;
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Begins a transaction
|
2022-09-25 08:10:30 +00:00
|
|
|
pub fn begin(self: *Db) !Tx {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (self.tx_open) return error.BadTransactionState;
|
|
|
|
|
2022-10-02 05:18:24 +00:00
|
|
|
try self.exec("BEGIN", {}, null);
|
2022-10-01 09:05:33 +00:00
|
|
|
self.tx_open = true;
|
2022-09-11 11:38:46 +00:00
|
|
|
|
2022-10-02 05:18:24 +00:00
|
|
|
return Tx{ .db = self };
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
pub const Tx = struct {
|
2022-09-25 08:10:30 +00:00
|
|
|
db: *Db,
|
2022-09-11 11:38:46 +00:00
|
|
|
|
2022-09-15 01:12:07 +00:00
|
|
|
pub fn queryWithOptions(
|
|
|
|
self: Tx,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-09-15 01:12:07 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
options: QueryOptions,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
|
|
|
return self.db.engine.queryWithOptions(RowType, sql, args, options);
|
2022-09-15 01:12:07 +00:00
|
|
|
}
|
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
// Executes a query and returns the result set
|
|
|
|
pub fn query(
|
|
|
|
self: Tx,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-09-11 11:38:46 +00:00
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!Results(RowType) {
|
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
|
|
|
return self.db.engine.query(RowType, sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Executes a query without returning results
|
|
|
|
pub fn exec(
|
|
|
|
self: Tx,
|
|
|
|
sql: [:0]const u8,
|
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-01 09:05:33 +00:00
|
|
|
) QueryError!void {
|
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
|
|
|
return self.db.engine.exec(sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Runs a query and returns a single row
|
|
|
|
pub fn queryRow(
|
|
|
|
self: Tx,
|
2022-09-29 21:52:01 +00:00
|
|
|
comptime RowType: type,
|
2022-10-01 09:05:33 +00:00
|
|
|
sql: [:0]const u8,
|
2022-09-11 11:38:46 +00:00
|
|
|
args: anytype,
|
|
|
|
alloc: ?Allocator,
|
2022-10-02 05:18:24 +00:00
|
|
|
) QueryRowError!RowType {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
|
|
|
return self.db.engine.queryRow(RowType, sql, args, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Inserts a single value into a table
|
|
|
|
pub fn insert(
|
|
|
|
self: Tx,
|
|
|
|
comptime table: []const u8,
|
|
|
|
value: anytype,
|
2022-10-02 05:18:24 +00:00
|
|
|
alloc: ?std.mem.Allocator,
|
2022-09-11 11:38:46 +00:00
|
|
|
) !void {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
2022-10-02 05:18:24 +00:00
|
|
|
return self.db.engine.insert(table, value, alloc);
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
|
|
|
|
2022-09-25 08:10:30 +00:00
|
|
|
pub fn sqlEngine(self: Tx) Engine {
|
2022-10-01 09:05:33 +00:00
|
|
|
return self.db.engine;
|
2022-09-25 08:10:30 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 05:18:24 +00:00
|
|
|
// Allows relaxing *some* constraints for the lifetime of the transaction.
|
|
|
|
// You should generally not do this, but it's useful when bootstrapping
|
|
|
|
// the initial admin community and cluster operator user.
|
2022-10-01 09:05:33 +00:00
|
|
|
pub fn setConstraintMode(self: Tx, mode: ConstraintMode) QueryError!void {
|
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
|
|
|
switch (self.db.engine) {
|
2022-09-25 08:10:30 +00:00
|
|
|
.sqlite => try self.exec(
|
|
|
|
switch (mode) {
|
|
|
|
.immediate => "PRAGMA defer_foreign_keys = FALSE",
|
|
|
|
.deferred => "PRAGMA defer_foreign_keys = TRUE",
|
|
|
|
},
|
2022-10-01 09:05:33 +00:00
|
|
|
{},
|
2022-09-25 08:10:30 +00:00
|
|
|
null,
|
|
|
|
),
|
|
|
|
.postgres => try self.exec(
|
|
|
|
switch (mode) {
|
|
|
|
.immediate => "SET CONSTRAINTS ALL IMMEDIATE",
|
|
|
|
.deferred => "SET CONSTRAINTS ALL DEFERRED",
|
|
|
|
},
|
2022-10-01 09:05:33 +00:00
|
|
|
{},
|
2022-09-25 08:10:30 +00:00
|
|
|
null,
|
|
|
|
),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-11 11:38:46 +00:00
|
|
|
pub fn rollback(self: Tx) void {
|
2022-10-01 09:05:33 +00:00
|
|
|
if (!self.db.tx_open) @panic("Transaction not open");
|
|
|
|
self.exec("ROLLBACK", {}, null) catch |err| {
|
2022-09-11 11:38:46 +00:00
|
|
|
std.log.err("Error occured during rollback operation: {}", .{err});
|
2022-09-11 08:55:20 +00:00
|
|
|
};
|
2022-10-01 09:05:33 +00:00
|
|
|
self.db.tx_open = false;
|
2022-07-22 04:18:20 +00:00
|
|
|
}
|
2022-09-11 11:38:46 +00:00
|
|
|
|
2022-10-01 09:05:33 +00:00
|
|
|
pub fn commit(self: Tx) CommitError!void {
|
|
|
|
if (!self.db.tx_open) return error.BadTransactionState;
|
2022-10-02 05:18:24 +00:00
|
|
|
self.exec("COMMIT", {}, null) catch |err| switch (err) {
|
|
|
|
error.InvalidArgument,
|
|
|
|
error.OutOfMemory,
|
|
|
|
error.UndefinedParameter,
|
|
|
|
=> return error.Unexpected,
|
|
|
|
|
|
|
|
else => return err,
|
|
|
|
};
|
2022-10-01 09:05:33 +00:00
|
|
|
self.db.tx_open = false;
|
2022-09-11 11:38:46 +00:00
|
|
|
}
|
2022-07-15 00:58:08 +00:00
|
|
|
};
|