diff options
Diffstat (limited to '')
| -rw-r--r-- | README.md | 10 | ||||
| -rw-r--r-- | sqlite.zig | 44 |
2 files changed, 50 insertions, 4 deletions
| @@ -119,4 +119,12 @@ Since sqlite doesn't have many [types](https://www.sqlite.org/datatype3.html) on | |||
| 119 | Here are the rules for bind parameters: | 119 | Here are the rules for bind parameters: |
| 120 | * any Zig `Int` or `ComptimeInt` is tread as a `INTEGER`. | 120 | * any Zig `Int` or `ComptimeInt` is tread as a `INTEGER`. |
| 121 | * any Zig `Float` or `ComptimeFloat` is treated as a `REAL`. | 121 | * any Zig `Float` or `ComptimeFloat` is treated as a `REAL`. |
| 122 | * `[]const u8`, `[]u8` or any array of `u8` is treated as a `TEXT` or `BLOB`. | 122 | * `[]const u8`, `[]u8` or any array of `u8` is treated as a `TEXT`. |
| 123 | * The custom `sqlite.Bytes` type is treated as a `TEXT` or `BLOB`. | ||
| 124 | |||
| 125 | Here are the resules for resultset rows: | ||
| 126 | * `INTEGER` can be read into any Zig `Int` provided the data fits. | ||
| 127 | * `REAL` can be read into any Zig `Float` provided the data fits. | ||
| 128 | * `TEXT` can be read into a `[]const u8` or `[]u8`. | ||
| 129 | * `TEXT` can be read into any array of `u8` provided the data fits. | ||
| 130 | * `BLOB` follows the same rules as `TEXT`. | ||
| @@ -117,6 +117,17 @@ pub const Db = struct { | |||
| 117 | } | 117 | } |
| 118 | }; | 118 | }; |
| 119 | 119 | ||
| 120 | /// Bytes is used to represent a byte slice with its SQLite datatype. | ||
| 121 | /// | ||
| 122 | /// Since Zig doesn't have strings we can't tell if a []u8 must be stored as a SQLite TEXT or BLOB, | ||
| 123 | /// this type can be used to communicate this when executing a statement. | ||
| 124 | /// | ||
| 125 | /// If a []u8 or []const u8 is passed as bind parameter it will be treated as TEXT. | ||
| 126 | pub const Bytes = union(enum) { | ||
| 127 | Blob: []const u8, | ||
| 128 | Text: []const u8, | ||
| 129 | }; | ||
| 130 | |||
| 120 | /// Statement is a wrapper around a SQLite statement, providing high-level functions to execute | 131 | /// Statement is a wrapper around a SQLite statement, providing high-level functions to execute |
| 121 | /// a statement and retrieve rows for SELECT queries. | 132 | /// a statement and retrieve rows for SELECT queries. |
| 122 | /// | 133 | /// |
| @@ -153,11 +164,17 @@ pub const Statement = struct { | |||
| 153 | 164 | ||
| 154 | stmt: *c.sqlite3_stmt, | 165 | stmt: *c.sqlite3_stmt, |
| 155 | 166 | ||
| 167 | const BytesType = enum { | ||
| 168 | Text, | ||
| 169 | Blob, | ||
| 170 | }; | ||
| 171 | |||
| 156 | fn prepare(db: *Db, flags: c_uint, comptime query: []const u8, values: anytype) !Self { | 172 | fn prepare(db: *Db, flags: c_uint, comptime query: []const u8, values: anytype) !Self { |
| 157 | const StructType = @typeInfo(@TypeOf(values)).Struct; | 173 | const StructType = @TypeOf(values); |
| 174 | const StructTypeInfo = @typeInfo(StructType).Struct; | ||
| 158 | comptime { | 175 | comptime { |
| 159 | const bind_parameter_count = std.mem.count(u8, query, "?"); | 176 | const bind_parameter_count = std.mem.count(u8, query, "?"); |
| 160 | if (bind_parameter_count != StructType.fields.len) { | 177 | if (bind_parameter_count != StructTypeInfo.fields.len) { |
| 161 | @compileError("bind parameter count != number of fields in tuple/struct"); | 178 | @compileError("bind parameter count != number of fields in tuple/struct"); |
| 162 | } | 179 | } |
| 163 | } | 180 | } |
| @@ -183,7 +200,7 @@ pub const Statement = struct { | |||
| 183 | 200 | ||
| 184 | // Bind | 201 | // Bind |
| 185 | 202 | ||
| 186 | inline for (StructType.fields) |struct_field, _i| { | 203 | inline for (StructTypeInfo.fields) |struct_field, _i| { |
| 187 | const i = @as(usize, _i); | 204 | const i = @as(usize, _i); |
| 188 | const field_type_info = @typeInfo(struct_field.field_type); | 205 | const field_type_info = @typeInfo(struct_field.field_type); |
| 189 | const field_value = @field(values, struct_field.name); | 206 | const field_value = @field(values, struct_field.name); |
| @@ -193,6 +210,10 @@ pub const Statement = struct { | |||
| 193 | []const u8, []u8 => { | 210 | []const u8, []u8 => { |
| 194 | _ = c.sqlite3_bind_text(stmt, column, field_value.ptr, @intCast(c_int, field_value.len), null); | 211 | _ = c.sqlite3_bind_text(stmt, column, field_value.ptr, @intCast(c_int, field_value.len), null); |
| 195 | }, | 212 | }, |
| 213 | Bytes => switch (field_value) { | ||
| 214 | .Text => |v| _ = c.sqlite3_bind_text(stmt, column, v.ptr, @intCast(c_int, v.len), null), | ||
| 215 | .Blob => |v| _ = c.sqlite3_bind_blob(stmt, column, v.ptr, @intCast(c_int, v.len), null), | ||
| 216 | }, | ||
| 196 | else => switch (field_type_info) { | 217 | else => switch (field_type_info) { |
| 197 | .Int, .ComptimeInt => _ = c.sqlite3_bind_int64(stmt, column, @intCast(c_longlong, field_value)), | 218 | .Int, .ComptimeInt => _ = c.sqlite3_bind_int64(stmt, column, @intCast(c_longlong, field_value)), |
| 198 | .Float, .ComptimeFloat => _ = c.sqlite3_bind_double(stmt, column, field_value), | 219 | .Float, .ComptimeFloat => _ = c.sqlite3_bind_double(stmt, column, field_value), |
| @@ -512,6 +533,23 @@ test "sqlite: statement exec" { | |||
| 512 | 533 | ||
| 513 | testing.expectEqual(@as(usize, 33), age.?); | 534 | testing.expectEqual(@as(usize, 33), age.?); |
| 514 | } | 535 | } |
| 536 | |||
| 537 | // Test with a Bytes struct | ||
| 538 | |||
| 539 | { | ||
| 540 | // NOTE(vincent): can't yet pass an anonymous struct, the compiler crashes. | ||
| 541 | const Params = struct { | ||
| 542 | id: usize, | ||
| 543 | name: Bytes, | ||
| 544 | age: usize, | ||
| 545 | }; | ||
| 546 | |||
| 547 | try db.exec("INSERT INTO user(id, name, age) VALUES(?, ?, ?)", Params{ | ||
| 548 | .id = 200, | ||
| 549 | .name = .{ .Text = "hello" }, | ||
| 550 | .age = 20, | ||
| 551 | }); | ||
| 552 | } | ||
| 515 | } | 553 | } |
| 516 | 554 | ||
| 517 | fn dbMode() Db.Mode { | 555 | fn dbMode() Db.Mode { |