Passing allocPrint-ed []u8 to sqlite3 C functions does not work

I must be doing something wrong, but not that experienced with either Zig or C interop, need help.

When I pass string literals to sqlite3_exec(), it works without any problems, but when I try to pass a string which I allocPrinted, like this:

const query = try std.fmt.allocPrint(
    self.arena.allocator(),
    "INSERT OR REPLACE INTO table VALUES ({d}, {d}, \"{s}\", \"{s}\");",
    .{ id1, id2, str1, str2 },
);
const result = c.sqlite3_exec(self.db, query.ptr, null, null, null);

I get an error from sqlite:

near \252\252\252...<repeated a lot>: syntax error

I guess I did something wrong with the pointer to the string argument, but not sure what exactly. At first I tried @ptrCast(query), it did compile, but produced the same result.

If I do std.debug.print on query, I get a correctly formatted string which, when inserted into sqlite3 cmdline app, works OK, so it’s indeed something with the interop.

You should use std.fmt.allocPrintZ to get a null terminated string.

The sqlite3_exec function expects a C string that is null terminated (it doesn’t take the length of the string as a parameter). In Zig the default string type is []u8 which contains a pointer to an array of bytes (with no null terminator) and the length of the array, but you want the null terminated type [:0]u8.

https://ziglang.org/documentation/master/#Sentinel-Terminated-Slices

Oh, right! :man_facepalming: It works now, thank you!

I wonder why passing a string literal worked then. Does [n]u8 coerce to a zero terminated c-string automatically?

Oh yeah, literals have type *const [_:0]u8 so literals are null terminated by default. So I was incorrect in saying that []u8 is the default string type, what I should have said is that most standard library string functions operate on []u8.

Just wanted to add that you use []u8 when you need to modify the bytes, otherwise use []const u8 instead.

1 Like

Nice! Everything is clear now, thanks.

Putting aside the question itself, isn’t it a bad idea to directly execute sql queries like that? Pretty sure best practice is to use a prepared statement.

I think it depends on where the data is coming from. If you trust the source or not. In this case it might be okay.

This is a quick-n-dirty experiment for a private program of mine, so I don’t really bother – just having fun with learning zig while doing something practical. In other words, this is a fully conscious decision.

1 Like