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,