execute method
Execute the statement with positional (1-based, in source order)
and/or named bindings.
All positional placeholders the SQL references must be supplied;
extra positional values are tolerated. All named placeholders must
be present in named; extras throw to catch typos early.
Implementation
Future<QueryResult> execute({
List<Object?> positional = const [],
Map<String, Object?> named = const {},
}) async {
if (positional.length < positionalCount) {
throw ArgumentError(
'Prepared statement expects $positionalCount positional '
'parameters, got ${positional.length}');
}
// Validate named bindings by bare name (sigil-agnostic): every
// name referenced must be supplied, and every supplied name must
// be referenced (catches typos).
String stripSigil(String s) =>
(s.isNotEmpty && (s[0] == ':' || s[0] == '@' || s[0] == r'$'))
? s.substring(1)
: s;
final referencedBare = {for (final r in namedParams) stripSigil(r)};
final suppliedBare = {for (final k in named.keys) stripSigil(k)};
for (final ref in referencedBare) {
if (!suppliedBare.contains(ref)) {
throw ArgumentError(
'Named parameter $ref referenced in SQL but not supplied');
}
}
for (final supplied in suppliedBare) {
if (!referencedBare.contains(supplied)) {
throw ArgumentError('Named binding $supplied does not appear in SQL');
}
}
final normalisedNamed = <String, Object?>{
for (final entry in named.entries) stripSigil(entry.key): entry.value,
};
final scope = BindScope(positional: positional, named: normalisedNamed);
BindParamExpr.scopeStack.add(scope);
try {
return await _db.executeStmt(_stmt);
} finally {
BindParamExpr.scopeStack.removeLast();
}
}