From 68c81ce08ddb0f9e13606ec1aefd97be4c7a3a72 Mon Sep 17 00:00:00 2001 From: Dan Gallagher Date: Wed, 24 Jan 2024 13:35:22 -0500 Subject: [PATCH] check for sqlite version at runtime 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 --- .github/workflows/ci.yaml | 12 +++--- src/main.zig | 12 +++++- src/sqlite3/c.zig | 15 +++----- src/sqlite3/errors.zig | 38 +++++++------------ src/sqlite3/vtab.zig | 79 +++++++++++++-------------------------- 5 files changed, 62 insertions(+), 94 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 43e592f..74132d9 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 }} diff --git a/src/main.zig b/src/main.zig index bbf229c..29f8cbc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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; @@ -18,6 +21,8 @@ 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, @@ -25,6 +30,11 @@ pub export fn sqlite3_stanchion_init( ) 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(.{}){}; diff --git a/src/sqlite3/c.zig b/src/sqlite3/c.zig index b338735..9efb45d 100644 --- a/src/sqlite3/c.zig +++ b/src/sqlite3/c.zig @@ -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); } diff --git a/src/sqlite3/errors.zig b/src/sqlite3/errors.zig index d6a866b..40182e7 100644 --- a/src/sqlite3/errors.zig +++ b/src/sqlite3/errors.zig @@ -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, @@ -132,37 +134,21 @@ 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, @@ -170,8 +156,10 @@ pub fn errorFromResultCode(code: c_int) Error { 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 => {}, diff --git a/src/sqlite3/vtab.zig b/src/sqlite3/vtab.zig index 8e98546..d2bca46 100644 --- a/src/sqlite3/vtab.zig +++ b/src/sqlite3/vtab.zig @@ -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,