tiny.sql.PreparedStatement
Defined in tiny.sql.
API (22)
Actions
Public operations.
bindbindNamecacheKeyclearBindingclearBindingscurrentCacheKeydeinitexecuteexpressionValueopenCursor: Opens a cursor over the rows the select returns intarget, which the cursor fills in place.parameterCountparameterIndexparameterName
Fields and members
Public fields and members.
Source
Source: lib/sql/src/statement/execute.zig:141
zig
pub const Prepared = struct { allocator: Allocator, catalog: catalog_mod.Catalog, source: []u8, statement: ast_mod.Statement, parameters: []ast_mod.Parameter, bindings: []result_mod.Binding, relation: ?plan.PreparedRelation, execution: Execution, validation: plan.Validation = .content, pub fn deinit(self: *Prepared) void { for (self.bindings) |*binding| binding.deinit(self.allocator); self.allocator.free(self.bindings); self.allocator.free(self.parameters); self.execution.deinit(self.allocator); if (self.relation) |*relation| relation.deinit(); self.statement.deinit(self.allocator); self.allocator.free(self.source); self.* = undefined; } pub fn execute(self: *Prepared, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { return self.executeResolved(result_allocator, options) catch |err| switch (err) { error.RelationNotFound => error.TableNotFound, else => err, }; } fn executeResolved(self: *Prepared, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { return self.executeOnce(result_allocator, options) catch |err| switch (err) { error.PlanChanged => { try self.reprepare(); return try self.executeOnce(result_allocator, options); }, else => err, }; } /// Opens a cursor over the rows the select returns in `target`, which the /// cursor fills in place. `target` is undefined after an error. pub fn openCursor( self: *Prepared, target: *cursor_mod.Cursor, result_allocator: Allocator, ) ast_mod.Error!void { self.openCursorResolved(target, result_allocator) catch |err| return switch (err) { error.RelationNotFound => error.TableNotFound, else => err, }; } fn openCursorResolved( self: *Prepared, target: *cursor_mod.Cursor, result_allocator: Allocator, ) ast_mod.Error!void { self.openCursorOnce(target, result_allocator) catch |err| switch (err) { error.PlanChanged => { try self.reprepare(); try self.openCursorOnce(target, result_allocator); }, else => return err, }; } pub fn parameterCount(self: *const Prepared) usize { return self.parameters.len; } pub fn cacheKey(self: *const Prepared) plan.PlanKey { return self.relation.?.cacheKey(); } pub fn currentCacheKey(self: *Prepared) ast_mod.Error!plan.PlanKey { const relation = try self.relationPtr(); return try relation.currentCacheKey(); } pub fn parameterName(self: *const Prepared, index: usize) ast_mod.Error!?[]const u8 { if (index == 0 or index > self.parameters.len) return error.ParameterIndexOutOfBounds; return self.parameters[index - 1].name; } pub fn parameterIndex(self: *const Prepared, name: []const u8) ?usize { for (self.parameters, 0..) |parameter, offset| { if (parameter.name) |parameter_name| { if (std.mem.eql(u8, parameter_name, name)) return offset + 1; } } return null; } pub fn bind(self: *Prepared, index: usize, bound_value: row.Value) ast_mod.Error!void { if (index == 0 or index > self.bindings.len) return error.ParameterIndexOutOfBounds; try self.bindings[index - 1].set(self.allocator, bound_value); } pub fn bindName(self: *Prepared, name: []const u8, bound_value: row.Value) ast_mod.Error!void { const index = self.parameterIndex(name) orelse return error.ParameterNotFound; try self.bind(index, bound_value); } pub fn clearBinding(self: *Prepared, index: usize) ast_mod.Error!void { if (index == 0 or index > self.bindings.len) return error.ParameterIndexOutOfBounds; self.bindings[index - 1].clear(); } pub fn clearBindings(self: *Prepared) void { for (self.bindings) |*binding| binding.clear(); } fn executeOnce(self: *Prepared, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { const phase = trace.scope("statement.execute"); defer phase.end(); return switch (self.statement) { .insert => |insert| try self.executeInsert(insert, result_allocator, options), .select => |select| .{ .rows = try self.executeSelect(select, result_allocator), }, .update => |update| try self.executeUpdate(update, result_allocator, options), .delete => |delete| try self.executeDelete(delete, result_allocator, options), .create_table => |create_table| .{ .catalog = try self.executeCreateTable(create_table, result_allocator, options), }, .create_index => |create_index| .{ .catalog = try self.executeCreateIndex(create_index, result_allocator, options), }, .drop_table => |drop_table| .{ .catalog = try self.executeDropTable(drop_table, result_allocator, options), }, .analyze => |analyze| .{ .catalog = try self.executeAnalyze(analyze, result_allocator, options), }, }; } fn executeInsert(self: *Prepared, insert: ast_mod.Insert, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { const insert_execution = switch (self.execution) { .insert => |*execution| execution, else => unreachable, }; const relation = try self.relationPtr(); const rowid_slot = insert_execution.rowidSlot(); std.debug.assert(rowid_slot < insert.values.len); const rowid = try self.rowidValue(insert.values[rowid_slot]); const values = insert_execution.resolve( insert, relation.handle.definitions, self, ); return try applyEdits(relation, result_allocator, options, &.{.{ .put = .{ .rowid = rowid, .values = values, } }}); } fn executeUpdate(self: *Prepared, update: ast_mod.Update, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { const update_execution = switch (self.execution) { .update => |execution| execution, else => unreachable, }; const relation = try self.relationPtr(); const assignments = try result_allocator.alloc(relation_mod.Edit.Assignment, update.assignments.len); defer result_allocator.free(assignments); for (assignments, update.assignments, update_execution.assignment_fields) |*target, assignment, field| { target.* = .{ .column = field, .value = self.expressionValue(assignment.value), }; } if (predicate_mod.rowidEqualityOnly(update.predicates)) |expression| { return try applyEdits(relation, result_allocator, options, &.{.{ .update = .{ .rowid = try self.rowidValue(expression), .assignments = assignments, } }}); } const matched = try self.matchedRowids(relation, update.predicates, result_allocator, options); defer result_allocator.free(matched); const edits = try result_allocator.alloc(PendingEdit, matched.len); defer result_allocator.free(edits); for (edits, matched) |*edit, rowid| { edit.* = .{ .update = .{ .rowid = rowid, .assignments = assignments, } }; } return try applyEdits(relation, result_allocator, options, edits); } fn executeDelete(self: *Prepared, delete: ast_mod.Delete, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!result_mod.Result { const relation = try self.relationPtr(); if (predicate_mod.rowidEqualityOnly(delete.predicates)) |expression| { return try applyEdits(relation, result_allocator, options, &.{.{ .delete = try self.rowidValue(expression), }}); } const matched = try self.matchedRowids(relation, delete.predicates, result_allocator, options); defer result_allocator.free(matched); const edits = try result_allocator.alloc(PendingEdit, matched.len); defer result_allocator.free(edits); for (edits, matched) |*edit, rowid| edit.* = .{ .delete = rowid }; return try applyEdits(relation, result_allocator, options, edits); } fn matchedRowids(self: *Prepared, relation: *plan.PreparedRelation, predicates: []const ast_mod.Predicate, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error![]i64 { const phase = trace.scope("statement.matched_rowids"); defer phase.end(); var reader = try relation.execute(); defer reader.deinit(); var predicate_stack: [relation_mod.max_index_fields + 1]ast_mod.Predicate = undefined; var value_stack: [relation_mod.max_index_fields + 1]row.Value = undefined; const runtime = try predicate_mod.runtimePredicates(self, predicates, result_allocator, &predicate_stack, &value_stack); defer runtime.deinit(result_allocator); var overlay = try StagedOverlay.init( result_allocator, options, relation, reader.borrow(), ); defer overlay.deinit(); var matched: std.ArrayList(i64) = .empty; errdefer matched.deinit(result_allocator); if (predicate_mod.firstRowidPredicate(predicates, .eq)) |offset| { const rowid = try predicate_mod.rowidFromValue(runtime.values[offset]); if (overlay.visible(rowid)) |staged| { if (staged) |bytes| { if (try predicate_mod.rowBytesMatchPredicates(runtime.predicates, runtime.values, &relation.handle, rowid, bytes)) { try matched.append(result_allocator, rowid); } } } else if (try reader.get(result_allocator, rowid)) |bytes| { defer result_allocator.free(bytes); if (try predicate_mod.rowBytesMatchPredicates(runtime.predicates, runtime.values, &relation.handle, rowid, bytes)) { try matched.append(result_allocator, rowid); } } return try matched.toOwnedSlice(result_allocator); } var start: ?i64 = null; var end: ?i64 = null; if (predicate_mod.firstRowidRangePredicate(predicates)) |offset| { const rowid = try predicate_mod.rowidFromValue(runtime.values[offset]); start = predicate_mod.rowidStart(predicates[offset].operator, rowid); end = predicate_mod.rowidEnd(predicates[offset].operator, rowid); } var scan: sql.TableScan = undefined; try reader.scan(&scan, result_allocator, start, end); defer scan.deinit(); while (try scan.next()) |entry| { if (overlay.shadows(entry.rowid)) continue; if (!try predicate_mod.rowBytesMatchPredicates(runtime.predicates, runtime.values, &relation.handle, entry.rowid, entry.bytes)) continue; try matched.append(result_allocator, entry.rowid); } var overlay_rows = overlay.rows.iterator(); while (overlay_rows.next()) |staged| { const bytes = staged.value_ptr.* orelse continue; if (!try predicate_mod.rowBytesMatchPredicates(runtime.predicates, runtime.values, &relation.handle, staged.key_ptr.*, bytes)) continue; try matched.append(result_allocator, staged.key_ptr.*); } std.mem.sort(i64, matched.items, {}, std.sort.asc(i64)); return try matched.toOwnedSlice(result_allocator); } fn executeCreateTable(self: *Prepared, create_table: ast_mod.CreateTable, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!session_mod.CatalogFlush { const database_session = try options.databaseSession(); return try database_session.createRelation(result_allocator, &self.catalog, create_table.relationDefinition(), options.commit()); } fn executeCreateIndex(self: *Prepared, create_index: ast_mod.CreateIndex, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!session_mod.CatalogFlush { const database_session = try options.databaseSession(); const create_index_execution = switch (self.execution) { .create_index => |execution| execution, else => unreachable, }; return try database_session.createIndex(result_allocator, &self.catalog, create_index.table, .{ .name = create_index.name, .fields = create_index_execution.fields, .columns = create_index_execution.columns, }, options.commit()); } fn executeDropTable(self: *Prepared, drop_table: ast_mod.DropTable, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!session_mod.CatalogFlush { const database_session = try options.databaseSession(); return try database_session.dropRelation(result_allocator, &self.catalog, drop_table.table, options.commit()); } fn executeAnalyze(self: *Prepared, analyze: ast_mod.Analyze, result_allocator: Allocator, options: ExecuteOptions) ast_mod.Error!session_mod.CatalogFlush { const database_session = try options.databaseSession(); return try database_session.analyzeRelation(result_allocator, &self.catalog, analyze.table, options.commit()); } fn executeSelect(self: *Prepared, select: ast_mod.Select, result_allocator: Allocator) ast_mod.Error!result_mod.Rows { const select_execution = switch (self.execution) { .select => |execution| execution, else => unreachable, }; if (select_execution.order_fields.len != 0 and !orderIsRowidAscending(select_execution.order_fields)) { return try self.executeSortedSelect(select, result_allocator); } const relation = try self.relationPtr(); var reader = try relation.execute(); defer reader.deinit(); var predicate_stack: [relation_mod.max_index_fields + 1]ast_mod.Predicate = undefined; var value_stack: [relation_mod.max_index_fields + 1]row.Value = undefined; const predicates = try predicate_mod.runtimePredicates(self, select.predicates, result_allocator, &predicate_stack, &value_stack); defer predicates.deinit(result_allocator); const access = try access_mod.runtimeSelectAccess(select, relation, select_execution.fields, predicates.values); if (select_execution.order_fields.len != 0 and (access == .index or access == .covering)) { return try self.executeSortedSelect(select, result_allocator); } var window = try self.selectWindow(select); switch (access) { .rowid => |rowid_access| if (rowid_access.operator == .eq) { const rowid = try predicate_mod.rowidFromValue(predicates.values[rowid_access.predicate_index]); const bytes = (try reader.get(result_allocator, rowid)) orelse return .{ .allocator = result_allocator, .storage = .{ .single = null }, }; errdefer result_allocator.free(bytes); if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, rowid, bytes)) { result_allocator.free(bytes); return .{ .allocator = result_allocator, .storage = .{ .single = null }, }; } if (!window.admit()) { result_allocator.free(bytes); return .{ .allocator = result_allocator, .storage = .{ .single = null }, }; } if (select_execution.fields.len == 0) { return .{ .allocator = result_allocator, .storage = .{ .single = bytes }, }; } const projected = try result_mod.selectedRow(result_allocator, select_execution.fields, rowid, bytes); result_allocator.free(bytes); return .{ .allocator = result_allocator, .storage = .{ .single = projected }, }; }, else => {}, } var selected: std.ArrayList([]u8) = .empty; errdefer result_mod.freeSelectedRows(result_allocator, selected.items); switch (access) { .rowid => |rowid_access| { const rowid = try predicate_mod.rowidFromValue(predicates.values[rowid_access.predicate_index]); var scan: sql.TableScan = undefined; try reader.scan( &scan, result_allocator, predicate_mod.rowidStart(rowid_access.operator, rowid), predicate_mod.rowidEnd(rowid_access.operator, rowid), ); defer scan.deinit(); while (try scan.next()) |entry| { if (window.full()) break; if (!predicate_mod.rowidMatches(entry.rowid, rowid_access.operator, rowid)) continue; if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, entry.rowid, entry.bytes)) continue; if (!window.admit()) continue; try result_mod.appendSelectedRow(result_allocator, &selected, select_execution.fields, entry.rowid, entry.bytes); } }, .index => |indexed| { var bound_storage: [relation_mod.max_index_fields]row.Value = undefined; const bound_values = predicate_mod.indexedPrefixValues(indexed, predicates.values, &bound_storage); const covered = access_mod.indexCoversPredicates(&relation.handle, indexed.index_slot, select_execution.fields, predicates.predicates); var index_scan: sql.IndexScan = undefined; switch (indexed.operator) { .eq => try reader.lookup( &index_scan, result_allocator, indexed.index_slot, bound_values, ), .lt, .lte, .gt, .gte => try reader.indexRange( &index_scan, result_allocator, indexed.index_slot, predicate_mod.indexedRangeStart(indexed, bound_values), predicate_mod.indexedRangeEnd(indexed, bound_values), ), } defer index_scan.deinit(); while (try index_scan.next()) |entry| { if (window.full()) break; if (covered) { var index_values: [catalog_mod.max_columns]row.Value = undefined; var index_scratch: [page.size]u8 = undefined; const values = try predicate_mod.coveredIndexValues( predicates.predicates, predicates.values, &relation.handle, indexed.index_slot, entry, &index_values, &index_scratch, ) orelse continue; if (!window.admit()) continue; try result_mod.appendSelectedIndexRow( result_allocator, &selected, select_execution.fields, &relation.handle, indexed.index_slot, values, entry.rowid, ); continue; } const bytes = (try reader.get(result_allocator, entry.rowid)) orelse continue; errdefer result_allocator.free(bytes); if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, entry.rowid, bytes)) { result_allocator.free(bytes); continue; } if (!window.admit()) { result_allocator.free(bytes); continue; } try result_mod.appendSelectedRow(result_allocator, &selected, select_execution.fields, entry.rowid, bytes); result_allocator.free(bytes); } }, .covering => unreachable, .scan => { var scan: sql.TableScan = undefined; try reader.scan(&scan, result_allocator, null, null); defer scan.deinit(); while (try scan.next()) |entry| { if (window.full()) break; if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, entry.rowid, entry.bytes)) continue; if (!window.admit()) continue; try result_mod.appendSelectedRow(result_allocator, &selected, select_execution.fields, entry.rowid, entry.bytes); } }, } return .{ .allocator = result_allocator, .storage = .{ .many = try selected.toOwnedSlice(result_allocator) }, }; } fn executeSortedSelect(self: *Prepared, select: ast_mod.Select, result_allocator: Allocator) ast_mod.Error!result_mod.Rows { const phase = trace.scope("statement.execute_sorted_select"); defer phase.end(); const relation = try self.relationPtr(); var reader = try relation.execute(); defer reader.deinit(); return .{ .allocator = result_allocator, .storage = .{ .many = try self.sortedSelectedRows(select, result_allocator, &reader) }, }; } fn sortedSelectedRows( self: *Prepared, select: ast_mod.Select, result_allocator: Allocator, reader: *const plan.RelationRead, ) ast_mod.Error![][]u8 { const select_execution = switch (self.execution) { .select => |execution| execution, else => unreachable, }; const collected = try self.collectSortedRows(select, result_allocator, reader); errdefer result_mod.freeSortedRows(result_allocator, collected); std.mem.sort(result_mod.SortedRow, collected, result_mod.SortedRowContext{ .order_fields = select_execution.order_fields, }, result_mod.sortedRowLessThan); const window = try self.selectWindow(select); const start = @min(window.skip, collected.len); const end = if (window.remaining) |remaining| @min(start +| remaining, collected.len) else collected.len; std.debug.assert(start <= end); std.debug.assert(end <= collected.len); const selected = try result_allocator.alloc([]u8, end - start); for (selected, collected[start..end]) |*out, sorted_row| out.* = sorted_row.projected; for (collected[0..start]) |sorted_row| result_allocator.free(sorted_row.projected); for (collected[end..]) |sorted_row| result_allocator.free(sorted_row.projected); for (collected) |sorted_row| result_mod.freeSortedKeys(result_allocator, sorted_row.keys); result_allocator.free(collected); return selected; } fn collectSortedRows( self: *Prepared, select: ast_mod.Select, result_allocator: Allocator, reader: *const plan.RelationRead, ) ast_mod.Error![]result_mod.SortedRow { const select_execution = switch (self.execution) { .select => |execution| execution, else => unreachable, }; const relation = try self.relationPtr(); var predicate_stack: [relation_mod.max_index_fields + 1]ast_mod.Predicate = undefined; var value_stack: [relation_mod.max_index_fields + 1]row.Value = undefined; const predicates = try predicate_mod.runtimePredicates(self, select.predicates, result_allocator, &predicate_stack, &value_stack); defer predicates.deinit(result_allocator); const access = try access_mod.runtimeSelectAccess(select, relation, select_execution.fields, predicates.values); var collected: std.ArrayList(result_mod.SortedRow) = .empty; errdefer { result_mod.freeSortedRowContents(result_allocator, collected.items); collected.deinit(result_allocator); } switch (access) { .rowid => |rowid_access| { const rowid = try predicate_mod.rowidFromValue(predicates.values[rowid_access.predicate_index]); if (rowid_access.operator == .eq) { const bytes = (try reader.get(result_allocator, rowid)) orelse return try collected.toOwnedSlice(result_allocator); defer result_allocator.free(bytes); if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, rowid, bytes)) { return try collected.toOwnedSlice(result_allocator); } try result_mod.appendSortedRow(result_allocator, &collected, select_execution.fields, select_execution.order_fields, rowid, bytes); return try collected.toOwnedSlice(result_allocator); } var scan: sql.TableScan = undefined; try reader.scan( &scan, result_allocator, predicate_mod.rowidStart(rowid_access.operator, rowid), predicate_mod.rowidEnd(rowid_access.operator, rowid), ); defer scan.deinit(); while (try scan.next()) |entry| { if (!predicate_mod.rowidMatches(entry.rowid, rowid_access.operator, rowid)) continue; if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, entry.rowid, entry.bytes)) continue; try result_mod.appendSortedRow(result_allocator, &collected, select_execution.fields, select_execution.order_fields, entry.rowid, entry.bytes); } }, .index => |indexed| { var bound_storage: [relation_mod.max_index_fields]row.Value = undefined; const bound_values = predicate_mod.indexedPrefixValues(indexed, predicates.values, &bound_storage); var index_scan: sql.IndexScan = undefined; switch (indexed.operator) { .eq => try reader.lookup( &index_scan, result_allocator, indexed.index_slot, bound_values, ), .lt, .lte, .gt, .gte => try reader.indexRange( &index_scan, result_allocator, indexed.index_slot, predicate_mod.indexedRangeStart(indexed, bound_values), predicate_mod.indexedRangeEnd(indexed, bound_values), ), } defer index_scan.deinit(); const covered = access_mod.indexCoversPredicates( &relation.handle, indexed.index_slot, select_execution.fields, predicates.predicates, ) and access_mod.indexCoversOrder( &relation.handle, indexed.index_slot, select.order, ); try collectIndexRows( result_allocator, &collected, reader, &index_scan, select_execution, &relation.handle, predicates, indexed.index_slot, covered, ); }, .covering => |index_slot| { var index_scan: sql.IndexScan = undefined; try reader.indexScan(&index_scan, result_allocator, index_slot, null, null); defer index_scan.deinit(); try collectIndexRows( result_allocator, &collected, reader, &index_scan, select_execution, &relation.handle, predicates, index_slot, true, ); }, .scan => { var scan: sql.TableScan = undefined; try reader.scan(&scan, result_allocator, null, null); defer scan.deinit(); while (try scan.next()) |entry| { if (!try predicate_mod.rowBytesMatchPredicates(predicates.predicates, predicates.values, &relation.handle, entry.rowid, entry.bytes)) continue; try result_mod.appendSortedRow(result_allocator, &collected, select_execution.fields, select_execution.order_fields, entry.rowid, entry.bytes); } }, } return try collected.toOwnedSlice(result_allocator); } /// Appends a sorted row for each entry an open index scan yields. A covered scan builds the /// row from the decoded key, and an uncovered scan reads the table row. fn collectIndexRows( result_allocator: Allocator, collected: *std.ArrayList(result_mod.SortedRow), reader: *const plan.RelationRead, index_scan: *sql.IndexScan, select_execution: SelectExecution, handle: *const catalog_mod.RelationHandle, predicates: predicate_mod.RuntimePredicates, index_slot: usize, covered: bool, ) ast_mod.Error!void { while (try index_scan.next()) |entry| { if (covered) { var index_values: [catalog_mod.max_columns]row.Value = undefined; var index_scratch: [page.size]u8 = undefined; const values = try predicate_mod.coveredIndexValues( predicates.predicates, predicates.values, handle, index_slot, entry, &index_values, &index_scratch, ) orelse continue; try result_mod.appendSortedIndexRow( result_allocator, collected, select_execution.fields, select_execution.order_fields, handle, index_slot, values, entry.rowid, ); continue; } const bytes = (try reader.get(result_allocator, entry.rowid)) orelse continue; defer result_allocator.free(bytes); if (!try predicate_mod.rowBytesMatchPredicates( predicates.predicates, predicates.values, handle, entry.rowid, bytes, )) continue; try result_mod.appendSortedRow( result_allocator, collected, select_execution.fields, select_execution.order_fields, entry.rowid, bytes, ); } } fn openCursorOnce( self: *Prepared, target: *cursor_mod.Cursor, result_allocator: Allocator, ) ast_mod.Error!void { const phase = trace.scope("statement.cursor.open"); defer phase.end(); const select = switch (self.statement) { .select => |select_statement| select_statement, else => return error.UnsupportedStatement, }; const select_execution = switch (self.execution) { .select => |execution| execution, else => unreachable, }; const relation = try self.relationPtr(); var reader = try relation.execute(); var reader_live = true; errdefer if (reader_live) reader.deinit(); var owned_predicates = try predicate_mod.ownedPredicates(result_allocator, self, select.predicates); var owned_predicates_live = true; errdefer if (owned_predicates_live) owned_predicates.deinit(result_allocator); const access = try access_mod.runtimeSelectAccess(select, relation, select_execution.fields, owned_predicates.values); const sort_needed = select_execution.order_fields.len != 0 and (!orderIsRowidAscending(select_execution.order_fields) or access == .index or access == .covering); target.* = .{ .allocator = result_allocator, .relation = relation, .reader = null, .fields = select_execution.fields, .access = access, .predicates = owned_predicates.predicates, .predicate_values = owned_predicates.values, .state = .empty, .window = if (sort_needed) .{} else try self.selectWindow(select), }; owned_predicates_live = false; errdefer target.deinit(); if (sort_needed) { const rows = try self.sortedSelectedRows(select, result_allocator, &reader); target.state = .{ .sorted = .{ .rows = rows } }; reader.deinit(); reader_live = false; return; } target.reader = reader; reader_live = false; switch (access) { .rowid => |rowid_access| { const rowid = try predicate_mod.rowidFromValue( target.predicate_values[rowid_access.predicate_index], ); if (rowid_access.operator == .eq) { const bytes = (try reader.get(result_allocator, rowid)) orelse { target.state = .empty; return; }; errdefer result_allocator.free(bytes); if (!try predicate_mod.rowBytesMatchPredicates( target.predicates, target.predicate_values, &relation.handle, rowid, bytes, )) { result_allocator.free(bytes); target.state = .empty; return; } if (select_execution.fields.len == 0) { target.state = .{ .single = .{ .bytes = bytes } }; return; } const projected = try result_mod.selectedRow(result_allocator, select_execution.fields, rowid, bytes); result_allocator.free(bytes); target.state = .{ .single = .{ .bytes = projected } }; return; } target.state = .{ .rowid = undefined }; errdefer target.state = .empty; try reader.scan( &target.state.rowid, result_allocator, predicate_mod.rowidStart(rowid_access.operator, rowid), predicate_mod.rowidEnd(rowid_access.operator, rowid), ); }, .index => |indexed| { var bound_storage: [relation_mod.max_index_fields]row.Value = undefined; const bound_values = predicate_mod.indexedPrefixValues( indexed, target.predicate_values, &bound_storage, ); const covered = access_mod.indexCoversPredicates( &relation.handle, indexed.index_slot, select_execution.fields, target.predicates, ); target.state = .{ .index = .{ .scan = undefined, .covered = covered } }; errdefer target.state = .empty; const index_scan = &target.state.index.scan; switch (indexed.operator) { .eq => try reader.lookup( index_scan, result_allocator, indexed.index_slot, bound_values, ), .lt, .lte, .gt, .gte => try reader.indexRange( index_scan, result_allocator, indexed.index_slot, predicate_mod.indexedRangeStart(indexed, bound_values), predicate_mod.indexedRangeEnd(indexed, bound_values), ), } }, .covering => unreachable, .scan => { target.state = .{ .scan = undefined }; errdefer target.state = .empty; try reader.scan(&target.state.scan, result_allocator, null, null); }, } } fn selectWindow(self: *const Prepared, select: ast_mod.Select) ast_mod.Error!Window { var window = Window{}; if (select.limit) |expression| window.remaining = try self.windowCount(expression); if (select.offset) |expression| window.skip = try self.windowCount(expression); return window; } fn windowCount(self: *const Prepared, expression: ast_mod.Expression) ast_mod.Error!usize { return switch (self.expressionValue(expression)) { .integer => |integer| if (integer < 0) error.InvalidLimit else @intCast(integer), else => error.InvalidLimit, }; } fn rowidValue(self: *const Prepared, expression: ast_mod.Expression) ast_mod.Error!i64 { return try predicate_mod.rowidFromValue(self.expressionValue(expression)); } pub fn expressionValue(self: *const Prepared, expression: ast_mod.Expression) row.Value { return switch (expression) { .literal => |literal| literal, .parameter => |index| self.bindings[index].value(), }; } fn reprepare(self: *Prepared) ast_mod.Error!void { const phase = trace.scope("statement.reprepare"); defer phase.end(); if (self.relation == null) return; var relation = try prepareStatementRelation(&self.catalog, self.allocator, self.statement, ast_mod.parameterShape(self.parameters), self.validation) orelse return; errdefer relation.deinit(); var execution = try prepareExecution(self.allocator, self.statement, &relation); errdefer execution.deinit(self.allocator); self.execution.deinit(self.allocator); self.relation.?.deinit(); self.relation = relation; self.execution = execution; } fn relationPtr(self: *Prepared) ast_mod.Error!*plan.PreparedRelation { if (self.relation) |*relation| return relation; return error.UnsupportedStatement; }};Source: lib/sql/src/root.zig:175
zig
pub const PreparedStatement = statement.Prepared;Also reachable as
Audit
| Definitions | 14 |
|---|---|
| Public names | 28 |
| Members | 9 |
| Version | 26.7.0 |
| Revision | daab053ee433 |