docs/plans/SCENARIOS-mysql-completion.md
Goal: Align MySQL completion test coverage with PG's 235 test cases Verification:
go test -count=1 ./backend/plugin/parser/mysql/ -run ^TestCompletion$Reference: PG completion tests atbackend/plugin/parser/pg/test-data/test_completion.yamlTest file:backend/plugin/parser/mysql/test-data/test_completion.yamlTest runner:backend/plugin/parser/mysql/completion_test.go
Status: [ ] pending, [x] passing, [~] partial
SELECT * FROM | — tables, databases, viewsSELECT * FROM db.| — tables and views in specific databaseSELECT | FROM t1 — columns of t1, plus tables/databases/viewsSELECT | FROM t2 x — columns of t2 (aliased as x), tables including aliasSELECT DISTINCT | FROM t2 — same as SELECT columnsSELECT | FROM t2 LIMIT 10 — columns despite trailing LIMITSELECT c1, | FROM t2 — columns after comma in select listSELECT * FROM t1, | — second table after commaSELECT\n |\nFROM t1 — multiline with cursor on empty line before FROMSELECT *\nFROM\n | — multiline FROM on separate lineSELECT * FROM | — extra whitespaceSELECT * FROM /* comment */ | — inline block comment before cursorSELECT * FROM t1 -- comment\nWHERE | — line comment before WHERESELECT | FROM t2 FOR UPDATE — columns despite trailing FOR UPDATESELECT t1.| FROM t1 — columns of t1 via direct table nameSELECT cc1.| FROM t2 cc1 — columns of t2 via aliasSELECT cc1.| FROM t1 cc1 JOIN t2 ON NOT cc1.c1 = t2.c1 — alias columns in JOIN contextSELECT db.t2.| FROM t2 — fully qualified database.table.columnSELECT * FROM t2 x ORDER BY x.| — alias columns in ORDER BYSELECT * FROM t2 x GROUP BY x.| — alias columns in GROUP BYSELECT MAX(cc1.|) FROM t2 cc1 — alias columns inside function callSELECT a.| FROM t1 a JOIN t1 b ON a.c1 = b.c1 — disambiguate self-join aliases (a)SELECT b.| FROM t1 a JOIN t1 b ON a.c1 = b.c1 — disambiguate self-join aliases (b)SELECT COUNT(|) FROM t1 — columns inside aggregate functionSELECT | FROM t1 JOIN t2 ON t1.c1 = t2.c1 — columns from both joined tablesSELECT * FROM t1 cc1 JOIN t2 ON cc1.| — alias columns in ON clause (left)SELECT * FROM t1 cc1 JOIN t2 ON cc1.c1 = t2.| — table columns in ON clause (right)SELECT | FROM t1 LEFT JOIN t2 ON t1.c1 = t2.c1 — LEFT JOINSELECT | FROM t1 RIGHT JOIN t2 ON t1.c1 = t2.c1 — RIGHT JOINSELECT | FROM t1 CROSS JOIN t2 — CROSS JOIN (no ON)SELECT | FROM t1 NATURAL JOIN t2 — NATURAL JOINSELECT | FROM t1 a JOIN t2 b ON a.c1 = b.c1 JOIN t2 c ON b.c1 = c.c1 — 3-way JOINSELECT c.| FROM t1 a JOIN t2 b ON a.c1 = b.c1 JOIN t2 c ON b.c1 = c.c1 — 3rd alias columnsSELECT * FROM t1 RIGHT JOIN t2 ON t1.c1 = t2.| — columns in RIGHT JOIN ONSELECT * FROM t1 CROSS JOIN t2 WHERE t1.| — columns after CROSS JOIN in WHERESELECT * FROM t1 JOIN t2 ON t1.c1 = t2.c1 LEFT JOIN t2 t3 ON t2.c1 = t3.c1 WHERE | — multi-JOIN WHERESELECT a.| FROM t1 a NATURAL JOIN t2 b — alias after NATURAL JOINSELECT * FROM t1 JOIN | — table candidates after JOIN keywordSELECT * FROM t1 LEFT JOIN | — table candidates after LEFT JOINSELECT FROM t1 JOIN | — skeleton: table candidates after JOIN (no select expr)SELECT | FROM t1 JOIN t2 ON t1.c1 = t2.c1 LEFT JOIN t2 t3 ON t2.c1 = t3.c1 — columns from all 3 tablesSELECT * FROM t1 JOIN t2 USING (|) — columns for USING clauseSELECT | FROM (SELECT c1 FROM t1) cc1 — derived table columns (inferred)SELECT | FROM (SELECT c1 FROM t1) cc1(cc1c1) — derived table columns (explicit alias)SELECT x.| FROM (SELECT * FROM t2) x — derived table qualified columnsSELECT | FROM (SELECT * FROM (SELECT c1 FROM t1) inner_q) outer_q — nested derived tablesSELECT | FROM (SELECT c1 FROM t1) sub1 JOIN t2 ON sub1.c1 = t2.c1 — derived table JOINSELECT | FROM (SELECT c1 FROM (SELECT c1 FROM (SELECT c1 FROM t1) a) b) c — triple nestedSELECT b.| FROM (SELECT * FROM (SELECT c1 FROM t1) a) b — nested qualified access(SELECT c1 FROM t1) UNION (SELECT | FROM t2) — subquery in parenthesized UNIONSELECT * FROM t1 WHERE c1 IN (SELECT | FROM t2) — subquery in WHERE INSELECT * FROM t1 WHERE EXISTS (SELECT | FROM t2) — subquery in WHERE EXISTSSELECT * FROM t1 WHERE c1 = (SELECT MAX(|) FROM t2) — scalar subquerySELECT * FROM t1 x WHERE x.c1 IN (SELECT c1 FROM t2 WHERE |) — correlated subquery WHERESELECT * FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.c1 = t1.|) — correlated column refSELECT (SELECT | FROM t1) FROM t2 — scalar subquery in SELECT listWITH x AS (SELECT * FROM t2) SELECT x.| FROM x; — CTE qualified columns (inferred)WITH x(x1, x2) AS (SELECT * FROM t2) SELECT x.| FROM x; — CTE with explicit columnsWITH x(x1, x2) AS (SELECT * FROM t2) SELECT | FROM x — CTE unqualified columnsWITH a AS (SELECT c1 FROM t1), b AS (SELECT c1, c2 FROM t2) SELECT | FROM a JOIN b ON a.c1 = b.c1 — multiple CTEsWITH a AS (SELECT c1 FROM t1), b AS (SELECT c1, c2 FROM t2) SELECT b.| FROM a JOIN b ON a.c1 = b.c1 — multiple CTE qualifiedWITH x AS (SELECT c1 FROM t1) SELECT | FROM (SELECT * FROM x) sub1 — CTE used in derived tableWITH a AS (SELECT c1 FROM t1), b AS (SELECT * FROM a) SELECT | FROM b — CTE referencing CTEWITH a AS (SELECT c1 FROM t1), b AS (SELECT a.| FROM a) SELECT * FROM b — CTE qualified in CTE definitionWITH x AS (SELECT c1 FROM t1) SELECT | FROM x, t2 — CTE mixed with regular tableWITH x AS (SELECT c1 FROM t1) SELECT x.| FROM x JOIN t2 ON x.c1 = t2.c1 — CTE in JOINWITH x(a, b) AS (SELECT c1, c2 FROM t2) SELECT | FROM x JOIN t1 ON x.a = t1.c1 — CTE explicit cols in JOINWITH x AS (SELECT * FROM t2) INSERT INTO t1 SELECT | FROM x — CTE in INSERT SELECTWITH x AS (SELECT c1, c2 FROM t2) UPDATE t1 SET c1 = (SELECT | FROM x) — CTE in UPDATE subqueryWITH x AS (SELECT * FROM t2) DELETE FROM t1 WHERE c1 IN (SELECT | FROM x) — CTE in DELETE subqueryWITH RECURSIVE x AS (SELECT c1 FROM t1 UNION ALL SELECT c1 FROM x) SELECT | FROM x — recursive CTEWITH x AS (SELECT c1 FROM t1 UNION SELECT c1 FROM t2) SELECT x.| FROM x — CTE with UNION bodySELECT * FROM t1 WHERE | — basic WHERESELECT * FROM t2 WHERE c1 = 1 AND | — after ANDSELECT * FROM t2 WHERE (c1 = 1 AND |) — inside parenthesized ANDSELECT * FROM t2 WHERE c1 IS NOT NULL AND | — after IS NOT NULL ANDSELECT * FROM t2 WHERE c1 IS NULL OR | — after IS NULL ORSELECT * FROM t2 WHERE NOT | — after NOTSELECT * FROM t2 WHERE c1 > | — after comparison operatorSELECT * FROM t2 WHERE c1 + | > 0 — in arithmetic expressionSELECT * FROM t2 WHERE c1 IN (1, 2) AND | — after IN list ANDSELECT * FROM t2 WHERE c1 BETWEEN | AND c2 — BETWEEN leftSELECT * FROM t2 WHERE c1 BETWEEN c2 AND | — BETWEEN rightSELECT * FROM t2 WHERE c1 LIKE | — after LIKESELECT * FROM t2 WHERE CASE WHEN c1 > 0 THEN | END = 1 — inside CASE THENSELECT * FROM t1 a JOIN t2 b ON a.c1 = b.c1 WHERE | — WHERE after JOINSELECT * FROM t1 a JOIN t2 b ON a.c1 = b.c1 WHERE a.| — qualified column in WHERE after JOINSELECT FROM t1 WHERE | — skeleton: WHERE without select expressionsSELECT\nFROM t1\nWHERE | — skeleton multiline WHERESELECT c1 as eid FROM t1 WHERE | — WHERE with select alias (alias NOT valid in WHERE)SELECT c1 as eid FROM t1 ORDER BY | — ORDER BY with aliasSELECT c1 as eid, c2 as xid FROM t2 ORDER BY | — ORDER BY with multiple aliasesSELECT c1 as eid FROM t1 GROUP BY | — GROUP BY with aliasSELECT c1 as eid, c2 as xid FROM t2 HAVING | — HAVING with aliasesSELECT FROM t1 ORDER BY | — skeleton ORDER BYSELECT FROM t1 GROUP BY | — skeleton GROUP BYSELECT c1 FROM t1 UNION SELECT c1 FROM t2 ORDER BY | — ORDER BY on UNION resultSELECT c1 FROM t1 UNION ALL SELECT c1 FROM t2 ORDER BY | — ORDER BY on UNION ALL resultSELECT * FROM t2 LIMIT | — keywords only in LIMIT positionSELECT * FROM t2 ORDER BY c1 LIMIT | — LIMIT after ORDER BYINSERT INTO | — table candidatesINSERT INTO db.| — database-qualified table candidatesINSERT INTO t1(|); — column candidates for target tableINSERT INTO t2(c1, |); — remaining columns after commaINSERT INTO t1 SELECT | FROM t2 — INSERT SELECT columnsINSERT INTO t1 VALUES (|) — inside VALUES (columns/functions)SELECT 1; INSERT INTO | — INSERT after prior statementUPDATE | SET c1 = 1 — UPDATE with partial contextUPDATE | — table candidatesUPDATE db.| — database-qualified table candidatesUPDATE t1 SET | — SET column candidatesUPDATE t1 SET c1 = | — value position (columns/functions)UPDATE t1 SET c1 = 1 WHERE | — UPDATE WHEREDELETE FROM | — table candidatesDELETE FROM db.| — database-qualified table candidatesDELETE FROM t1 WHERE | — DELETE WHERESELECT 1; UPDATE | — UPDATE after prior statementSELECT 1; DELETE FROM | — DELETE after prior statementSELECT FROM t1 WHERE | — skeleton WHERE (already in 1.6, cross-ref)DELETE FROM t1 WHERE c1 > 0 AND | — compound DELETE WHEREALTER TABLE | — table candidatesALTER TABLE db.| — database-qualifiedALTER TABLE t1 ADD COLUMN c2 int REFERENCES | — FK reference tableALTER TABLE t1 DROP COLUMN | — column candidatesALTER TABLE t1 RENAME COLUMN | TO new_name — column candidatesALTER TABLE t1 MODIFY COLUMN | — MySQL-specific MODIFYALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (|) REFERENCES t2 — FK source columnsALTER TABLE t1 ADD CONSTRAINT fk FOREIGN KEY (c1) REFERENCES t2(|) — FK target columnsDROP TABLE | — table candidatesDROP TABLE db.| — database-qualifiedDROP TABLE IF EXISTS | — table candidates after IF EXISTSDROP VIEW | — view candidatesDROP INDEX | — index candidates (table context)TRUNCATE TABLE | — table candidates (MySQL requires TABLE keyword)CREATE TABLE t3 (id int, FOREIGN KEY (id) REFERENCES |) — FK reference tableCREATE TABLE t3 (id int REFERENCES t1(|)) — FK reference columnsCREATE INDEX idx ON | — table candidatesCREATE INDEX idx ON t1 (|) — column candidates for indexCREATE INDEX idx ON t2 (c1, |) — second column in compound indexCREATE VIEW v2 AS SELECT | FROM t1 — columns in CREATE VIEWCREATE VIEW v2 AS SELECT * FROM | — tables in CREATE VIEW FROMCREATE TABLE t3 AS SELECT | FROM t1 — CREATE TABLE AS SELECTSELECT c1 FROM t1 UNION SELECT | FROM t2 — after UNION SELECTSELECT c1 FROM t1 UNION ALL SELECT | FROM t2 — after UNION ALL SELECTSELECT c1 FROM t1 INTERSECT SELECT | FROM t2 — INTERSECTSELECT c1 FROM t1 EXCEPT SELECT | FROM t2 — EXCEPTSELECT c1 FROM t1 UNION SELECT c1 FROM t1 UNION SELECT | FROM t2 — triple UNIONSELECT c1 FROM t1 EXCEPT ALL SELECT | FROM t2 — EXCEPT ALLSELECT c1 FROM t1 INTERSECT ALL SELECT | FROM t2 — INTERSECT ALLSELECT | FROM t2 UNION SELECT * FROM t1 — first SELECT in UNIONSELECT * FROM (SELECT c1 FROM t1 UNION SELECT c1 FROM t2) x WHERE x.| — qualified from UNION subqueryWITH x AS (SELECT c1 FROM t1 UNION SELECT c1 FROM t2) SELECT x.| FROM x — CTE with UNION (dup of 1.5, cross-ref)SELECT | FROM (SELECT c1, c2 FROM t2 UNION SELECT c1, c2 FROM t2) sub — derived table from UNIONSELECT c1 FROM t1 UNION ALL SELECT c1 FROM t2 ORDER BY | — ORDER BY on UNION (dup of 2.1, cross-ref)SELECT 1; SELECT * FROM | — after valid statement (complex variant)SELECT FROM basdkfjasldf; SELECT | FROM t1 — after invalid statementselect count(1) from t1 where id 'asdfsadf'; SELECT * FROM | — after complex invalid (multiline)INVALID SQL; SELECT * FROM | — after completely invalid SQLSELECT * FROM t1; SELECT * FROM t2 WHERE | — WHERE in second statementSELECT; SELECT | FROM t1 — after empty SELECTSELECT * FROM t1 WHERE; SELECT | FROM t2 — after incomplete WHERECREATE TABLE x (id int); SELECT | FROM t1 — after DDLSELECT\nFROM | — skeleton (no select expressions)SELECT\n\nFROM | — skeleton with blank lineSELECT FROM | — skeleton single lineSELECT\nFROM t1\nWHERE | — skeleton multiline WHERE (dup of 1.6)SELECT 1; UPDATE | — UPDATE after prior statementSELECT 1; DELETE FROM | — DELETE after prior statementSELECT * FROM t| — partial table nameSELECT * FROM db.t| — partial after database dotSELECT t1.c| — partial column nameINSERT INTO t| — partial in INSERTUPDATE t| — partial in UPDATEDELETE FROM t| — partial in DELETESELECT * FROM t1 JOIN t| — partial after JOINSELECT * FROM t1 WHERE t1.c| — partial qualified column in WHEREALTER TABLE t| — partial in ALTERDROP TABLE t| — partial in DROPSELECT | FROM `t1` — backtick-quoted tableSELECT `t1`.| FROM t1 — backtick-quoted qualifierSELECT * FROM `db`.| — backtick-quoted databaseSELECT * FROM `db`.`t1` WHERE | — fully backtick-quoted table in WHERESELECT\t|\tFROM t1 — tab whitespaceSELECT * FROM t1 WHERE | — trailing space after cursorSELECT\n *\n FROM\n t1\n WHERE | — fully expanded multiline/* leading comment */ SELECT * FROM | — leading block commentSELECT * FROM t1 WHERE c1 = /* inline */ | — inline comment in expressionINSERT INTO `t1`(|) — backtick-quoted table in INSERTSELECT c1, SUM(c2) OVER (PARTITION BY |) FROM t2 — PARTITION BY columnsSELECT c1, SUM(c2) OVER (ORDER BY |) FROM t2 — window ORDER BY columnsSELECT c1, ROW_NUMBER() OVER (PARTITION BY | ORDER BY c1) FROM t2 — PARTITION BY in compound windowSELECT COALESCE(|, 0) FROM t2 — first arg of COALESCESELECT COALESCE(c1, |) FROM t2 — second arg of COALESCESELECT NULLIF(|, 0) FROM t2 — first arg of NULLIFSELECT CAST(| AS int) FROM t2 — CAST argumentSELECT IF(|, 1, 0) FROM t2 — MySQL IF functionSELECT IFNULL(|, 0) FROM t2 — MySQL IFNULL functionSELECT * FROM t1 WHERE c1 > (SELECT MAX(|) FROM t2) — aggregate in scalar subquery (dup of 1.4)The current test metadata has: db with t1(c1), t2(c1,c2), v1.
To support database-qualified tests (MySQL equivalent of PG's schema-qualified tests), the test metadata getter needs to handle db as database name and return tables/views. No second database needed — MySQL's db.| tests verify the database-qualified path works with the default database.
Each section is verified by:
test_completion.yamlgo test -count=1 ./backend/plugin/parser/mysql/ -run ^TestCompletion$record = true to capture actual output, then verify output is sensible before committing as expected| Phase | Sections | Total | Already Passing | New |
|---|---|---|---|---|
| Phase 1 | 6 | 90 | 15 | 75 |
| Phase 2 | 4 | 48 | 12 | 36 |
| Phase 3 | 5 | 56 | 4 | 52 |
| Total | 15 | 194 | 31 | 163 |