diff --git a/moor_ffi/lib/src/bindings/bindings.dart b/moor_ffi/lib/src/bindings/bindings.dart index 86ba7c0a..43d6c419 100644 --- a/moor_ffi/lib/src/bindings/bindings.dart +++ b/moor_ffi/lib/src/bindings/bindings.dart @@ -67,6 +67,7 @@ class _SQLiteBindings { int Function(Pointer db) sqlite3_changes; int Function(Pointer db) sqlite3_last_insert_rowid; + int Function(Pointer db) sqlite3_extended_errcode; Pointer Function(int code) sqlite3_errstr; Pointer Function(Pointer database) sqlite3_errmsg; @@ -165,6 +166,10 @@ class _SQLiteBindings { sqlite3_finalize = sqlite .lookup>('sqlite3_finalize') .asFunction(); + sqlite3_extended_errcode = sqlite + .lookup>( + 'sqlite3_extended_errcode') + .asFunction(); sqlite3_errstr = sqlite .lookup>('sqlite3_errstr') .asFunction(); diff --git a/moor_ffi/lib/src/bindings/signatures.dart b/moor_ffi/lib/src/bindings/signatures.dart index 2bb916cc..1dfb293b 100644 --- a/moor_ffi/lib/src/bindings/signatures.dart +++ b/moor_ffi/lib/src/bindings/signatures.dart @@ -36,6 +36,9 @@ typedef sqlite3_reset_native_t = Int32 Function(Pointer statement); typedef sqlite3_finalize_native_t = Int32 Function( Pointer statement); +typedef sqlite3_extended_errcode_native_t = Int32 Function( + Pointer database); + typedef sqlite3_errstr_native_t = Pointer Function(Int32 error); typedef sqlite3_errmsg_native_t = Pointer Function( diff --git a/moor_ffi/lib/src/impl/errors.dart b/moor_ffi/lib/src/impl/errors.dart index dd485947..3ec6ce6e 100644 --- a/moor_ffi/lib/src/impl/errors.dart +++ b/moor_ffi/lib/src/impl/errors.dart @@ -16,7 +16,12 @@ class SqliteException implements Exception { String explanation; if (code != null) { - explanation = bindings.sqlite3_errstr(code).readString(); + // Getting hold of more explanatory error code as SQLITE_IOERR error group + // has an extensive list of extended error codes + final extendedCode = bindings.sqlite3_extended_errcode(db); + final errStr = bindings.sqlite3_errstr(extendedCode).readString(); + + explanation = '$errStr (code $extendedCode)'; } return SqliteException(dbMessage, explanation); diff --git a/moor_ffi/lib/src/impl/prepared_statement.dart b/moor_ffi/lib/src/impl/prepared_statement.dart index 9ccaa5bc..18da258b 100644 --- a/moor_ffi/lib/src/impl/prepared_statement.dart +++ b/moor_ffi/lib/src/impl/prepared_statement.dart @@ -45,10 +45,15 @@ class PreparedStatement { names[i] = bindings.sqlite3_column_name(_stmt, i).readString(); } - while (_step() == Errors.SQLITE_ROW) { + int resultCode; + while ((resultCode = _step()) == Errors.SQLITE_ROW) { rows.add([for (var i = 0; i < columnCount; i++) _readValue(i)]); } + if (resultCode != Errors.SQLITE_OK && resultCode != Errors.SQLITE_DONE) { + throw SqliteException._fromErrorCode(_db._db, resultCode); + } + return Result(names, rows); } diff --git a/moor_ffi/test/database/prepared_statements_test.dart b/moor_ffi/test/database/prepared_statements_test.dart index 31fb85ba..02211b08 100644 --- a/moor_ffi/test/database/prepared_statements_test.dart +++ b/moor_ffi/test/database/prepared_statements_test.dart @@ -1,3 +1,4 @@ +import 'dart:ffi'; import 'dart:typed_data'; import 'package:moor_ffi/database.dart'; @@ -85,4 +86,38 @@ void main() { db.close(); }); + + test('throws an exception when iterating over result rows', () { + final db = Database.memory() + ..createFunction( + 'raise_if_two', + 1, + Pointer.fromFunction(_raiseIfTwo), + ); + + db.execute( + 'CREATE TABLE tbl (a INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT)'); + // insert with a = 1..3 + for (var i = 0; i < 3; i++) { + db.execute('INSERT INTO tbl DEFAULT VALUES'); + } + + final statement = db.prepare('SELECT raise_if_two(a) FROM tbl ORDER BY a'); + + expect( + statement.select, + throwsA(isA() + .having((e) => e.message, 'message', contains('was two'))), + ); + }); +} + +void _raiseIfTwo(Pointer ctx, int argCount, + Pointer> args) { + final value = args[0].value; + if (value == 2) { + ctx.resultError('parameter was two'); + } else { + ctx.resultNull(); + } } diff --git a/moor_ffi/test/ffi/extended_error_codes_test.dart b/moor_ffi/test/ffi/extended_error_codes_test.dart new file mode 100644 index 00000000..a2438a57 --- /dev/null +++ b/moor_ffi/test/ffi/extended_error_codes_test.dart @@ -0,0 +1,24 @@ +import 'package:moor_ffi/database.dart'; +import 'package:test/test.dart'; + +void main() { + Database database; + + setUp(() => database = Database.memory()); + + tearDown(() => database.close()); + + test('violating constraint throws exception with extended error code', () { + database.execute('CREATE TABLE tbl(a INTEGER NOT NULL)'); + + final statement = database.prepare('INSERT INTO tbl DEFAULT VALUES'); + + expect( + statement.execute, + throwsA( + isA().having( + (e) => e.explanation, 'explanation', endsWith(' (code 1299)')), + ), + ); + }); +}