Add open/close methods
This commit is contained in:
parent
ed515c059d
commit
81df6d8461
2 changed files with 95 additions and 12 deletions
|
@ -1,7 +1,7 @@
|
||||||
const common = @import("../lib.zig");
|
const common = @import("../lib.zig");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const c = @cImport({
|
const c = @cImport({
|
||||||
//@cInclude("sqlite3.h");
|
@cInclude("sqlite3.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
const QueryOptions = common.QueryOptions;
|
const QueryOptions = common.QueryOptions;
|
||||||
|
@ -12,22 +12,71 @@ const Results = common.Results;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const Engine = struct {
|
pub const Engine = struct {
|
||||||
//db: *c.sqlite3,
|
conn: *c.sqlite3,
|
||||||
dummy: usize = 0,
|
|
||||||
|
pub fn open(path: [:0]const u8) !Engine {
|
||||||
|
return openInternal(path, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn openUri(path: [:0]const u8) !Engine {
|
||||||
|
return openInternal(path, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn openInternal(path: [:0]const u8, is_uri: bool) !Engine {
|
||||||
|
const flags = c.SQLITE_OPEN_READWRITE | c.SQLITE_OPEN_CREATE | c.SQLITE_OPEN_EXRESCODE | if (is_uri) c.SQLITE_OPEN_URI else 0;
|
||||||
|
|
||||||
|
var conn: ?*c.sqlite3 = null;
|
||||||
|
switch (c.sqlite3_open_v2(@ptrCast([*c]const u8, path), &conn, flags, null)) {
|
||||||
|
c.SQLITE_OK => {},
|
||||||
|
else => |code| {
|
||||||
|
if (conn == null) {
|
||||||
|
// this path should only be hit if out of memory, but log it anyways
|
||||||
|
std.log.err(
|
||||||
|
"Unable to open SQLite DB \"{s}\". Error: {?s} ({})",
|
||||||
|
.{ path, c.sqlite3_errstr(code), code },
|
||||||
|
);
|
||||||
|
return error.BadConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ext_code = c.sqlite3_extended_errcode(conn);
|
||||||
|
std.log.err(
|
||||||
|
\\Unable to open SQLite DB "{s}". Error: {?s} ({})
|
||||||
|
\\Details: {?s}
|
||||||
|
,
|
||||||
|
.{ path, c.sqlite3_errstr(ext_code), ext_code, c.sqlite3_errmsg(conn) },
|
||||||
|
);
|
||||||
|
|
||||||
|
return error.Unexpected;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return Engine{
|
||||||
|
.conn = conn.?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn db(self: *Engine) common.Db {
|
pub fn db(self: *Engine) common.Db {
|
||||||
return .{
|
return .{
|
||||||
.ptr = self,
|
.ptr = self,
|
||||||
.vtable = &.{
|
.vtable = &.{
|
||||||
.close = Engine.close,
|
|
||||||
.exec = Engine.exec,
|
.exec = Engine.exec,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close(ctx: *anyopaque) void {
|
pub fn close(self: *Engine) void {
|
||||||
const self: *Engine = @ptrCast(*Engine, @alignCast(@alignOf(Engine), ctx));
|
switch (c.sqlite3_close(self.conn)) {
|
||||||
_ = self;
|
c.SQLITE_OK => {},
|
||||||
|
|
||||||
|
c.SQLITE_BUSY => {
|
||||||
|
std.log.err("SQLite DB could not be closed as it is busy.\n{s}", .{c.sqlite3_errmsg(self.conn)});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => |err| {
|
||||||
|
std.log.err("Could not close SQLite DB", .{});
|
||||||
|
handleUnexpectedError(self.conn, err, null) catch {};
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec(
|
fn exec(
|
||||||
|
@ -42,3 +91,39 @@ pub const Engine = struct {
|
||||||
@panic("unimplemented");
|
@panic("unimplemented");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn getCharPos(text: []const u8, offset: c_int) struct { row: usize, col: usize } {
|
||||||
|
var row: usize = 0;
|
||||||
|
var col: usize = 0;
|
||||||
|
var i: usize = 0;
|
||||||
|
|
||||||
|
if (offset > text.len) return .{ .row = 0, .col = 0 };
|
||||||
|
|
||||||
|
while (i != offset) : (i += 1) {
|
||||||
|
if (text[i] == '\n') {
|
||||||
|
row += 1;
|
||||||
|
col = 0;
|
||||||
|
} else {
|
||||||
|
col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .row = row, .col = col };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleUnexpectedError(db: *c.sqlite3, code: c_int, sql_text: ?[]const u8) error{Unexpected} {
|
||||||
|
std.log.err("Unexpected error in SQLite engine: {s} ({})", .{ c.sqlite3_errstr(code), code });
|
||||||
|
|
||||||
|
std.log.debug("Additional details:", .{});
|
||||||
|
std.log.debug("{?s}", .{c.sqlite3_errmsg(db)});
|
||||||
|
if (sql_text) |sql| {
|
||||||
|
const byte_offset = c.sqlite3_error_offset(db);
|
||||||
|
if (byte_offset >= 0) {
|
||||||
|
const pos = getCharPos(sql, byte_offset);
|
||||||
|
std.log.debug("Failed at char ({}:{}) of SQL:\n{s}", .{ pos.row, pos.col, sql });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std.log.debug("{?}", .{@errorReturnTrace()});
|
||||||
|
|
||||||
|
return error.Unexpected;
|
||||||
|
}
|
||||||
|
|
|
@ -48,9 +48,6 @@ pub const ExecError = error{
|
||||||
|
|
||||||
pub const Db = struct {
|
pub const Db = struct {
|
||||||
pub const VTable = struct {
|
pub const VTable = struct {
|
||||||
/// Closes the database connection
|
|
||||||
close: *const fn (ctx: *anyopaque) void,
|
|
||||||
|
|
||||||
/// Executes a SQL query.
|
/// Executes a SQL query.
|
||||||
/// All memory allocated with this allocator must be freed before this function returns.
|
/// All memory allocated with this allocator must be freed before this function returns.
|
||||||
exec: *const fn (ctx: *anyopaque, sql: []const u8, args: []const SqlValue, opt: QueryOptions, allocator: Allocator) ExecError!Results,
|
exec: *const fn (ctx: *anyopaque, sql: []const u8, args: []const SqlValue, opt: QueryOptions, allocator: Allocator) ExecError!Results,
|
||||||
|
@ -104,8 +101,9 @@ pub const Row = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
test "test" {
|
test "test" {
|
||||||
const backend = @import("./engines/sqlite2.zig");
|
const backend = @import("./engines/sqlite.zig");
|
||||||
var engine: backend.Engine = undefined;
|
var engine = try backend.Engine.open(":memory:");
|
||||||
|
defer engine.close();
|
||||||
|
|
||||||
const db = engine.db();
|
const db = engine.db();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue