diff options
| author | 2024-04-14 20:50:44 +0200 | |
|---|---|---|
| committer | 2024-04-14 20:50:44 +0200 | |
| commit | 19dd52b7b7e9fa5ec029c4ac3bd53fb41e5be12c (patch) | |
| tree | 453f927a6926b4f778a4be2a14164896734a60f0 | |
| parent | Merge pull request #157 from vrischmann/update-latest-zig (diff) | |
| parent | ci: run with -fqemu and -frosetta (diff) | |
| download | zig-sqlite-19dd52b7b7e9fa5ec029c4ac3bd53fb41e5be12c.tar.gz zig-sqlite-19dd52b7b7e9fa5ec029c4ac3bd53fb41e5be12c.tar.xz zig-sqlite-19dd52b7b7e9fa5ec029c4ac3bd53fb41e5be12c.zip | |
Merge pull request #158 from vrischmann/workaround-sqlite-transient
Workaround SQLITE_TRANSIENT
| -rw-r--r-- | .github/workflows/main.yml | 26 | ||||
| -rw-r--r-- | build.zig | 140 | ||||
| -rw-r--r-- | c.zig | 1 | ||||
| -rw-r--r-- | c/loadable_extension.zig | 1 | ||||
| -rw-r--r-- | c/workaround.c | 5 | ||||
| -rw-r--r-- | c/workaround.h | 3 | ||||
| -rw-r--r-- | helpers.zig | 8 | ||||
| -rw-r--r-- | sqlite.zig | 2 |
8 files changed, 75 insertions, 111 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index adc7db1..6a6093c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml | |||
| @@ -29,18 +29,26 @@ jobs: | |||
| 29 | strategy: | 29 | strategy: |
| 30 | fail-fast: false | 30 | fail-fast: false |
| 31 | matrix: | 31 | matrix: |
| 32 | os: [ubuntu-latest, macos-latest, macos-12] | 32 | os: [ubuntu-latest, windows-latest, macos-latest] |
| 33 | runs-on: ${{ matrix.os }} | 33 | runs-on: ${{ matrix.os }} |
| 34 | steps: | 34 | steps: |
| 35 | - uses: actions/checkout@v4 | 35 | - name: Checkout repository |
| 36 | uses: actions/checkout@v4 | ||
| 36 | with: | 37 | with: |
| 37 | submodules: true | 38 | submodules: true |
| 38 | - uses: goto-bus-stop/setup-zig@v2 | 39 | |
| 40 | - name: Setup zig | ||
| 41 | uses: goto-bus-stop/setup-zig@v2 | ||
| 39 | with: | 42 | with: |
| 40 | version: master | 43 | version: master |
| 41 | 44 | ||
| 42 | - uses: actions/cache@v4 | 45 | - name: Install qemu |
| 43 | if: ${{ matrix.os != 'windows-latest' }} | 46 | if: ${{ matrix.os == 'ubuntu-latest' }} |
| 47 | run: | | ||
| 48 | sudo apt-get update -y && sudo apt-get install -y qemu-user-binfmt | ||
| 49 | |||
| 50 | - name: Restore cache | ||
| 51 | uses: actions/cache@v4 | ||
| 44 | with: | 52 | with: |
| 45 | path: | | 53 | path: | |
| 46 | zig-cache | 54 | zig-cache |
| @@ -49,9 +57,17 @@ jobs: | |||
| 49 | restore-keys: ${{ runner.os }}-${{ matrix.os }}-zig- | 57 | restore-keys: ${{ runner.os }}-${{ matrix.os }}-zig- |
| 50 | 58 | ||
| 51 | - name: Run Tests in memory | 59 | - name: Run Tests in memory |
| 60 | if: ${{ matrix.os == 'ubuntu-latest' }} | ||
| 61 | run: zig build test -Dci=true -Din_memory=true --summary all -fqemu -fwine | ||
| 62 | - name: Run Tests in memory | ||
| 63 | if: ${{ matrix.os == 'macos-latest' }} | ||
| 64 | run: zig build test -Dci=true -Din_memory=true --summary all -frosetta | ||
| 65 | - name: Run Tests in memory | ||
| 66 | if: ${{ matrix.os == 'windows-latest' }} | ||
| 52 | run: zig build test -Dci=true -Din_memory=true --summary all | 67 | run: zig build test -Dci=true -Din_memory=true --summary all |
| 53 | 68 | ||
| 54 | - name: Build the example zigcrypto loadable extension | 69 | - name: Build the example zigcrypto loadable extension |
| 55 | run: zig build zigcrypto | 70 | run: zig build zigcrypto |
| 56 | - name: Test the zigcrypto loadable extension | 71 | - name: Test the zigcrypto loadable extension |
| 72 | if: ${{ matrix.os != 'windows-latest' }} | ||
| 57 | run: ./zig-out/bin/zigcrypto-test | 73 | run: ./zig-out/bin/zigcrypto-test |
| @@ -4,17 +4,6 @@ const Step = std.Build.Step; | |||
| 4 | const ResolvedTarget = std.Build.ResolvedTarget; | 4 | const ResolvedTarget = std.Build.ResolvedTarget; |
| 5 | const Query = std.Target.Query; | 5 | const Query = std.Target.Query; |
| 6 | 6 | ||
| 7 | var sqlite3: ?*Step.Compile = null; | ||
| 8 | |||
| 9 | fn linkSqlite(b: *Step.Compile) void { | ||
| 10 | if (sqlite3) |lib| { | ||
| 11 | b.linkLibrary(lib); | ||
| 12 | } else { | ||
| 13 | b.linkLibC(); | ||
| 14 | b.linkSystemLibrary("sqlite3"); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | fn getTarget(original_target: ResolvedTarget, bundled: bool) ResolvedTarget { | 7 | fn getTarget(original_target: ResolvedTarget, bundled: bool) ResolvedTarget { |
| 19 | if (bundled) { | 8 | if (bundled) { |
| 20 | var tmp = original_target; | 9 | var tmp = original_target; |
| @@ -46,70 +35,21 @@ const TestTarget = struct { | |||
| 46 | const ci_targets = switch (builtin.target.cpu.arch) { | 35 | const ci_targets = switch (builtin.target.cpu.arch) { |
| 47 | .x86_64 => switch (builtin.target.os.tag) { | 36 | .x86_64 => switch (builtin.target.os.tag) { |
| 48 | .linux => [_]TestTarget{ | 37 | .linux => [_]TestTarget{ |
| 49 | // Targets linux but other CPU archs. | 38 | TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .musl }, .bundled = true }, |
| 50 | TestTarget{ | 39 | TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .musl }, .bundled = true }, |
| 51 | .query = .{}, | 40 | TestTarget{ .query = .{ .cpu_arch = .aarch64, .abi = .musl }, .bundled = true }, |
| 52 | .bundled = false, | ||
| 53 | }, | ||
| 54 | TestTarget{ | ||
| 55 | .query = .{ | ||
| 56 | .cpu_arch = .x86_64, | ||
| 57 | .abi = .musl, | ||
| 58 | }, | ||
| 59 | .bundled = true, | ||
| 60 | }, | ||
| 61 | TestTarget{ | ||
| 62 | .query = .{ | ||
| 63 | .cpu_arch = .x86, | ||
| 64 | .abi = .musl, | ||
| 65 | }, | ||
| 66 | .bundled = true, | ||
| 67 | }, | ||
| 68 | }, | 41 | }, |
| 69 | .windows => [_]TestTarget{ | 42 | .windows => [_]TestTarget{ |
| 70 | TestTarget{ | 43 | TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .gnu }, .bundled = true }, |
| 71 | .query = .{ | 44 | TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .gnu }, .bundled = true }, |
| 72 | .cpu_arch = .x86_64, | ||
| 73 | .abi = .gnu, | ||
| 74 | }, | ||
| 75 | .bundled = true, | ||
| 76 | }, | ||
| 77 | TestTarget{ | ||
| 78 | .query = .{ | ||
| 79 | .cpu_arch = .x86, | ||
| 80 | .abi = .gnu, | ||
| 81 | }, | ||
| 82 | .bundled = true, | ||
| 83 | }, | ||
| 84 | }, | 45 | }, |
| 85 | .macos => [_]TestTarget{ | 46 | .macos => [_]TestTarget{ |
| 86 | TestTarget{ | 47 | TestTarget{ .query = .{ .cpu_arch = .x86_64 }, .bundled = true }, |
| 87 | .query = .{ | 48 | TestTarget{ .query = .{ .cpu_arch = .aarch64 }, .bundled = true }, |
| 88 | .cpu_arch = .x86_64, | ||
| 89 | }, | ||
| 90 | .bundled = true, | ||
| 91 | }, | ||
| 92 | // TODO(vincent): this fails for some reason | ||
| 93 | // TestTarget{ | ||
| 94 | // .query =.{ | ||
| 95 | // .cpu_arch = .aarch64, | ||
| 96 | // }, | ||
| 97 | // .bundled = true, | ||
| 98 | // }, | ||
| 99 | }, | ||
| 100 | else => [_]TestTarget{ | ||
| 101 | TestTarget{ | ||
| 102 | .query = .{}, | ||
| 103 | .bundled = false, | ||
| 104 | }, | ||
| 105 | }, | ||
| 106 | }, | ||
| 107 | else => [_]TestTarget{ | ||
| 108 | TestTarget{ | ||
| 109 | .query = .{}, | ||
| 110 | .bundled = false, | ||
| 111 | }, | 49 | }, |
| 50 | else => unreachable, | ||
| 112 | }, | 51 | }, |
| 52 | else => unreachable, | ||
| 113 | }; | 53 | }; |
| 114 | 54 | ||
| 115 | const all_test_targets = switch (builtin.target.cpu.arch) { | 55 | const all_test_targets = switch (builtin.target.cpu.arch) { |
| @@ -267,6 +207,8 @@ pub fn build(b: *std.Build) !void { | |||
| 267 | const target = b.resolveTargetQuery(query); | 207 | const target = b.resolveTargetQuery(query); |
| 268 | const optimize = b.standardOptimizeOption(.{}); | 208 | const optimize = b.standardOptimizeOption(.{}); |
| 269 | 209 | ||
| 210 | const c_flags = &[_][]const u8{"-std=c99"}; | ||
| 211 | |||
| 270 | const sqlite_lib = b.addStaticLibrary(.{ | 212 | const sqlite_lib = b.addStaticLibrary(.{ |
| 271 | .name = "sqlite", | 213 | .name = "sqlite", |
| 272 | .target = target, | 214 | .target = target, |
| @@ -274,9 +216,12 @@ pub fn build(b: *std.Build) !void { | |||
| 274 | }); | 216 | }); |
| 275 | 217 | ||
| 276 | sqlite_lib.addIncludePath(.{ .path = "c/" }); | 218 | sqlite_lib.addIncludePath(.{ .path = "c/" }); |
| 277 | sqlite_lib.addCSourceFile(.{ | 219 | sqlite_lib.addCSourceFiles(.{ |
| 278 | .file = .{ .path = "c/sqlite3.c" }, | 220 | .files = &[_][]const u8{ |
| 279 | .flags = &[_][]const u8{"-std=c99"}, | 221 | "c/sqlite3.c", |
| 222 | "c/workaround.c", | ||
| 223 | }, | ||
| 224 | .flags = c_flags, | ||
| 280 | }); | 225 | }); |
| 281 | sqlite_lib.linkLibC(); | 226 | sqlite_lib.linkLibC(); |
| 282 | sqlite_lib.installHeader(.{ .path = "c/sqlite3.h" }, "sqlite3.h"); | 227 | sqlite_lib.installHeader(.{ .path = "c/sqlite3.h" }, "sqlite3.h"); |
| @@ -330,6 +275,20 @@ pub fn build(b: *std.Build) !void { | |||
| 330 | single_threaded_txt, | 275 | single_threaded_txt, |
| 331 | }); | 276 | }); |
| 332 | 277 | ||
| 278 | const test_sqlite_lib = b.addStaticLibrary(.{ | ||
| 279 | .name = "sqlite", | ||
| 280 | .target = cross_target, | ||
| 281 | .optimize = optimize, | ||
| 282 | }); | ||
| 283 | test_sqlite_lib.addCSourceFiles(.{ | ||
| 284 | .files = &[_][]const u8{ | ||
| 285 | "c/sqlite3.c", | ||
| 286 | "c/workaround.c", | ||
| 287 | }, | ||
| 288 | .flags = c_flags, | ||
| 289 | }); | ||
| 290 | test_sqlite_lib.linkLibC(); | ||
| 291 | |||
| 333 | const tests = b.addTest(.{ | 292 | const tests = b.addTest(.{ |
| 334 | .name = test_name, | 293 | .name = test_name, |
| 335 | .target = cross_target, | 294 | .target = cross_target, |
| @@ -337,40 +296,22 @@ pub fn build(b: *std.Build) !void { | |||
| 337 | .root_source_file = .{ .path = "sqlite.zig" }, | 296 | .root_source_file = .{ .path = "sqlite.zig" }, |
| 338 | .single_threaded = test_target.single_threaded, | 297 | .single_threaded = test_target.single_threaded, |
| 339 | }); | 298 | }); |
| 340 | const run_tests = b.addRunArtifact(tests); | 299 | tests.addIncludePath(.{ .path = "c" }); |
| 341 | |||
| 342 | if (bundled) { | 300 | if (bundled) { |
| 343 | const lib = b.addStaticLibrary(.{ | 301 | tests.linkLibrary(test_sqlite_lib); |
| 344 | .name = "sqlite", | 302 | } else { |
| 345 | .target = cross_target, | 303 | tests.linkLibC(); |
| 346 | .optimize = optimize, | 304 | tests.addCSourceFile(.{ .file = .{ .path = "c/workaround.c" }, .flags = c_flags }); |
| 347 | }); | 305 | tests.linkSystemLibrary("sqlite3"); |
| 348 | lib.addCSourceFile(.{ | ||
| 349 | .file = .{ .path = "c/sqlite3.c" }, | ||
| 350 | .flags = &[_][]const u8{"-std=c99"}, | ||
| 351 | }); | ||
| 352 | lib.linkLibC(); | ||
| 353 | sqlite3 = lib; | ||
| 354 | } | 306 | } |
| 355 | 307 | ||
| 356 | if (bundled) tests.addIncludePath(.{ .path = "c" }); | ||
| 357 | linkSqlite(tests); | ||
| 358 | |||
| 359 | const lib = b.addStaticLibrary(.{ | ||
| 360 | .name = "zig-sqlite", | ||
| 361 | .root_source_file = .{ .path = "sqlite.zig" }, | ||
| 362 | .target = cross_target, | ||
| 363 | .optimize = optimize, | ||
| 364 | }); | ||
| 365 | if (bundled) lib.addIncludePath(.{ .path = "c" }); | ||
| 366 | linkSqlite(lib); | ||
| 367 | |||
| 368 | const tests_options = b.addOptions(); | 308 | const tests_options = b.addOptions(); |
| 369 | tests.root_module.addImport("build_options", tests_options.createModule()); | 309 | tests.root_module.addImport("build_options", tests_options.createModule()); |
| 370 | 310 | ||
| 371 | tests_options.addOption(bool, "in_memory", in_memory); | 311 | tests_options.addOption(bool, "in_memory", in_memory); |
| 372 | tests_options.addOption(?[]const u8, "dbfile", dbfile); | 312 | tests_options.addOption(?[]const u8, "dbfile", dbfile); |
| 373 | 313 | ||
| 314 | const run_tests = b.addRunArtifact(tests); | ||
| 374 | test_step.dependOn(&run_tests.step); | 315 | test_step.dependOn(&run_tests.step); |
| 375 | } | 316 | } |
| 376 | 317 | ||
| @@ -381,10 +322,7 @@ pub fn build(b: *std.Build) !void { | |||
| 381 | .target = getTarget(target, true), | 322 | .target = getTarget(target, true), |
| 382 | .optimize = optimize, | 323 | .optimize = optimize, |
| 383 | }); | 324 | }); |
| 384 | lib.addCSourceFile(.{ | 325 | lib.addCSourceFile(.{ .file = .{ .path = "c/sqlite3.c" }, .flags = c_flags }); |
| 385 | .file = .{ .path = "c/sqlite3.c" }, | ||
| 386 | .flags = &[_][]const u8{"-std=c99"}, | ||
| 387 | }); | ||
| 388 | lib.addIncludePath(.{ .path = "c" }); | 326 | lib.addIncludePath(.{ .path = "c" }); |
| 389 | lib.linkLibC(); | 327 | lib.linkLibC(); |
| 390 | 328 | ||
| @@ -5,6 +5,7 @@ pub const c = if (@hasDecl(root, "loadable_extension")) | |||
| 5 | else | 5 | else |
| 6 | @cImport({ | 6 | @cImport({ |
| 7 | @cInclude("sqlite3.h"); | 7 | @cInclude("sqlite3.h"); |
| 8 | @cInclude("workaround.h"); | ||
| 8 | }); | 9 | }); |
| 9 | 10 | ||
| 10 | // versionGreaterThanOrEqualTo returns true if the SQLite version is >= to the major.minor.patch provided. | 11 | // versionGreaterThanOrEqualTo returns true if the SQLite version is >= to the major.minor.patch provided. |
diff --git a/c/loadable_extension.zig b/c/loadable_extension.zig index 4b27534..fdfe15e 100644 --- a/c/loadable_extension.zig +++ b/c/loadable_extension.zig | |||
| @@ -1,5 +1,6 @@ | |||
| 1 | const c = @cImport({ | 1 | const c = @cImport({ |
| 2 | @cInclude("loadable-ext-sqlite3ext.h"); | 2 | @cInclude("loadable-ext-sqlite3ext.h"); |
| 3 | @cInclude("workaround.h"); | ||
| 3 | }); | 4 | }); |
| 4 | 5 | ||
| 5 | pub usingnamespace c; | 6 | pub usingnamespace c; |
diff --git a/c/workaround.c b/c/workaround.c new file mode 100644 index 0000000..592d33d --- /dev/null +++ b/c/workaround.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | #include "workaround.h" | ||
| 2 | |||
| 3 | my_sqlite3_destructor_type sqliteTransientAsDestructor() { | ||
| 4 | return (my_sqlite3_destructor_type)-1; | ||
| 5 | } | ||
diff --git a/c/workaround.h b/c/workaround.h new file mode 100644 index 0000000..ae243b8 --- /dev/null +++ b/c/workaround.h | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | typedef void (*my_sqlite3_destructor_type)(void *); | ||
| 2 | |||
| 3 | my_sqlite3_destructor_type sqliteTransientAsDestructor(); | ||
diff --git a/helpers.zig b/helpers.zig index 7bb695e..b9a6131 100644 --- a/helpers.zig +++ b/helpers.zig | |||
| @@ -13,8 +13,8 @@ pub fn setResult(ctx: ?*c.sqlite3_context, result: anytype) void { | |||
| 13 | const ResultType = @TypeOf(result); | 13 | const ResultType = @TypeOf(result); |
| 14 | 14 | ||
| 15 | switch (ResultType) { | 15 | switch (ResultType) { |
| 16 | Text => c.sqlite3_result_text(ctx, result.data.ptr, @intCast(result.data.len), c.SQLITE_TRANSIENT), | 16 | Text => c.sqlite3_result_text(ctx, result.data.ptr, @intCast(result.data.len), c.sqliteTransientAsDestructor()), |
| 17 | Blob => c.sqlite3_result_blob(ctx, result.data.ptr, @intCast(result.data.len), c.SQLITE_TRANSIENT), | 17 | Blob => c.sqlite3_result_blob(ctx, result.data.ptr, @intCast(result.data.len), c.sqliteTransientAsDestructor()), |
| 18 | else => switch (@typeInfo(ResultType)) { | 18 | else => switch (@typeInfo(ResultType)) { |
| 19 | .Int => |info| if ((info.bits + if (info.signedness == .unsigned) 1 else 0) <= 32) { | 19 | .Int => |info| if ((info.bits + if (info.signedness == .unsigned) 1 else 0) <= 32) { |
| 20 | c.sqlite3_result_int(ctx, result); | 20 | c.sqlite3_result_int(ctx, result); |
| @@ -26,12 +26,12 @@ pub fn setResult(ctx: ?*c.sqlite3_context, result: anytype) void { | |||
| 26 | .Float => c.sqlite3_result_double(ctx, result), | 26 | .Float => c.sqlite3_result_double(ctx, result), |
| 27 | .Bool => c.sqlite3_result_int(ctx, if (result) 1 else 0), | 27 | .Bool => c.sqlite3_result_int(ctx, if (result) 1 else 0), |
| 28 | .Array => |arr| switch (arr.child) { | 28 | .Array => |arr| switch (arr.child) { |
| 29 | u8 => c.sqlite3_result_blob(ctx, &result, arr.len, c.SQLITE_TRANSIENT), | 29 | u8 => c.sqlite3_result_blob(ctx, &result, arr.len, c.sqliteTransientAsDestructor()), |
| 30 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), | 30 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), |
| 31 | }, | 31 | }, |
| 32 | .Pointer => |ptr| switch (ptr.size) { | 32 | .Pointer => |ptr| switch (ptr.size) { |
| 33 | .Slice => switch (ptr.child) { | 33 | .Slice => switch (ptr.child) { |
| 34 | u8 => c.sqlite3_result_text(ctx, result.ptr, @intCast(result.len), c.SQLITE_TRANSIENT), | 34 | u8 => c.sqlite3_result_text(ctx, result.ptr, @intCast(result.len), c.sqliteTransientAsDestructor()), |
| 35 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), | 35 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), |
| 36 | }, | 36 | }, |
| 37 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), | 37 | else => @compileError("cannot use a result of type " ++ @typeName(ResultType)), |
| @@ -1676,7 +1676,7 @@ pub const DynamicStatement = struct { | |||
| 1676 | const data: []const u8 = field[0..field.len]; | 1676 | const data: []const u8 = field[0..field.len]; |
| 1677 | 1677 | ||
| 1678 | // NOTE(vincent): The array is temporary and must be copied, therefore we use SQLITE_TRANSIENT | 1678 | // NOTE(vincent): The array is temporary and must be copied, therefore we use SQLITE_TRANSIENT |
| 1679 | const result = c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(data.len), c.SQLITE_TRANSIENT); | 1679 | const result = c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(data.len), c.sqliteTransientAsDestructor()); |
| 1680 | return convertResultToError(result); | 1680 | return convertResultToError(result); |
| 1681 | }, | 1681 | }, |
| 1682 | else => @compileError("cannot bind field " ++ field_name ++ " of type array of " ++ @typeName(arr.child)), | 1682 | else => @compileError("cannot bind field " ++ field_name ++ " of type array of " ++ @typeName(arr.child)), |