@@ -1074,205 +1074,6 @@ jobs:
10741074 print('patched CHECK TABLE missing-table check')
10751075 PY
10761076
1077- - name : Capture failing SQL from the first failing driver test
1078- continue-on-error : true
1079- env :
1080- LD_PRELOAD : ${{ steps.preload.outputs.value }}
1081- working-directory : packages/mysql-on-sqlite
1082- run : |
1083- # Reproduce the first failing test's setUp path with a query logger
1084- # wired up. The driver replaces any logger during construction, so
1085- # we install ours on the driver's own connection afterwards.
1086- php <<'PHP'
1087- <?php
1088- require __DIR__ . '/tests/bootstrap.php';
1089-
1090- $pdo = new PDO('sqlite::memory:');
1091- $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
1092- $conn = new WP_SQLite_Connection([ 'pdo' => $pdo ]);
1093-
1094- $engine = new WP_SQLite_Driver($conn, 'wp');
1095- fwrite(STDERR, "Driver construction ok\n");
1096-
1097- $logged = [];
1098- $engine->get_connection()->set_query_logger(
1099- function (string $sql, array $params) use (&$logged) {
1100- $logged[] = [$sql, $params];
1101- }
1102- );
1103-
1104- try {
1105- $engine->query("CREATE TABLE _options (
1106- ID INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL,
1107- option_name TEXT NOT NULL default '',
1108- option_value TEXT NOT NULL default ''
1109- );");
1110- fwrite(STDERR, "CREATE TABLE _options ok\n");
1111- } catch (\Throwable $e) {
1112- fwrite(STDERR, "EXC: " . $e->getMessage() . "\n");
1113- fwrite(STDERR, "--- last 3 SQL statements sent to SQLite ---\n");
1114- foreach (array_slice($logged, -3) as $i => $entry) {
1115- [$sql, $params] = $entry;
1116- fwrite(STDERR, "---\nSQL (length " . strlen($sql) . "):\n" . $sql . "\n");
1117- if ($params) {
1118- fwrite(STDERR, "params: " . json_encode($params) . "\n");
1119- }
1120- fwrite(STDERR, sprintf(
1121- "window [offset 180..209]:\n %s\n",
1122- substr($sql, 180, 30)
1123- ));
1124- }
1125- }
1126- PHP
1127-
1128- - name : Diagnose createFunction behavior
1129- continue-on-error : true
1130- env :
1131- # Intentionally NOT setting LD_PRELOAD at step level — we enable it
1132- # only inside the script so gdb itself doesn't link against Turso.
1133- PRELOAD : ${{ steps.preload.outputs.value }}
1134- working-directory : packages/mysql-on-sqlite
1135- run : |
1136- # Progress logging goes to STDERR so it's line-buffered and flushed
1137- # before any crash. The PHP script is run under gdb so we get a
1138- # backtrace if it segfaults.
1139- cat > /tmp/diag.php <<'PHP'
1140- <?php
1141- $log = fn(string $s) => fwrite(STDERR, $s . "\n");
1142-
1143- $pdo_class = PHP_VERSION_ID >= 80400 ? PDO\SQLite::class : PDO::class;
1144- $pdo = new $pdo_class('sqlite::memory:');
1145- $log("[a] PDO({$pdo_class}) ok");
1146-
1147- // 1) Closure
1148- try {
1149- $pdo->createFunction('mk_closure', function ($x) { return $x . '!'; });
1150- $log('[b] createFunction(closure) ok');
1151- $r = $pdo->query("SELECT mk_closure('x') AS v")->fetch(PDO::FETCH_ASSOC);
1152- $log('[b.call] ' . json_encode($r));
1153- } catch (\Throwable $e) {
1154- $log('[b] EXC ' . $e->getMessage());
1155- }
1156-
1157- // 2) String callable
1158- try {
1159- $pdo->createFunction('mk_builtin', 'md5');
1160- $log('[c] createFunction("md5") ok');
1161- $r = $pdo->query("SELECT mk_builtin('abc') AS v")->fetch(PDO::FETCH_ASSOC);
1162- $log('[c.call] ' . json_encode($r));
1163- } catch (\Throwable $e) {
1164- $log('[c] EXC ' . $e->getMessage());
1165- }
1166-
1167- // 3) [object, method] callable
1168- try {
1169- $obj = new class {
1170- public function greet($x) { return "hi $x"; }
1171- };
1172- $pdo->createFunction('mk_method', [$obj, 'greet']);
1173- $log('[d] createFunction([obj,method]) ok');
1174- $r = $pdo->query("SELECT mk_method('bob') AS v")->fetch(PDO::FETCH_ASSOC);
1175- $log('[d.call] ' . json_encode($r));
1176- } catch (\Throwable $e) {
1177- $log('[d] EXC ' . $e->getMessage());
1178- }
1179-
1180- // 4) Reproduce testFromBase64Function in a clean Turso FUNC_SLOTS
1181- // state (skipping earlier [e] UDF registrations which would
1182- // consume slots 0..49 globally in the same process).
1183- try {
1184- require getcwd() . '/tests/bootstrap.php';
1185- $pdo2 = new PDO\SQLite('sqlite::memory:');
1186- $pdo2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
1187- $engine = new WP_SQLite_Driver(new WP_SQLite_Connection(['pdo' => $pdo2]), 'wp');
1188- $log('[f] driver constructed');
1189- $r = $engine->query("SELECT FROM_BASE64('SGVsbG8gV29ybGQ=') AS decoded");
1190- $log('[f.call] ' . json_encode($r));
1191- } catch (\Throwable $e) {
1192- $log('[f] EXC: ' . $e->getMessage());
1193- }
1194-
1195- $log('[done] script finished cleanly');
1196- PHP
1197-
1198- timeout --kill-after=5 60 gdb -batch \
1199- -ex "set confirm off" \
1200- -ex "set pagination off" \
1201- -ex "set environment LD_PRELOAD=$PRELOAD" \
1202- -ex "run /tmp/diag.php" \
1203- -ex "bt" \
1204- --args "$(command -v php)" || true
1205-
1206- - name : Run PHPUnit testFromBase64Function under gdb
1207- continue-on-error : true
1208- env :
1209- PRELOAD : ${{ steps.preload.outputs.value }}
1210- working-directory : packages/mysql-on-sqlite
1211- run : |
1212- timeout --kill-after=10 120 gdb -batch \
1213- -ex "set confirm off" \
1214- -ex "set pagination off" \
1215- -ex "set environment LD_PRELOAD=$PRELOAD" \
1216- -ex "run ./vendor/bin/phpunit -c ./phpunit.xml.dist --filter 'WP_SQLite_Driver_Tests::testFromBase64Function$'" \
1217- -ex "bt 40" \
1218- --args "$(command -v php)" || true
1219-
1220- - name : Probe full main run with Translation unskipped + gdb watchdog
1221- continue-on-error : true
1222- env :
1223- LD_PRELOAD : ${{ steps.preload.outputs.value }}
1224- working-directory : packages/mysql-on-sqlite
1225- # Reproduce the 4c4f491 main-run state (Translation_Tests unskipped)
1226- # and capture what PHP is actually doing during the hang.
1227- #
1228- # Timeline budget (~7 min total):
1229- # 0-30s: build testcases, run PDO_API + Driver_Tests (fast, ~5k tests)
1230- # 30-60s: Metadata_Tests + Translation_Tests (completed at 60s in 45)
1231- # 60s: testReconstructTable starts and hangs
1232- # 360s: first gdb snapshot (5 min in)
1233- # 400s: second gdb snapshot (in case first detached/crashed)
1234- # 420s: timeout kills php
1235- run : |
1236- set +e
1237- skip_regex='^(?!WP_MySQL_Server_Suite_).+'
1238-
1239- dump_backtraces() {
1240- local label=$1
1241- # Target the PHP process (not the timeout wrapper). Use exact name.
1242- local PHP_PID
1243- PHP_PID=$(pgrep -x php | head -1)
1244- if [ -z "$PHP_PID" ]; then
1245- echo "=== watchdog ($label): no php pid found ==="
1246- return
1247- fi
1248- echo "=== watchdog ($label): attaching gdb to php pid $PHP_PID ==="
1249- # /proc/PID/stack shows what the kernel thinks PHP is waiting for
1250- # — free even without ptrace and cheap to read.
1251- echo "--- /proc/$PHP_PID/wchan: $(cat /proc/$PHP_PID/wchan 2>/dev/null) ---"
1252- echo "--- /proc/$PHP_PID/stack ---"
1253- sudo cat /proc/$PHP_PID/stack 2>/dev/null | head -30
1254- echo "--- gdb bt ---"
1255- sudo gdb -p "$PHP_PID" -batch \
1256- -ex 'set pagination off' \
1257- -ex 'info threads' \
1258- -ex 'thread apply all bt 40' \
1259- 2>&1 | head -400
1260- echo "=== watchdog ($label): done ==="
1261- }
1262-
1263- (
1264- sleep 360 && dump_backtraces "T+360s"
1265- sleep 40 && dump_backtraces "T+400s"
1266- ) &
1267- WATCHDOG=$!
1268-
1269- timeout --kill-after=10 420 \
1270- php ./vendor/bin/phpunit -c ./phpunit.xml.dist --debug \
1271- --filter "$skip_regex" 2>&1 | tail -80
1272- echo "full-main+Translation exit: $?"
1273-
1274- wait "$WATCHDOG" 2>/dev/null
1275-
12761077 - name : Run PHPUnit tests against Turso DB
12771078 env :
12781079 LD_PRELOAD : ${{ steps.preload.outputs.value }}
0 commit comments