execute method

Future<QueryResult> execute({
  1. List<Object?> positional = const [],
  2. Map<String, Object?> named = const {},
})

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();
  }
}