Skip to content

Commit d276e75

Browse files
committed
Add targeted Doltlite write-visibility probes
Seven minimal PHP/PDO probes to narrow where the remaining ALTER TABLE failures come from: sequential DELETEs on rowid and composite-PK tables, prepared-statement reuse, INSERT+DELETE ordering, NOCASE round-trip, and the INSERT..SELECT table-rebuild path.
1 parent ab029f2 commit d276e75

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

.github/workflows/phpunit-tests-doltlite.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,113 @@ jobs:
119119
exit 1
120120
fi
121121
122+
- name: Probe Doltlite write-visibility on info-schema-shaped tables
123+
# The ALTER TABLE failures look like "the last DELETE/UPDATE against
124+
# info_schema.columns isn't visible to the follow-up SELECT". Run
125+
# a few targeted scenarios to narrow where the bug is.
126+
run: |
127+
php -r '
128+
$db = new PDO("sqlite::memory:");
129+
$db->exec("PRAGMA foreign_keys = ON");
130+
131+
function probe(PDO $db, string $label, string $setup, string $check, $expected): void {
132+
$db->exec($setup);
133+
$actual = $db->query($check)->fetchAll(PDO::FETCH_NUM);
134+
$ok = json_encode($actual) === json_encode($expected);
135+
printf("[%s] %s\n", $ok ? "PASS" : "FAIL", $label);
136+
if (!$ok) {
137+
printf(" setup: %s\n", str_replace("\n", " ", $setup));
138+
printf(" check: %s\n", $check);
139+
printf(" expected: %s\n", json_encode($expected));
140+
printf(" actual: %s\n", json_encode($actual));
141+
}
142+
}
143+
144+
// 1. Plain rowid table, two sequential DELETEs.
145+
probe($db, "two DELETEs on rowid table",
146+
"DROP TABLE IF EXISTS t1;
147+
CREATE TABLE t1 (ts TEXT, tn TEXT, cn TEXT);
148+
INSERT INTO t1 VALUES (\"s\",\"t\",\"a\"),(\"s\",\"t\",\"b\"),(\"s\",\"t\",\"c\");
149+
DELETE FROM t1 WHERE ts=\"s\" AND tn=\"t\" AND cn=\"a\";
150+
DELETE FROM t1 WHERE ts=\"s\" AND tn=\"t\" AND cn=\"b\";",
151+
"SELECT cn FROM t1 ORDER BY cn",
152+
[["c"]]
153+
);
154+
155+
// 2. Same, but with the composite PK shape info_schema uses.
156+
// (After our ROWID patch, this should be a rowid table + unique index.)
157+
probe($db, "two DELETEs on composite-PK table (rowid-patched)",
158+
"DROP TABLE IF EXISTS t2;
159+
CREATE TABLE t2 (ts TEXT, tn TEXT, cn TEXT, PRIMARY KEY (ts, tn, cn));
160+
INSERT INTO t2 VALUES (\"s\",\"t\",\"a\"),(\"s\",\"t\",\"b\"),(\"s\",\"t\",\"c\");
161+
DELETE FROM t2 WHERE ts=\"s\" AND tn=\"t\" AND cn=\"a\";
162+
DELETE FROM t2 WHERE ts=\"s\" AND tn=\"t\" AND cn=\"b\";",
163+
"SELECT cn FROM t2 ORDER BY cn",
164+
[["c"]]
165+
);
166+
167+
// 3. Same, with prepared statements reused across the two DELETEs
168+
// (same shape the driver uses).
169+
probe($db, "two prepared DELETEs on composite-PK table",
170+
"DROP TABLE IF EXISTS t3;
171+
CREATE TABLE t3 (ts TEXT, tn TEXT, cn TEXT, PRIMARY KEY (ts, tn, cn));
172+
INSERT INTO t3 VALUES (\"s\",\"t\",\"a\"),(\"s\",\"t\",\"b\"),(\"s\",\"t\",\"c\");",
173+
"SELECT cn FROM t3 WHERE cn NOT IN (\"a\",\"b\") ORDER BY cn",
174+
[["c"]]
175+
);
176+
$stmt = $db->prepare("DELETE FROM t3 WHERE ts=? AND tn=? AND cn=?");
177+
$stmt->execute(["s","t","a"]);
178+
$stmt->execute(["s","t","b"]);
179+
$after = $db->query("SELECT cn FROM t3 ORDER BY cn")->fetchAll(PDO::FETCH_NUM);
180+
printf("[%s] two prepared DELETEs actual: %s (expect [[\"c\"]])\n",
181+
json_encode($after) === json_encode([["c"]]) ? "PASS" : "FAIL",
182+
json_encode($after));
183+
184+
// 4. INSERT then DELETE a different row, then SELECT.
185+
probe($db, "INSERT then DELETE on composite-PK table",
186+
"DROP TABLE IF EXISTS t4;
187+
CREATE TABLE t4 (ts TEXT, tn TEXT, cn TEXT, PRIMARY KEY (ts, tn, cn));
188+
INSERT INTO t4 VALUES (\"s\",\"t\",\"a\");
189+
INSERT INTO t4 VALUES (\"s\",\"t\",\"b\");
190+
DELETE FROM t4 WHERE ts=\"s\" AND tn=\"t\" AND cn=\"a\";",
191+
"SELECT cn FROM t4 ORDER BY cn",
192+
[["b"]]
193+
);
194+
195+
// 5. NOCASE column: does Doltlite return the stored case or folded?
196+
probe($db, "NOCASE column preserves original case",
197+
"DROP TABLE IF EXISTS t5;
198+
CREATE TABLE t5 (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
199+
INSERT INTO t5 VALUES (1, \"Johnny\");",
200+
"SELECT name FROM t5",
201+
[["Johnny"]]
202+
);
203+
204+
// 6. Same NOCASE column, but covered by a unique index (what VARCHAR
205+
// PK columns look like in the driver).
206+
probe($db, "NOCASE column with unique index preserves original case",
207+
"DROP TABLE IF EXISTS t6;
208+
CREATE TABLE t6 (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE, UNIQUE(name));
209+
INSERT INTO t6 VALUES (1, \"Johnny\");",
210+
"SELECT name FROM t6",
211+
[["Johnny"]]
212+
);
213+
214+
// 7. Rebuild via INSERT...SELECT, similar to the ALTER TABLE
215+
// rewrite path; does the case survive the round-trip?
216+
probe($db, "rebuild via INSERT..SELECT preserves case",
217+
"DROP TABLE IF EXISTS t7_old;
218+
DROP TABLE IF EXISTS t7_new;
219+
CREATE TABLE t7_old (ID INTEGER NOT NULL, name TEXT COLLATE NOCASE NOT NULL, PRIMARY KEY (ID, name));
220+
INSERT INTO t7_old VALUES (1, \"Johnny\");
221+
CREATE TABLE t7_new (ID INTEGER NOT NULL, firstname TEXT COLLATE NOCASE, PRIMARY KEY (ID, firstname));
222+
INSERT INTO t7_new (ID, firstname) SELECT ID, name FROM t7_old;",
223+
"SELECT firstname FROM t7_new",
224+
[["Johnny"]]
225+
);
226+
'
227+
continue-on-error: true
228+
122229
- name: Install Composer dependencies (root)
123230
uses: ramsey/composer-install@v3
124231
with:

0 commit comments

Comments
 (0)