vrischmann / zig-sqlite

zig-sqlite is a small wrapper around sqlite's C API, making it easier to use with Zig.
MIT License
367 stars 49 forks source link

Can't create a new database #107

Closed greenfork closed 2 months ago

greenfork commented 2 years ago

I have followed the instructions on the readme page to install the package. It works when the database file already exists but fails when there's no file. Here I have come up with a minimal example, copy these to files in a directory:

// build.zig
const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    var target = b.standardTargetOptions(.{});
    const mode = b.standardReleaseOptions();
    const sqlite = b.addStaticLibrary("sqlite", null);
    sqlite.addCSourceFile("zig-sqlite/c/sqlite3.c", &[_][]const u8{"-std=c99"});
    sqlite.linkLibC();
    target.setGnuLibCVersion(2, 28, 0);

    const test_all = b.step("test", "Run tests");
    {
        const test_cases = b.addTest("test.zig");
        test_cases.use_stage1 = true;
        test_cases.linkLibC();
        test_cases.linkLibrary(sqlite);
        test_cases.addPackagePath("sqlite", "zig-sqlite/sqlite.zig");
        test_cases.addIncludeDir("zig-sqlite/c");
        test_cases.setTarget(target);
        test_cases.setBuildMode(mode);
        test_all.dependOn(&test_cases.step);
    }
}
// test.zig
const std = @import("std");
const testing = std.testing;
const sqlite = @import("sqlite");

test {
    var sqlite_diags = sqlite.Diagnostics{};
    var db = sqlite.Db.init(.{
        .mode = .{ .File = "/tmp/my1.db" },
        .open_flags = .{
            .write = true,
            .create = true,
        },
        .diags = &sqlite_diags,
    }) catch |err| {
        std.log.err("Unable to open a database, got error {}. Diagnostics: {s}", .{ err, sqlite_diags });
        return err;
    };
    _ = db;
}

Then in this directory do:

git clone https://github.com/vrischmann/zig-sqlite.git
zig build test

produces errors:

Test [1/1] test ""... [default] (err): Unable to open a database, got error error.SQLiteCantOpen. Diagnostics: {code: 14, near: -1, message: unable to open database file}
Test [1/1] test ""... FAIL (SQLiteCantOpen)
???:?:?: 0x23a9df in ??? (???)
???:?:?: 0x2371fa in ??? (???)
???:?:?: 0x234cc5 in ??? (???)
0 passed; 0 skipped; 1 failed.
1 errors were logged.
error: the following test command failed with exit code 1:
/home/grfork/playground/zig/sqlite-create/zig-cache/o/44494cb3e2811184d2e00efd590e9a75/test /home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/zig
error: test...
error: The following command exited with error code 1:
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/zig test -fstage1 /home/grfork/playground/zig/sqlite-create/test.zig -lc /home/grfork/playground/zig/sqlite-create/zig-cache/o/3949b424e2d7bce08f1f932ad6d308e5/libsqlite.a -lc --cache-dir /home/grfork/playground/zig/sqlite-create/zig-cache --global-cache-dir /home/grfork/.cache/zig --name test -target native-native-gnu.2.28 -mcpu=znver1+clwb+rdpid+wbnoinvd --pkg-begin sqlite /home/grfork/playground/zig/sqlite-create/zig-sqlite/sqlite.zig --pkg-end -I /home/grfork/playground/zig/sqlite-create/zig-sqlite/c --enable-cache
error: the following build command failed with exit code 1:
/home/grfork/playground/zig/sqlite-create/zig-cache/o/ac78f7eee61de2d365856bb219b09142/build /home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/zig /home/grfork/playground/zig/sqlite-create /home/grfork/playground/zig/sqlite-create/zig-cache /home/grfork/.cache/zig test
vrischmann commented 2 years ago

You need to set the target and mode on the sqlite library too:

    sqlite.setTarget(target);
    sqlite.setBuildMode(mode);

I'm not sure why it fails when not adding this.

And yeah it looks like the readme instructions are incomplete.

greenfork commented 2 years ago

Added:

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    var target = b.standardTargetOptions(.{});
    const mode = b.standardReleaseOptions();
    const sqlite = b.addStaticLibrary("sqlite", null);
    sqlite.addCSourceFile("zig-sqlite/c/sqlite3.c", &[_][]const u8{"-std=c99"});
    sqlite.linkLibC();
    target.setGnuLibCVersion(2, 28, 0);
    sqlite.setTarget(target);
    sqlite.setBuildMode(mode);

    const test_all = b.step("test", "Run tests");
    {
        const test_cases = b.addTest("test.zig");
        test_cases.use_stage1 = true;
        test_cases.linkLibrary(sqlite);
        test_cases.addPackagePath("sqlite", "zig-sqlite/sqlite.zig");
        test_cases.addIncludeDir("zig-sqlite/c");
        test_cases.setTarget(target);
        test_cases.setBuildMode(mode);
        test_all.dependOn(&test_cases.step);
    }
}

same error.

vrischmann commented 2 years ago

Right, I accidentally executed the test with the file already existing so it passed. I already saw this error some time ago but found no way to resolve it.

Building with musl instead works fine (zig build -Dtarget=native-linux-musl).

vrischmann commented 2 years ago

I did a little digging, the error SQLITE_CANTOPEN is ultimately due to a lstat call here.

For /tmp/my1.db there's two calls to this function and the last to osLstat returns -1 indicating an error but somehow errno is 0. According to the manpage of lstat:

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set to indicate the error.

so I'm not sure what exactly is wrong here, especially since building with musl works fine.

vrischmann commented 2 years ago

Pretty sure it's this issue actually.

greenfork commented 2 years ago

Wow, that is really great digging! Although compiling with musl doesn't work likely because of this bug, stacktrace below. I guess we will have to wait.

LLD Link... ld.lld: error: undefined symbol: fcntl64
>>> referenced by sqlite3.c
>>>               /home/grfork/reps/kisa/zig-cache/o/c2a889cf3c0c39e873430b7b2a06c1ae/sqlite3.o:(aSyscall) in archive /home/grfork/reps/kisa/zig-cache/o/fd2cb9b8210617d6e8799fc92a43893c/libsqlite.a
error: FileNotFound
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/io/reader.zig:141:39: 0x2b727e in readUntilDelimiter (build)
        /// large enough to hold the entire contents, `error.StreamTooLong` is returned.
                                      ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/os.zig:3072:19: 0x2b6a4f in readlinkZ (build)
        .INVAL => return error.NotLink,
                  ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/os.zig:3049:9: 0x2cdbd3 in readlink (build)
        return readlinkZ(&file_path_c, out_buffer);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/os.zig:2753:19: 0x29dc3c in mkdiratZ (build)
        .EXIST => return error.PathAlreadyExists,
                  ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/os.zig:2713:9: 0x28ba4d in mkdirat (build)
        return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/fs.zig:1361:9: 0x28b94b in makeDir (build)
        try os.mkdirat(self.fd, sub_path, default_new_dir_mode);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/os.zig:1683:23: 0x2d7060 in openatZ (build)
            .NOENT => return error.FileNotFound,
                      ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/fs.zig:1110:13: 0x2c212c in openFileZ (build)
            try os.openatZ(self.fd, sub_path, os_flags, 0);
            ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/fs.zig:1037:9: 0x2a8c5e in openFile (build)
        return self.openFileZ(&path_c, flags);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/fs.zig:2250:24: 0x31af00 in updateFile (build)
        var src_file = try source_dir.openFile(source_path, .{});
                       ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:1088:29: 0x32e6e7 in updateFile (build)
        const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
                            ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:3420:9: 0x312e27 in make (build)
        try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:3655:9: 0x29e037 in make (build)
        try self.makeFn(self);
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:508:9: 0x28c65a in makeOneStep (build)
        try s.make();
        ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:502:17: 0x28c5d2 in makeOneStep (build)
                return err;
                ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:502:17: 0x28c5d2 in makeOneStep (build)
                return err;
                ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:502:17: 0x28c5d2 in makeOneStep (build)
                return err;
                ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/std/build.zig:463:13: 0x28c338 in make (build)
            try self.makeOneStep(s);
            ^
/home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/lib/build_runner.zig:213:21: 0x28f17b in main (build)
            else => return err,
                    ^
error: the following build command failed with exit code 1:
/home/grfork/reps/kisa/zig-cache/o/23e6fb3e45833344747486887817bc72/build /home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/zig /home/grfork/reps/kisa /home/grfork/reps/kisa/zig-cache /home/grfork/.cache/zig -Dtarget=native-linux-musl run
vrischmann commented 2 years ago

This is weird, this linking error happens when building with glibc and not specifying the glibc version with

target.setGnuLibCVersion(2, 28, 0);

In your reproducer code you have to remove this line, then building with zig build test -Dtarget=native-linux-musl works for me.

vrischmann commented 2 months ago

Hi. Is this issue still relevant @greenfork ?

greenfork commented 2 months ago

I couldn't quickly restore the example, I suppose we can close this, I will re-open if I get this behavior again.