diff options
Diffstat (limited to 'README.md')
| -rw-r--r-- | README.md | 49 |
1 files changed, 35 insertions, 14 deletions
| @@ -124,6 +124,8 @@ Only the `mode` field is mandatory, the other fields have sane default values. | |||
| 124 | sqlite works exclusively by using prepared statements. The wrapper type is `sqlite.Statement`. Here is how you get one: | 124 | sqlite works exclusively by using prepared statements. The wrapper type is `sqlite.Statement`. Here is how you get one: |
| 125 | 125 | ||
| 126 | ```zig | 126 | ```zig |
| 127 | try db.exec("CREATE TABLE IF NOT EXISTS employees(id integer primary key, name text, age integer, salary integer)", .{}, .{}); | ||
| 128 | |||
| 127 | const query = | 129 | const query = |
| 128 | \\SELECT id, name, age, salary FROM employees WHERE age > ? AND age < ? | 130 | \\SELECT id, name, age, salary FROM employees WHERE age > ? AND age < ? |
| 129 | ; | 131 | ; |
| @@ -153,15 +155,16 @@ For queries which do not return data (`INSERT`, `UPDATE`) you can use the `exec` | |||
| 153 | 155 | ||
| 154 | ```zig | 156 | ```zig |
| 155 | const query = | 157 | const query = |
| 156 | \\UPDATE foo SET salary = ? WHERE id = ? | 158 | \\INSERT INTO employees(name, age, salary) VALUES(?, ?, ?) |
| 157 | ; | 159 | ; |
| 158 | 160 | ||
| 159 | var stmt = try db.prepare(query); | 161 | var stmt = try db.prepare(query); |
| 160 | defer stmt.deinit(); | 162 | defer stmt.deinit(); |
| 161 | 163 | ||
| 162 | try stmt.exec(.{}, .{ | 164 | try stmt.exec(.{}, .{ |
| 165 | .name = "José", | ||
| 166 | .age = 40, | ||
| 163 | .salary = 20000, | 167 | .salary = 20000, |
| 164 | .id = 40, | ||
| 165 | }); | 168 | }); |
| 166 | ``` | 169 | ``` |
| 167 | 170 | ||
| @@ -172,7 +175,7 @@ See the section "Bind parameters and resultset rows" for more information on the | |||
| 172 | You can reuse a statement by resetting it like this: | 175 | You can reuse a statement by resetting it like this: |
| 173 | ```zig | 176 | ```zig |
| 174 | const query = | 177 | const query = |
| 175 | \\UPDATE foo SET salary = ? WHERE id = ? | 178 | \\UPDATE employees SET salary = ? WHERE id = ? |
| 176 | ; | 179 | ; |
| 177 | 180 | ||
| 178 | var stmt = try db.prepare(query); | 181 | var stmt = try db.prepare(query); |
| @@ -284,6 +287,8 @@ const query = | |||
| 284 | var stmt = try db.prepare(query); | 287 | var stmt = try db.prepare(query); |
| 285 | defer stmt.deinit(); | 288 | defer stmt.deinit(); |
| 286 | 289 | ||
| 290 | const allocator = std.heap.page_allocator; // Use a suitable allocator | ||
| 291 | |||
| 287 | const names = try stmt.all([]const u8, allocator, .{}, .{ | 292 | const names = try stmt.all([]const u8, allocator, .{}, .{ |
| 288 | .age1 = 20, | 293 | .age1 = 20, |
| 289 | .age2 = 40, | 294 | .age2 = 40, |
| @@ -303,11 +308,13 @@ const query = | |||
| 303 | var stmt = try db.prepare(query); | 308 | var stmt = try db.prepare(query); |
| 304 | defer stmt.deinit(); | 309 | defer stmt.deinit(); |
| 305 | 310 | ||
| 311 | const allocator = std.heap.page_allocator; // Use a suitable allocator | ||
| 312 | |||
| 306 | const row = try stmt.oneAlloc([]const u8, allocator, .{}, .{ | 313 | const row = try stmt.oneAlloc([]const u8, allocator, .{}, .{ |
| 307 | .id = 200, | 314 | .id = 200, |
| 308 | }); | 315 | }); |
| 309 | if (row) |name| { | 316 | if (row) |name| { |
| 310 | std.log.debug("name: {}", .{name}); | 317 | std.log.debug("name: {s}", .{name}); |
| 311 | } | 318 | } |
| 312 | ``` | 319 | ``` |
| 313 | 320 | ||
| @@ -326,7 +333,7 @@ Iterating is done by calling the `next` or `nextAlloc` method on an iterator. Ju | |||
| 326 | ### `Iterator.next` | 333 | ### `Iterator.next` |
| 327 | 334 | ||
| 328 | ```zig | 335 | ```zig |
| 329 | var stmt = try db.prepare("SELECT age FROM user WHERE age < ?"); | 336 | var stmt = try db.prepare("SELECT age FROM employees WHERE age < ?"); |
| 330 | defer stmt.deinit(); | 337 | defer stmt.deinit(); |
| 331 | 338 | ||
| 332 | var iter = try stmt.iterator(usize, .{ | 339 | var iter = try stmt.iterator(usize, .{ |
| @@ -341,19 +348,21 @@ while (try iter.next(.{})) |age| { | |||
| 341 | ### `Iterator.nextAlloc` | 348 | ### `Iterator.nextAlloc` |
| 342 | 349 | ||
| 343 | ```zig | 350 | ```zig |
| 344 | var stmt = try db.prepare("SELECT name FROM user WHERE age < ?"); | 351 | var stmt = try db.prepare("SELECT name FROM employees WHERE age < ?"); |
| 345 | defer stmt.deinit(); | 352 | defer stmt.deinit(); |
| 346 | 353 | ||
| 347 | var iter = try stmt.iterator([]const u8, .{ | 354 | var iter = try stmt.iterator([]const u8, .{ |
| 348 | .age = 20, | 355 | .age = 20, |
| 349 | }); | 356 | }); |
| 350 | 357 | ||
| 358 | const allocator = std.heap.page_allocator; // Use a suitable allocator | ||
| 359 | |||
| 351 | while (true) { | 360 | while (true) { |
| 352 | var arena = std.heap.ArenaAllocator.init(allocator); | 361 | var arena = std.heap.ArenaAllocator.init(allocator); |
| 353 | defer arena.deinit(); | 362 | defer arena.deinit(); |
| 354 | 363 | ||
| 355 | const name = (try iter.nextAlloc(arena.allocator(), .{})) orelse break; | 364 | const name = (try iter.nextAlloc(arena.allocator(), .{})) orelse break; |
| 356 | std.debug.print("name: {}\n", .{name}); | 365 | std.debug.print("name: {s}\n", .{name}); |
| 357 | } | 366 | } |
| 358 | ``` | 367 | ``` |
| 359 | 368 | ||
| @@ -438,6 +447,8 @@ This will do multiple allocations: | |||
| 438 | 447 | ||
| 439 | To facilitate memory handling, consider using an arena allocator like this: | 448 | To facilitate memory handling, consider using an arena allocator like this: |
| 440 | ```zig | 449 | ```zig |
| 450 | const allocator = std.heap.page_allocator; // Use a suitable allocator | ||
| 451 | |||
| 441 | var arena = std.heap.ArenaAllocator.init(allocator); | 452 | var arena = std.heap.ArenaAllocator.init(allocator); |
| 442 | defer arena.deinit(); | 453 | defer arena.deinit(); |
| 443 | 454 | ||
| @@ -460,7 +471,9 @@ Take the following code: | |||
| 460 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?"); | 471 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?"); |
| 461 | defer stmt.deinit(); | 472 | defer stmt.deinit(); |
| 462 | 473 | ||
| 463 | const rows = try stmt.all(usize, .{}, .{ | 474 | const allocator = std.heap.page_allocator; // Use a suitable allocator |
| 475 | |||
| 476 | const rows = try stmt.all(usize, allocator, .{}, .{ | ||
| 464 | .age_1 = 10, | 477 | .age_1 = 10, |
| 465 | .age_2 = 20, | 478 | .age_2 = 20, |
| 466 | }); | 479 | }); |
| @@ -495,7 +508,9 @@ For example, take the same code as above but now we also bind the last parameter | |||
| 495 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?"); | 508 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?"); |
| 496 | defer stmt.deinit(); | 509 | defer stmt.deinit(); |
| 497 | 510 | ||
| 498 | const rows = try stmt.all(usize, .{ .allocator = allocator }, .{ | 511 | const allocator = std.heap.page_allocator; // Use a suitable allocator |
| 512 | |||
| 513 | const rows = try stmt.all(usize, allocator, .{}, .{ | ||
| 499 | .age_1 = 10, | 514 | .age_1 = 10, |
| 500 | .age_2 = 20, | 515 | .age_2 = 20, |
| 501 | .weight = false, | 516 | .weight = false, |
| @@ -510,7 +525,9 @@ We can make sure the bind parameters have the right type if we rewrite the query | |||
| 510 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}"); | 525 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}"); |
| 511 | defer stmt.deinit(); | 526 | defer stmt.deinit(); |
| 512 | 527 | ||
| 513 | const rows = try stmt.all(usize, .{ .allocator = allocator }, .{ | 528 | const allocator = std.heap.page_allocator; // Use a suitable allocator |
| 529 | |||
| 530 | const rows = try stmt.all(usize, allocator, .{}, .{ | ||
| 514 | .age_1 = 10, | 531 | .age_1 = 10, |
| 515 | .age_2 = 20, | 532 | .age_2 = 20, |
| 516 | .weight = false, | 533 | .weight = false, |
| @@ -554,7 +571,9 @@ To finish our example, passing the proper type allows it compile: | |||
| 554 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}"); | 571 | var stmt = try db.prepare("SELECT id FROM user WHERE age > ? AND age < ? AND weight > ?{usize}"); |
| 555 | defer stmt.deinit(); | 572 | defer stmt.deinit(); |
| 556 | 573 | ||
| 557 | const rows = try stmt.all(usize, .{}, .{ | 574 | const allocator = std.heap.page_allocator; // Use a suitable allocator |
| 575 | |||
| 576 | const rows = try stmt.all(usize, allocator, .{}, .{ | ||
| 558 | .age_1 = 10, | 577 | .age_1 = 10, |
| 559 | .age_2 = 20, | 578 | .age_2 = 20, |
| 560 | .weight = @as(usize, 200), | 579 | .weight = @as(usize, 200), |
| @@ -597,7 +616,7 @@ Each input arguments in the function call in the statement is passed on to the r | |||
| 597 | 616 | ||
| 598 | ## Aggregate functions | 617 | ## Aggregate functions |
| 599 | 618 | ||
| 600 | You can define a scalar function using `db.createAggregateFunction`: | 619 | You can define a aggregate function using `db.createAggregateFunction`: |
| 601 | ```zig | 620 | ```zig |
| 602 | const MyContext = struct { | 621 | const MyContext = struct { |
| 603 | sum: u32, | 622 | sum: u32, |
| @@ -608,12 +627,14 @@ try db.createAggregateFunction( | |||
| 608 | "mySum", | 627 | "mySum", |
| 609 | &my_ctx, | 628 | &my_ctx, |
| 610 | struct { | 629 | struct { |
| 611 | fn step(ctx: *MyContext, input: u32) void { | 630 | fn step(fctx: sqlite.FunctionContext, input: u32) void { |
| 631 | var ctx = fctx.userContext(*MyContext) orelse return; | ||
| 612 | ctx.sum += input; | 632 | ctx.sum += input; |
| 613 | } | 633 | } |
| 614 | }.step, | 634 | }.step, |
| 615 | struct { | 635 | struct { |
| 616 | fn finalize(ctx: *MyContext) u32 { | 636 | fn finalize(fctx: sqlite.FunctionContext) u32 { |
| 637 | const ctx = fctx.userContext(*MyContext) orelse return 0; | ||
| 617 | return ctx.sum; | 638 | return ctx.sum; |
| 618 | } | 639 | } |
| 619 | }.finalize, | 640 | }.finalize, |