Skip to content

Commit

Permalink
Merge pull request #158 from vrischmann/workaround-sqlite-transient
Browse files Browse the repository at this point in the history
Workaround SQLITE_TRANSIENT
  • Loading branch information
vrischmann authored Apr 14, 2024
2 parents a16c3fe + 2c75186 commit 19dd52b
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 111 deletions.
26 changes: 21 additions & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,26 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, macos-12]
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
- uses: goto-bus-stop/setup-zig@v2

- name: Setup zig
uses: goto-bus-stop/setup-zig@v2
with:
version: master

- uses: actions/cache@v4
if: ${{ matrix.os != 'windows-latest' }}
- name: Install qemu
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
sudo apt-get update -y && sudo apt-get install -y qemu-user-binfmt
- name: Restore cache
uses: actions/cache@v4
with:
path: |
zig-cache
Expand All @@ -49,9 +57,17 @@ jobs:
restore-keys: ${{ runner.os }}-${{ matrix.os }}-zig-

- name: Run Tests in memory
if: ${{ matrix.os == 'ubuntu-latest' }}
run: zig build test -Dci=true -Din_memory=true --summary all -fqemu -fwine
- name: Run Tests in memory
if: ${{ matrix.os == 'macos-latest' }}
run: zig build test -Dci=true -Din_memory=true --summary all -frosetta
- name: Run Tests in memory
if: ${{ matrix.os == 'windows-latest' }}
run: zig build test -Dci=true -Din_memory=true --summary all

- name: Build the example zigcrypto loadable extension
run: zig build zigcrypto
- name: Test the zigcrypto loadable extension
if: ${{ matrix.os != 'windows-latest' }}
run: ./zig-out/bin/zigcrypto-test
140 changes: 39 additions & 101 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ const Step = std.Build.Step;
const ResolvedTarget = std.Build.ResolvedTarget;
const Query = std.Target.Query;

var sqlite3: ?*Step.Compile = null;

fn linkSqlite(b: *Step.Compile) void {
if (sqlite3) |lib| {
b.linkLibrary(lib);
} else {
b.linkLibC();
b.linkSystemLibrary("sqlite3");
}
}

fn getTarget(original_target: ResolvedTarget, bundled: bool) ResolvedTarget {
if (bundled) {
var tmp = original_target;
Expand Down Expand Up @@ -46,70 +35,21 @@ const TestTarget = struct {
const ci_targets = switch (builtin.target.cpu.arch) {
.x86_64 => switch (builtin.target.os.tag) {
.linux => [_]TestTarget{
// Targets linux but other CPU archs.
TestTarget{
.query = .{},
.bundled = false,
},
TestTarget{
.query = .{
.cpu_arch = .x86_64,
.abi = .musl,
},
.bundled = true,
},
TestTarget{
.query = .{
.cpu_arch = .x86,
.abi = .musl,
},
.bundled = true,
},
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .musl }, .bundled = true },
TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .musl }, .bundled = true },
TestTarget{ .query = .{ .cpu_arch = .aarch64, .abi = .musl }, .bundled = true },
},
.windows => [_]TestTarget{
TestTarget{
.query = .{
.cpu_arch = .x86_64,
.abi = .gnu,
},
.bundled = true,
},
TestTarget{
.query = .{
.cpu_arch = .x86,
.abi = .gnu,
},
.bundled = true,
},
TestTarget{ .query = .{ .cpu_arch = .x86_64, .abi = .gnu }, .bundled = true },
TestTarget{ .query = .{ .cpu_arch = .x86, .abi = .gnu }, .bundled = true },
},
.macos => [_]TestTarget{
TestTarget{
.query = .{
.cpu_arch = .x86_64,
},
.bundled = true,
},
// TODO(vincent): this fails for some reason
// TestTarget{
// .query =.{
// .cpu_arch = .aarch64,
// },
// .bundled = true,
// },
},
else => [_]TestTarget{
TestTarget{
.query = .{},
.bundled = false,
},
},
},
else => [_]TestTarget{
TestTarget{
.query = .{},
.bundled = false,
TestTarget{ .query = .{ .cpu_arch = .x86_64 }, .bundled = true },
TestTarget{ .query = .{ .cpu_arch = .aarch64 }, .bundled = true },
},
else => unreachable,
},
else => unreachable,
};

const all_test_targets = switch (builtin.target.cpu.arch) {
Expand Down Expand Up @@ -267,16 +207,21 @@ pub fn build(b: *std.Build) !void {
const target = b.resolveTargetQuery(query);
const optimize = b.standardOptimizeOption(.{});

const c_flags = &[_][]const u8{"-std=c99"};

const sqlite_lib = b.addStaticLibrary(.{
.name = "sqlite",
.target = target,
.optimize = optimize,
});

sqlite_lib.addIncludePath(.{ .path = "c/" });
sqlite_lib.addCSourceFile(.{
.file = .{ .path = "c/sqlite3.c" },
.flags = &[_][]const u8{"-std=c99"},
sqlite_lib.addCSourceFiles(.{
.files = &[_][]const u8{
"c/sqlite3.c",
"c/workaround.c",
},
.flags = c_flags,
});
sqlite_lib.linkLibC();
sqlite_lib.installHeader(.{ .path = "c/sqlite3.h" }, "sqlite3.h");
Expand Down Expand Up @@ -330,47 +275,43 @@ pub fn build(b: *std.Build) !void {
single_threaded_txt,
});

const test_sqlite_lib = b.addStaticLibrary(.{
.name = "sqlite",
.target = cross_target,
.optimize = optimize,
});
test_sqlite_lib.addCSourceFiles(.{
.files = &[_][]const u8{
"c/sqlite3.c",
"c/workaround.c",
},
.flags = c_flags,
});
test_sqlite_lib.linkLibC();

const tests = b.addTest(.{
.name = test_name,
.target = cross_target,
.optimize = optimize,
.root_source_file = .{ .path = "sqlite.zig" },
.single_threaded = test_target.single_threaded,
});
const run_tests = b.addRunArtifact(tests);

tests.addIncludePath(.{ .path = "c" });
if (bundled) {
const lib = b.addStaticLibrary(.{
.name = "sqlite",
.target = cross_target,
.optimize = optimize,
});
lib.addCSourceFile(.{
.file = .{ .path = "c/sqlite3.c" },
.flags = &[_][]const u8{"-std=c99"},
});
lib.linkLibC();
sqlite3 = lib;
tests.linkLibrary(test_sqlite_lib);
} else {
tests.linkLibC();
tests.addCSourceFile(.{ .file = .{ .path = "c/workaround.c" }, .flags = c_flags });
tests.linkSystemLibrary("sqlite3");
}

if (bundled) tests.addIncludePath(.{ .path = "c" });
linkSqlite(tests);

const lib = b.addStaticLibrary(.{
.name = "zig-sqlite",
.root_source_file = .{ .path = "sqlite.zig" },
.target = cross_target,
.optimize = optimize,
});
if (bundled) lib.addIncludePath(.{ .path = "c" });
linkSqlite(lib);

const tests_options = b.addOptions();
tests.root_module.addImport("build_options", tests_options.createModule());

tests_options.addOption(bool, "in_memory", in_memory);
tests_options.addOption(?[]const u8, "dbfile", dbfile);

const run_tests = b.addRunArtifact(tests);
test_step.dependOn(&run_tests.step);
}

Expand All @@ -381,10 +322,7 @@ pub fn build(b: *std.Build) !void {
.target = getTarget(target, true),
.optimize = optimize,
});
lib.addCSourceFile(.{
.file = .{ .path = "c/sqlite3.c" },
.flags = &[_][]const u8{"-std=c99"},
});
lib.addCSourceFile(.{ .file = .{ .path = "c/sqlite3.c" }, .flags = c_flags });
lib.addIncludePath(.{ .path = "c" });
lib.linkLibC();

Expand Down
1 change: 1 addition & 0 deletions c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub const c = if (@hasDecl(root, "loadable_extension"))
else
@cImport({
@cInclude("sqlite3.h");
@cInclude("workaround.h");
});

// versionGreaterThanOrEqualTo returns true if the SQLite version is >= to the major.minor.patch provided.
Expand Down
1 change: 1 addition & 0 deletions c/loadable_extension.zig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const c = @cImport({
@cInclude("loadable-ext-sqlite3ext.h");
@cInclude("workaround.h");
});

pub usingnamespace c;
Expand Down
5 changes: 5 additions & 0 deletions c/workaround.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "workaround.h"

my_sqlite3_destructor_type sqliteTransientAsDestructor() {
return (my_sqlite3_destructor_type)-1;
}
3 changes: 3 additions & 0 deletions c/workaround.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
typedef void (*my_sqlite3_destructor_type)(void *);

my_sqlite3_destructor_type sqliteTransientAsDestructor();
8 changes: 4 additions & 4 deletions helpers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub fn setResult(ctx: ?*c.sqlite3_context, result: anytype) void {
const ResultType = @TypeOf(result);

switch (ResultType) {
Text => c.sqlite3_result_text(ctx, result.data.ptr, @intCast(result.data.len), c.SQLITE_TRANSIENT),
Blob => c.sqlite3_result_blob(ctx, result.data.ptr, @intCast(result.data.len), c.SQLITE_TRANSIENT),
Text => c.sqlite3_result_text(ctx, result.data.ptr, @intCast(result.data.len), c.sqliteTransientAsDestructor()),
Blob => c.sqlite3_result_blob(ctx, result.data.ptr, @intCast(result.data.len), c.sqliteTransientAsDestructor()),
else => switch (@typeInfo(ResultType)) {
.Int => |info| if ((info.bits + if (info.signedness == .unsigned) 1 else 0) <= 32) {
c.sqlite3_result_int(ctx, result);
Expand All @@ -26,12 +26,12 @@ pub fn setResult(ctx: ?*c.sqlite3_context, result: anytype) void {
.Float => c.sqlite3_result_double(ctx, result),
.Bool => c.sqlite3_result_int(ctx, if (result) 1 else 0),
.Array => |arr| switch (arr.child) {
u8 => c.sqlite3_result_blob(ctx, &result, arr.len, c.SQLITE_TRANSIENT),
u8 => c.sqlite3_result_blob(ctx, &result, arr.len, c.sqliteTransientAsDestructor()),
else => @compileError("cannot use a result of type " ++ @typeName(ResultType)),
},
.Pointer => |ptr| switch (ptr.size) {
.Slice => switch (ptr.child) {
u8 => c.sqlite3_result_text(ctx, result.ptr, @intCast(result.len), c.SQLITE_TRANSIENT),
u8 => c.sqlite3_result_text(ctx, result.ptr, @intCast(result.len), c.sqliteTransientAsDestructor()),
else => @compileError("cannot use a result of type " ++ @typeName(ResultType)),
},
else => @compileError("cannot use a result of type " ++ @typeName(ResultType)),
Expand Down
2 changes: 1 addition & 1 deletion sqlite.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1676,7 +1676,7 @@ pub const DynamicStatement = struct {
const data: []const u8 = field[0..field.len];

// NOTE(vincent): The array is temporary and must be copied, therefore we use SQLITE_TRANSIENT
const result = c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(data.len), c.SQLITE_TRANSIENT);
const result = c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(data.len), c.sqliteTransientAsDestructor());
return convertResultToError(result);
},
else => @compileError("cannot bind field " ++ field_name ++ " of type array of " ++ @typeName(arr.child)),
Expand Down

0 comments on commit 19dd52b

Please sign in to comment.