const std = @import("std"); const assert = std.debug.assert; const c = @cImport({ @cInclude("duckdb.h"); }); const Result = @import("Result.zig").Result; const Connection = @import("Connection.zig"); const PreparedStatement = @import("PreparedStatement.zig"); const Self = @This(); _db: c.duckdb_database, /// Creates (opens) a new database that needs to call .deinit() later pub fn init(file: [*c]const u8) !Self{ var db : Self = undefined; if (c.duckdb_open(file, &db._db) == c.DuckDBError) { return error.DuckDBError; } return db; } pub fn deinit(self: *Self) void{ c.duckdb_close(&self._db); } /// Returns a new Connection that needs to call .deinit() later pub fn connect(self: Self) !Connection { var conn: c.duckdb_connection = undefined; if( c.duckdb_connect(self._db, &conn) == c.DuckDBError ){ return error.DuckDBError; } return Connection.init(conn); } test "Open and connect" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); } test "Simple querying" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); const s : type = comptime struct { primer: i32, // This is safe because the first column is NOT NULL segund: ?i32 }; try connection.run("CREATE TABLE integers (i INTEGER NOT NULL, j INTEGER);"); try connection.run("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);"); var result = try connection.query("SELECT * FROM integers;", s); defer result.deinit(); try std.testing.expect(2 == result.getColumnCount()); var z = try result.next(); try std.testing.expect(z.primer == 3); try std.testing.expect(z.segund.? == 4); z = try result.next(); try std.testing.expect(z.primer == 5); try std.testing.expect(z.segund.? == 6); z = try result.next(); try std.testing.expect(z.primer == 7); try std.testing.expect(z.segund == null); } test "Simple querying with conversion" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); const s : type = comptime struct { primer: f64, // Converting to f64 segund: ?i32 }; try connection.run("CREATE TABLE integers (i INTEGER NOT NULL, j INTEGER);"); try connection.run("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);"); var result = try connection.query("SELECT * FROM integers;", s); defer result.deinit(); try std.testing.expect(2 == result.getColumnCount()); var z = try result.next(); try std.testing.expect(z.primer == 3.0); try std.testing.expect(z.segund.? == 4); z = try result.next(); try std.testing.expect(z.primer == 5.0); try std.testing.expect(z.segund.? == 6); z = try result.next(); try std.testing.expect(z.primer == 7.0); try std.testing.expect(z.segund == null); } test "All fields are captured" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); const s : type = comptime struct { primer: ?i32, segund: ?i32, tercer: ?i32, }; try connection.run("CREATE TABLE integers (i INTEGER NOT NULL, j INTEGER);"); try connection.run("INSERT INTO integers VALUES (3, 4), (5, 6), (7, NULL);"); try std.testing.expectError(error.QueryColumnCountCapture, connection.query("SELECT * FROM integers;", s)); } test "String queries" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); const s : type = comptime struct { primer: []const u8 }; try connection.run("CREATE TABLE text (i VARCHAR NOT NULL );"); try connection.run("INSERT INTO text VALUES ('Inlined');"); try connection.run("INSERT INTO text VALUES ('A very long string that is not inlined');"); var result = try connection.query("SELECT * FROM text;", s); defer result.deinit(); try std.testing.expect(1 == result.getColumnCount()); var w = try result.next(); try std.testing.expect(std.mem.eql(u8, w.primer, "Inlined")); w = try result.next(); try std.testing.expect(std.mem.eql(u8, w.primer, "A very long string that is not inlined")); } test "Prepared queries" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); try connection.run("CREATE TABLE ints (i INTEGER NOT NULL );"); var prepared = try connection.prepareStatement("INSERT INTO ints VALUES (?), (?);"); defer prepared.deinit(); const uno: i32 = 1; const dos: i32 = 2; try prepared.bindAll(.{uno, dos}); var res = try prepared.exec(void); res.deinit(); const s: type = struct { primer: i32, }; var result = try connection.query("SELECT * FROM ints;", s); defer result.deinit(); var r = try result.next(); try std.testing.expect(r.primer == uno); r = try result.next(); try std.testing.expect(r.primer == dos); } test "Prepared queries with strings" { var database = try Self.init(":memory:"); defer database.deinit(); var connection = try database.connect(); defer connection.deinit(); try connection.run("CREATE TABLE ints (i STRING NOT NULL );"); var prepared = try connection.prepareStatement("INSERT INTO ints VALUES (?), (?);"); defer prepared.deinit(); const uno = "1"; const dos = "2"; try prepared.bindAll(.{uno, dos}); var res = try prepared.exec(void); res.deinit(); const s: type = struct { primer: []const u8, }; var result = try connection.query("SELECT * FROM ints;", s); defer result.deinit(); var w = try result.next(); try std.testing.expect(std.mem.eql(u8, w.primer, "1")); w = try result.next(); try std.testing.expect(std.mem.eql(u8, w.primer, "2")); }