diff --git a/moor_ffi/lib/src/bindings/bindings.dart b/moor_ffi/lib/src/bindings/bindings.dart index 65d12cb0..05c4ad58 100644 --- a/moor_ffi/lib/src/bindings/bindings.dart +++ b/moor_ffi/lib/src/bindings/bindings.dart @@ -90,6 +90,7 @@ class _SQLiteBindings { int length, Pointer disposeCb) sqlite3_bind_blob; int Function(Pointer statement, int columnIndex) sqlite3_bind_null; + sqlite3_bind_parameter_count_dart sqlite3_bind_parameter_count; int Function( Pointer db, @@ -136,6 +137,9 @@ class _SQLiteBindings { sqlite3_bind_null = sqlite .lookup>('sqlite3_bind_null') .asFunction(); + sqlite3_bind_parameter_count = sqlite.lookupFunction< + sqlite3_bind_parameter_count_native, + sqlite3_bind_parameter_count_dart>('sqlite3_bind_parameter_count'); sqlite3_open_v2 = sqlite .lookup>('sqlite3_open_v2') .asFunction(); diff --git a/moor_ffi/lib/src/bindings/signatures.dart b/moor_ffi/lib/src/bindings/signatures.dart index 83e4f4e2..678f4c79 100644 --- a/moor_ffi/lib/src/bindings/signatures.dart +++ b/moor_ffi/lib/src/bindings/signatures.dart @@ -93,6 +93,10 @@ typedef sqlite3_bind_blob_native = Int32 Function( Pointer callback); typedef sqlite3_bind_null_native = Int32 Function( Pointer statement, Int32 columnIndex); +typedef sqlite3_bind_parameter_count_dart = int Function( + Pointer stmt); +typedef sqlite3_bind_parameter_count_native = Int32 Function( + Pointer statement); typedef sqlite3_function_handler = Void Function( Pointer context, diff --git a/moor_ffi/lib/src/impl/prepared_statement.dart b/moor_ffi/lib/src/impl/prepared_statement.dart index e0ce0eb3..54cdfedb 100644 --- a/moor_ffi/lib/src/impl/prepared_statement.dart +++ b/moor_ffi/lib/src/impl/prepared_statement.dart @@ -21,6 +21,12 @@ class PreparedStatement { _closed = true; } + /// Returns the amount of parameters in this prepared statement. + /// + /// See also: + /// - `sqlite3_bind_parameter_count`: https://www.sqlite.org/c3ref/bind_parameter_count.html + int get parameterCount => bindings.sqlite3_bind_parameter_count(_stmt); + void _ensureNotFinalized() { if (_closed) { throw StateError('Tried to operate on a released prepared statement'); @@ -30,6 +36,11 @@ class PreparedStatement { /// Executes this prepared statement as a select statement. The returned rows /// will be returned. Result select([List params]) { + assert( + (params?.length ?? 0) == parameterCount, + 'Expected $parameterCount params, but got ${params?.length ?? 0}.', + ); + _ensureNotFinalized(); _reset(); _bindParams(params); diff --git a/moor_ffi/test/database/prepared_statements_test.dart b/moor_ffi/test/database/prepared_statements_test.dart index 65ece1d1..2eed2bc6 100644 --- a/moor_ffi/test/database/prepared_statements_test.dart +++ b/moor_ffi/test/database/prepared_statements_test.dart @@ -118,6 +118,22 @@ void main() { expect(() => stmt.execute([false]), throwsArgumentError); db.close(); }); + + group('asserts that the amount of parameters are correct', () { + final db = Database.memory(); + + test('when no parameters are set', () { + final stmt = db.prepare('SELECT ?'); + expect(stmt.select, throwsA(isA())); + }); + + test('when the wrong amount of parameters are set', () { + final stmt = db.prepare('SELECT ?, ?'); + expect(() => stmt.select([1]), throwsA(isA())); + }); + + tearDownAll(db.close); + }); } void _raiseIfTwo(Pointer ctx, int argCount,