{"id":13711884,"url":"https://github.com/vrischmann/zig-sqlite","last_synced_at":"2025-05-15T15:05:12.587Z","repository":{"id":40258847,"uuid":"320724996","full_name":"vrischmann/zig-sqlite","owner":"vrischmann","description":"zig-sqlite is a small wrapper around sqlite's C API, making it easier to use with Zig. ","archived":false,"fork":false,"pushed_at":"2025-03-08T16:59:12.000Z","size":10495,"stargazers_count":458,"open_issues_count":0,"forks_count":63,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-31T20:06:23.749Z","etag":null,"topics":["sqlite","zig"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vrischmann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-12-12T01:45:21.000Z","updated_at":"2025-03-31T03:02:57.000Z","dependencies_parsed_at":"2023-01-30T23:46:00.159Z","dependency_job_id":"dd3585b6-6454-4bbc-b4de-2eb532c1e739","html_url":"https://github.com/vrischmann/zig-sqlite","commit_stats":{"total_commits":556,"total_committers":26,"mean_commits":"21.384615384615383","dds":"0.21043165467625902","last_synced_commit":"712da48cb7f48358ceaa925887c83b6d93887503"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vrischmann%2Fzig-sqlite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vrischmann%2Fzig-sqlite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vrischmann%2Fzig-sqlite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vrischmann%2Fzig-sqlite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vrischmann","download_url":"https://codeload.github.com/vrischmann/zig-sqlite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247730068,"owners_count":20986404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["sqlite","zig"],"created_at":"2024-08-02T23:01:12.610Z","updated_at":"2025-05-15T15:05:12.580Z","avatar_url":"https://github.com/vrischmann.png","language":"C","readme":"# zig-sqlite\n\nThis package is a thin wrapper around [sqlite](https://sqlite.org/index.html)'s C API.\n\n_Maintainer note_: I'm currently on a break working with Zig and don't intend to work on new features for zig-sqlite.\nI will keep it updated for the latest Zig versions because that doesn't take too much of my time.\n\n# Status\n\nWhile the core functionality works right now, the API is still subject to changes.\n\nIf you use this library, expect to have to make changes when you update the code.\n\n# Zig release support\n\n`zig-sqlite` only tracks Zig master (as can be found [here](https://ziglang.org/download/)). The plan is to support releases once Zig 1.0 is released but this can still change.\n\nSo your mileage may vary if you try to use `zig-sqlite`.\n\n# Table of contents\n\n\u003c!--toc:start--\u003e\n- [zig-sqlite](#zig-sqlite)\n- [Status](#status)\n- [Zig release support](#zig-release-support)\n- [Table of contents](#table-of-contents)\n- [Requirements](#requirements)\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Demo](#demo)\n  - [Initialization](#initialization)\n  - [Preparing a statement](#preparing-a-statement)\n    - [Common use](#common-use)\n    - [Diagnostics](#diagnostics)\n  - [Executing a statement](#executing-a-statement)\n  - [Reuse a statement](#reuse-a-statement)\n  - [Reading data in one go](#reading-data-in-one-go)\n    - [Type parameter](#type-parameter)\n    - [`Statement.one`](#statementone)\n    - [`Statement.all` and `Statement.oneAlloc`](#statementall-and-statementonealloc)\n  - [Iterating](#iterating)\n    - [`Iterator.next`](#iteratornext)\n    - [`Iterator.nextAlloc`](#iteratornextalloc)\n  - [Bind parameters and resultset rows](#bind-parameters-and-resultset-rows)\n  - [Custom type binding and reading](#custom-type-binding-and-reading)\n  - [Note about complex allocations](#note-about-complex-allocations)\n- [Comptime checks](#comptime-checks)\n  - [Check the number of bind parameters.](#check-the-number-of-bind-parameters)\n  - [Assign types to bind markers and check them.](#assign-types-to-bind-markers-and-check-them)\n- [User defined SQL functions](#user-defined-sql-functions)\n  - [Scalar functions](#scalar-functions)\n  - [Aggregate functions](#aggregate-functions)\n\u003c!--toc:end--\u003e\n\n# Requirements\n\n[Zig master](https://ziglang.org/download/) is the only required dependency.\n\nFor sqlite, you have options depending on your target:\n* On Windows the only supported way at the moment to build `zig-sqlite` is with the bundled sqlite source code file.\n* On Linux we have two options:\n  * use the system and development package for sqlite (`libsqlite3-dev` for Debian and derivatives, `sqlite3-devel` for Fedora)\n  * use the bundled sqlite source code file.\n\n# Features\n\n* Preparing, executing statements\n* comptime checked bind parameters\n* user defined SQL functions\n\n# Installation\n\nUse the following `zig fetch` command:\n\n```\nzig fetch --save git+https://github.com/vrischmann/zig-sqlite\n```\n\nNow in your `build.zig` you can access the module like this:\n```zig\nconst sqlite = b.dependency(\"sqlite\", .{\n    .target = target,\n    .optimize = optimize,\n});\nexe.root_module.addImport(\"sqlite\", sqlite.module(\"sqlite\"));\n```\n\n# Usage\n\n## Demo\n\nSee https://github.com/vrischmann/zig-sqlite-demo for a quick demo.\n\n## Initialization\n\nImport `zig-sqlite` like this:\n\n```zig\nconst sqlite = @import(\"sqlite\");\n```\n\nYou must create and initialize an instance of `sqlite.Db`:\n\n```zig\nvar db = try sqlite.Db.init(.{\n    .mode = sqlite.Db.Mode{ .File = \"/home/vincent/mydata.db\" },\n    .open_flags = .{\n        .write = true,\n        .create = true,\n    },\n    .threading_mode = .MultiThread,\n});\n```\n\nThe `init` method takes a `InitOptions` struct which will be used to configure sqlite.\n\nOnly the `mode` field is mandatory, the other fields have sane default values.\n\n## Preparing a statement\n\n### Common use\n\nsqlite works exclusively by using prepared statements. The wrapper type is `sqlite.Statement`. Here is how you get one:\n\n```zig\ntry db.exec(\"CREATE TABLE IF NOT EXISTS employees(id integer primary key, name text, age integer, salary integer)\", .{}, .{});\n\nconst query =\n    \\\\SELECT id, name, age, salary FROM employees WHERE age \u003e ? AND age \u003c ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n```\n\nThe `Db.prepare` method takes a `comptime` query string.\n\n### Diagnostics\n\nIf you want failure diagnostics you can use `prepareWithDiags` like this:\n\n```zig\nvar diags = sqlite.Diagnostics{};\nvar stmt = db.prepareWithDiags(query, .{ .diags = \u0026diags }) catch |err| {\n    std.log.err(\"unable to prepare statement, got error {}. diagnostics: {s}\", .{ err, diags });\n    return err;\n};\ndefer stmt.deinit();\n```\n\n## Executing a statement\n\nFor queries which do not return data (`INSERT`, `UPDATE`) you can use the `exec` method:\n\n```zig\nconst query =\n    \\\\INSERT INTO employees(name, age, salary) VALUES(?, ?, ?)\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\ntry stmt.exec(.{}, .{\n    .name = \"José\",\n    .age = 40,\n    .salary = 20000,\n});\n```\n\nSee the section \"Bind parameters and resultset rows\" for more information on the types mapping rules.\n\n## Reuse a statement\n\nYou can reuse a statement by resetting it like this:\n```zig\nconst query =\n    \\\\UPDATE employees SET salary = ? WHERE id = ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\nvar id: usize = 0;\nwhile (id \u003c 20) : (id += 1) {\n    stmt.reset();\n    try stmt.exec(.{}, .{\n        .salary = 2000,\n        .id = id,\n    });\n}\n```\n\n## Reading data in one go\n\nFor queries which return data you have multiple options:\n* `Statement.all` which takes an allocator and can allocate memory.\n* `Statement.one` which does not take an allocator and cannot allocate memory (aside from what sqlite allocates itself).\n* `Statement.oneAlloc` which takes an allocator and can allocate memory.\n\n### Type parameter\n\nAll these methods take a type as first parameter.\n\nThe type represents a \"row\", it can be:\n* a struct where each field maps to the corresponding column in the resultset (so field 0 must map to column 1 and so on).\n* a single type, in that case the resultset must only return one column.\n\nThe type can be a pointer but only when using the methods taking an allocator.\n\nNot all types are allowed, see the section \"Bind parameters and resultset rows\" for more information on the types mapping rules.\n\n### `Statement.one`\n\nUsing `one`:\n\n```zig\nconst query =\n    \\\\SELECT name, age FROM employees WHERE id = ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\nconst row = try stmt.one(\n    struct {\n        name: [128:0]u8,\n        age: usize,\n    },\n    .{},\n    .{ .id = 20 },\n);\nif (row) |r| {\n    const name_ptr: [*:0]const u8 = \u0026r.name;\n    std.log.debug(\"name: {s}, age: {}\", .{ std.mem.span(name_ptr), r.age });\n}\n}\n```\nNotice 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.\n\nIf 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.\n\nHowever if the length is fixed, you can read into a non 0-terminated array, for example:\n\n```zig\nconst query =\n    \\\\SELECT id FROM employees WHERE name = ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\nconst row = try stmt.one(\n    [16]u8,\n    .{},\n    .{ .name = \"Vincent\" },\n);\nif (row) |id| {\n    std.log.debug(\"id: {s}\", .{std.fmt.fmtSliceHexLower(\u0026id)});\n}\n```\n\nIf the column data doesn't have the correct length a `error.ArraySizeMismatch` will be returned.\n\nThe convenience function `sqlite.Db.one` works exactly the same way:\n\n```zig\nconst query =\n    \\\\SELECT age FROM employees WHERE id = ?\n;\n\nconst row = try db.one(usize, query, .{}, .{ .id = 20 });\nif (row) |age| {\n    std.log.debug(\"age: {}\", .{age});\n}\n```\n\n### `Statement.all` and `Statement.oneAlloc`\n\nUsing `all`:\n\n```zig\nconst query =\n    \\\\SELECT name FROM employees WHERE age \u003e ? AND age \u003c ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst names = try stmt.all([]const u8, allocator, .{}, .{\n    .age1 = 20,\n    .age2 = 40,\n});\nfor (names) |name| {\n    std.log.debug(\"name: {s}\", .{ name });\n}\n```\n\nUsing `oneAlloc`:\n\n```zig\nconst query =\n    \\\\SELECT name FROM employees WHERE id = ?\n;\n\nvar stmt = try db.prepare(query);\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst row = try stmt.oneAlloc([]const u8, allocator, .{}, .{\n    .id = 200,\n});\nif (row) |name| {\n    std.log.debug(\"name: {s}\", .{name});\n}\n```\n\n## Iterating\n\nAnother way to get the data returned by a query is to use the `sqlite.Iterator` type.\n\nYou can only get one by calling the `iterator` method on a statement.\n\nThe `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.\n\nIterating is done by calling the `next` or `nextAlloc` method on an iterator. Just like before, `next` cannot allocate memory while `nextAlloc` can allocate memory.\n\n`next` or `nextAlloc` will either return an optional value or an error; you should keep iterating until `null` is returned.\n\n### `Iterator.next`\n\n```zig\nvar stmt = try db.prepare(\"SELECT age FROM employees WHERE age \u003c ?\");\ndefer stmt.deinit();\n\nvar iter = try stmt.iterator(usize, .{\n    .age = 20,\n});\n\nwhile (try iter.next(.{})) |age| {\n    std.debug.print(\"age: {}\\n\", .{age});\n}\n```\n\n### `Iterator.nextAlloc`\n\n```zig\nvar stmt = try db.prepare(\"SELECT name FROM employees WHERE age \u003c ?\");\ndefer stmt.deinit();\n\nvar iter = try stmt.iterator([]const u8, .{\n    .age = 20,\n});\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nwhile (true) {\n    var arena = std.heap.ArenaAllocator.init(allocator);\n    defer arena.deinit();\n\n    const name = (try iter.nextAlloc(arena.allocator(), .{})) orelse break;\n    std.debug.print(\"name: {s}\\n\", .{name});\n}\n```\n\n## Bind parameters and resultset rows\n\nSince sqlite doesn't have many [types](https://www.sqlite.org/datatype3.html) only a small number of Zig types are allowed in binding parameters and in resultset mapping types.\n\nHere are the rules for bind parameters:\n* any Zig `Int` or `ComptimeInt` is treated as a `INTEGER`.\n* any Zig `Float` or `ComptimeFloat` is treated as a `REAL`.\n* `[]const u8`, `[]u8` is treated as a `TEXT`.\n* the custom `sqlite.Blob` type is treated as a `BLOB`.\n* the custom `sqlite.Text` type is treated as a `TEXT`.\n* the `null` value is treated as a `NULL`.\n* non-null optionals are treated like a regular value, null optionals are treated as a `NULL`.\n\nHere are the rules for resultset rows:\n* `INTEGER` can be read into any Zig `Int` provided the data fits.\n* `REAL` can be read into any Zig `Float` provided the data fits.\n* `TEXT` can be read into a `[]const u8` or `[]u8`.\n* `TEXT` can be read into any array of `u8` with a sentinel provided the data fits.\n* `BLOB` follows the same rules as `TEXT`.\n* `NULL` can be read into any optional.\n\nNote 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.\n\n## Custom type binding and reading\n\nSometimes 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\nif you want to store a byte slice as an hex string.\n\nTo 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:\n```zig\npub const MyArray = struct {\n    data: [4]u8,\n\n    pub const BaseType = u32;\n\n    pub fn bindField(self: MyArray, _: std.mem.Allocator) !BaseType {\n        return std.mem.readIntNative(BaseType, \u0026self.data);\n    }\n\n    pub fn readField(_: std.mem.Allocator, value: BaseType) !MyArray {\n        var arr: MyArray = undefined;\n        std.mem.writeIntNative(BaseType, \u0026arr.data, value);\n        return arr;\n    }\n};\n```\n\nNow when you bind a value of type `MyArray` the value returned by `bindField` will be used for binding instead.\n\nSame for reading, when you select _into_ a `MyArray` row or field the value returned by `readField` will be used instead.\n\n_NOTE_: when you _do_ allocate in `bindField` or `readField` make sure to pass a `std.heap.ArenaAllocator`-based allocator.\n\nThe 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\nto use an arena to prevent memory leaks.\n\n## Note about complex allocations\n\nDepending on your queries and types there can be a lot of allocations required. Take the following example:\n```zig\nconst User = struct {\n    id: usize,\n    first_name: []const u8,\n    last_name: []const u8,\n    data: []const u8,\n};\n\nfn fetchUsers(allocator: std.mem.Allocator, db: *sqlite.Db) ![]User {\n    var stmt = try db.prepare(\"SELECT id FROM user WHERE id \u003e $id\");\n    defer stmt.deinit();\n\n    return stmt.all(User, allocator, .{}, .{ .id = 20 });\n}\n```\n\nThis will do multiple allocations:\n* one for each id field in the `User` type\n* one for the resulting slice\n\nTo facilitate memory handling, consider using an arena allocator like this:\n```zig\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nvar arena = std.heap.ArenaAllocator.init(allocator);\ndefer arena.deinit();\n\nconst users = try fetchUsers(arena.allocator(), db);\n_ = users;\n```\n\nThis is especially recommended if you use custom types that allocate memory since, as noted above, it's necessary to prevent memory leaks.\n\n# Comptime checks\n\nPrepared statements contain _comptime_ metadata which is used to validate every call to `exec`, `one` and `all` _at compile time_.\n\n## Check the number of bind parameters.\n\nThe first check makes sure you provide the same number of bind parameters as there are bind markers in the query string.\n\nTake the following code:\n```zig\nvar stmt = try db.prepare(\"SELECT id FROM user WHERE age \u003e ? AND age \u003c ? AND weight \u003e ?\");\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst rows = try stmt.all(usize, allocator, .{}, .{\n    .age_1 = 10,\n    .age_2 = 20,\n});\n_ = rows;\n```\nIt fails with this compilation error:\n```\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:738:17: error: number of bind markers not equal to number of fields\n                @compileError(\"number of bind markers not equal to number of fields\");\n                ^\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:817:22: note: called from here\n            self.bind(values);\n                     ^\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:905:41: note: called from here\n            var iter = try self.iterator(Type, values);\n                                        ^\n./src/main.zig:19:30: note: called from here\n    const rows = try stmt.all(usize, allocator, .{}, .{\n                             ^\n./src/main.zig:5:29: note: called from here\npub fn main() anyerror!void {\n```\n\n## Assign types to bind markers and check them.\n\nThe second (and more interesting) check makes sure you provide appropriately typed values as bind parameters.\n\nThis 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.\n\nFor example, take the same code as above but now we also bind the last parameter:\n```zig\nvar stmt = try db.prepare(\"SELECT id FROM user WHERE age \u003e ? AND age \u003c ? AND weight \u003e ?\");\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst rows = try stmt.all(usize, allocator, .{}, .{\n    .age_1 = 10,\n    .age_2 = 20,\n    .weight = false,\n});\n_ = rows;\n```\n\nThis compiles correctly even if the `weight` field in our `user` table is of the type `INTEGER`.\n\nWe can make sure the bind parameters have the right type if we rewrite the query like this:\n```zig\nvar stmt = try db.prepare(\"SELECT id FROM user WHERE age \u003e ? AND age \u003c ? AND weight \u003e ?{usize}\");\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst rows = try stmt.all(usize, allocator, .{}, .{\n    .age_1 = 10,\n    .age_2 = 20,\n    .weight = false,\n});\n_ = rows;\n```\nNow this fails to compile:\n```\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:745:25: error: value type bool is not the bind marker type usize\n                        @compileError(\"value type \" ++ @typeName(struct_field.field_type) ++ \" is not the bind marker type \" ++ @typeName(typ));\n                        ^\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:817:22: note: called from here\n            self.bind(values);\n                     ^\n/home/vincent/dev/perso/libs/zig-sqlite/sqlite.zig:905:41: note: called from here\n            var iter = try self.iterator(Type, values);\n                                        ^\n./src/main.zig:19:30: note: called from here\n    const rows = try stmt.all(usize, allocator, .{}, .{\n                             ^\n./src/main.zig:5:29: note: called from here\npub fn main() anyerror!void {\n```\nThe syntax is straightforward: a bind marker `?` followed by `{`, a Zig type name and finally `}`.\n\nThere are a limited number of types allowed currently:\n * all [integer](https://ziglang.org/documentation/master/#Primitive-Types) types.\n * all [arbitrary bit-width integer](https://ziglang.org/documentation/master/#Primitive-Types) types.\n * all [float](https://ziglang.org/documentation/master/#Primitive-Types) types.\n * bool.\n * strings with `[]const u8` or `[]u8`.\n * strings with `sqlite.Text`.\n * blobs with `sqlite.Blob`.\n\nIt is probably possible to support arbitrary types if they can be marshaled to an SQLite type. This is something to investigate.\n\n**NOTE**: this is done at compile time and is quite CPU intensive, therefore it's possible you'll have to play with [@setEvalBranchQuota](https://ziglang.org/documentation/master/#setEvalBranchQuota) to make it compile.\n\nTo finish our example, passing the proper type allows it compile:\n```zig\nvar stmt = try db.prepare(\"SELECT id FROM user WHERE age \u003e ? AND age \u003c ? AND weight \u003e ?{usize}\");\ndefer stmt.deinit();\n\nconst allocator = std.heap.page_allocator; // Use a suitable allocator\n\nconst rows = try stmt.all(usize, allocator, .{}, .{\n    .age_1 = 10,\n    .age_2 = 20,\n    .weight = @as(usize, 200),\n});\n_ = rows;\n```\n\n# User defined SQL functions\n\nsqlite supports [user-defined SQL functions](https://www.sqlite.org/c3ref/create_function.html) which come in two types:\n* scalar functions\n* aggregate functions\n\nIn both cases the arguments are [sqlite3\\_values](https://www.sqlite.org/c3ref/value_blob.html) and are converted to Zig values using the following rules:\n* `TEXT` values can be either `sqlite.Text` or `[]const u8`\n* `BLOB` values can be either `sqlite.Blob` or `[]const u8`\n* `INTEGER` values can be any Zig integer\n* `REAL` values can be any Zig float\n\n## Scalar functions\n\nYou can define a scalar function using `db.createScalarFunction`:\n```zig\ntry db.createScalarFunction(\n    \"blake3\",\n    struct {\n        fn run(input: []const u8) [std.crypto.hash.Blake3.digest_length]u8 {\n            var hash: [std.crypto.hash.Blake3.digest_length]u8 = undefined;\n            std.crypto.hash.Blake3.hash(input, \u0026hash, .{});\n            return hash;\n        }\n    }.run,\n    .{},\n);\n\nconst hash = try db.one([std.crypto.hash.Blake3.digest_length]u8, \"SELECT blake3('hello')\", .{}, .{});\n```\n\nEach input arguments in the function call in the statement is passed on to the registered `run` function.\n\n## Aggregate functions\n\nYou can define an aggregate function using `db.createAggregateFunction`:\n```zig\nconst MyContext = struct {\n    sum: u32,\n};\nvar my_ctx = MyContext{ .sum = 0 };\n\ntry db.createAggregateFunction(\n    \"mySum\",\n    \u0026my_ctx,\n    struct {\n        fn step(fctx: sqlite.FunctionContext, input: u32) void {\n            var ctx = fctx.userContext(*MyContext) orelse return;\n            ctx.sum += input;\n        }\n    }.step,\n    struct {\n        fn finalize(fctx: sqlite.FunctionContext) u32 {\n            const ctx = fctx.userContext(*MyContext) orelse return 0;\n            return ctx.sum;\n        }\n    }.finalize,\n    .{},\n);\n\nconst result = try db.one(usize, \"SELECT mySum(nb) FROM foobar\", .{}, .{});\n```\n\nEach input arguments in the function call in the statement is passed on to the registered `step` function.\nThe `finalize` function is called once at the end.\n\nThe context (2nd argument of `createAggregateFunction`) can be whatever you want; both the `step` and `finalize` functions must\nhave their first argument of the same type as the context.\n","funding_links":[],"categories":["Data \u0026 Science","C","Libraries"],"sub_categories":["Database"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvrischmann%2Fzig-sqlite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvrischmann%2Fzig-sqlite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvrischmann%2Fzig-sqlite/lists"}