diff --git a/docs/bootstrap-inventory.md b/docs/bootstrap-inventory.md index 52d1c53d0a5..b3e5c55b7c1 100644 --- a/docs/bootstrap-inventory.md +++ b/docs/bootstrap-inventory.md @@ -8,11 +8,11 @@ Regenerate: `php script/bootstrap-inventory.php` | Metric | Count | |--------|------:| -| PHP files on vm.php path | 5679 | -| Phase A inventory files (M2 ratio SSOT) | 5679 | +| PHP files on vm.php path | 5685 | +| Phase A inventory files (M2 ratio SSOT) | 5685 | | Phase A ratio-deferred paths | 0 | | Source constructs flagged (blockers) | 0 | -| Source constructs flagged (warnings) | 16269 | +| Source constructs flagged (warnings) | 16291 | ## Compiler CFG gaps (`lib/Compiler.php`) @@ -1466,24 +1466,30 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag | `ext/spl/spl_classes.php` | 0 | 2 | | `ext/sqlite3/BuiltinClasses.php` | 0 | 1 | | `ext/sqlite3/Module.php` | 0 | 1 | +| `ext/sqlite3/SQLite3Backup.php` | 0 | 2 | | `ext/sqlite3/SQLite3BusyTimeout.php` | 0 | 2 | | `ext/sqlite3/SQLite3Changes.php` | 0 | 1 | | `ext/sqlite3/SQLite3Close.php` | 0 | 1 | | `ext/sqlite3/SQLite3Construct.php` | 0 | 2 | +| `ext/sqlite3/SQLite3CreateCollation.php` | 0 | 2 | | `ext/sqlite3/SQLite3CreateFunction.php` | 0 | 2 | | `ext/sqlite3/SQLite3EnableExceptions.php` | 0 | 1 | | `ext/sqlite3/SQLite3EscapeString.php` | 0 | 2 | | `ext/sqlite3/SQLite3Exec.php` | 0 | 2 | +| `ext/sqlite3/SQLite3LastErrorCode.php` | 0 | 1 | +| `ext/sqlite3/SQLite3LastErrorMsg.php` | 0 | 1 | | `ext/sqlite3/SQLite3LastInsertRowID.php` | 0 | 1 | +| `ext/sqlite3/SQLite3Open.php` | 0 | 2 | | `ext/sqlite3/SQLite3Prepare.php` | 0 | 2 | | `ext/sqlite3/SQLite3Query.php` | 0 | 2 | | `ext/sqlite3/SQLite3QuerySingle.php` | 0 | 2 | +| `ext/sqlite3/SQLite3Version.php` | 0 | 4 | | `ext/sqlite3/Sqlite3ClassMethod.php` | 0 | 1 | | `ext/sqlite3/Sqlite3ExtensionPolicy.php` | 0 | 1 | -| `ext/sqlite3/VmSQLite3.php` | 0 | 17 | +| `ext/sqlite3/VmSQLite3.php` | 0 | 24 | | `ext/sqlite3/VmSQLite3Result.php` | 0 | 12 | | `ext/sqlite3/VmSQLite3Stmt.php` | 0 | 15 | -| `ext/sqlite3/VmSqlite3Native.php` | 0 | 6 | +| `ext/sqlite3/VmSqlite3Native.php` | 0 | 9 | | `ext/sqlite3/VmSqlite3Udf.php` | 0 | 3 | | `ext/standard/AbsJitHelper.php` | 0 | 1 | | `ext/standard/AcosJitHelper.php` | 0 | 1 | @@ -16556,6 +16562,12 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag **Warnings** (review for bootstrap subset): - 2 class method(s) +### `ext/sqlite3/SQLite3Backup.php` + +**Warnings** (review for bootstrap subset): +- new ArgumentCountError (line 25) +- 1 class method(s) + ### `ext/sqlite3/SQLite3BusyTimeout.php` **Warnings** (review for bootstrap subset): @@ -16578,6 +16590,12 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ArgumentCountError (line 20) - 1 class method(s) +### `ext/sqlite3/SQLite3CreateCollation.php` + +**Warnings** (review for bootstrap subset): +- new ArgumentCountError (line 29) +- 1 class method(s) + ### `ext/sqlite3/SQLite3CreateFunction.php` **Warnings** (review for bootstrap subset): @@ -16601,11 +16619,27 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ArgumentCountError (line 21) - 1 class method(s) +### `ext/sqlite3/SQLite3LastErrorCode.php` + +**Warnings** (review for bootstrap subset): +- 1 class method(s) + +### `ext/sqlite3/SQLite3LastErrorMsg.php` + +**Warnings** (review for bootstrap subset): +- 1 class method(s) + ### `ext/sqlite3/SQLite3LastInsertRowID.php` **Warnings** (review for bootstrap subset): - 1 class method(s) +### `ext/sqlite3/SQLite3Open.php` + +**Warnings** (review for bootstrap subset): +- new ArgumentCountError (line 34) +- 1 class method(s) + ### `ext/sqlite3/SQLite3Prepare.php` **Warnings** (review for bootstrap subset): @@ -16624,6 +16658,14 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new ArgumentCountError (line 21) - 1 class method(s) +### `ext/sqlite3/SQLite3Version.php` + +**Warnings** (review for bootstrap subset): +- new HashTable (line 28) +- new Variable (line 29) +- new Variable (line 32) +- 1 class method(s) + ### `ext/sqlite3/Sqlite3ClassMethod.php` **Warnings** (review for bootstrap subset): @@ -16641,19 +16683,26 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new Variable (line 38) - new SQLite3Construct (line 45) - new SQLite3Close (line 50) -- new SQLite3Exec (line 51) -- new SQLite3QuerySingle (line 52) -- new SQLite3Query (line 53) -- new SQLite3Prepare (line 54) -- new SQLite3Changes (line 55) -- new SQLite3LastInsertRowID (line 56) -- new SQLite3EscapeString (line 57) -- new SQLite3BusyTimeout (line 58) -- new SQLite3EnableExceptions (line 59) -- new SQLite3CreateFunction (line 60) -- new Sqlite3State (line 78) -- new Variable (line 210) -- 14 class method(s) +- new SQLite3Open (line 51) +- new SQLite3Exec (line 52) +- new SQLite3QuerySingle (line 53) +- new SQLite3Query (line 54) +- new SQLite3Prepare (line 55) +- new SQLite3Changes (line 56) +- new SQLite3LastInsertRowID (line 57) +- new SQLite3LastErrorCode (line 58) +- new SQLite3LastErrorMsg (line 59) +- new SQLite3EscapeString (line 60) +- new SQLite3BusyTimeout (line 61) +- new SQLite3EnableExceptions (line 62) +- new SQLite3CreateFunction (line 63) +- new SQLite3CreateCollation (line 64) +- new SQLite3Backup (line 65) +- new SQLite3Version (line 66) +- new Sqlite3State (line 85) +- new Exception (line 104) +- new Variable (line 268) +- 18 class method(s) ### `ext/sqlite3/VmSQLite3Result.php` @@ -16698,7 +16747,10 @@ Rank live CFG gaps across inventory files: `php script/bootstrap-inventory-triag - new SQLite3Exception (line 92) - new SQLite3Exception (line 139) - new SQLite3Exception (line 183) -- 33 class method(s) +- new SQLite3Exception (line 262) +- new SQLite3Exception (line 265) +- new SQLite3Exception (line 268) +- 36 class method(s) ### `ext/sqlite3/VmSqlite3Udf.php` diff --git a/ext/sqlite3/SQLite3Backup.php b/ext/sqlite3/SQLite3Backup.php new file mode 100644 index 00000000000..7341b0dbd50 --- /dev/null +++ b/ext/sqlite3/SQLite3Backup.php @@ -0,0 +1,70 @@ +receiver($frame, 'SQLite3::backup()'); + if (\count($frame->calledArgs) < 2) { + throw new \ArgumentCountError('SQLite3::backup() expects at least 1 argument, 0 given'); + } + $destVar = $frame->calledArgs[1]->resolveIndirect(); + if (Variable::TYPE_OBJECT !== $destVar->type) { + throw new \TypeError(\sprintf( + 'SQLite3::backup(): Argument #1 ($destination) must be of type SQLite3, %s given', + VmSQLite3::publicTypeLabel($destVar) + )); + } + $dest = $destVar->toObject(); + if (VmSQLite3::CLASS_LC !== strtolower($dest->class->name)) { + throw new \TypeError(\sprintf( + 'SQLite3::backup(): Argument #1 ($destination) must be of type SQLite3, %s given', + $dest->class->name + )); + } + if (!$dest->constructed) { + throw new \TypeError('SQLite3::backup(): Argument #1 ($destination) must be of type SQLite3, uninitialized SQLite3 given'); + } + $sourceDb = 'main'; + $destDb = 'main'; + if (\count($frame->calledArgs) >= 3) { + $sourceDb = $this->stringArg($frame->calledArgs[2], 'SQLite3::backup', 1, 'sourceDatabase'); + } + if (\count($frame->calledArgs) >= 4) { + $destDb = $this->stringArg($frame->calledArgs[3], 'SQLite3::backup', 2, 'destinationDatabase'); + } + $srcHandle = VmSQLite3::requireOpenDb($receiver, 'SQLite3::backup'); + $dstHandle = VmSQLite3::requireOpenDb($dest, 'SQLite3::backup'); + try { + $ok = VmSqlite3Native::backup($srcHandle, $sourceDb, $dstHandle, $destDb); + } catch (\SQLite3Exception $e) { + if (VmSQLite3::handleException($receiver, $e)) { + if (null !== $frame->returnVar) { + $frame->returnVar->bool(false); + } + + return; + } + $ok = false; + } + if (null !== $frame->returnVar) { + $frame->returnVar->bool($ok); + } + } +} diff --git a/ext/sqlite3/SQLite3CreateCollation.php b/ext/sqlite3/SQLite3CreateCollation.php new file mode 100644 index 00000000000..fc8f6b03f0e --- /dev/null +++ b/ext/sqlite3/SQLite3CreateCollation.php @@ -0,0 +1,60 @@ +receiver($frame, 'SQLite3::createCollation()'); + VmSQLite3::requireOpenDb($receiver, 'SQLite3::createCollation'); + if (\count($frame->calledArgs) < 3) { + throw new \ArgumentCountError( + 'SQLite3::createCollation() expects exactly 2 arguments, '.(\count($frame->calledArgs) - 1).' given' + ); + } + $name = $this->stringArg($frame->calledArgs[1], 'SQLite3::createCollation', 0, 'name'); + if ('' === $name) { + if (null !== $frame->returnVar) { + $frame->returnVar->bool(false); + } + + return; + } + $ctx = $frame->vmContext; + if (null === $ctx) { + throw new \LogicException('SQLite3::createCollation() requires a VM context'); + } + $callback = $frame->calledArgs[2]->resolveIndirect(); + if (!VmCallable::isCallable($ctx, $callback)) { + throw new \TypeError(VmCallable::invalidCallbackTypeError('SQLite3::createCollation')); + } + [$pinned, $closureState] = SplIteratorSupport::pinCallback($callback); + $state = VmSQLite3::state($receiver); + $state->collations[strtolower($name)] = [ + 'callback' => $pinned, + 'closure' => $closureState, + 'ctx' => $ctx, + ]; + if (null !== $frame->returnVar) { + $frame->returnVar->bool(true); + } + } +} diff --git a/ext/sqlite3/SQLite3LastErrorCode.php b/ext/sqlite3/SQLite3LastErrorCode.php new file mode 100644 index 00000000000..02188bbf53d --- /dev/null +++ b/ext/sqlite3/SQLite3LastErrorCode.php @@ -0,0 +1,24 @@ +receiver($frame, 'SQLite3::lastErrorCode()'); + if (null !== $frame->returnVar) { + $frame->returnVar->int(VmSQLite3::lastErrorCode($receiver)); + } + } +} diff --git a/ext/sqlite3/SQLite3LastErrorMsg.php b/ext/sqlite3/SQLite3LastErrorMsg.php new file mode 100644 index 00000000000..a67bd1d885d --- /dev/null +++ b/ext/sqlite3/SQLite3LastErrorMsg.php @@ -0,0 +1,24 @@ +receiver($frame, 'SQLite3::lastErrorMsg()'); + if (null !== $frame->returnVar) { + $frame->returnVar->string(VmSQLite3::lastErrorMsg($receiver)); + } + } +} diff --git a/ext/sqlite3/SQLite3Open.php b/ext/sqlite3/SQLite3Open.php new file mode 100644 index 00000000000..680cb6a7c2f --- /dev/null +++ b/ext/sqlite3/SQLite3Open.php @@ -0,0 +1,43 @@ +calledArgs) < 1) { + throw new \LogicException('SQLite3::open() called without $this'); + } + $var = $frame->calledArgs[0]->resolveIndirect(); + if (\PHPCompiler\VM\Variable::TYPE_OBJECT !== $var->type) { + throw new \TypeError('SQLite3::open() must be called on SQLite3'); + } + $receiver = $var->toObject(); + if (VmSQLite3::CLASS_LC !== strtolower($receiver->class->name)) { + throw new \TypeError('SQLite3::open() must be called on SQLite3'); + } + if (\count($frame->calledArgs) < 2) { + throw new \ArgumentCountError('SQLite3::open() expects at least 1 argument, 0 given'); + } + $filename = $this->stringArg($frame->calledArgs[1], 'SQLite3::open', 0, 'filename'); + $defaultFlags = Sqlite3Constants::OPEN_READWRITE | Sqlite3Constants::OPEN_CREATE; + $flags = \count($frame->calledArgs) >= 3 + ? $this->intArg($frame->calledArgs[2], 'SQLite3::open', 1, 'flags', $defaultFlags) + : $defaultFlags; + VmSQLite3::openObject($receiver, $filename, $flags); + } +} diff --git a/ext/sqlite3/SQLite3Version.php b/ext/sqlite3/SQLite3Version.php new file mode 100644 index 00000000000..6daac45d3da --- /dev/null +++ b/ext/sqlite3/SQLite3Version.php @@ -0,0 +1,37 @@ +returnVar) { + return; + } + $info = VmSqlite3Native::version(); + $ht = new HashTable(); + $str = new Variable(); + $str->string($info['versionString']); + $ht->add('versionString', $str); + $num = new Variable(); + $num->int($info['versionNumber']); + $ht->add('versionNumber', $num); + $frame->returnVar->array($ht); + } +} diff --git a/ext/sqlite3/Sqlite3State.php b/ext/sqlite3/Sqlite3State.php index ded192c3d8e..63251cab882 100644 --- a/ext/sqlite3/Sqlite3State.php +++ b/ext/sqlite3/Sqlite3State.php @@ -23,4 +23,11 @@ final class Sqlite3State * @var array */ public array $functions = []; + + /** + * Registered collations (name lc => entry) — php-src createCollation (#20565). + * + * @var array + */ + public array $collations = []; } diff --git a/ext/sqlite3/VmSQLite3.php b/ext/sqlite3/VmSQLite3.php index 77496372f01..15d3fda1e9e 100644 --- a/ext/sqlite3/VmSQLite3.php +++ b/ext/sqlite3/VmSQLite3.php @@ -48,16 +48,22 @@ public static function registerClass(Context $ctx): void $methods = [ 'close' => new SQLite3Close(), + 'open' => new SQLite3Open(), 'exec' => new SQLite3Exec(), 'querysingle' => new SQLite3QuerySingle(), 'query' => new SQLite3Query(), 'prepare' => new SQLite3Prepare(), 'changes' => new SQLite3Changes(), 'lastinsertrowid' => new SQLite3LastInsertRowID(), + 'lasterrorcode' => new SQLite3LastErrorCode(), + 'lasterrormsg' => new SQLite3LastErrorMsg(), 'escapestring' => new SQLite3EscapeString(), 'busytimeout' => new SQLite3BusyTimeout(), 'enableexceptions' => new SQLite3EnableExceptions(), 'createfunction' => new SQLite3CreateFunction(), + 'createcollation' => new SQLite3CreateCollation(), + 'backup' => new SQLite3Backup(), + 'version' => new SQLite3Version(), ]; foreach ($methods as $name => $method) { $entry->methods[$name] = $method; @@ -65,6 +71,7 @@ public static function registerClass(Context $ctx): void $entry->methodNames[$name] = self::methodDisplayName($name); } $entry->methodVisibility['escapestring'] = CfgFunc::FLAG_STATIC | $pub; + $entry->methodVisibility['version'] = CfgFunc::FLAG_STATIC | $pub; $ctx->classes[self::CLASS_LC] = $entry; } @@ -83,6 +90,57 @@ public static function initObject(ObjectEntry $entry, string $filename, int $fla $entry->constructed = true; } + /** + * SQLite3::open() — reopen after close, or reject when already open (php-src; #20565). + */ + public static function openObject(ObjectEntry $entry, string $filename, int $flags): void + { + if (!VmSqlite3Native::available()) { + throw new \LogicException('SQLite3 requires libsqlite3 FFI in this compiler build'); + } + if ($entry->constructed) { + $state = self::state($entry); + if (!$state->closed && null !== $state->db) { + throw new \Exception('Already initialised DB Object'); + } + $state->filename = $filename; + $state->db = VmSqlite3Native::open($filename, $flags); + $state->closed = false; + $state->functions = []; + $state->collations = []; + + return; + } + self::initObject($entry, $filename, $flags); + } + + /** php-src lastErrorCode — 0 when closed / not initialised. */ + public static function lastErrorCode(ObjectEntry $entry): int + { + $state = self::state($entry); + if ($state->closed || null === $state->db) { + return 0; + } + + return VmSqlite3Native::errcode($state->db); + } + + /** php-src lastErrorMsg — '' when closed / not initialised. */ + public static function lastErrorMsg(ObjectEntry $entry): string + { + $state = self::state($entry); + if ($state->closed || null === $state->db) { + return ''; + } + + return VmSqlite3Native::errmsg($state->db); + } + + public static function publicTypeLabel(Variable $var): string + { + return self::typeLabel($var); + } + public static function objectById(int $id): ObjectEntry { if (!isset(self::$objects[$id])) { @@ -219,10 +277,13 @@ private static function methodDisplayName(string $lc): string return match ($lc) { 'querysingle' => 'querySingle', 'lastinsertrowid' => 'lastInsertRowID', + 'lasterrorcode' => 'lastErrorCode', + 'lasterrormsg' => 'lastErrorMsg', 'escapestring' => 'escapeString', 'busytimeout' => 'busyTimeout', 'enableexceptions' => 'enableExceptions', 'createfunction' => 'createFunction', + 'createcollation' => 'createCollation', default => $lc, }; } diff --git a/ext/sqlite3/VmSqlite3Native.php b/ext/sqlite3/VmSqlite3Native.php index 264fff935bd..3708fa1c72b 100644 --- a/ext/sqlite3/VmSqlite3Native.php +++ b/ext/sqlite3/VmSqlite3Native.php @@ -219,6 +219,58 @@ public static function lastInsertRowId(\FFI\CData $db): int return (int) self::requireFfi()->sqlite3_last_insert_rowid($db); } + public static function errcode(\FFI\CData $db): int + { + return (int) self::requireFfi()->sqlite3_errcode($db); + } + + /** @return array{versionString: string, versionNumber: int} */ + public static function version(): array + { + $ffi = self::requireFfi(); + + return [ + 'versionString' => self::ffiString($ffi->sqlite3_libversion()), + 'versionNumber' => (int) $ffi->sqlite3_libversion_number(), + ]; + } + + /** + * SQLite3::backup — copy source DB into destination (php-src; #20565). + * + * @param \FFI\CData $sourceDb sqlite3* + * @param \FFI\CData $destDb sqlite3* + */ + public static function backup( + \FFI\CData $sourceDb, + string $sourceName, + \FFI\CData $destDb, + string $destName + ): bool { + $ffi = self::requireFfi(); + $backup = $ffi->sqlite3_backup_init($destDb, $destName, $sourceDb, $sourceName); + if (null === $backup) { + $rc = (int) $ffi->sqlite3_errcode($sourceDb); + } else { + do { + $rc = (int) $ffi->sqlite3_backup_step($backup, -1); + } while (self::SQLITE_OK === $rc); + $rc = (int) $ffi->sqlite3_backup_finish($backup); + } + if (self::SQLITE_OK !== $rc) { + if (5 === $rc) { // SQLITE_BUSY + throw new \SQLite3Exception('Backup failed: source database is busy'); + } + if (6 === $rc) { // SQLITE_LOCKED + throw new \SQLite3Exception('Backup failed: source database is locked'); + } + $message = self::errmsg($sourceDb); + throw new \SQLite3Exception('Backup failed: '.('' !== $message ? $message : 'unknown error')); + } + + return true; + } + /** php-src SQLite3::escapeString — sqlite3_mprintf("%q", …). */ public static function escapeString(string $value): string { @@ -421,6 +473,13 @@ private static function ffi() int sqlite3_finalize(sqlite3_stmt *pStmt); int sqlite3_changes(sqlite3 *db); sqlite3_int64 sqlite3_last_insert_rowid(sqlite3 *db); +int sqlite3_errcode(sqlite3 *db); +const char *sqlite3_libversion(void); +int sqlite3_libversion_number(void); +typedef struct sqlite3_backup sqlite3_backup; +sqlite3_backup *sqlite3_backup_init(sqlite3 *pDest, const char *zDestName, sqlite3 *pSource, const char *zSourceName); +int sqlite3_backup_step(sqlite3_backup *p, int nPage); +int sqlite3_backup_finish(sqlite3_backup *p); char *sqlite3_mprintf(const char *zFormat, ...); int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt); int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName); diff --git a/test/compliance/cases/ext/sqlite3_open_error_version_backup.phpt b/test/compliance/cases/ext/sqlite3_open_error_version_backup.phpt new file mode 100644 index 00000000000..29926379d78 --- /dev/null +++ b/test/compliance/cases/ext/sqlite3_open_error_version_backup.phpt @@ -0,0 +1,68 @@ +--TEST-- +ext/sqlite3 open/lastError*/version/createCollation/backup (#20565, ext/sqlite3/sqlite3.c) +--ENV-- +PHP_COMPILER_PROFILE=8.4 +--FILE-- + 0 ? 'ok' : 'bad', "\n"; + +$db = new SQLite3(':memory:'); +echo 'idle_err=', $db->lastErrorCode(), "\n"; +echo 'idle_msg=', $db->lastErrorMsg(), "\n"; +$db->exec('CREATE TABLE t(x INT); INSERT INTO t VALUES (1);'); +@$db->exec('SELECT no_such_column FROM t'); +echo 'bad_err=', $db->lastErrorCode() > 0 ? 'pos' : 'zero', "\n"; +echo 'bad_msg=', (false !== stripos($db->lastErrorMsg(), 'no such') || false !== stripos($db->lastErrorMsg(), 'column')) ? 'ok' : $db->lastErrorMsg(), "\n"; + +$ok = $db->createCollation('rev', function (string $a, string $b): int { + return strcmp($b, $a); +}); +echo 'collation=', $ok ? '1' : '0', "\n"; + +try { + $db->open(':memory:'); + echo "open2=ok\n"; +} catch (Throwable $e) { + echo 'open2=', get_class($e), "\n"; +} + +$db->close(); +echo 'closed_err=', $db->lastErrorCode(), "\n"; +echo 'closed_msg=', var_export($db->lastErrorMsg(), true), "\n"; +$db->open(':memory:'); +$db->exec('CREATE TABLE u(y INT); INSERT INTO u VALUES (42);'); +echo 'reopen_row=', $db->querySingle('SELECT y FROM u'), "\n"; + +$src = new SQLite3(':memory:'); +$src->exec('CREATE TABLE t(v TEXT); INSERT INTO t VALUES ("hi");'); +$dst = new SQLite3(':memory:'); +echo 'backup=', $src->backup($dst) ? '1' : '0', "\n"; +echo 'backup_row=', $dst->querySingle('SELECT v FROM t'), "\n"; +?> +--EXPECT-- +open:yes +lastErrorCode:yes +lastErrorMsg:yes +version:yes +createCollation:yes +backup:yes +createFunction:yes +ver_str=ok +ver_num=ok +idle_err=0 +idle_msg=not an error +bad_err=pos +bad_msg=ok +collation=1 +open2=Exception +closed_err=0 +closed_msg='' +reopen_row=42 +backup=1 +backup_row=hi diff --git a/test/repro/sqlite3_20565_aot_advertise.php b/test/repro/sqlite3_20565_aot_advertise.php new file mode 100644 index 00000000000..2f54b5962df --- /dev/null +++ b/test/repro/sqlite3_20565_aot_advertise.php @@ -0,0 +1,10 @@ +exec('SELECT no_such'); +if ($db->lastErrorCode() <= 0) { + fwrite(STDERR, "expected error code\n"); + exit(1); +} +$db->close(); +$db->open(':memory:'); +$db->exec('CREATE TABLE t(x); INSERT INTO t VALUES (1);'); +$dst = new SQLite3(':memory:'); +if (!$db->backup($dst)) { + fwrite(STDERR, "backup failed\n"); + exit(1); +} +if (1 != $dst->querySingle('SELECT x FROM t')) { + fwrite(STDERR, "backup data mismatch\n"); + exit(1); +} +echo "ok\n"; diff --git a/test/selfhost/compiler_lib_spine_smoke/main.php b/test/selfhost/compiler_lib_spine_smoke/main.php index e28108af6a2..11372aea670 100644 --- a/test/selfhost/compiler_lib_spine_smoke/main.php +++ b/test/selfhost/compiler_lib_spine_smoke/main.php @@ -1542,18 +1542,24 @@ require_once __DIR__.'/../../../ext/spl/spl_classes.php'; require_once __DIR__.'/../../../ext/sqlite3/BuiltinClasses.php'; require_once __DIR__.'/../../../ext/sqlite3/Module.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3Backup.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3BusyTimeout.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Changes.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Close.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Construct.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3CreateCollation.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3CreateFunction.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3EnableExceptions.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3EscapeString.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Exec.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3LastErrorCode.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3LastErrorMsg.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3LastInsertRowID.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3Open.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Prepare.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3Query.php'; require_once __DIR__.'/../../../ext/sqlite3/SQLite3QuerySingle.php'; +require_once __DIR__.'/../../../ext/sqlite3/SQLite3Version.php'; require_once __DIR__.'/../../../ext/sqlite3/Sqlite3ClassMethod.php'; require_once __DIR__.'/../../../ext/sqlite3/Sqlite3Constants.php'; require_once __DIR__.'/../../../ext/sqlite3/Sqlite3ExtensionPolicy.php';