Skip to content

[Bug]: SQL PREPARE maps inner SQL syntax errors to 1105/HY000 instead of 1064/42000 #30434

Closed

Description

@XuPeng-SH

Summary

SQL PREPARE name FROM '...' reports an inner SQL syntax error as generic MySQL error 1105 / HY000, instead of 1064 / 42000. The SQL is correctly rejected and the connection remains usable, but clients cannot reliably classify the failure using the normal syntax-error code/SQLSTATE.

Found while independently validating PR #30151. This is separate from the empty-CTE panic fixed there; an unrelated malformed SQL control reproduces the mapping problem without an empty CTE.

Verified revision / environment

  • Commit: f149b512a6d24b0f1af22aa86f781833221c6608 (PR fix(parser): reject empty CTE bodies to avoid Format panic #30151, main-based).
  • Linux amd64, Go 1.27.1; native prerequisites built from this revision.
  • Real single-CN embedded MatrixOne cluster, MySQL TCP frontend, go-sql-driver/mysql client; one pinned physical sql.Conn throughout.
  • No claim that latest main or 4.2-dev was independently executed for this issue.

Minimal reproduction

Execute through the MySQL client on the same connection:

PREPARE unrelated_syntax FROM 'SELECT FROM';
SELECT 1;

Actual error (independently observed):

Error 1105 (HY000): You have an error in your SQL syntax; check the manual that corresponds to your MatrixOne server version for the right syntax to use. syntax error at line 1 column 11 near " from";

SELECT 1 then succeeds. The failure is error classification, not a disconnect or panic.

Additional observed comparison on this revision:

Entry point Input Result
Direct SQL WITH a AS () SELECT 1 1064 / 42000
CTAS CREATE TABLE bad_ctas AS WITH a AS () SELECT 1 1064 / 42000; no table created
SQL PREPARE PREPARE bad_stmt FROM 'WITH a AS () SELECT 1' 1105 / HY000 with syntax-error message, no panic
Binary COM_STMT_PREPARE WITH a AS () SELECT 1 1064 / 42000

Expected behavior

An inner SQL parser error in SQL PREPARE should retain syntax-error classification: 1064 / 42000, consistent with the other parser-facing entry points. Preserve the useful parser diagnostic and position. Do not rewrite unrelated planning, authorization, cancellation or runtime errors as syntax errors.

Root-cause analysis / existing ownership

The inner parse in pkg/sql/plan/build_dcl.go (buildPrepare, *tree.PrepareString case) calls mysql.ParseWithSQLMode and returns its parser error directly. pkg/sql/parsers/dialect/mysql/mysql_lexer.go (Lexer.Error) constructs a PositionedErr. In pkg/frontend/util.go, RewriteError recognizes *moerr.Error, while its default branch maps an untyped/non-moerr error to the generic MySQL error. The inner PREPARE parse error therefore loses SQL syntax-error classification at the frontend boundary.

Check other existing inner-parse entry points, including parsePreparedStatement, before choosing the narrowest shared correction. Prefer the existing parser-error conversion owner; avoid matching error-message strings, duplicating conversion rules, or adding work to successful SQL execution.

These files/paths are unchanged between PR #30151 and its merge base a53390c57de48ac280249ff0e88831e502e20fb0. This is an existing error-mapping gap, not a new mapping branch introduced by #30151. Historical introduction commit is not established.

Acceptance / focused validation

  • Public SQL PREPARE of ordinary malformed SQL returns 1064 / 42000.
  • Cover empty-CTE inner SQL where the parser fix is present; no internal-panic response.
  • Same physical connection executes SELECT 1 immediately after rejection.
  • Valid SQL PREPARE / EXECUTE / DEALLOCATE still works.
  • Direct SQL and binary COM_STMT_PREPARE syntax-error behavior remains correct.
  • Confirm unrelated error classes retain their codes; no broad error conversion or success-path parsing/allocation overhead.
  • Add a small regression through the real frontend entry point; parser-only assertions do not prove the client-visible mapping.

Evidence

Independent review and executed public-path results: #30151 (review)

The review includes the complete temporary test source, exact revision, commands and terminal results. Parser packages passed; public SQL probe passed after explicitly distinguishing the observed entrypoint-specific error codes. The original 1064 assertion on SQL PREPARE failed and prompted this investigation.

Activity

  1. XuPeng-SH commented on Oct 19, 2026

    @XuPeng-SH
    ContributorAuthor

    功能验证结果(2026-10-19 UTC):PR #30543 的修复在最新 main 上通过真实 MySQL 协议与 race 复测。分支 main,完整 SHA 8e63625c4e9baeb1d28c1095c07732f056c04895;Linux/amd64、Go 1.27.1,隔离嵌入式单 CN、MySQL TCP、MO_TEST_CLUSTER_TCP_ONLY=1。

    在已提交的 TestPreparedSyntaxErrorOverMySQLProtocol 上用 Go overlay 加入 issue 原始空 CTE 用例 WITH a AS () SELECT 1,并按实际 parser 诊断断言 empty common table expression body。真实 SQL PREPARE 返回 1064/42000;直接 SQL、SQL 字符串 PREPARE、用户变量 PREPARE、二进制 COM_STMT_PREPARE、类型参数溢出、活动事务中的失败与连接/事务恢复均通过。包含空 CTE 的完整协议测试 exit 0。

    命令:

    MO_TEST_CLUSTER_TCP_ONLY=1 .agents/skills/mo-dev/scripts/mo-cgo-test -overlay=/workspace/scratch/72331d2b67de/continue22/runs/30543-main-race-original-cte-corrected/overlay.json -race -p 1 -count=1 -v -timeout=15m -run '^TestPreparedSyntaxErrorOverMySQLProtocol$' ./pkg/embed
    

    原始日志 SHA-256 01f087b4495fd8c3283e93d0cfe7e46c0b1fce52e0f9300524793e9c50e5938f;测试源和 overlay、命令、环境及退出码已归档在 continue22/runs/30543-main-race-original-cte-corrected/。该 PR 的目标分支是 main,本项不需要 4.2 回移验证。

  2. Ariznawlll commented on Oct 19, 2026

    @Ariznawlll
    Collaborator

    测试版本

    • 分支:官方 main
    • commit:8e63625c4e9baeb1d28c1095c07732f056c04895
    • 修复:fix(frontend): report PREPARE inner syntax errors as 1064 instead of 1105 (#30434) #30543 已 MERGED,merge commit 57fb9530859d92791100098d312ea8cacc671910 经 GitHub compare 确认为该 main 的祖先。
    • 部署方式:隔离本地原生构建,1 CN / 1 TN / 1 LogService、MySQL TCP;独立临时数据库。另在同 SHA 上运行真实 MySQL 协议嵌入式 Go race 测试。
    • 测试日期:2026-10-19 UTC。

    原问题验证

    同一物理 SQL 连接中执行原始 PREPARE unrelated_syntax FROM 'SELECT FROM',独立 3/3 返回 ERROR 1064 (42000),保留 syntax error at line 1 column 11 定位信息;旧错码 1105/HY000 未出现。每轮紧接的 SELECT 1 均返回 1。

    Happy Path 覆盖

    1. 合法 PREPARE good FROM 'SELECT ? AS v'、EXECUTE USING @n=42、DEALLOCATE 各 3/3,实际值 42。
    2. 同 SHA 已提交的真实协议测试 TestPreparedSyntaxErrorOverMySQLProtocol 以 -race -count=3 PASS,覆盖 SQL 字符串与用户变量 PREPARE、二进制 COM_STMT_PREPARE、活动事务中的 admission、替换语句清理以及连接恢复;不是只检查 parser 内部错误文本。

    Unhappy Path 覆盖

    1. 空 CTE 内层 PREPARE ... FROM 'WITH a AS () SELECT 1'、用户变量形式和直接 SELECT FROM 各 3/3 返回 1064/42000,且报文包含对应 parser 位置。
    2. CREATE TABLE bad_ctas AS WITH a AS () SELECT 1 各 3/3 返回 1064/42000;随后 information_schema.tables 查得目标表 0 个,验证失败无部分建表。
    3. Go 协议回归对超限类型参数等非普通语法场景核对原有错误分类,并断言失败后同连接与事务可恢复;本轮未做外部网络断连/取消注入,此错误映射缺陷不依赖该路径。临时数据库已删除。

    白盒验证

    在该完整 SHA 上以仓库 CGo wrapper 运行 MO_TEST_CLUSTER_TCP_ONLY=1 ... -race -p 1 -count=3 -run '^TestPreparedSyntaxErrorOverMySQLProtocol$' ./pkg/embed,PASS。此测试对客户端可见的 MySQL 错误号、SQLSTATE、诊断内容和后续可用性均有明确断言。

    回归测试

    修复 PR #30543 已提交 pkg/embed/prepared_syntax_error_test.go 真实客户端协议回归,覆盖原始路径及关键错误/恢复边界;依据你允许的“现有 Go 用例完整覆盖则不重复补 MOTR”规则,本轮未新增等价 MOTR。MatrixOne 全量 BVT/集成 CI 本轮未执行。

    测试证据

    • 修复 PR #30543 的测试代码和变更;本轮同 SHA 定向 race 命令 -count=3 返回 ok github.com/matrixorigin/matrixone/pkg/embed。
    • 本轮外部 MySQL 客户端三轮实际错误码均为 1064/42000;每轮 bad_ctas_exists=0、恢复查询 1/2/3、合法预处理值 42。未声称其他发布线已验收。

    测试结论

    测试通过。

  3. daviszhen commented on Oct 20, 2026

    @daviszhen
    Contributor

    最新 main 验证(2026-10-20)

    基线:309f618995af4ee23e3b9149365f4af77264090d;关联修复 PR:#30543。

    结论:原问题已解决。对 PREPARE 'SELECT FROM' 返回 1064/42000,随后同一连接执行 SELECT 1 正常,连接/session 没有被错误状态污染。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions