GithubHelp home page GithubHelp logo

vrischmann / zig-sqlite Goto Github PK

View Code? Open in Web Editor NEW
355.0 8.0 47.0 7.24 MB

zig-sqlite is a small wrapper around sqlite's C API, making it easier to use with Zig.

License: MIT License

Zig 30.51% C 69.49%
zig sqlite

zig-sqlite's Introduction

zig-sqlite

This package is a thin wrapper around sqlite's C API.

Maintainer note: I'm currently on a break working with Zig and don't intend to work on new features for zig-sqlite. I will keep it updated for the latest Zig versions because that doesn't take too much of my time.

Status

While the core functionality works right now, the API is still subject to changes.

If you use this library, expect to have to make changes when you update the code.

Zig release support

zig-sqlite only tracks Zig master (as can be found here). The plan is to support releases once Zig 1.0 is released but this can still change.

So your mileage may vary if you try to use zig-sqlite.

Table of contents

Requirements

Zig master is the only required dependency.

For sqlite, you have options depending on your target:

  • On Windows the only supported way at the moment to build zig-sqlite is with the bundled sqlite source code file.
  • On Linux we have two options:
    • use the system and development package for sqlite (libsqlite3-dev for Debian and derivatives, sqlite3-devel for Fedora)
    • use the bundled sqlite source code file.

Features

  • Preparing, executing statements
  • comptime checked bind parameters
  • user defined SQL functions

Installation

There are three main ways to include zig-sqlite in your project:

  • using zig's official package manager
  • using the zigmod package manager
  • using a git submodule

Official package manager

Add this as one of the .dependencies inside your build.zig.zon file:

.sqlite = .{
    .url = "https://github.com/vrischmann/zig-sqlite/archive/COMMIT.tar.gz",
    .hash = <hash value>,
},

This tells zig to fetch zig-sqlite from a tarball provided by GitHub. Make sure to replace the COMMIT part with an actual commit SHA in long form, like 219faa2a5cd5a268a865a1100e92805df4b84610. Every time you want to update zig-sqlite you'll have to update this commit.

You'll have to provide the hash field too which is actually a litte annoying because the hash is of the content, not the archive (see the Zig 0.11 release notes). The easiest way to get the hash value is to omit it from the file and run zig build, it will report an error like this:

Fetch Packages... sqlite... /Users/vincent/dev/perso/projects/zig-sqlite-demo/build.zig.zon:6:11: error: url field is missing corresponding hash field
   .url = "https://github.com/vrischmann/zig-sqlite/archive/219faa2a5cd5a268a865a1100e92805df4b84610.tar.gz",
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: expected .hash = "122088f0b73f5adcf07c9af8437c5149ed35c3f16f6393c330a294bdd5f91f069a08",

Now in your build.zig you can access the module like this:

const sqlite = b.dependency("sqlite", .{
    .target = target,
    .optimize = optimize,
});

exe.root_module.addImport("sqlite", sqlite.module("sqlite"));

// links the bundled sqlite3, so leave this out if you link the system one
exe.linkLibrary(sqlite.artifact("sqlite"));

zigmod

Add this to your zig.mod file:

dependencies:
  - src: git https://github.com/vrischmann/zig-sqlite branch-master

Note that if you're building an executable and not a library you should use dev_dependencies instead.

Next run zigmod fetch; it should create a deps.zig file.

Now in your build.zig you can access the package like this:

const deps = @import("deps.zig");
...
deps.addAllTo(exe);

This is the easiest way to add zig-sqlite because it uses the bundled source code, avoiding all sorts of linking problems.

Git submodule

If you don't want to use a package manager you can simply add this repository as a git submodule.

Then you need to chose if you want to use the system sqlite library or the bundled source code.

Using the system sqlite library

If you want to use the system sqlite library, add the following to your build.zig target(s):

const sqlite = b.addModule("sqlite", .{
    .root_source_file = b.path("third_party/zig-sqlite/sqlite.zig"),
});
sqlite.addCSourceFiles(.{
    .files = &[_][]const u8{
        "third_party/zig-sqlite/c/workaround.c",
    },
    .flags = &[_][]const u8{"-std=c99"},
});
sqlite.addIncludePath(b.path("third_party/sqlite/c"));

exe.linkLibC();
exe.linkSystemLibrary("sqlite3");
exe.root_module.addImport("sqlite", sqlite);

Using the bundled sqlite source code file

If you want to use the bundled sqlite source code file, first you need to add it to the module in your build.zig file:

const sqlite = b.addModule("sqlite", .{
    .root_source_file = b.path("third_party/zig-sqlite/sqlite.zig"),
});
sqlite.addCSourceFiles(.{
    .files = &[_][]const u8{
        "third_party/zig-sqlite/c/sqlite3.c",
        "third_party/zig-sqlite/c/workaround.c",
    },
    .flags = &[_][]const u8{"-std=c99"},
});
sqlite.addIncludePath(b.path("third_party/sqlite/c"));

exe.linkLibC();
exe.root_module.addImport("sqlite", sqlite);

If you need to define custom compile-time options for sqlite, modify the flags (second argument to addCSourceFiles).

If you're building with glibc you must make sure that the version used is at least 2.28.

You can do that in your build.zig file:

var target = b.standardTargetOptions(.{});
target.setGnuLibCVersion(2, 28, 0);
exe.setTarget(target);

Or with -Dtarget:

$ zig build -Dtarget=native-linux-gnu.2.28

Usage

Demo

See https://github.com/vrischmann/zig-sqlite-demo for a quick demo.

Initialization

Import zig-sqlite like this:

const sqlite = @import("sqlite");

You must create and initialize an instance of sqlite.Db:

var db = try sqlite.Db.init(.{
    .mode = sqlite.Db.Mode{ .File = "/home/vincent/mydata.db" },
    .open_flags = .{
        .write = true,
        .create = true,
    },
    .threading_mode = .MultiThread,
});

The init method takes a InitOptions struct which will be used to configure sqlite.

Only the mode field is mandatory, the other fields have sane default values.

Preparing a statement

Common use

sqlite works exclusively by using prepared statements. The wrapper type is sqlite.Statement. Here is how you get one:

const query =
    \\SELECT id, name, age, salary FROM employees WHERE age > ? AND age < ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

The Db.prepare method takes a comptime query string.

Diagnostics

If you want failure diagnostics you can use prepareWithDiags like this:

var diags = sqlite.Diagnostics{};
var stmt = db.prepareWithDiags(query, .{ .diags = &diags }) catch |err| {
    std.log.err("unable to prepare statement, got error {}. diagnostics: {s}", .{ err, diags });
    return err;
};
defer stmt.deinit();

Executing a statement

For queries which do not return data (INSERT, UPDATE) you can use the exec method:

const query =
    \\UPDATE foo SET salary = ? WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

try stmt.exec(.{}, .{
    .salary = 20000,
    .id = 40,
});

See the section "Bind parameters and resultset rows" for more information on the types mapping rules.

Reuse a statement

You can reuse a statement by resetting it like this:

const query =
    \\UPDATE foo SET salary = ? WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

var id: usize = 0;
while (id < 20) : (id += 1) {
    stmt.reset();
    try stmt.exec(.{}, .{
        .salary = 2000,
        .id = id,
    });
}

Reading data

For queries which return data you have multiple options:

  • Statement.all which takes an allocator and can allocate memory.
  • Statement.one which does not take an allocator and cannot allocate memory (aside from what sqlite allocates itself).
  • Statement.oneAlloc which takes an allocator and can allocate memory.

Type parameter

All these methods take a type as first parameter.

The type represents a "row", it can be:

  • a struct where each field maps to the corresponding column in the resultset (so field 0 must map to column 1 and so on).
  • a single type, in that case the resultset must only return one column.

The type can be a pointer but only when using the methods taking an allocator.

Not all types are allowed, see the section "Bind parameters and resultset rows" for more information on the types mapping rules.

Non allocating

Using one:

const query =
    \\SELECT name, age FROM employees WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

const row = try stmt.one(
    struct {
        name: [128:0]u8,
        age: usize,
    },
    .{},
    .{ .id = 20 },
);
if (row) |row| {
    std.log.debug("name: {}, age: {}", .{std.mem.spanZ(&row.name), row.age});
}

Notice that to read text we need to use a 0-terminated array; if the name column is bigger than 127 bytes the call to one will fail.

If the length of the data is variable then the sentinel is mandatory: without one there would be no way to know where the data ends in the array.

However if the length is fixed, you can read into a non 0-terminated array, for example:

const query =
    \\SELECT id FROM employees WHERE name = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

const row = try stmt.one(
    [16]u8,
    .{},
    .{ .name = "Vincent" },
);
if (row) |id| {
    std.log.debug("id: {s}", .{std.fmt.fmtSliceHexLower(&id)});
}

If the column data doesn't have the correct length a error.ArraySizeMismatch will be returned.

The convenience function sqlite.Db.one works exactly the same way:

const query =
    \\SELECT age FROM employees WHERE id = ?
;

const row = try db.one(usize, query, .{}, .{ .id = 20 });
if (row) |age| {
    std.log.debug("age: {}", .{age});
}

Allocating

Using all:

const query =
    \\SELECT name FROM employees WHERE age > ? AND age < ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

const names = try stmt.all([]const u8, allocator, .{}, .{
    .age1 = 20,
    .age2 = 40,
});
for (names) |name| {
    std.log.debug("name: {s}", .{ name });
}

Using oneAlloc:

const query =
    \\SELECT name FROM employees WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

const row = try stmt.oneAlloc([]const u8, allocator, .{}, .{
    .id = 200,
});
if (row) |name| {
    std.log.debug("name: {}", .{name});
}

Iterating

Another way to get the data returned by a query is to use the sqlite.Iterator type.

You can only get one by calling the iterator method on a statement.

The iterator method takes a type which is the same as with all, one or oneAlloc: every row retrieved by calling next or nextAlloc will have this type.

Iterating is done by calling the next or nextAlloc method on an iterator. Just like before, next cannot allocate memory while nextAlloc can allocate memory.

next or nextAlloc will either return an optional value or an error; you should keep iterating until null is returned.

Non allocating

var stmt = try db.prepare("SELECT age FROM user WHERE age < ?");
defer stmt.deinit();

var iter = try stmt.iterator(usize, .{
    .age = 20,
});

while (try iter.next(.{})) |age| {
    std.debug.print("age: {}\n", .{age});
}

Allocating

var stmt = try db.prepare("SELECT name FROM user WHERE age < ?");
defer stmt.deinit();

var iter = try stmt.iterator([]const u8, .{
    .age = 20,
});

while (true) {
    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();

    const name = (try iter.nextAlloc(arena.allocator(), .{})) orelse break;
    std.debug.print("name: {}\n", .{name});
}

Bind parameters and resultset rows

Since sqlite doesn't have many types only a small number of Zig types are allowed in binding parameters and in resultset mapping types.

Here are the rules for bind parameters:

  • any Zig Int or ComptimeInt is treated as a INTEGER.
  • any Zig Float or ComptimeFloat is treated as a REAL.
  • []const u8, []u8 is treated as a TEXT.
  • the custom sqlite.Blob type is treated as a BLOB.
  • the custom sqlite.Text type is treated as a TEXT.
  • the null value is treated as a NULL.
  • non-null optionals are treated like a regular value, null optionals are treated as a NULL.

Here are the rules for resultset rows:

  • INTEGER can be read into any Zig Int provided the data fits.
  • REAL can be read into any Zig Float provided the data fits.
  • TEXT can be read into a []const u8 or []u8.
  • TEXT can be read into any array of u8 with a sentinel provided the data fits.
  • BLOB follows the same rules as TEXT.
  • NULL can be read into any optional.

Note that arrays must have a sentinel because we need a way to communicate where the data actually stops in the array, so for example use [200:0]u8 for a TEXT field.

Custom type binding and reading

Sometimes the default field binding or reading logic is not what you want, for example if you want to store an enum using its tag name instead of its integer value or if you want to store a byte slice as an hex string.

To accomplish this you must first define a wrapper struct for your type. For example if your type is a [4]u8 and you want to treat it as an integer:

pub const MyArray = struct {
    data: [4]u8,

    pub const BaseType = u32;

    pub fn bindField(self: MyArray, _: std.mem.Allocator) !BaseType {
        return std.mem.readIntNative(BaseType, &self.data);
    }

    pub fn readField(_: std.mem.Allocator, value: BaseType) !MyArray {
        var arr: MyArray = undefined;
        std.mem.writeIntNative(BaseType, &arr.data, value);
        return arr;
    }
};

Now when you bind a value of type MyArray the value returned by bindField will be used for binding instead.

Same for reading, when you select into a MyArray row or field the value returned by readField will be used instead.

NOTE: when you do allocate in bindField or readField make sure to pass a std.heap.ArenaAllocator-based allocator.

The binding or reading code does not keep tracking of allocations made in custom types so it can't free the allocated data itself; it's therefore required to use an arena to prevent memory leaks.

Note about complex allocations

Depending on your queries and types there can be a lot of allocations required. Take the following example:

const User = struct {
    id: usize,
    first_name: []const u8,
    last_name: []const u8,
    data: []const u8,
};

fn fetchUsers(allocator: std.mem.Allocator, db: *sqlite.Db) ![]User {
    var stmt = try db.prepare("SELECT id FROM user WHERE id > $id");
    defer stmt.deinit();

    return stmt.all(User, allocator, .{}, .{ .id = 20 });
}

This will do multiple allocations:

  • one for each id field in the User type
  • one for the resulting slice

To facilitate memory handling, consider using an arena allocator like this:

var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();

const users = try fetchUsers(arena.allocator(), db);
_ = users;

This is especially recommended if you use custom types that allocate memory since, as noted above, it's necessary to prevent memory leaks.

Comptime checks

Prepared statements contain comptime metadata which is used to validate every call to exec, one and all at compile time.

Check the number of bind parameters.

The first check makes sure you provide the same number of bind parameters as there are bind markers in the query string.

Take the following code:

var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?");
defer stmt.deinit();

const rows = try stmt.all(usize, .{}, .{
    .age_1 = 10,
    .age_2 = 20,
});
_ = rows;

It fails with this compilation error:

/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:738:17: error: number of bind markers not equal to number of fields
                @compileError("number of bind markers not equal to number of fields");
                ^
/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:817:22: note: called from here
            self.bind(values);
                     ^
/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:905:41: note: called from here
            var iter = try self.iterator(Type, values);
                                        ^
./src/main.zig:19:30: note: called from here
    const rows = try stmt.all(usize, allocator, .{}, .{
                             ^
./src/main.zig:5:29: note: called from here
pub fn main() anyerror!void {

Assign types to bind markers and check them.

The second (and more interesting) check makes sure you provide appropriately typed values as bind parameters.

This check is not automatic since with a standard SQL query we have no way to know the types of the bind parameters, to use it you must provide theses types in the SQL query with a custom syntax.

For example, take the same code as above but now we also bind the last parameter:

var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?");
defer stmt.deinit();

const rows = try stmt.all(usize, .{ .allocator = allocator }, .{
    .age_1 = 10,
    .age_2 = 20,
    .weight = false,
});
_ = rows;

This compiles correctly even if the weight field in our user table is of the type INTEGER.

We can make sure the bind parameters have the right type if we rewrite the query like this:

var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}");
defer stmt.deinit();

const rows = try stmt.all(usize, .{ .allocator = allocator }, .{
    .age_1 = 10,
    .age_2 = 20,
    .weight = false,
});
_ = rows;

Now this fails to compile:

/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:745:25: error: value type bool is not the bind marker type usize
                        @compileError("value type " ++ @typeName(struct_field.field_type) ++ " is not the bind marker type " ++ @typeName(typ));
                        ^
/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:817:22: note: called from here
            self.bind(values);
                     ^
/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:905:41: note: called from here
            var iter = try self.iterator(Type, values);
                                        ^
./src/main.zig:19:30: note: called from here
    const rows = try stmt.all(usize, allocator, .{}, .{
                             ^
./src/main.zig:5:29: note: called from here
pub fn main() anyerror!void {

The syntax is straightforward: a bind marker ? followed by {, a Zig type name and finally }.

There are a limited number of types allowed currently:

It's probably possible to support arbitrary types if they can be marshaled to a sqlite type. This is something to investigate.

NOTE: this is done at compile time and is quite CPU intensive, therefore it's possible you'll have to play with @setEvalBranchQuota to make it compile.

To finish our example, passing the proper type allows it compile:

var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}");
defer stmt.deinit();

const rows = try stmt.all(usize, .{}, .{
    .age_1 = 10,
    .age_2 = 20,
    .weight = @as(usize, 200),
});
_ = rows;

User defined SQL functions

sqlite supports user-defined SQL functions which come in two types:

  • scalar functions
  • aggregate functions

In both cases the arguments are sqlite3_values and are converted to Zig values using the following rules:

  • TEXT values can be either sqlite.Text or []const u8
  • BLOB values can be either sqlite.Blob or []const u8
  • INTEGER values can be any Zig integer
  • REAL values can be any Zig float

Scalar functions

You can define a scalar function using db.createScalarFunction:

try db.createScalarFunction(
    "blake3",
    struct {
        fn run(input: []const u8) [std.crypto.hash.Blake3.digest_length]u8 {
            var hash: [std.crypto.hash.Blake3.digest_length]u8 = undefined;
            std.crypto.hash.Blake3.hash(input, &hash, .{});
            return hash;
        }
    }.run,
    .{},
);

const hash = try db.one([std.crypto.hash.Blake3.digest_length]u8, "SELECT blake3('hello')", .{}, .{});

Each input arguments in the function call in the statement is passed on to the registered run function.

Aggregate functions

You can define a scalar function using db.createAggregateFunction:

const MyContext = struct {
    sum: u32,
};
var my_ctx = MyContext{ .sum = 0 };

try db.createAggregateFunction(
    "mySum",
    &my_ctx,
    struct {
        fn step(ctx: *MyContext, input: u32) void {
            ctx.sum += input;
        }
    }.step,
    struct {
        fn finalize(ctx: *MyContext) u32 {
            return ctx.sum;
        }
    }.finalize,
    .{},
);

const result = try db.one(usize, "SELECT mySum(nb) FROM foobar", .{}, .{});

Each input arguments in the function call in the statement is passed on to the registered step function. The finalize function is called once at the end.

The context (2nd argument of createAggregateFunction) can be whatever you want; both step and finalize function must have their first argument of the same type as the context.

zig-sqlite's People

Contributors

akvadrako avatar cloudef avatar data-man avatar daurnimator avatar edyu avatar greenfork avatar jacobcrabill avatar jiacai2050 avatar johnbcodes avatar krvopije avatar ktiy avatar lithdew avatar luexa avatar lun-4 avatar malcolmstill avatar mfashby avatar miaourt avatar nektro avatar okamo1984 avatar sobeston avatar squeek502 avatar thislight avatar vrischmann avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zig-sqlite's Issues

Windows support

Thanks to #4 (Being worked on on #5) the wrapper could support pretty much all platforms supported by sqlite and zig, without the need to dynamically link sqlite.

We also might wish a CI entry for Windows.

Possible improvement on errorFromResultCode

// TODO(vincent): can we do something with comptime here ?

I notice this comment when randomly read code, I think a possible way to use comptime here:

@@ -128,12 +128,17 @@ pub const Error = SQLiteError ||
     SQLiteExtendedReadOnlyError ||
     SQLiteExtendedConstraintError;
 
+fn sqliteVersionLargeThan(actual: i32) bool {
+    return c.SQLITE_VERSION_NUMBER >= actual;
+}
+
 pub fn errorFromResultCode(code: c_int) Error {
     // TODO(vincent): can we do something with comptime here ?
     // The version number is always static and defined by sqlite.
 
     // These errors are only available since 3.25.0.
-    if (c.SQLITE_VERSION_NUMBER >= 3025000) {
+    // or just   const is_gt_3025000 = c.SQLITE_VERSION_NUMBER >= 3025000;
+    if (comptime sqliteVersionLargeThan(3025000)) {

What do you think?

Somehow provide an API for dynamic query texts

Right now it looks to be impossible to compose a SQL query at runtime, as all prepare functions take a comptime query.

Do i have to use the native API for this or is it planned to add this feature?

Another option would be something like query macros where i can dynamically insert some code in the query when preparing it...

Make allocators optional for custom struct binding

My custom struct is a wrapper around a [26]u8

pub const ID = struct {
    data: [26]u8,
    // .. various helper methods to generate IDs
    
    /// Turn ID into a SQL argument
    pub fn sql(self: *const Self) sqlite.Text {
        return sqlite.Text{ .data = &self.data };
    }
}

While exploring things, I noticed that bindField() override exists, and that I could use it to remove the need to write id.sql() in my queries, however, that method interface would not suffice for my usecase:

const field_value = try field.bindField(options.allocator);

If I wanted to remove id.sql() calls, I would then need to add unnecessary allocators to every SQL call that gives an ID struct as an argument, even though the memory that I want to pass to SQLite is already available on the stack by the time I want to execute the query. Example

            try db.exec(
                \\ delete from tag_names
                \\ where core_hash = ?
            ,
                .{},
                .{self.core.id}, // self.core.id is a full ID struct, which means its inner [26]u8 is available for us to use directly.
            );

Maybe we could check argument count at comptime and not require allocators if argument count is 1?

Support large SQL statements

ParsedQuery.query only holds 1KB worth of SQL text, which is not enough for the use case of a migration tool, since after parsing it, we copy the entire query over to that field:

/usr/lib/zig/std/debug.zig:234:14: error: reached unreachable code
    if (!ok) unreachable; // assertion failure
             ^
/usr/lib/zig/std/mem.zig:219:11: note: called from here
    assert(dest.len >= source.len);
          ^
./.zigmod/deps/v/git/github.com/vrischmann/zig-sqlite/branch-master/query.zig:144:17: note: called from here
        mem.copy(u8, &parsed_query.query, &buf);
                ^
./.zigmod/deps/v/git/github.com/vrischmann/zig-sqlite/branch-master/sqlite.zig:1191:44: note: called from here
    return Statement(opts, ParsedQuery.from(query));
                                           ^
./.zigmod/deps/v/git/github.com/vrischmann/zig-sqlite/branch-master/sqlite.zig:528:33: note: called from here
        break :blk StatementType(.{}, query);
                                ^
./.zigmod/deps/v/git/github.com/vrischmann/zig-sqlite/branch-master/sqlite.zig:526:90: note: called from here
    pub fn prepare(self: *Self, comptime query: []const u8) DynamicStatement.PrepareError!blk: {
                                                                                         ^
./src/main.zig:165:41: note: called from here
                    try self.executeOnce(decl_sql);
                                        ^
./src/main.zig:141:46: note: called from here
    pub fn migrateCommand(self: *Self) !void {
                                             ^

SQLite documentation says:

The maximum number of bytes in the text of an SQL statement is limited to SQLITE_MAX_SQL_LENGTH which defaults to 1,000,000,000.

We are some orders of magnitude back on that number... maybe as a paliative we could bump ParsedQuery.query to 32KB? A suggestion that could help it in design-space would be receiving the maximum size of a SQL query as a parameter for the ParsedQuery type itself, and setting it to query.len when we're in Db.prepare, preventing us from always having a 1GB buffer at comptime.

empty slice bound to null

in a text field, &[_]T{} seems to be bound to null by sqlite instead of an empty string.

need to investigate and see if it actually is a bug.

error: 'workaround.h' file not found

zig-sqlite commit

91e5fed

Zig version

0.12.0

Steps to reproduce

Following the documentation for installing zig-sqlite using git submudule I have added the following to my build.zig

const sqlite = b.addModule("sqlite", .{ .root_source_file = .{ .path = "lib/zig-sqlite/sqlite.zig" }, }); exe.linkLibC(); exe.linkSystemLibrary("sqlite3"); exe.root_module.addImport("sqlite", sqlite);

When I try to compile I got the error
lib/zig-sqlite/c.zig:6:5: error: C import failed
..zig-cache/o/746be78b01d8cca46a4793fd42b7ca76/cimport.h:2:10: error: 'workaround.h' file not found

Expected behaviour

Build should succeed.

exec expects two arguments, but README only lists one argument.

zig-sqlite commit

19535aa

Zig version

0.11.0

Steps to reproduce

statement.exec needs extra argument, e.g. code in README does not work this way:

const query =
    \\UPDATE foo SET salary = ? WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

try stmt.exec(.{
    .salary = 20000,
    .id = 40,
});

but it works if an empty struct is added as the first argument to exec

const query =
    \\UPDATE foo SET salary = ? WHERE id = ?
;

var stmt = try db.prepare(query);
defer stmt.deinit();

try stmt.exec(.{}, .{
    .salary = 20000,
    .id = 40,
});

Expected behaviour

According to the README, the exec function of a prepared statement takes only one argument. So either the README should be modified for all occurrences of the exec function or the API should be changed.

Build failure using example usage

When following the instructions on the main README.md, I am getting the following compile time error:

./.zigmod/deps/v/git/github.com/vrischmann/zig-sqlite/branch-stage2/sqlite.zig:335:50: error: expected type '[*c]const u8', found '[:0]const u8'
                const result = c.sqlite3_open_v2(path, &db, flags, null);
                                                 ^~~~

My main.zig file is the following:

const sqlite = @import("sqlite");

pub fn main() !void {
    var db = try sqlite.Db.init(.{ .mode = sqlite.Db.Mode{ .File = "/tmp/test.db" } });
    const query =
        \\SELECT id, name, age, salary FROM employees WHERE age > ? AND age < ?
    ;

    var stmt = try db.prepare(query);
    defer stmt.deinit();
}

I've tried with both master and the stage2 branch, both using zigmod, and receive the same error. My zig version is 0.10.0-dev.3874+9ce841a0f.

Thanks in advance for any help/feedback.

Does not work on latest Zig

Build system can not import it as dependency using latest Zig. There was a build system overhaul a few days ago.

translate-c name mangling causing compilation issues

/home/runner/work/zig-sqlite/zig-sqlite/zig-cache/o/1d89e7d91170a50b500bd328be51c64b/cimport.zig:1356:67: note: called from here
vtab.zig:235:34: error: root struct of file '.home.runner.work.zig-sqlite.zig-sqlite.zig-cache.o.1d89e7d91170a50b500bd328be51c64b.cimport' has no member named 'struct_sqlite3_index_constraint_usage_4'
vtab.zig:235:34: error: root struct of file '.home.runner.work.zig-sqlite.zig-sqlite.zig-cache.o.0a5ce126024b37aaeb18f9cd8de97216.cimport' has no member named 'struct_sqlite3_index_constraint_usage_4'
vtab.zig:235:34: error: root struct of file '.home.runner.work.zig-sqlite.zig-sqlite.zig-cache.o.fe0b1d530a3ec6a11431b8fa6f3a780d.cimport' has no member named 'struct_sqlite3_index_constraint_usage_4'
vtab.zig:235:34: error: root struct of file '.home.runner.work.zig-sqlite.zig-sqlite.zig-cache.o.488cb338597fd0c7228241b5321547a0.cimport' has no member named 'struct_sqlite3_index_constraint_usage_4'

Originally posted by @johnbcodes in #133 (comment)

Illegal instruction at zig-sqlite/c/sqlite3.c:35765:20

zig-sqlite commit

19535aa

Zig version

0.11.0

Steps to reproduce

Context: I am using the built-in version of sqlite, the one included with this library.

  1. Have a struct that holds a pointer to a database:
        // Create database object
        var db = try sqlite.Db.init(.{
            .mode = sqlite.Db.Mode{ .File = db_path },
            .open_flags = .{
                .write = true,
                .create = true,
            },
            .threading_mode = .MultiThread,
        });

        return .{ .db = &db, .files = &files, .next_id = next_id };
  1. Try to prepare a statement inside a function
        var stmt = try self.db.prepare(query);
        defer stmt.deinit();

        try stmt.exec(.{}, .{
            self.next_id,
            new_file.timestamp,
            fmt_path,
            new_file.hash,
        });
  1. Observe an error when preparing the statement:
steps [2/6] zig build-exe syntholink Debug native-native-gnu.2.28... LLVM Emit Object...

Illegal instruction at address 0x34c593
libs/zig-sqlite/c/sqlite3.c:35765:20: 0x34c593 in sqlite3SafetyCheckOk 
  eOpenState = db->eOpenState;
                   ^
libs/zig-sqlite/c/sqlite3.c:140165:8: 0x34e5b8 in sqlite3LockAndPrepare 
  if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
       ^
libs/zig-sqlite/c/sqlite3.c:140281:8: 0x34e938 in sqlite3_prepare_v3 
  rc = sqlite3LockAndPrepare(db,zSql,nBytes,
       ^
zig-sqlite/sqlite.zig:1521:48: 0x2abf85 in prepare
            const result = c.sqlite3_prepare_v3(
                                               ^
zig-sqlite/sqlite.zig:1981:61: 0x277d76 in prepare
                .dynamic_stmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
                                                            ^
zig-sqlite/sqlite.zig:516:49: 0x26db07 in prepare__anon_8127
        return StatementType(.{}, query).prepare(self, .{}, 0);
                                                ^

        var stmt = try self.db.prepare(query);

Note that I have checked that the database exists, I have permission to open and write to it and that the query is correct (I can run it manually).

Expected behaviour

The database opens correctly when preparing a statement.

sqlite3.h not found

I made an attempt to use your wrapper. I added your repository as a git submodule in a vendor directory.

I followed the steps in your readme; although I made a minor change in the following line as it appears the path of sqlite.c has changed.

// Before
sqlite.addCSourceFile("vendor/zig-sqlite/sqlite3.c", &[_][]const u8{"-std=c99"});

// After
sqlite.addCSourceFile("vendor/zig-sqlite/c/sqlite3.c", &[_][]const u8{"-std=c99"});

The project would compile fine but would fail at runtime due to missing sqlite.h. Not being a zig build expert by any means, I also included the sqlite.h in my build.zig. Now, it fails at build-time. Here is the my build file:

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    // Standard target options allows the person running `zig build` to choose
    // what target to build for. Here we do not override the defaults, which
    // means any target is allowed, and the default is native. Other options
    // for restricting supported target set are available.
    var target = b.standardTargetOptions(.{});

    // Standard release options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
    const mode = b.standardReleaseOptions();

    const exe = b.addExecutable("zig-http", "src/main.zig");

    // Build sqlite
    // https://github.com/vrischmann/zig-sqlite
    const sqlite = b.addStaticLibrary("sqlite", null);
    sqlite.addCSourceFile("vendor/zig-sqlite/c/sqlite3.h", &[_][]const u8{"-std=c99"});
    sqlite.addCSourceFile("vendor/zig-sqlite/c/sqlite3.c", &[_][]const u8{"-std=c99"});
    sqlite.linkLibC();
    exe.linkLibC();
    exe.linkLibrary(sqlite);
    exe.addPackage(.{ .name = "sqlite", .path = "vendor/zig-sqlite/sqlite.zig" });
    target.setGnuLibCVersion(2, 28, 0);

    exe.setTarget(target);
    exe.setBuildMode(mode);
    exe.install();

    const run_cmd = exe.run();
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

When running zig build, I get the following error:

$ zig build
./vendor/zig-sqlite/sqlite.zig:8:15: error: C import failed
pub const c = @cImport({
              ^
./zig-cache/o/14e06b957c8c350ff835e95d31fd2640/cimport.h:1:10: note: 'sqlite3.h' file not found
#include <sqlite3.h>
         ^
./vendor/zig-sqlite/sqlite.zig:298:10: note: referenced here
    db: *c.sqlite3,
         ^
./src/main.zig:6:27: note: referenced here
    var db = try sqlite.Db.init(.{
                          ^
zig-http...The following command exited with error code 1:
/usr/bin/zig build-exe /home/kurtis/Code/sandbox/zig-http/src/main.zig -lc /home/kurtis/Code/sandbox/zig-http/zig-cache/o/cc98d7cbd6b618a77ecb081e2f6f160e/libsqlite.a --cache-dir /home/kurtis/Code/sandbox/zig-http/zig-cache --global-cache-dir /home/kurtis/.cache/zig --name zig-http -target native-native-gnu.2.28 -mcpu znver2 --pkg-begin sqlite /home/kurtis/Code/sandbox/zig-http/vendor/zig-sqlite/sqlite.zig --pkg-end --enable-cache 
error: the following build command failed with exit code 1:
/home/kurtis/Code/sandbox/zig-http/zig-cache/o/1a19e351c473280c46346cace1ff8995/build /usr/bin/zig /home/kurtis/Code/sandbox/zig-http /home/kurtis/Code/sandbox/zig-http/zig-cache /home/kurtis/.cache/zig

This is my directory structure (including your repository):

.
โ”œโ”€โ”€ build.zig
โ”œโ”€โ”€ src
โ”‚ย ย  โ”œโ”€โ”€ main.zig
โ”œโ”€โ”€ vendor
โ”‚ย ย  โ””โ”€โ”€ zig-sqlite
โ”‚ย ย      โ”œโ”€โ”€ build.zig
โ”‚ย ย      โ”œโ”€โ”€ c
โ”‚ย ย      โ”‚ย ย  โ”œโ”€โ”€ sqlite3.c
โ”‚ย ย      โ”‚ย ย  โ””โ”€โ”€ sqlite3.h
โ”‚ย ย      โ”œโ”€โ”€ errors.zig
โ”‚ย ย      โ”œโ”€โ”€ gyro.zzz
โ”‚ย ย      โ”œโ”€โ”€ LICENSE
โ”‚ย ย      โ”œโ”€โ”€ query.zig
โ”‚ย ย      โ”œโ”€โ”€ README.md
โ”‚ย ย      โ”œโ”€โ”€ sqlite.zig
โ”‚ย ย      โ”œโ”€โ”€ tools
โ”‚ย ย      โ”‚ย ย  โ”œโ”€โ”€ Dockerfile.debian
โ”‚ย ย      โ”‚ย ย  โ””โ”€โ”€ Dockerfile.fedora
โ”‚ย ย      โ”œโ”€โ”€ zig.mod
โ”‚ย ย      โ””โ”€โ”€ zigmod.lock

Here is my simple main.zig source file:

const std = @import("std");
const sqlite = @import("sqlite");

pub fn main() anyerror!void {

    var db = try sqlite.Db.init(.{
        .mode = sqlite.Db.Mode{ .File="sqlite.db" },
        .open_flags = .{
            .write = true,
            .create = true,
        },
        .threading_mode = .MultiThread,
    });

    

    std.log.info("All your codebase are belong to us.", .{});
}

I am using Zig version 0.8.1 on Manjaro Linux (x86_64)

From the error, I believe that the issue is related to the build but I honestly don't know enough about the build system yet to debug it. My hunch is that it's related to the path specified here in sqlite.zig:

pub const c = @cImport({
    @cInclude("sqlite3.h");
});

Reading this old Github Issue suggests that @cInclude only searches system paths.

Do you have any suggestions? Worst-case, I can install the sqlite header system-wide but it'd be a better experience to simply vendor it in like this.

Thanks!

remove dummy_diags and embed a default diag in Db for use

There are some dummy_diags used in several API, it is called dummy because user can't access it, some example:

I propose another way to populate diags

Add a Diagnostics object in Db, and use it instead of create a dummy one if user don't provide it

Users don't need to create a diag object when invoke API after adopt this, and I think this make it easy for user to use.

Any ideas?

Build panics when specify a glibc version

zig-sqlite commit

3d8e617

Zig version

0.12.0-dev.1800+559e216f3

Steps to reproduce

I am run an x86_64 linux and want to target to a diffirent glibc version.

โ•ฐโ”€โžค  uname -m -s -r
Linux 6.1.64-1-MANJARO x86_64

โ•ฐโ”€โžคzig build -Doptimize=ReleaseSafe -Dtarget=native-linux-gnu.2.28
thread 119243 panic: invalid enum value
/usr/lib/zig/lib/std/zig/CrossTarget.zig:616:46: 0x2dbd80 in zigTriple (build)
        try result.writer().print("-{s}.", .{@tagName(self.getAbi())});
                                             ^
/home/hequn/router/zig/zig-sqlite/build.zig:330:39: 0x2d6ccf in build (build)
            try cross_target.zigTriple(b.allocator),
                                      ^
/usr/lib/zig/lib/std/Build.zig:1883:43: 0x2c2b84 in runBuild__anon_6856 (build)
        .ErrorUnion => try build_zig.build(b),
                                          ^
/usr/lib/zig/lib/build_runner.zig:313:29: 0x2be702 in main (build)
        try builder.runBuild(root);
                            ^
/usr/lib/zig/lib/std/start.zig:585:37: 0x2a9a55 in posixCallMainAndExit (build)
            const result = root.main() catch |err| {
                                    ^
/usr/lib/zig/lib/std/start.zig:253:5: 0x2a9541 in _start (build)
    asm volatile (switch (native_arch) {
    ^
???:?:?: 0x8 in ??? (???)
Unwind information for `???:0x8` was not available, trace may be incomplete

Expected behaviour

This cross compile can compile successful.

Support vcpkg

On Windows the zig build system supports finding libraries using vcpkg, we should make sure zig-sqlite can work with it.

Relates to #17

Build panics on aarch64

zig-sqlite commit

eb7f58d

Zig version

0.12.0-dev.1092+68ed78775

Steps to reproduce

This switch

const ci_targets = switch (builtin.target.cpu.arch) {
panics if the CPU architecture is anything other than .x86_64, meaning the library can't be built on e.g. modern macOS systems.

Expected behaviour

The library builds on aarch64 systems.

Testing with dbfile and in_memory=false options fail

This produces errors:

zig build test -Din_memory=false -Ddbfile=/tmp/my.db
1/49 test "native-Debug-multi sqlite: db init"... OK
2/49 test "native-Debug-multi sqlite: exec multi"... OK
3/49 test "native-Debug-multi sqlite: exec multi with single statement"... FAIL (SQLiteError)
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:486:16: 0x2439a4 in Db.prepareDynamicWithDiags (test)
        return try DynamicStatement.prepare(self, query, options, 0);
               ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:863:24: 0x237ed1 in Db.execMulti (test)
                stmt = try self.prepareDynamicWithDiags(query, new_options);
                       ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2355:5: 0x21f294 in test "native-Debug-multi sqlite: exec multi with single statement" (test)
    try db.execMulti("create table a(b int);", .{});
    ^
4/49 test "native-Debug-multi sqlite: db pragma"... OK
5/49 test "native-Debug-multi sqlite: last insert row id"... OK
6/49 test "native-Debug-multi sqlite: statement exec"... OK
7/49 test "native-Debug-multi sqlite: statement execDynamic"... OK
8/49 test "native-Debug-multi sqlite: db execAlloc"... OK
9/49 test "native-Debug-multi sqlite: read a single user into a struct"... OK
10/49 test "native-Debug-multi sqlite: read all users into a struct"... OK
11/49 test "native-Debug-multi sqlite: read in an anonymous struct"... OK
12/49 test "native-Debug-multi sqlite: read in a Text struct"... OK
13/49 test "native-Debug-multi sqlite: read a single text value"... OK
14/49 test "native-Debug-multi sqlite: read a single integer value"... OK
15/49 test "native-Debug-multi sqlite: read a single value into an enum backed by an integer"... OK
16/49 test "native-Debug-multi sqlite: read a single value into an enum backed by a string"... OK
17/49 test "native-Debug-multi sqlite: read a single value into void"... OK
18/49 test "native-Debug-multi sqlite: read a single value into bool"... OK
19/49 test "native-Debug-multi sqlite: insert bool and bind bool"... OK
20/49 test "native-Debug-multi sqlite: bind string literal"... OK
21/49 test "native-Debug-multi sqlite: bind pointer"... OK
22/49 test "native-Debug-multi sqlite: read pointers"... OK
23/49 test "native-Debug-multi sqlite: optional"... OK
24/49 test "native-Debug-multi sqlite: statement reset"... OK
25/49 test "native-Debug-multi sqlite: statement iterator"... OK
26/49 test "native-Debug-multi sqlite: blob open, reopen"... OK
27/49 test "native-Debug-multi sqlite: failing open"... OK
28/49 test "native-Debug-multi sqlite: failing prepare statement"... OK
29/49 test "native-Debug-multi sqlite: diagnostics format"... OK
30/49 test "native-Debug-multi sqlite: exec with diags, failing statement"... OK
31/49 test "native-Debug-multi sqlite: savepoint with no failures"... OK
32/49 test "native-Debug-multi sqlite: two nested savepoints with inner failure"... OK
33/49 test "native-Debug-multi sqlite: two nested savepoints with outer failure"... OK
34/49 test "native-Debug-multi sqlite: bind custom type"... OK
35/49 test "native-Debug-multi sqlite: bind runtime slice"... OK
36/49 test "native-Debug-multi sqlite: prepareDynamic"... OK
37/49 test "native-Debug-multi sqlite: oneDynamic"... OK
38/49 test "native-Debug-multi sqlite: one with all named parameters"... OK
39/49 test "native-Debug-multi sqlite: create scalar function"... OK
40/49 test "native-Debug-multi sqlite: create aggregate function with no aggregate context"... OK
41/49 test "native-Debug-multi sqlite: create aggregate function with an aggregate context"... FAIL (SQLiteError)
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:486:16: 0x2439a4 in Db.prepareDynamicWithDiags (test)
        return try DynamicStatement.prepare(self, query, options, 0);
               ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:863:24: 0x237ed1 in Db.execMulti (test)
                stmt = try self.prepareDynamicWithDiags(query, new_options);
                       ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2355:5: 0x21f294 in test "native-Debug-multi sqlite: exec multi with single statement" (test)
    try db.execMulti("create table a(b int);", .{});
    ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:192:30: 0x24bdcf in errors.errorFromResultCode (test)
        c.SQLITE_CANTOPEN => return error.SQLiteCantOpen,
                             ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:342:21: 0x240d9a in Db.init (test)
                    return errors.errorFromResultCode(result);
                    ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2035:33: 0x283b19 in Statement((struct StatementOptions constant),(struct query.ParsedQueryState(21) constant)).prepare (test)
                .dynamic_stmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
                                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:481:9: 0x2412da in Db.prepareWithDiags (test)
        return StatementType(.{}, query).prepare(self, options, 0);
        ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2144:21: 0x241a4c in Statement((struct StatementOptions constant),(struct query.ParsedQueryState(8) constant)).exec (test)
                    return errors.errorFromResultCode(result);
                    ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2035:33: 0x288d99 in Statement((struct StatementOptions constant),(struct query.ParsedQueryState(63) constant)).prepare (test)
                .dynamic_stmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
                                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:481:9: 0x288b6a in Db.prepareWithDiags (test)
        return StatementType(.{}, query).prepare(self, options, 0);
        ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:428:20: 0x242701 in Db.exec (test)
        var stmt = try self.prepareWithDiags(query, options);
                   ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2035:33: 0x288d99 in Statement((struct StatementOptions constant),(struct query.ParsedQueryState(63) constant)).prepare (test)
                .dynamic_stmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
                                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:481:9: 0x28af3a in Db.prepareWithDiags (test)
        return StatementType(.{}, query).prepare(self, options, 0);
        ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:428:20: 0x242bf1 in Db.exec (test)
        var stmt = try self.prepareWithDiags(query, options);
                   ^
/home/grfork/reps/kisa/deps/zig-sqlite/errors.zig:179:27: 0x24badd in errors.errorFromResultCode (test)
        c.SQLITE_ERROR => return error.SQLiteError,
                          ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:1591:17: 0x24d442 in DynamicStatement.prepare (test)
                return errors.errorFromResultCode(result);
                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:2035:33: 0x297749 in Statement((struct StatementOptions constant),(struct query.ParsedQueryState(63) constant)).prepare (test)
                .dynamic_stmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
                                ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:481:9: 0x29751a in Db.prepareWithDiags (test)
        return StatementType(.{}, query).prepare(self, options, 0);
        ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:428:20: 0x2469c1 in Db.exec (test)
        var stmt = try self.prepareWithDiags(query, options);
                   ^
/home/grfork/reps/kisa/deps/zig-sqlite/sqlite.zig:3867:5: 0x230e26 in test "native-Debug-multi sqlite: create aggregate function with an aggregate context" (test)
    try db.exec("CREATE TABLE view(id integer PRIMARY KEY, a integer, b integer)", .{}, .{});
    ^
42/49 test "native-Debug-multi sqlite: empty slice"... OK
43/49 test "native-Debug-multi sqlite: fuzzer found crashes"... OK
44/49 test "native-Debug-multi tagged union"... OK
45/49 query.test "native-Debug-multi parsed query: query"... OK
46/49 query.test "native-Debug-multi parsed query: bind markers types"... OK
47/49 query.test "native-Debug-multi parsed query: bind markers identifier"... OK
48/49 query.test "native-Debug-multi parsed query: query bind identifier"... OK
49/49 query.test "native-Debug-multi parsed query: bind marker character inside string"... OK
47 passed; 0 skipped; 2 failed.
error: the following test command failed with exit code 1:
/home/grfork/reps/kisa/deps/zig-sqlite/zig-cache/o/9d26e6dd2d41d7da3e75aeaa719bf63d/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/reps/kisa/deps/zig-sqlite/sqlite.zig -lc -lsqlite3 --test-name-prefix native-Debug-multi  --cache-dir /home/grfork/reps/kisa/deps/zig-sqlite/zig-cache --global-cache-dir /home/grfork/.cache/zig --name test -fno-single-threaded --pkg-begin build_options /home/grfork/reps/kisa/deps/zig-sqlite/zig-cache/options/4nHAGINNEroBRc8FiT6lgf-aAeWAO-6XQNU2mVqz2-ZyD-032_o-sPOHpurnzg8P --pkg-end --enable-cache 
error: the following build command failed with exit code 1:
/home/grfork/reps/kisa/deps/zig-sqlite/zig-cache/o/e8c7e47bf7278113a988e32bda21a73b/build /home/grfork/zig-linux-x86_64-0.10.0-dev.3685+dae7aeb33/zig /home/grfork/reps/kisa/deps/zig-sqlite /home/grfork/reps/kisa/deps/zig-sqlite/zig-cache /home/grfork/.cache/zig test -Din_memory=false -Ddbfile=/tmp/my.db

Support host parameter names in SQLite3

In SQLite3 references, Binding Values To Prepared Statements describes:

In the SQL statement text input to sqlite3_prepare_v2() and its variants, literals may be replaced by a parameter that matches one of following templates:

  • ?
  • ?NNN
  • :VVV
  • @VVV
  • $VVV
    In the templates above, NNN represents an integer literal, and VVV represents an alphanumeric identifier. The values of these parameters (also called "host parameter names" or "SQL parameters") can be set using the sqlite3_bind_*() routines defined here.

Is it possible to replace current "auto-generated" (as i think) names with the names, or to use the names in some way?

Error when using statement iterator

Hi!

This library looks very neat! However I ran into a problem.

I have created a statement and want to iterate over the results, e.g:

var iter = try stmt.iterator(MyItem, .{});
while(try iter.next(.{}))` |item| {
...

This raises a compile error:

./libs/zig-sqlite/sqlite.zig:1355:17: error: options passed to readPointer must have an allocator field
                @compileError("options passed to readPointer must have an allocator field");
                ^
./libs/zig-sqlite/sqlite.zig:1470:53: note: called from here
                    .Pointer => try self.readPointer(FieldType, options, i),
                                                    ^
./libs/zig-sqlite/sqlite.zig:1436:47: note: called from here
                const ret = try self.readField(field.field_type, options, i);
                                              ^
./libs/zig-sqlite/sqlite.zig:1118:47: note: called from here
                    return try self.readStruct(options);
                                              ^
./libs/zig-sqlite/sqlite.zig:1067:64: note: called from here
        pub fn next(self: *Self, options: QueryOptions) !?Type {

However the QueryOptions taken by next() does not have such a field.

I worked around it by adding an allocator field to QueryOptions but it's unclear to me if it's really necessary or if the check is in error.

I am using your master branch, and zig 0.10.0-dev.2431+0e6285c8f.

check the column type at comptime

Same as bind markers, we can store metadata for the columns and comptime check that calls to all, one, Iterator.next use a compatible row type.

failing build on alpine edge

see this build log, specifically:

+ cd zig-sqlite
+ TERM=dumb
+ zig build test -Din_memory=true
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crt1.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crt1.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crt1.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crt1.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crt1.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crti.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crti.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crtn.o: contains a compressed section, but zlib is not available
ld.lld: error: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../lib/crtn.o: contains a compressed section, but zlib is not available
error: LLDReportedFailure
The following command exited with error code 1:
/home/build/zig-master/zig test /home/build/zig-sqlite/sqlite.zig -lc -lsqlite3 --pkg-begin build_options /home/build/zig-sqlite/zig-cache/test_build_options.zig --pkg-end --test-name-prefix native-Debug-multi  --cache-dir /home/build/zig-sqlite/zig-cache --global-cache-dir /home/build/.cache/zig --name test -I /home/build/zig-sqlite/c 
error: the following build command failed with exit code 1:
/home/build/zig-sqlite/zig-cache/o/723ce6041c0fc5ff598968f8b1b58c82/build /home/build/zig-master/zig /home/build/zig-sqlite /home/build/zig-sqlite/zig-cache /home/build/.cache/zig test -Din_memory=true

apparently related to how the zig tarball is built by the CI system.

resolution depends on ziglang/zig#8932 I think.

"PRAGMA journal_mode = WAL" gives "panic: invalid result code 100"

zig-sqlite commit

fd17eb9

Zig version

0.13.0-dev.211+6a65561e3

Steps to reproduce

const std = @import("std");
const sqlite = @import("sqlite");

pub fn main() !void {
var db = try sqlite.Db.init(.{
        .mode = sqlite.Db.Mode{ .File = "test.db"},
        .open_flags = .{
            .write = true,
            .create = true,
        },
        .threading_mode = .Serialized,
});
try db.exec("PRAGMA journal_mode = WAL", .{}, .{});
}

or something similar to that... gives something similar to the following error

thread 4191359 panic: invalid result code 100
/Users/rob/projects/pacz/lib/zig-sqlite/errors.zig:269:32: 0x1059575c0 in errorFromResultCode (pacz)
        else => std.debug.panic("invalid result code {}", .{code}),
                               ^
/Users/rob/projects/pacz/lib/zig-sqlite/sqlite.zig:2140:54: 0x105959065 in exec__anon_4777 (pacz)
                    return errors.errorFromResultCode(result);
                                                     ^
/Users/rob/projects/pacz/lib/zig-sqlite/sqlite.zig:476:22: 0x105959145 in exec__anon_4753 (pacz)
        try stmt.exec(options, values);
                     ^
/Users/rob/projects/pacz/src/db.zig:35:16: 0x10595cafd in createTables (pacz)
    try db.exec("PRAGMA journal_mode = WAL", .{}, .{});

The short version of this, is that running some PRAGMA expressions fails with a 100 result code.

https://www.sqlite.org/rescode.html says:

(100) SQLITE_ROW

The SQLITE_ROW result code returned by sqlite3_step() indicates that another row of output is available.

I think this is because, when in the cli, the command returns "wal" or "memory". Which can safely be ignored.

Anyway.. I'll just conclude this issue by saying I'm REALLY enjoying your library. Thank you so much, it's been a joy to learn zig with this at the center of my app.

Expected behaviour

just work/return normally. do I have to keep writing in here to get the green button to show up? oh there it goes

maybe map all sqlite result code

sqlite's result codes are well defined, we could map all error result-code to an error without too much trouble.

The diagnostics struct will probably still be necessary to provide the sqlite error message, unless we want to provide a wrapper for sqlite3_errstr and it's up to the user to get the error message if they want too.

Is it not possible to do something like that ?

zig-sqlite commit

latest

Zig version

0.13.0

Steps to reproduce

const query2 =
    \\ INSERT INTO villages(name,player_id,x_position,y_position) VALUES(?,(SELECT id FROM players WHERE session_id = ?),?,?);
;
var stmt2 = try app.db.prepare(query2);
defer stmt2.deinit();
// FIXME pour eviter l erreur verifier avec une requete avant ??
stmt2.exec(.{}, .{ .name = "vilajo", .session_id = session_id, .x_position = 1.0, .y_position = 2.0 }) catch {
    try res.json(.{ .message = "Error while creating village" }, .{});
    return;
};

Expected behaviour

I would except to pass the parameter to the SELECT inside the INSERT but I simply get an "Err: error.SQLiteError". I have no idea why. Is it possible to do requests the way I did ?

add diagnostics to exec

Db.exec and Statement.exec don't take an options argument and therefore can't store diagnostics.

This is incoherent with one, all etc, and should be fixed.

This is going to be a breaking change but easily fixable.

Characters inside strings incorrectly identified as bind markers

In at least some circumstances, $, ?, etc, will be detected as bind markers for the statement, even when they are used inside of strings.

For example:

const create =
    \\CREATE TABLE IF NOT EXISTS image(
    \\  iid INTEGER PRIMARY KEY,
    \\  eid INTEGER UNIQUE,
    \\  metadata TEXT,
    \\  full_url TEXT GENERATED ALWAYS AS
    \\    (json_extract(metadata, '$.image.representations.full')) VIRTUAL,
    \\  thumb_url TEXT GENERATED ALWAYS AS
    \\    (json_extract(metadata, '$.image.representations.thumb')) VIRTUAL,
    \\  extension TEXT GENERATED ALWAYS AS
    \\    (json_extract(metadata, '$.image.format')) VIRTUAL,
    \\  hash_meta TEXT,
    \\  image_id INTEGER,
    \\  thumb_id INTEGER
    \\);
;

Here, the dollar signs inside the PATH argument to json_extract are detected as being bind markers, resulting in a compile time error due to a mismatch of the number of bind markers vs the number of value fields.

Can't fetch package using the Official package manager

zig-sqlite commit

master

Zig version

0.11.0

Steps to reproduce

I'm getting the following error whenever trying to use the build.zig.zon dependency:

error: Expected response status '200 OK' got '404 Not Found'
     .url = "https://github.com/vrischmann/zig-sqlite/archive/COMMIT.tar.gz",

This is what my dependencies look like:

    .dependencies = .{
	.sqlite = .{
		.url = "https://github.com/vrischmann/zig-sqlite/archive/COMMIT.tar.gz",
	},
    },

My build.zig looks like this:

const sqlite = b.dependency("sqlite", .{
    .target = target,
    .optimize = mode,
});

exe.addModule("sqlite", sqlite.module("sqlite"));

// Sqlite
exe.linkLibC();
exe.linkSystemLibrary("sqlite3");
exe.addPackage(.{ .name = "sqlite", .path = "third_party/zig-sqlite/sqlite.zig" });

Expected behaviour

It to fetch correctly.

Can't create a new database

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

Large queries get: evaluation exceeded backwards branches

e.g.

/home/daurnimator/src/zig/lib/std/mem.zig:159:5: error: evaluation exceeded 1000 backwards branches
    for (source) |s, i|
    ^
./vendor/zig-sqlite/query.zig:100:17: note: called from here
        mem.copy(u8, &parsed_query.query, &buf);
                ^
./vendor/zig-sqlite/sqlite.zig:466:93: note: called from here
    pub fn prepare(self: *Self, comptime query: []const u8) !Statement(.{}, ParsedQuery.from(query)) {
                                                                                            ^
./vendor/zig-sqlite/sqlite.zig:466:71: note: called from here
    pub fn prepare(self: *Self, comptime query: []const u8) !Statement(.{}, ParsedQuery.from(query)) {
                                                                      ^

extend the API to be able to have an allocator in bindField

There's a use case @nektro talked to me about which I didn't have in mind when writing the bind API, which is being able to convert the bind parameter to some other type and use this as the real sqlite bind parameter.

This is useful for types which need to be marshaled, for example a UUID can internally be a 16 byte array but stored in sqlite as text; instead of requiring the user to marshal the UUID itself like this:

const rows = try db.oneAlloc("SELECT foo FROM stuff WHERE id = ?", .{
    .id = try my_uuid.toString(allocator),
});

we could have this:

const MyUUID = struct {
    data: [16]u8,

    const BaseType = []const u8;

    fn bindField(self: *MyUUID, allocator: *std.mem.Allocator) BaseType {
        return std.fmt.allocPrint("{x}", .{self.data});
    }
};

const rows = try db.oneAlloc("SELECT foo FROM stuff WHERE id = ?", .{
    .id = my_uuid,
});

then in bindField we can detect bind parameters of this form and automatically do the right thing.

To be useful this custom bindField method needs an allocator but with the current bind and bindField this isn't possible.

Here's my proposal:

  • modify bind and bindField to take an options: anytype just like readField
  • add execAlloc with the signature pub fn execAlloc(self: *Self, allocator: *std.mem.Allocator, options: QueryOptions, values: anytype) !void and pass the allocator to bind
  • add iteratorAlloc with the signature pub fn iteratorAlloc(self: *Self, comptime Type: type, allocator: *std.mem.Allocator, values: anytype) !Iterator(Type) and pass the allocator to bind

This way custom bind parameters can work and exec and iterator stay allocator free just like one/next

@nektro let me know if you want to work on this (or if something is unclear)

[RFC] - Finalizing a prepared statement that thew an error shouldn't log the error

Imagine I have a UNIQUE constraint in my Db. I may use the error that the prepared statement generates to detect that the value I'm trying to insert is already present. If I catch the error and handle it myself, zig-sqlite still prints an error when calling stmt.deinit():

logger.err("unable to finalize prepared statement, result: {}, detailed error: {}", .{ result, detailed_error });

This isn't desirable because it's being logged with the "error" level which always shows. This is a good approach for debugging during development, but it's ugly in production.


Note: Sorry for not following the template, I clicked on "open a new issue" on the line of code mentioned above and created the issue by itself :(

Readme out of Date Linking Instructions

Took me a bit of time looking through the std lib and discord to get the right calls, just two changed when linking with the bundled sqlite source code. I have a branch with the two readme edits I'm happy to submit to PR.

Changed relevant Readme section to:

Using the bundled sqlite source code file

If you want to use the bundled sqlite source code file, first you need to add it as a static library in your build.zig file:

const sqlite = b.addStaticLibrary(.{ .name = "sqlite", .target = target, .optimize = .ReleaseSafe });
sqlite.addCSourceFile("third_party/zig-sqlite/c/sqlite3.c", &[_][]const u8{"-std=c99"});
sqlite.linkLibC();

If you need to define custom compile-time options for sqlite, modify the flags (second argument to addCSourceFile).

Now it's just a matter of linking your build.zig target(s) to this library instead of the system one:

exe.linkLibrary(sqlite);
exe.addModule("sqlite", b.createModule(.{ .source_file = .{ .path = "third_party/zig-sqlite/sqlite.zig", } }));
exe.addIncludePath("third_party/zig-sqlite/c");

fyi: api changes in zig master

I completely understand if you wish to only track the latest stable release...nonetheless, here is some information regarding breaking changes on zig master:

$ zig version                                                                                      
0.9.0-dev.1896+a1b79ea58

trying to build some code using this lib:

/home/brad/.zig/zig-linux-x86_64-0.9.0-dev.1896+a1b79ea58/lib/std/mem.zig:664:19: error: deprecated; use use std.mem.span() or std.mem.sliceTo()                                                      pub const spanZ = @compileError("deprecated; use use std.mem.span() or std.mem.sliceTo()");                          ^                                                                                ./.zigmod/deps/git/github.com/vrischmann/zig-sqlite/sqlite.zig:289:27: note: referenced here                   break :blk mem.spanZ(msg);                                                                                       ^

thanks for making this library, I don't think I would have bothered with zig without it
Brad

allow binding a string literal

Binding a string literal fails with this error:

/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:501:33: error: cannot bind field weight of type *const [6:0]u8
                        else => @compileError("cannot bind field " ++ struct_field.name ++ " of type " ++ @typeName(struct_field.field_type)),

it should be possible to make this work.

Wrap sqlite3_create_function

I would like to write functions in zig for sqlite to use and pass them in via https://www.sqlite.org/c3ref/create_function.html
e.g. I have a custom function I want to use for an index.

little sample I wrote:

const result = sqlite.c.sqlite3_create_function(
    db.db,
    "MessageHash.calculate",
    1,
    sqlite.c.SQLITE_UTF8 | sqlite.c.SQLITE_DETERMINISTIC,
    null,
    struct {
        fn xFunc(context: ?*sqlite.c.sqlite3_context, argc: c_int, argv: ?[*]?*sqlite.c.sqlite3_value) callconv(.C) void {
            assert(argc == 1);
            const arg = argv.?[0];

            const message_len = sqlite.c.sqlite3_value_bytes(arg);
            if (message_len != @sizeOf(Message)) {
                sqlite.c.sqlite3_result_error(context, "incorrect Message size", sqlite.c.SQLITE_CONSTRAINT_FUNCTION);
                return;
            }
            const message_ptr = @ptrCast([*]const u8, sqlite.c.sqlite3_value_blob(arg));
            const message = std.mem.bytesToValue(Message, message_ptr[0..@sizeOf(Message)]);

            const msg_hash = MessageHash.calculate(message);

            // https://github.com/ziglang/zig/issues/8595
            const SQLITE_TRANSIENT = @intToPtr(sqlite.c.sqlite3_destructor_type, @bitCast(usize, @as(isize, -1)));
            sqlite.c.sqlite3_result_blob(context, &msg_hash.hash, MessageHash.len, SQLITE_TRANSIENT);
        }
    }.xFunc,
    null,
    null,
);
if (result != sqlite.c.SQLITE_OK) {
    return sqlite.errorFromResultCode(result);
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.