Bind query parameters

This commit is contained in:
jaina heartles 2023-07-06 01:52:41 -07:00
parent 81df6d8461
commit 3365ce7a11
2 changed files with 85 additions and 3 deletions

View file

@ -84,12 +84,94 @@ pub const Engine = struct {
sql: []const u8,
args: []const SqlValue,
opts: QueryOptions,
alloc: Allocator,
_: Allocator,
) ExecError!Results {
const self: *Engine = @ptrCast(*Engine, @alignCast(@alignOf(Engine), ctx));
_ = .{ self, sql, args, opts, alloc };
var stmt: ?*c.sqlite3_stmt = undefined;
switch (c.sqlite3_prepare_v2(self.conn, sql.ptr, @intCast(c_int, sql.len), &stmt, null)) {
c.SQLITE_OK => {},
else => |err| return handleUnexpectedError(self.conn, err, sql),
}
errdefer switch (c.sqlite3_finalize(stmt)) {
c.SQLITE_OK => {},
else => |err| {
handleUnexpectedError(self.conn, err, sql) catch {};
},
};
if (args.len != 0) {
for (args) |arg, i| {
const buf_size = 21; // ceil(log10(2^64)) + 1
var name_buf: [buf_size]u8 = undefined;
const name = std.fmt.bufPrintZ(&name_buf, "{}", .{i}) catch unreachable;
const db_idx = c.sqlite3_bind_parameter_index(stmt.?, name.ptr);
if (db_idx != 0)
try self.bindArgument(stmt.?, @intCast(u15, db_idx), arg)
else if (!opts.ignore_unused_arguments)
return error.UnusedArgument;
}
}
@panic("unimplemented");
}
fn bindArgument(self: *Engine, stmt: *c.sqlite3_stmt, idx: u15, val: SqlValue) !void {
return switch (val) {
.int => |v| self.bindInt(stmt, idx, i64, v),
.uint => |v| self.bindInt(stmt, idx, u64, v),
.str => |str| {
const len = std.math.cast(c_int, str.len) orelse {
std.log.err("SQLite: string len {} too large", .{str.len});
return error.BindException;
};
switch (c.sqlite3_bind_text(stmt, idx, str.ptr, len, c.SQLITE_TRANSIENT)) {
c.SQLITE_OK => {},
else => |result| {
std.log.err("SQLite: Unable to bind string to index {}", .{idx});
std.log.debug("SQLite: {s}", .{str});
return handleUnexpectedError(self.conn, result, null);
},
}
},
.float => |v| {
switch (c.sqlite3_bind_double(stmt, idx, v)) {
c.SQLITE_OK => {},
else => |result| {
std.log.err("SQLite: Unable to bind float to index {}", .{idx});
std.log.debug("SQLite: {}", .{v});
return handleUnexpectedError(self.conn, result, null);
},
}
},
.@"null" => {
switch (c.sqlite3_bind_null(stmt, idx)) {
c.SQLITE_OK => {},
else => |result| {
std.log.err("SQLite: Unable to bind NULL to index {}", .{idx});
return handleUnexpectedError(self.conn, result, null);
},
}
},
};
}
fn bindInt(self: *Engine, stmt: *c.sqlite3_stmt, idx: u15, comptime T: type, val: T) !void {
const v = std.math.cast(i64, val) orelse {
std.log.err("SQLite: integer {} does not fit within i64", .{val});
return error.BindException;
};
switch (c.sqlite3_bind_int64(stmt, idx, v)) {
c.SQLITE_OK => {},
else => |result| {
std.log.err("SQLite: Unable to bind int to index {}", .{idx});
std.log.debug("SQLite: {}", .{v});
return handleUnexpectedError(self.conn, result, null);
},
}
}
};
fn getCharPos(text: []const u8, offset: c_int) struct { row: usize, col: usize } {

View file

@ -107,5 +107,5 @@ test "test" {
const db = engine.db();
_ = db;
_ = try db.vtable.exec(db.ptr, "CREATE TABLE foo(bar INT PRIMARY KEY);", &.{}, .{}, std.testing.allocator);
}