Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
zig-cache
*.db
.zigmod
deps.zig
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2022 LeRoyce Pearson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
40 changes: 33 additions & 7 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,56 @@ const std = @import("std");
const Builder = std.build.Builder;

const EXAMPLES = .{
"simple-exec",
"simple",
"blog",
};

pub fn build(b: *Builder) void {
const target = b.standardTargetOptions(.{
// To deal with the error message:
//
// LLD Link... ld.lld: error: undefined symbol: fcntl64
//
// We are using musl by default; though specifying a version of glibc would also work.
//
// See https://github.com/ziglang/zig/issues/9485#issue-956197415
.default_target = .{
.abi = .musl,
},
});

const mode = b.standardReleaseOptions();

const tests = b.addTest("src/sqlite.zig");
const lib = b.addStaticLibrary("sqlite3", null);
lib.setTarget(target);
lib.setBuildMode(mode);
lib.linkLibC();
lib.addCSourceFile("src/sqlite3.c", &.{});
lib.install();

const tests = b.addTest("src/sqlite3.zig");
tests.setBuildMode(mode);
tests.linkSystemLibrary("sqlite3");
tests.linkSystemLibrary("c");
tests.setTarget(target);
tests.addCSourceFile("src/sqlite3.c", &.{});
tests.linkLibC();

const test_step = b.step("test", "Run all tests");
test_step.dependOn(&tests.step);

const all_example_step = b.step("examples", "Build examples");
inline for (EXAMPLES) |example_name| {
const example = b.addExecutable(example_name, "examples" ++ std.fs.path.sep_str ++ example_name ++ ".zig");
example.addPackagePath("sqlite", "src/sqlite.zig");
example.addPackagePath("sqlite", "src/sqlite3.zig");
example.setBuildMode(mode);
example.linkSystemLibrary("sqlite3");
example.linkSystemLibrary("c");
example.setTarget(target);
example.addCSourceFile("src/sqlite3.c", &.{});
example.linkLibC();

var run = example.run();
if (b.args) |args| run.addArgs(args);
b.step("run-example-" ++ example_name, "Run the " ++ example_name ++ " example").dependOn(&run.step);

b.step("run-example-" ++ example_name, "Run the " ++ example_name ++ " example").dependOn(&example.run().step);
all_example_step.dependOn(&example.step);
}
}
107 changes: 62 additions & 45 deletions examples/blog.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ const CMD_DELETE_POST = "delete";
pub fn main() !void {
const alloc = std.heap.c_allocator;

const db = try sqlite.Db.open("blog.db");
const db = try sqlite.SQLite3.open("blog.db");
defer db.close() catch unreachable;

// Create the posts table if it doesn't exist
db.exec(SQL_CREATE_TABLE).finish() catch |e| return printSqliteErrMsg(&db, e);
db.exec(SQL_CREATE_TABLE, null, null, null) catch |e| return printSqliteErrMsg(db, e);

// Get commandline arguments
const args = try std.process.argsAlloc(alloc);
defer std.process.argsFree(alloc, args);
if (args.len < 2) {
std.debug.warn(
std.log.warn(
\\ Incorrect usage.
\\ Usage: {s} <cmd>
\\ Possible commands:
Expand All @@ -67,56 +67,72 @@ pub fn main() !void {
var readOpts = ReadOptions{};
if (std.mem.eql(u8, args[1], CMD_READ_POSTS) and args.len == 3) {
const postId = std.fmt.parseInt(i64, args[2], 10) catch {
std.debug.warn("Invalid post id\nUsage: {s} read [post-id]", .{args[0]});
std.log.warn("Invalid post id\nUsage: {s} read [post-id]", .{args[0]});
return error.NotAnInt;
};
readOpts.singlePost = GetPostBy{ .Id = postId };
} else if (std.mem.eql(u8, args[1], CMD_CREATE_POST)) {
if (args.len != 4) {
std.debug.warn("Error: wrong number of args\nUsage: {s} create <title> <content>\n", .{args[0]});
std.log.warn("Error: wrong number of args\nUsage: {s} create <title> <content>\n", .{args[0]});
return;
}

var exec = try db.execBind(SQL_INSERT_POST, .{ args[2], args[3] });
try exec.finish();
var stmt = (try db.prepare_v2(SQL_INSERT_POST, null)).?;
try stmt.bindText(1, args[2], .static);
try stmt.bindText(2, args[2], .static);

std.debug.assert((try stmt.step()) == .Done);

try stmt.finalize();

readOpts.singlePost = GetPostBy{ .LastInserted = {} };
} else if (std.mem.eql(u8, args[1], CMD_UPDATE_POST)) {
if (args.len != 5) {
std.debug.warn("Not enough arguments\nUsage: {s} update <post-id> <title> <content>\n", .{args[0]});
std.log.warn("Not enough arguments\nUsage: {s} update <post-id> <title> <content>\n", .{args[0]});
return error.NotEnoughArguments;
}
const id = std.fmt.parseInt(i64, args[2], 10) catch {
std.debug.warn("Invalid post id\nUsage: {s} update <post-id> <title> <content>\n", .{args[0]});
std.log.warn("Invalid post id\nUsage: {s} update <post-id> <title> <content>\n", .{args[0]});
return error.NotAnInt;
};
const title = args[3];
const content = args[4];

var exec = try db.execBind(SQL_UPDATE_POST, .{ title, content, id });
try exec.finish();
var stmt = (try db.prepare_v2(SQL_UPDATE_POST, null)).?;
try stmt.bindText(1, title, .static);
try stmt.bindText(2, content, .static);
try stmt.bindInt64(3, id);

std.debug.assert((try stmt.step()) == .Done);

try stmt.finalize();

readOpts.singlePost = GetPostBy{ .Id = id };
} else if (std.mem.eql(u8, args[1], CMD_DELETE_POST)) {
if (args.len != 3) {
std.debug.warn("Not enough arguments\nUsage: {s} delete <post-id>\n", .{args[0]});
std.log.warn("Not enough arguments\nUsage: {s} delete <post-id>\n", .{args[0]});
return error.WrongNumberOfArguments;
}
const id = std.fmt.parseInt(i64, args[2], 10) catch {
std.debug.warn("Invalid post id\nUsage: {s} delete <post-id>\n", .{args[0]});
std.log.warn("Invalid post id\nUsage: {s} delete <post-id>\n", .{args[0]});
return error.NotAnInt;
};

var exec = try db.execBind(SQL_DELETE_POST, .{id});
try exec.finish();
var stmt = (try db.prepare_v2(SQL_DELETE_POST, null)).?;
try stmt.bindInt64(3, id);

std.debug.assert((try stmt.step()) == .Done);

try stmt.finalize();
}

const stdout = &std.io.getStdOut().writer();

try read(stdout, &db, readOpts);
try read(stdout, db, readOpts);
}

fn printSqliteErrMsg(db: *const sqlite.Db, e: sqlite.Error) !void {
std.debug.warn("sqlite3 errmsg: {s}\n", .{db.errmsg()});
fn printSqliteErrMsg(db: *sqlite.SQLite3, e: sqlite.Error) !void {
std.log.warn("sqlite3 errmsg: {s}\n", .{db.errmsg()});
return e;
}

Expand All @@ -133,44 +149,45 @@ const ReadOptions = struct {
singlePost: ?GetPostBy = null,
};

fn read(out: anytype, db: *const sqlite.Db, opts: ReadOptions) !void {
fn read(out: anytype, db: *sqlite.SQLite3, opts: ReadOptions) !void {
if (opts.singlePost) |post| {
var rows: sqlite.RowsIterator = undefined;
switch (post) {
.Id => |postId| rows = try db.execBind(SQL_GET_POST_BY_ID, .{postId}),
.LastInserted => rows = db.exec(SQL_GET_LAST_INSERTED),
}
defer rows.finalize() catch {};

const item = (try rows.next()) orelse {
std.debug.warn("No post with id '{}'\n", .{post});
return error.InvalidPostId;
const sql = switch (post) {
.Id => SQL_GET_POST_BY_ID,
.LastInserted => SQL_GET_LAST_INSERTED,
};
try displaySinglePost(out, &item.Row);

try rows.finish();
var stmt = (try db.prepare_v2(sql, null)).?;
defer stmt.finalize() catch {};
if (post == .LastInserted) {
try stmt.bindInt64(1, post.Id);
}

switch (try stmt.step()) {
.Ok, .Row => {},
.Done => {
std.log.warn("No post with id '{}'\n", .{post});
return error.InvalidPostId;
},
}
try displaySinglePost(out, stmt);
} else {
var rows = db.exec(SQL_GET_POSTS);
defer rows.finalize() catch {};
var stmt = (try db.prepare_v2(SQL_GET_POSTS, null)).?;
defer stmt.finalize() catch {};

try out.print("Posts:\n", .{});
while (try rows.next()) |row_item| {
const row = switch (row_item) {
.Row => |r| r,
.Done => continue,
};
const id = row.columnInt64(0);
const title = row.columnText(1);
const content = row.columnText(2);
while ((try stmt.step()) == .Row) {
const id = stmt.columnInt64(0);
const title = stmt.columnText(1);
const content = stmt.columnText(2);
try out.print("\t{}\t{s}\t{s}\n", .{ id, title, content });
}
}
}

fn displaySinglePost(out: anytype, row: *const sqlite.Row) !void {
const id = row.columnInt64(0);
const title = row.columnText(1);
const content = row.columnText(2);
fn displaySinglePost(out: anytype, stmt: *sqlite.Stmt) !void {
const id = stmt.columnInt64(0);
const title = stmt.columnText(1);
const content = stmt.columnText(2);
try out.print(
\\ Id: {}
\\ Title: {s}
Expand Down
35 changes: 35 additions & 0 deletions examples/simple-exec.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const std = @import("std");
const sqlite = @import("sqlite");

pub fn main() !void {
const db = try sqlite.SQLite3.open("simple.db");
defer db.close() catch unreachable;

try std.io.getStdOut().writer().print(" {s}\t{s}\n", .{ "id", "username" });
try std.io.getStdOut().writer().print(" {s}\t{s}\n", .{ "--", "--------" });
try db.exec(
\\ DROP TABLE IF EXISTS users;
\\ CREATE TABLE users(
\\ id INTEGER PRIMARY KEY AUTOINCREMENT,
\\ username TEXT NOT NULL
\\ );
\\ INSERT INTO users(username)
\\ VALUES
\\ ("shallan"),
\\ ("kaladin"),
\\ ("adolin"),
\\ ("dalinar")
\\ ;
\\ SELECT id, username FROM users;
,
dataCallback,
null,
null,
);
}

pub fn dataCallback(_: ?*anyopaque, number_of_result_columns: c_int, columnsAsText: [*]?[*:0]u8, _: [*]?[*:0]u8) callconv(.C) c_int {
std.debug.assert(number_of_result_columns == 2);
std.io.getStdOut().writer().print(" {s}\t{s}\n", .{ columnsAsText[0], columnsAsText[1] }) catch {};
return 0;
}
39 changes: 20 additions & 19 deletions examples/simple.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ const std = @import("std");
const sqlite = @import("sqlite");

pub fn main() !void {
const db = try sqlite.Db.open("simple.db");
const db = try sqlite.SQLite3.open("simple.db");
defer db.close() catch unreachable;

var rows = db.exec(
try db.exec(
\\ DROP TABLE IF EXISTS users;
\\ CREATE TABLE users(
\\ id INTEGER PRIMARY KEY AUTOINCREMENT,
Expand All @@ -18,27 +18,28 @@ pub fn main() !void {
\\ ("adolin"),
\\ ("dalinar")
\\ ;
\\ SELECT id, username FROM users;
,
null,
null,
null,
);
defer rows.finalize() catch {};

std.debug.warn(" {s}\t{s}\n", .{ "id", "username" });
std.debug.warn(" {s}\t{s}\n", .{ "--", "--------" });
var stmt = (try db.prepare_v2("SELECT id, username FROM users;", null)).?;
defer stmt.finalize() catch unreachable;

const out = std.io.getStdOut().writer();
try out.print(" {s}\t{s}\n", .{ "id", "username" });
try out.print(" {s}\t{s}\n", .{ "--", "--------" });
while (true) {
const row_item_opt = rows.next() catch |e| {
std.debug.warn("sqlite3 errmsg: {s}\n", .{db.errmsg()});
return e;
};
const row_item = row_item_opt orelse break;
const row = switch (row_item) {
// Ignore when statements are completed
.Done => continue,
.Row => |r| r,
};
switch (try stmt.step()) {
.Done => break,
.Row => {},
.Ok => unreachable,
}

const id = row.columnInt(0);
const username = row.columnText(1);
const id = stmt.columnInt(0);
const username = stmt.columnText(1);

std.debug.warn(" {}\t{s}\n", .{ id, username });
try out.print(" {}\t{s}\n", .{ id, username });
}
}
27 changes: 27 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading