@@ -564,6 +564,34 @@ void testPreparedStatements(
564564 throwsSqlError (19 , 2067 ),
565565 );
566566 });
567+
568+ test ('recovers from SQLITE_BUSY' , () {
569+ final vfs = _ErrorInjectingVfs (InMemoryFileSystem (),
570+ name: 'test-recover-sqlite-busy' );
571+ sqlite3.registerVirtualFileSystem (vfs);
572+ addTearDown (() => sqlite3.unregisterVirtualFileSystem (vfs));
573+
574+ var db = sqlite3.open ('/db' , vfs: vfs.name);
575+ addTearDown (() => db.dispose ());
576+
577+ db
578+ ..execute ('CREATE TABLE foo (bar TEXT) STRICT' )
579+ ..execute ('INSERT INTO foo (bar) VALUES (?)' , ['testing' ])
580+ ..dispose ();
581+
582+ db = db = sqlite3.open ('/db' , vfs: vfs.name);
583+ final stmt = db.prepare ('SELECT * FROM foo' );
584+ final cursor = stmt.selectCursor ();
585+ vfs.maybeError = () => throw VfsException (SqlError .SQLITE_BUSY );
586+
587+ expect (() => cursor.moveNext (),
588+ throwsSqlError (SqlError .SQLITE_BUSY , SqlError .SQLITE_BUSY ));
589+ vfs.maybeError = null ;
590+ expect (cursor.moveNext (), isTrue);
591+ expect (cursor.current, {'bar' : 'testing' });
592+
593+ stmt.dispose ();
594+ });
567595 });
568596}
569597
@@ -581,3 +609,111 @@ class _CustomValue implements CustomStatementParameter {
581609 stmt.statement.sqlite3_bind_int64 (index, 42 );
582610 }
583611}
612+
613+ final class _ErrorInjectingVfs extends BaseVirtualFileSystem {
614+ final VirtualFileSystem _base;
615+ void Function ()? maybeError;
616+
617+ _ErrorInjectingVfs (this ._base, {required super .name});
618+
619+ void _op () {
620+ maybeError? .call ();
621+ }
622+
623+ @override
624+ int xAccess (String path, int flags) {
625+ _op ();
626+ return _base.xAccess (path, flags);
627+ }
628+
629+ @override
630+ void xDelete (String path, int syncDir) {
631+ _op ();
632+ return _base.xDelete (path, syncDir);
633+ }
634+
635+ @override
636+ String xFullPathName (String path) {
637+ _op ();
638+ return _base.xFullPathName (path);
639+ }
640+
641+ @override
642+ XOpenResult xOpen (Sqlite3Filename path, int flags) {
643+ _op ();
644+ final inner = _base.xOpen (path, flags);
645+ return (
646+ outFlags: inner.outFlags,
647+ file: _ErrorInjectingFile (this , inner.file)
648+ );
649+ }
650+
651+ @override
652+ void xSleep (Duration duration) {
653+ return _base.xSleep (duration);
654+ }
655+ }
656+
657+ final class _ErrorInjectingFile implements VirtualFileSystemFile {
658+ final _ErrorInjectingVfs _vfs;
659+ final VirtualFileSystemFile _base;
660+
661+ _ErrorInjectingFile (this ._vfs, this ._base);
662+
663+ @override
664+ void xRead (Uint8List target, int fileOffset) {
665+ _vfs._op ();
666+ return _base.xRead (target, fileOffset);
667+ }
668+
669+ @override
670+ int xCheckReservedLock () {
671+ _vfs._op ();
672+ return _base.xCheckReservedLock ();
673+ }
674+
675+ @override
676+ int get xDeviceCharacteristics => _base.xDeviceCharacteristics;
677+
678+ @override
679+ void xClose () {
680+ _vfs._op ();
681+ _base.xClose ();
682+ }
683+
684+ @override
685+ int xFileSize () {
686+ _vfs._op ();
687+ return _base.xFileSize ();
688+ }
689+
690+ @override
691+ void xLock (int mode) {
692+ _vfs._op ();
693+ _base.xLock (mode);
694+ }
695+
696+ @override
697+ void xSync (int flags) {
698+ _vfs._op ();
699+ _base.xSync (flags);
700+ }
701+
702+ @override
703+ void xTruncate (int size) {
704+ _vfs._op ();
705+ _base.xTruncate (size);
706+ }
707+
708+ @override
709+ void xUnlock (int mode) {
710+ _vfs._op ();
711+ _base.xUnlock (mode);
712+ }
713+
714+ @override
715+ void xWrite (Uint8List buffer, int fileOffset) {
716+ _vfs._op ();
717+ _base.xWrite (buffer, fileOffset);
718+ }
719+ }
0 commit comments