summaryrefslogtreecommitdiff
path: root/sqlite.zig
diff options
context:
space:
mode:
authorGravatar thisLight2021-09-24 14:52:32 +0800
committerGravatar thisLight2021-10-13 10:03:07 +0800
commitcab714e36e4827a84e6b19d298b9e664121a7a3a (patch)
treef1e93f71c7e2a0aa1bc6d4be00948aa23c7a687c /sqlite.zig
parenttest binding an optional value too (diff)
downloadzig-sqlite-cab714e36e4827a84e6b19d298b9e664121a7a3a.tar.gz
zig-sqlite-cab714e36e4827a84e6b19d298b9e664121a7a3a.tar.xz
zig-sqlite-cab714e36e4827a84e6b19d298b9e664121a7a3a.zip
DynamicStatment: introduce original sqlite3 statement.
Diffstat (limited to '')
-rw-r--r--sqlite.zig564
1 files changed, 442 insertions, 122 deletions
diff --git a/sqlite.zig b/sqlite.zig
index 8596c2c..04b805c 100644
--- a/sqlite.zig
+++ b/sqlite.zig
@@ -450,6 +450,13 @@ pub const Db = struct {
450 try stmt.exec(options, values); 450 try stmt.exec(options, values);
451 } 451 }
452 452
453 /// execDynamic is a convenience function which prepares a statement and executes it directly.
454 pub fn execDynamic(self: *Self, query: []const u8, options: QueryOptions, values: anytype) !void {
455 var stmt = try self.prepareDynamicWithDiags(query, options);
456 defer stmt.deinit();
457 try stmt.exec(options, values);
458 }
459
453 /// one is a convenience function which prepares a statement and reads a single row from the result set. 460 /// one is a convenience function which prepares a statement and reads a single row from the result set.
454 pub fn one(self: *Self, comptime Type: type, comptime query: []const u8, options: QueryOptions, values: anytype) !?Type { 461 pub fn one(self: *Self, comptime Type: type, comptime query: []const u8, options: QueryOptions, values: anytype) !?Type {
455 var stmt = try self.prepareWithDiags(query, options); 462 var stmt = try self.prepareWithDiags(query, options);
@@ -457,6 +464,13 @@ pub const Db = struct {
457 return try stmt.one(Type, options, values); 464 return try stmt.one(Type, options, values);
458 } 465 }
459 466
467 /// oneDynamic is a convenience function which prepares a statement and reads a single row from the result set.
468 pub fn oneDynamic(self: *Self, comptime Type: type, query: []const u8, options: QueryOptions, values: anytype) !?Type {
469 var stmt = try self.prepareDynamicWithDiags(query, options);
470 defer stmt.deinit();
471 return try stmt.one(Type, options, values);
472 }
473
460 /// oneAlloc is like `one` but can allocate memory. 474 /// oneAlloc is like `one` but can allocate memory.
461 pub fn oneAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, comptime query: []const u8, options: QueryOptions, values: anytype) !?Type { 475 pub fn oneAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, comptime query: []const u8, options: QueryOptions, values: anytype) !?Type {
462 var stmt = try self.prepareWithDiags(query, options); 476 var stmt = try self.prepareWithDiags(query, options);
@@ -464,6 +478,13 @@ pub const Db = struct {
464 return try stmt.oneAlloc(Type, allocator, options, values); 478 return try stmt.oneAlloc(Type, allocator, options, values);
465 } 479 }
466 480
481 /// oneDynamicAlloc is like `oneDynamic` but can allocate memory.
482 pub fn oneDynamicAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, query: []const u8, options: QueryOptions, values: anytype) !?Type {
483 var stmt = try self.prepareDynamicWithDiags(query, options);
484 defer stmt.deinit();
485 return try stmt.oneAlloc(Type, allocator, options, values);
486 }
487
467 /// prepareWithDiags is like `prepare` but takes an additional options argument. 488 /// prepareWithDiags is like `prepare` but takes an additional options argument.
468 pub fn prepareWithDiags(self: *Self, comptime query: []const u8, options: QueryOptions) !blk: { 489 pub fn prepareWithDiags(self: *Self, comptime query: []const u8, options: QueryOptions) !blk: {
469 @setEvalBranchQuota(100000); 490 @setEvalBranchQuota(100000);
@@ -474,6 +495,11 @@ pub const Db = struct {
474 return Statement(.{}, comptime parsed_query).prepare(self, options, 0); 495 return Statement(.{}, comptime parsed_query).prepare(self, options, 0);
475 } 496 }
476 497
498 /// prepareDynamicWithDiags is like `prepareDynamic` but takes an additional options argument.
499 pub fn prepareDynamicWithDiags(self: *Self, query: []const u8, options: QueryOptions) !DynamicStatement {
500 return try DynamicStatement.prepare(self, query, options, 0);
501 }
502
477 /// prepare prepares a statement for the `query` provided. 503 /// prepare prepares a statement for the `query` provided.
478 /// 504 ///
479 /// The query is analysed at comptime to search for bind markers. 505 /// The query is analysed at comptime to search for bind markers.
@@ -497,6 +523,16 @@ pub const Db = struct {
497 return Statement(.{}, comptime parsed_query).prepare(self, .{}, 0); 523 return Statement(.{}, comptime parsed_query).prepare(self, .{}, 0);
498 } 524 }
499 525
526 /// prepareDynamic prepares a dynamic statement for the `query` provided.
527 ///
528 /// The query will be directly sent to create statement without any analysing.
529 /// That means such statement does not support comptime type-checking.
530 ///
531 /// Dynamic statement supports host parameter names. See `DynamicStatement`
532 pub fn prepareDynamic(self: *Self, query: []const u8) !DynamicStatement {
533 return try self.prepareDynamicWithDiags(query, .{});
534 }
535
500 /// rowsAffected returns the number of rows affected by the last statement executed. 536 /// rowsAffected returns the number of rows affected by the last statement executed.
501 pub fn rowsAffected(self: *Self) usize { 537 pub fn rowsAffected(self: *Self) usize {
502 return @intCast(usize, c.sqlite3_changes(self.db)); 538 return @intCast(usize, c.sqlite3_changes(self.db));
@@ -994,6 +1030,351 @@ pub fn Iterator(comptime Type: type) type {
994 1030
995pub const StatementOptions = struct {}; 1031pub const StatementOptions = struct {};
996 1032
1033/// DynamicStatement represents a statement in sqlite3. It almost works like sqlite3_stmt.
1034/// The difference to `Statement` is that this structure comes without addtional comptime type-checking.
1035///
1036/// The structure supports "host parameter names", which used in query to identify bind marker:
1037/// ````
1038/// SELECT email FROM users WHERE name = @name AND password = $password;
1039/// ````
1040///
1041/// To use these names, pass a normal structure instead of a tuple. Set `stmt` is the related `DynamicStatement`:
1042/// ````
1043/// try stmt.one(.{
1044/// .name = "Tankman", .password = "Passw0rd",
1045/// })
1046/// ````
1047///
1048/// It doesn't matter "@", "$" or ":" is being used, the one will be automatically chosen,
1049/// but it's not recommended to mix them up, because: sqlite3 thinks @A, $A and :A are
1050/// different, but `DynamicStatement` will try :A, @A, $A in order when you passing an 'A' field.
1051/// The ":A" will be binded while "@A", "$A" are left behind.
1052/// TL;DR: don't use same name with different indicator ("@", "$", ":").
1053///
1054/// You can use unnamed markers with tuple:
1055/// ````
1056/// SELECT email FROM users WHERE name = ? AND password = ?;
1057/// ````
1058///
1059/// ````
1060/// try stmt.one(.{"Tankman", "Passw0rd"});
1061/// ````
1062///
1063/// Named and unnamed markers could not be mixed, functions might be failed in slient.
1064/// (Just like sqlite3's sqlite3_stmt, the unbinded values will be treated as NULL.)
1065pub const DynamicStatement = struct {
1066 db: *c.sqlite3,
1067 stmt: *c.sqlite3_stmt,
1068
1069 const Self = @This();
1070
1071 fn prepare(db: *Db, queryStr: []const u8, options: QueryOptions, flags: c_uint) !Self {
1072 var dummy_diags = Diagnostics{};
1073 var diags = options.diags orelse &dummy_diags;
1074 var stmt = blk: {
1075 var tmp: ?*c.sqlite3_stmt = undefined;
1076 const result = c.sqlite3_prepare_v3(
1077 db.db,
1078 queryStr.ptr,
1079 @intCast(c_int, queryStr.len),
1080 flags,
1081 &tmp,
1082 null,
1083 );
1084 if (result != c.SQLITE_OK) {
1085 diags.err = getLastDetailedErrorFromDb(db.db);
1086 return errors.errorFromResultCode(result);
1087 }
1088 break :blk tmp.?;
1089 };
1090 return Self{
1091 .db = db.db,
1092 .stmt = stmt,
1093 };
1094 }
1095
1096 /// deinit releases the prepared statement.
1097 ///
1098 /// After a call to `deinit` the statement must not be used.
1099 pub fn deinit(self: *Self) void {
1100 const result = c.sqlite3_finalize(self.stmt);
1101 if (result != c.SQLITE_OK) {
1102 const detailed_error = getLastDetailedErrorFromDb(self.db);
1103 logger.err("unable to finalize prepared statement, result: {}, detailed error: {}", .{ result, detailed_error });
1104 }
1105 }
1106
1107 /// reset resets the prepared statement to make it reusable.
1108 pub fn reset(self: *Self) void {
1109 const result = c.sqlite3_clear_bindings(self.stmt);
1110 if (result != c.SQLITE_OK) {
1111 const detailed_error = getLastDetailedErrorFromDb(self.db);
1112 logger.err("unable to clear prepared statement bindings, result: {}, detailed error: {}", .{ result, detailed_error });
1113 }
1114 const result2 = c.sqlite3_reset(self.stmt);
1115 if (result2 != c.SQLITE_OK) {
1116 const detailed_error = getLastDetailedErrorFromDb(self.db);
1117 logger.err("unable to reset prepared statement, result: {}, detailed error: {}", .{ result2, detailed_error });
1118 }
1119 }
1120
1121 fn tanslateError(value: anytype) !void {
1122 if (@TypeOf(value) != void) {
1123 if (@typeInfo(@TypeOf(value)) == .ErrorUnion and @typeInfo(@TypeOf(value)).ErrorUnion.payload == void) {
1124 return value;
1125 } else if (@TypeOf(value) == c_int and value == c.SQLITE_OK){
1126 return;
1127 } else {
1128 return errors.errorFromResultCode(value);
1129 }
1130 } else {
1131 return;
1132 }
1133 }
1134
1135 fn bindField(self: *Self, comptime FieldType: type, options: anytype, comptime field_name: []const u8, i: c_int, field: FieldType) !void {
1136 const field_type_info = @typeInfo(FieldType);
1137 const column = i + 1;
1138
1139 const val = switch (FieldType) {
1140 Text => c.sqlite3_bind_text(self.stmt, column, field.data.ptr, @intCast(c_int, field.data.len), null),
1141 Blob => c.sqlite3_bind_blob(self.stmt, column, field.data.ptr, @intCast(c_int, field.data.len), null),
1142 ZeroBlob => c.sqlite3_bind_zeroblob64(self.stmt, column, field.length),
1143 else => switch (field_type_info) {
1144 .Int, .ComptimeInt => c.sqlite3_bind_int64(self.stmt, column, @intCast(c_longlong, field)),
1145 .Float, .ComptimeFloat => c.sqlite3_bind_double(self.stmt, column, field),
1146 .Bool => c.sqlite3_bind_int64(self.stmt, column, @boolToInt(field)),
1147 .Pointer => |ptr| switch (ptr.size) {
1148 .One => self.bindField(ptr.child, options, field_name, i, field.*),
1149 .Slice => switch (ptr.child) {
1150 u8 => c.sqlite3_bind_text(self.stmt, column, field.ptr, @intCast(c_int, field.len), null),
1151 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1152 },
1153 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1154 },
1155 .Array => |arr| switch (arr.child) {
1156 u8 => u8arr: {
1157 const data: []const u8 = field[0..field.len];
1158
1159 break :u8arr c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(c_int, data.len), null);
1160 },
1161 else => @compileError("cannot bind field " ++ field_name ++ " of type array of " ++ @typeName(arr.child)),
1162 },
1163 .Optional => |opt| if (field) |non_null_field| {
1164 try self.bindField(opt.child, options, field_name, i, non_null_field);
1165 } else optional_null: {
1166 break :optional_null c.sqlite3_bind_null(self.stmt, column);
1167 },
1168 .Null => c.sqlite3_bind_null(self.stmt, column),
1169 .Enum => {
1170 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) {
1171 return try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field));
1172 } else if (@typeInfo(FieldType.BaseType) == .Int) {
1173 return try self.bindField(FieldType.BaseType, options, field_name, i, @enumToInt(field));
1174 }
1175 // Above if-else tree should have return to bypass @compileError below.
1176 @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int to bind");
1177 },
1178 .Struct => {
1179 return try self.bindField(FieldType.BaseType, options, field_name, i, try field.bindField(options.allocator));
1180 },
1181 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1182 },
1183 };
1184
1185 return tanslateError(val);
1186 }
1187
1188 fn bind(self: *Self, options: anytype, values: anytype) !void {
1189 const StructType = @TypeOf(values);
1190 const StructTypeInfo = @typeInfo(StructType).Struct;
1191
1192 inline for (StructTypeInfo.fields) |struct_field, i| {
1193 const field_value = @field(values, struct_field.name);
1194 try self.bindField(struct_field.field_type, options, struct_field.name, i, field_value);
1195 }
1196 }
1197
1198 fn sqlite3BindParameterIndex(stmt: *c.sqlite3_stmt, comptime name: []const u8) c_int {
1199 inline for (.{":", "@", "$"}) |prefix| {
1200 const id = std.fmt.comptimePrint(prefix++"{s}", .{name});
1201 const i = c.sqlite3_bind_parameter_index(stmt, id);
1202 if (i > 0) return i-1; // .bindField uses 0-based while sqlite3 uses 1-based index.
1203 }
1204 return -1;
1205 }
1206
1207 /// bind named structure
1208 fn bindNamedStruct(self: *Self, options: anytype, values: anytype) !void {
1209 const StructType = @TypeOf(values);
1210 const StructTypeInfo = @typeInfo(StructType).Struct;
1211
1212 inline for (StructTypeInfo.fields) |struct_field| {
1213 const i = sqlite3BindParameterIndex(self.stmt, struct_field.name);
1214 if (i >= 0) {
1215 try self.bindField(
1216 struct_field.field_type,
1217 options,
1218 struct_field.name,
1219 i,
1220 @field(values, struct_field.name));
1221 } else if (i == -1) {
1222 return errors.SQLiteError.SQLiteNotFound;
1223 // bug: do not put into a else block. reproduced in 0.8.1 and 0.9.0+dev.1193
1224 // title: broken LLVM module found: Operand is null.
1225 // TODO: fire an issue to ziglang/zig and place address here
1226 }
1227 }
1228 }
1229
1230 fn smartBind(self: *Self, options: anytype, values: anytype) !void {
1231 if (std.meta.fieldNames(@TypeOf(values)).len == 0){
1232 return;
1233 }else if (std.meta.trait.isTuple(@TypeOf(values))){
1234 try self.bind(options, values);
1235 } else {
1236 try self.bindNamedStruct(options, values);
1237 }
1238 }
1239
1240 /// exec executes a statement which does not return data.
1241 ///
1242 /// The `options` tuple is used to provide additional state in some cases.
1243 ///
1244 /// The `values` variable is used for the bind parameters. It must have as many fields as there are bind markers
1245 /// in the input query string.
1246 /// The values will be binded depends on the numberic name when it's a tuple, or the
1247 /// string name when it's a normal structure.
1248 ///
1249 /// Possible errors:
1250 /// - SQLiteError.SQLiteNotFound if some fields not found
1251 pub fn exec(self: *Self, options: QueryOptions, values: anytype) !void {
1252 try self.smartBind(.{}, values);
1253
1254 var dummy_diags = Diagnostics{};
1255 var diags = options.diags orelse &dummy_diags;
1256
1257 const result = c.sqlite3_step(self.stmt);
1258 switch (result) {
1259 c.SQLITE_DONE => {},
1260 else => {
1261 diags.err = getLastDetailedErrorFromDb(self.db);
1262 return errors.errorFromResultCode(result);
1263 },
1264 }
1265 }
1266
1267 /// iterator returns an iterator to read data from the result set, one row at a time.
1268 ///
1269 /// The data in the row is used to populate a value of the type `Type`.
1270 /// This means that `Type` must have as many fields as is returned in the query
1271 /// executed by this statement.
1272 /// This also means that the type of each field must be compatible with the SQLite type.
1273 ///
1274 /// Here is an example of how to use the iterator:
1275 ///
1276 /// var iter = try stmt.iterator(usize, .{});
1277 /// while (try iter.next(.{})) |row| {
1278 /// ...
1279 /// }
1280 ///
1281 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers
1282 /// in the input query string.
1283 /// The values will be binded depends on the numberic name when it's a tuple, or the
1284 /// string name when it's a normal structure.
1285 ///
1286 /// The iterator _must not_ outlive the statement.
1287 ///
1288 /// Possible errors:
1289 /// - SQLiteError.SQLiteNotFound if some fields not found
1290 pub fn iterator(self: *Self, comptime Type: type, values: anytype) !Iterator(Type) {
1291 try self.smartBind(values);
1292
1293 var res: Iterator(Type) = undefined;
1294 res.db = self.db;
1295 res.stmt = self.stmt;
1296
1297 return res;
1298 }
1299
1300 /// one reads a single row from the result set of this statement.
1301 ///
1302 /// The data in the row is used to populate a value of the type `Type`.
1303 /// This means that `Type` must have as many fields as is returned in the query
1304 /// executed by this statement.
1305 /// This also means that the type of each field must be compatible with the SQLite type.
1306 ///
1307 /// Here is an example of how to use an anonymous struct type:
1308 ///
1309 /// const row = try stmt.one(
1310 /// struct {
1311 /// id: usize,
1312 /// name: [400]u8,
1313 /// age: usize,
1314 /// },
1315 /// .{},
1316 /// .{ .foo = "bar", .age = 500 },
1317 /// );
1318 ///
1319 /// The `options` tuple is used to provide additional state in some cases.
1320 ///
1321 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers
1322 /// in the input query string.
1323 ///
1324 /// This cannot allocate memory. If you need to read TEXT or BLOB columns you need to use arrays or alternatively call `oneAlloc`.
1325 pub fn one(self: *Self, comptime Type: type, options: QueryOptions, values: anytype) !?Type {
1326 var iter = try self.iterator(Type, values);
1327
1328 const row = (try iter.next(options)) orelse return null;
1329 return row;
1330 }
1331
1332 /// oneAlloc is like `one` but can allocate memory.
1333 pub fn oneAlloc(self: *Self, comptime Type: type, allocator: *mem.Allocator, options: QueryOptions, values: anytype) !?Type {
1334 var iter = try self.iterator(Type, values);
1335
1336 const row = (try iter.nextAlloc(allocator, options)) orelse return null;
1337 return row;
1338 }
1339
1340 /// all reads all rows from the result set of this statement.
1341 ///
1342 /// The data in each row is used to populate a value of the type `Type`.
1343 /// This means that `Type` must have as many fields as is returned in the query
1344 /// executed by this statement.
1345 /// This also means that the type of each field must be compatible with the SQLite type.
1346 ///
1347 /// Here is an example of how to use an anonymous struct type:
1348 ///
1349 /// const rows = try stmt.all(
1350 /// struct {
1351 /// id: usize,
1352 /// name: []const u8,
1353 /// age: usize,
1354 /// },
1355 /// allocator,
1356 /// .{},
1357 /// .{ .foo = "bar", .age = 500 },
1358 /// );
1359 ///
1360 /// The `options` tuple is used to provide additional state in some cases.
1361 ///
1362 /// The `values` tuple is used for the bind parameters. It must have as many fields as there are bind markers
1363 /// in the input query string.
1364 ///
1365 /// Note that this allocates all rows into a single slice: if you read a lot of data this can use a lot of memory.
1366 pub fn all(self: *Self, comptime Type: type, allocator: *mem.Allocator, options: QueryOptions, values: anytype) ![]Type {
1367 var iter = try self.iterator(Type, values);
1368
1369 var rows = std.ArrayList(Type).init(allocator);
1370 while (try iter.nextAlloc(allocator, options)) |row| {
1371 try rows.append(row);
1372 }
1373
1374 return rows.toOwnedSlice();
1375 }
1376};
1377
997/// Statement is a wrapper around a SQLite statement, providing high-level functions to execute 1378/// Statement is a wrapper around a SQLite statement, providing high-level functions to execute
998/// a statement and retrieve rows for SELECT queries. 1379/// a statement and retrieve rows for SELECT queries.
999/// 1380///
@@ -1033,62 +1414,28 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1033 return struct { 1414 return struct {
1034 const Self = @This(); 1415 const Self = @This();
1035 1416
1036 db: *c.sqlite3, 1417 dynamicStmt: DynamicStatement,
1037 stmt: *c.sqlite3_stmt,
1038 1418
1039 fn prepare(db: *Db, options: QueryOptions, flags: c_uint) !Self { 1419 fn prepare(db: *Db, options: QueryOptions, flags: c_uint) !Self {
1040 var dummy_diags = Diagnostics{}; 1420 return Self {
1041 var diags = options.diags orelse &dummy_diags; 1421 .dynamicStmt = try DynamicStatement.prepare(db, query.getQuery(), options, flags),
1042
1043 var stmt = blk: {
1044 const real_query = query.getQuery();
1045
1046 var tmp: ?*c.sqlite3_stmt = undefined;
1047 const result = c.sqlite3_prepare_v3(
1048 db.db,
1049 real_query.ptr,
1050 @intCast(c_int, real_query.len),
1051 flags,
1052 &tmp,
1053 null,
1054 );
1055 if (result != c.SQLITE_OK) {
1056 diags.err = getLastDetailedErrorFromDb(db.db);
1057 return errors.errorFromResultCode(result);
1058 }
1059 break :blk tmp.?;
1060 }; 1422 };
1423 }
1061 1424
1062 return Self{ 1425 pub fn dynamic(self: *Self) *DynamicStatement {
1063 .db = db.db, 1426 return &self.dynamicStmt;
1064 .stmt = stmt,
1065 };
1066 } 1427 }
1067 1428
1068 /// deinit releases the prepared statement. 1429 /// deinit releases the prepared statement.
1069 /// 1430 ///
1070 /// After a call to `deinit` the statement must not be used. 1431 /// After a call to `deinit` the statement must not be used.
1071 pub fn deinit(self: *Self) void { 1432 pub fn deinit(self: *Self) void {
1072 const result = c.sqlite3_finalize(self.stmt); 1433 self.dynamic().deinit();
1073 if (result != c.SQLITE_OK) {
1074 const detailed_error = getLastDetailedErrorFromDb(self.db);
1075 logger.err("unable to finalize prepared statement, result: {}, detailed error: {}", .{ result, detailed_error });
1076 }
1077 } 1434 }
1078 1435
1079 /// reset resets the prepared statement to make it reusable. 1436 /// reset resets the prepared statement to make it reusable.
1080 pub fn reset(self: *Self) void { 1437 pub fn reset(self: *Self) void {
1081 const result = c.sqlite3_clear_bindings(self.stmt); 1438 self.dynamic().reset();
1082 if (result != c.SQLITE_OK) {
1083 const detailed_error = getLastDetailedErrorFromDb(self.db);
1084 logger.err("unable to clear prepared statement bindings, result: {}, detailed error: {}", .{ result, detailed_error });
1085 }
1086
1087 const result2 = c.sqlite3_reset(self.stmt);
1088 if (result2 != c.SQLITE_OK) {
1089 const detailed_error = getLastDetailedErrorFromDb(self.db);
1090 logger.err("unable to reset prepared statement, result: {}, detailed error: {}", .{ result2, detailed_error });
1091 }
1092 } 1439 }
1093 1440
1094 /// bind binds values to every bind marker in the prepared statement. 1441 /// bind binds values to every bind marker in the prepared statement.
@@ -1128,13 +1475,15 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1128 else => comptime assertMarkerType(struct_field.field_type, typ), 1475 else => comptime assertMarkerType(struct_field.field_type, typ),
1129 } 1476 }
1130 }, 1477 },
1478
1131 .Untyped => {}, 1479 .Untyped => {},
1132 } 1480 }
1133
1134 const field_value = @field(values, struct_field.name);
1135
1136 try self.bindField(struct_field.field_type, options, struct_field.name, _i, field_value);
1137 } 1481 }
1482
1483 return self.dynamic().bind(options, values) catch |e| switch (e) {
1484 errors.Error.SQLiteNotFound => unreachable, // impossible to have non-exists field
1485 else => e,
1486 };
1138 } 1487 }
1139 1488
1140 fn assertMarkerType(comptime Actual: type, comptime Expected: type) void { 1489 fn assertMarkerType(comptime Actual: type, comptime Expected: type) void {
@@ -1143,57 +1492,19 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1143 } 1492 }
1144 } 1493 }
1145 1494
1146 fn bindField(self: *Self, comptime FieldType: type, options: anytype, comptime field_name: []const u8, i: c_int, field: FieldType) !void { 1495 /// execAlloc is like `exec` but can allocate memory.
1147 const field_type_info = @typeInfo(FieldType); 1496 pub fn execAlloc(self: *Self, allocator: *std.mem.Allocator, options: QueryOptions, values: anytype) !void {
1148 const column = i + 1; 1497 try self.bind(.{ .allocator = allocator }, values);
1149 1498
1150 switch (FieldType) { 1499 var dummy_diags = Diagnostics{};
1151 Text => _ = c.sqlite3_bind_text(self.stmt, column, field.data.ptr, @intCast(c_int, field.data.len), null), 1500 var diags = options.diags orelse &dummy_diags;
1152 Blob => _ = c.sqlite3_bind_blob(self.stmt, column, field.data.ptr, @intCast(c_int, field.data.len), null), 1501
1153 ZeroBlob => _ = c.sqlite3_bind_zeroblob64(self.stmt, column, field.length), 1502 const result = c.sqlite3_step(self.dynamic().stmt);
1154 else => switch (field_type_info) { 1503 switch (result) {
1155 .Int, .ComptimeInt => _ = c.sqlite3_bind_int64(self.stmt, column, @intCast(c_longlong, field)), 1504 c.SQLITE_DONE => {},
1156 .Float, .ComptimeFloat => _ = c.sqlite3_bind_double(self.stmt, column, field), 1505 else => {
1157 .Bool => _ = c.sqlite3_bind_int64(self.stmt, column, @boolToInt(field)), 1506 diags.err = getLastDetailedErrorFromDb(self.dynamic().db);
1158 .Pointer => |ptr| switch (ptr.size) { 1507 return errors.errorFromResultCode(result);
1159 .One => try self.bindField(ptr.child, options, field_name, i, field.*),
1160 .Slice => switch (ptr.child) {
1161 u8 => {
1162 _ = c.sqlite3_bind_text(self.stmt, column, field.ptr, @intCast(c_int, field.len), null);
1163 },
1164 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1165 },
1166 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1167 },
1168 .Array => |arr| {
1169 switch (arr.child) {
1170 u8 => {
1171 const data: []const u8 = field[0..field.len];
1172
1173 _ = c.sqlite3_bind_text(self.stmt, column, data.ptr, @intCast(c_int, data.len), null);
1174 },
1175 else => @compileError("cannot bind field " ++ field_name ++ " of type array of " ++ @typeName(arr.child)),
1176 }
1177 },
1178 .Optional => |opt| if (field) |non_null_field| {
1179 try self.bindField(opt.child, options, field_name, i, non_null_field);
1180 } else {
1181 _ = c.sqlite3_bind_null(self.stmt, column);
1182 },
1183 .Null => _ = c.sqlite3_bind_null(self.stmt, column),
1184 .Enum => {
1185 if (comptime std.meta.trait.isZigString(FieldType.BaseType)) {
1186 return try self.bindField(FieldType.BaseType, options, field_name, i, @tagName(field));
1187 }
1188 if (@typeInfo(FieldType.BaseType) == .Int) {
1189 return try self.bindField(FieldType.BaseType, options, field_name, i, @enumToInt(field));
1190 }
1191 @compileError("enum column " ++ @typeName(FieldType) ++ " must have a BaseType of either string or int to bind");
1192 },
1193 .Struct => {
1194 return try self.bindField(FieldType.BaseType, options, field_name, i, try field.bindField(options.allocator));
1195 },
1196 else => @compileError("cannot bind field " ++ field_name ++ " of type " ++ @typeName(FieldType)),
1197 }, 1508 },
1198 } 1509 }
1199 } 1510 }
@@ -1206,33 +1517,16 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1206 /// in the input query string. 1517 /// in the input query string.
1207 /// 1518 ///
1208 pub fn exec(self: *Self, options: QueryOptions, values: anytype) !void { 1519 pub fn exec(self: *Self, options: QueryOptions, values: anytype) !void {
1209 try self.bind({}, values); 1520 try self.bind(.{}, values);
1210 1521
1211 var dummy_diags = Diagnostics{};
1212 var diags = options.diags orelse &dummy_diags;
1213
1214 const result = c.sqlite3_step(self.stmt);
1215 switch (result) {
1216 c.SQLITE_DONE => {},
1217 else => {
1218 diags.err = getLastDetailedErrorFromDb(self.db);
1219 return errors.errorFromResultCode(result);
1220 },
1221 }
1222 }
1223
1224 /// execAlloc is like `exec` but can allocate memory.
1225 pub fn execAlloc(self: *Self, allocator: *std.mem.Allocator, options: QueryOptions, values: anytype) !void {
1226 try self.bind(.{ .allocator = allocator }, values);
1227
1228 var dummy_diags = Diagnostics{}; 1522 var dummy_diags = Diagnostics{};
1229 var diags = options.diags orelse &dummy_diags; 1523 var diags = options.diags orelse &dummy_diags;
1230 1524
1231 const result = c.sqlite3_step(self.stmt); 1525 const result = c.sqlite3_step(self.dynamic().stmt);
1232 switch (result) { 1526 switch (result) {
1233 c.SQLITE_DONE => {}, 1527 c.SQLITE_DONE => {},
1234 else => { 1528 else => {
1235 diags.err = getLastDetailedErrorFromDb(self.db); 1529 diags.err = getLastDetailedErrorFromDb(self.dynamic().db);
1236 return errors.errorFromResultCode(result); 1530 return errors.errorFromResultCode(result);
1237 }, 1531 },
1238 } 1532 }
@@ -1257,11 +1551,11 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1257 /// 1551 ///
1258 /// The iterator _must not_ outlive the statement. 1552 /// The iterator _must not_ outlive the statement.
1259 pub fn iterator(self: *Self, comptime Type: type, values: anytype) !Iterator(Type) { 1553 pub fn iterator(self: *Self, comptime Type: type, values: anytype) !Iterator(Type) {
1260 try self.bind({}, values); 1554 try self.bind(.{}, values);
1261 1555
1262 var res: Iterator(Type) = undefined; 1556 var res: Iterator(Type) = undefined;
1263 res.db = self.db; 1557 res.db = self.dynamic().db;
1264 res.stmt = self.stmt; 1558 res.stmt = self.dynamic().stmt;
1265 1559
1266 return res; 1560 return res;
1267 } 1561 }
@@ -1271,8 +1565,8 @@ pub fn Statement(comptime opts: StatementOptions, comptime query: ParsedQuery) t
1271 try self.bind(.{ .allocator = allocator }, values); 1565 try self.bind(.{ .allocator = allocator }, values);
1272 1566
1273 var res: Iterator(Type) = undefined; 1567 var res: Iterator(Type) = undefined;
1274 res.db = self.db; 1568 res.db = self.dynamic().db;
1275 res.stmt = self.stmt; 1569 res.stmt = self.dynamic().stmt;
1276 1570
1277 return res; 1571 return res;
1278 } 1572 }
@@ -1504,6 +1798,32 @@ test "sqlite: statement exec" {
1504 } 1798 }
1505} 1799}
1506 1800
1801test "sqlite: statement execDynamic" {
1802 // It's a smoke test for DynamicStatment, because the DynamicStatment is almost a wrapper to sqlite3_stmt
1803 // , but it's not our task to test. This test is a simple test to check if the .bindNamedStruct working.
1804 // Because of the dependence of Statment to DynamicStatment, it's not required to test rest functions.
1805 var db = try getTestDb();
1806 try addTestData(&db);
1807
1808 // Test with a Blob struct
1809 {
1810 try db.execDynamic("INSERT INTO user(id, name, age) VALUES(@id, @name, @age)", .{}, .{
1811 .id = @as(usize, 200),
1812 .name = Blob{ .data = "hello" },
1813 .age = @as(u32, 20),
1814 });
1815 }
1816
1817 // Test with a Text struct
1818 {
1819 try db.execDynamic("INSERT INTO user(id, name, age) VALUES(@id, @name, @age)", .{}, .{
1820 .id = @as(usize, 201),
1821 .name = Text{ .data = "hello" },
1822 .age = @as(u32, 20),
1823 });
1824 }
1825}
1826
1507test "sqlite: read a single user into a struct" { 1827test "sqlite: read a single user into a struct" {
1508 var arena = std.heap.ArenaAllocator.init(testing.allocator); 1828 var arena = std.heap.ArenaAllocator.init(testing.allocator);
1509 defer arena.deinit(); 1829 defer arena.deinit();