Skip to content

Commit

Permalink
check for sqlite version at runtime
Browse files Browse the repository at this point in the history
Runtime check for sqlite version is needed for loading the extension at runtime. Bump the minimum version of sqlite to 3.26.0 to simplify module initialization
  • Loading branch information
dgllghr committed Jan 24, 2024
1 parent fd4aa73 commit 68c81ce
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 94 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ jobs:
matrix:
os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
sqlite:
- version: 3.23.1
- version: 3.26.0
year: 2018
- version: 3.29.0
- version: 3.30.1
year: 2019
- version: 3.35.5
- version: 3.34.1
year: 2021
- version: 3.38.5
year: 2022
- version: 3.41.2
year: 2023
- version: 3.44.2
- version: 3.42.0
year: 2023
- version: 3.45.0
year: 2024
runs-on: ${{ matrix.os }}
env:
SQLITE_VERSION: ${{ matrix.sqlite.version }}
Expand Down
12 changes: 11 additions & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator;

const c = @import("sqlite3/c.zig").c;
const c_mod = @import("sqlite3/c.zig");
const c = c_mod.c;
const encodeSqliteVersion = c_mod.encodeVersionNumber;
const sqliteVersion = c_mod.versionNumber;
const sqlite = @import("sqlite3.zig");
const sqlite_errors = sqlite.errors;
const vtab = sqlite.vtab;
Expand All @@ -18,13 +21,20 @@ const SegmentInfoTabValFn = vtab.VirtualTable(SegmentInfoFn);

var allocator: GeneralPurposeAllocator(.{}) = undefined;

const min_sqlite_version = encodeSqliteVersion(3, 26, 0);

pub export fn sqlite3_stanchion_init(
db: *c.sqlite3,
err_msg: [*c][*c]u8,
api: *c.sqlite3_api_routines,
) callconv(.C) c_int {
c.sqlite3_api = api;

if (sqliteVersion() < min_sqlite_version) {
err_msg.* = @constCast(@ptrCast("stanchion requires sqlite version >= 3.26.0"));
return c.SQLITE_ERROR;
}

// To store data common to all table instances (global to the module), replace this allocator
// with a struct containing the common data (see `ModuleContext` in `zig-sqlite`)
allocator = GeneralPurposeAllocator(.{}){};
Expand Down
15 changes: 6 additions & 9 deletions src/sqlite3/c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ else
@cInclude("result-transient.h");
});

/// versionGreaterThanOrEqualTo returns true if the SQLite version is >= to the
/// major.minor.patch provided.
pub fn versionGreaterThanOrEqualTo(major: u8, minor: u8, patch: u8) bool {
return c.SQLITE_VERSION_NUMBER >=
@as(u32, major) * 1000000 + @as(u32, minor) * 1000 + @as(u32, patch);
/// Returns the sqlite version encoded as a number
pub fn versionNumber() u32 {
return @intCast(c.sqlite3_libversion_number());
}

comptime {
if (!versionGreaterThanOrEqualTo(3, 23, 1)) {
@compileError("must use SQLite >= 3.23.1");
}
/// Generates a sqlite encoded version number as an integer from major.minor.patch
pub fn encodeVersionNumber(major: u8, minor: u8, patch: u8) u32 {
return @as(u32, major) * 1000000 + @as(u32, minor) * 1000 + @as(u32, patch);
}
38 changes: 13 additions & 25 deletions src/sqlite3/errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
const std = @import("std");
const mem = std.mem;

const c = @import("c.zig").c;
const versionGreaterThanOrEqualTo = @import("c.zig").versionGreaterThanOrEqualTo;
const c_wrapper = @import("c.zig");
const c = c_wrapper.c;
const encodeVersionNumber = c_wrapper.encodeVersionNumber;
const versionNumber = c_wrapper.versionNumber;

pub const SQLiteExtendedIOError = error{
SQLiteIOErrRead,
Expand Down Expand Up @@ -132,46 +134,32 @@ pub const Error = SQLiteError ||
SQLiteExtendedConstraintError;

pub fn errorFromResultCode(code: c_int) Error {
// These errors are only available since 3.22.0.
if (comptime versionGreaterThanOrEqualTo(3, 22, 0)) {
switch (code) {
c.SQLITE_ERROR_MISSING_COLLSEQ => return error.SQLiteErrorMissingCollSeq,
c.SQLITE_ERROR_RETRY => return error.SQLiteErrorRetry,
c.SQLITE_READONLY_CANTINIT => return error.SQLiteReadOnlyCantInit,
c.SQLITE_READONLY_DIRECTORY => return error.SQLiteReadOnlyDirectory,
else => {},
}
}
const sqlite_version = versionNumber();

// These errors are only available since 3.25.0.
if (comptime versionGreaterThanOrEqualTo(3, 25, 0)) {
switch (code) {
c.SQLITE_ERROR_SNAPSHOT => return error.SQLiteErrorSnapshot,
c.SQLITE_LOCKED_VTAB => return error.SQLiteLockedVTab,
c.SQLITE_CANTOPEN_DIRTYWAL => return error.SQLiteCantOpenDirtyWAL,
c.SQLITE_CORRUPT_SEQUENCE => return error.SQLiteCorruptSequence,
else => {},
}
}
// These errors are only available since 3.31.0.
if (comptime versionGreaterThanOrEqualTo(3, 31, 0)) {
const version_3_31_0 = encodeVersionNumber(3, 31, 0);
if (sqlite_version >= version_3_31_0) {
switch (code) {
c.SQLITE_CANTOPEN_SYMLINK => return error.SQLiteCantOpenSymlink,
c.SQLITE_CONSTRAINT_PINNED => return error.SQLiteConstraintPinned,
else => {},
}
}

// These errors are only available since 3.32.0.
if (comptime versionGreaterThanOrEqualTo(3, 32, 0)) {
const version_3_32_0 = encodeVersionNumber(3, 32, 0);
if (sqlite_version >= version_3_32_0) {
switch (code) {
c.SQLITE_IOERR_DATA => return error.SQLiteIOErrData, // See https://sqlite.org/cksumvfs.html
c.SQLITE_BUSY_TIMEOUT => return error.SQLiteBusyTimeout,
c.SQLITE_CORRUPT_INDEX => return error.SQLiteCorruptIndex,
else => {},
}
}

// These errors are only available since 3.34.0.
if (comptime versionGreaterThanOrEqualTo(3, 34, 0)) {
const version_3_34_0 = encodeVersionNumber(3, 34, 0);
if (sqlite_version >= version_3_34_0) {
switch (code) {
c.SQLITE_IOERR_CORRUPTFS => return error.SQLiteIOErrCorruptFS,
else => {},
Expand Down
79 changes: 26 additions & 53 deletions src/sqlite3/vtab.zig
Original file line number Diff line number Diff line change
Expand Up @@ -482,59 +482,32 @@ pub fn VirtualTable(comptime Table: type) type {
return struct {
const Self = @This();

pub const module = if (versionGreaterThanOrEqualTo(3, 26, 0))
c.sqlite3_module{
.iVersion = 3,
.xCreate = if (tableHasDecl("create")) Creatable.xCreate else null,
.xConnect = xConnect,
.xBestIndex = xBestIndex,
.xDisconnect = xDisconnect,
.xDestroy = if (tableHasDecl("destroy")) Creatable.xDestroy else xDisconnect,
.xOpen = xOpen,
.xClose = xClose,
.xFilter = xFilter,
.xNext = xNext,
.xEof = xEof,
.xColumn = xColumn,
.xRowid = xRowid,
.xUpdate = if (tableHasDecl("update")) Writeable.xUpdate else null,
.xBegin = if (tableHasDecl("begin")) Transactable.xBegin else null,
.xSync = if (tableHasDecl("sync")) Transactable.xSync else null,
.xCommit = if (tableHasDecl("commit")) Transactable.xCommit else null,
.xRollback = if (tableHasDecl("rollback")) Transactable.xRollback else null,
.xFindFunction = null,
.xRename = null,
.xSavepoint = if (tableHasDecl("savepoint")) Transactable.xSavepoint else null,
.xRelease = if (tableHasDecl("release")) Transactable.xRelease else null,
.xRollbackTo = if (tableHasDecl("rollbackTo")) Transactable.xRollbackTo else null,
.xShadowName = if (tableHasDecl("isShadowName")) HasShadowTables.xShadowName else null,
}
else
c.sqlite3_module{
.iVersion = 2,
.xCreate = if (tableHasDecl("create")) Creatable.xCreate else null,
.xConnect = xConnect,
.xBestIndex = xBestIndex,
.xDisconnect = xDisconnect,
.xDestroy = if (tableHasDecl("destroy")) Creatable.xDestroy else xDisconnect,
.xOpen = xOpen,
.xClose = xClose,
.xFilter = xFilter,
.xNext = xNext,
.xEof = xEof,
.xColumn = xColumn,
.xRowid = xRowid,
.xUpdate = if (tableHasDecl("update")) Writeable.xUpdate else null,
.xBegin = if (tableHasDecl("begin")) Transactable.xBegin else null,
.xSync = if (tableHasDecl("sync")) Transactable.xSync else null,
.xCommit = if (tableHasDecl("commit")) Transactable.xCommit else null,
.xRollback = if (tableHasDecl("rollback")) Transactable.xRollback else null,
.xFindFunction = null,
.xRename = null,
.xSavepoint = if (tableHasDecl("savepoint")) Transactable.xSavepoint else null,
.xRelease = if (tableHasDecl("release")) Transactable.xRelease else null,
.xRollbackTo = if (tableHasDecl("rollbackTo")) Transactable.xRollbackTo else null,
};
pub const module = c.sqlite3_module{
.iVersion = 3,
.xCreate = if (tableHasDecl("create")) Creatable.xCreate else null,
.xConnect = xConnect,
.xBestIndex = xBestIndex,
.xDisconnect = xDisconnect,
.xDestroy = if (tableHasDecl("destroy")) Creatable.xDestroy else xDisconnect,
.xOpen = xOpen,
.xClose = xClose,
.xFilter = xFilter,
.xNext = xNext,
.xEof = xEof,
.xColumn = xColumn,
.xRowid = xRowid,
.xUpdate = if (tableHasDecl("update")) Writeable.xUpdate else null,
.xBegin = if (tableHasDecl("begin")) Transactable.xBegin else null,
.xSync = if (tableHasDecl("sync")) Transactable.xSync else null,
.xCommit = if (tableHasDecl("commit")) Transactable.xCommit else null,
.xRollback = if (tableHasDecl("rollback")) Transactable.xRollback else null,
.xFindFunction = null,
.xRename = null,
.xSavepoint = if (tableHasDecl("savepoint")) Transactable.xSavepoint else null,
.xRelease = if (tableHasDecl("release")) Transactable.xRelease else null,
.xRollbackTo = if (tableHasDecl("rollbackTo")) Transactable.xRollbackTo else null,
.xShadowName = if (tableHasDecl("isShadowName")) HasShadowTables.xShadowName else null,
};

const table_decls = switch (@typeInfo(Table)) {
.Struct => |table_struct| table_struct.decls,
Expand Down

0 comments on commit 68c81ce

Please sign in to comment.