Skip to content

feat(sql): support quoted braced placeholders - #3666

Open
LwClick wants to merge 1 commit into
t8y2:mainfrom
LwClick:codex/feat/sql-quoted-variable-substitution
Open

feat(sql): support quoted braced placeholders#3666
LwClick wants to merge 1 commit into
t8y2:mainfrom
LwClick:codex/feat/sql-quoted-variable-substitution

Conversation

@LwClick

@LwClick LwClick commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Closes #3652

Problem

DBX skipped ${name} and #{name} placeholders inside quoted SQL, while scheduler-style templates commonly quote date and text placeholders before substitution.

Root cause

The SQL parameter scanner skipped every quoted region. The initial quoted-interpolation implementation retained the surrounding delimiter but did not apply database string rules consistently.

MySQL, MariaDB, Doris, and StarRocks interpret both single- and double-quoted text as strings by default, consume backslash sequences such as \n and \0, and change double quotes to identifier delimiters when ANSI_QUOTES is enabled. NO_BACKSLASH_ESCAPES changes both how quoted boundaries are scanned and how inserted values must be serialized. Applying either SQL mode only during replacement can therefore discover the wrong placeholder range or change the parameter bytes.

Changes

  • Add an opt-in, per-database setting for detecting ${name} and #{name} inside quoted SQL text and identifiers.
  • Resolve MySQL-family quote semantics once and propagate them through every parameter-scanner path.
  • Escape backslashes in both single- and double-quoted MySQL-family strings under the default SQL mode.
  • Add explicit ANSI_QUOTES and NO_BACKSLASH_ESCAPES compatibility settings for MySQL, Doris, and StarRocks.
  • Make NO_BACKSLASH_ESCAPES affect quote-boundary scanning as well as substitution, including strings ending in a literal backslash.
  • Treat double quotes as MySQL-family strings by default and as identifiers only when ANSI_QUOTES is enabled.
  • Preserve typed SQL literals outside quotes, delimiter doubling inside quotes, the default-off path, legacy sparse settings, syntax toggles, comments, PostgreSQL dollar-quoted bodies, asynchronous execution flow, and parameter preview behavior.

Compatibility contract

  • Quoted interpolation remains disabled by default.
  • MySQL/MariaDB, Doris, and StarRocks default to ordinary MySQL string semantics for both single and double quotes.
  • Enable ANSI_QUOTES when sessions use that SQL mode so double-quoted placeholders are treated as identifiers.
  • Enable NO_BACKSLASH_ESCAPES when sessions use that SQL mode so backslashes remain literal during both scanning and substitution.
  • The compatibility settings are scoped by DBX database type, like the existing syntax settings. Mixed SQL modes across connections of the same type are not inferred automatically; those connections should use consistent modes before quoted interpolation is enabled.
  • ?, :name, and @name remain ignored inside quoted regions.
  • Unquoted typed values, inline @set expansion, and native database variables keep their existing behavior.

References

Tests

  • pnpm vitest run apps/desktop/src/lib/__tests__/sql/sqlParameters.spec.ts apps/desktop/src/lib/__tests__/sql/sqlVariableSyntax.spec.ts apps/desktop/src/composables/__tests__/useSqlExecution.spec.ts packages/app-tests/settingsStore.test.ts — 4 files, 156 tests passed
  • pnpm typecheck
  • pnpm lint
  • pnpm test — 447 files, 3,691 tests passed
  • pnpm build
  • Changed-file oxfmt --check
  • git diff --check

Manual verification

A temporary mysql:8.0 container running MySQL 8.0.46 was used to execute the generated forms under the default session mode, NO_BACKSLASH_ESCAPES, ANSI_QUOTES, and both modes together.

  • Default single- and double-quoted C:\new values both returned HEX(...) = 433A5C6E6577.
  • Default single- and double-quoted literal \n values both returned HEX(...) = 5C6E.
  • Under NO_BACKSLASH_ESCAPES, a preceding string ending in a literal backslash parsed correctly, and single- and double-quoted C:\new values returned 433A5C6E6577.
  • Under ANSI_QUOTES, a double-quoted identifier containing a literal backslash executed successfully without doubling the identifier bytes.
  • Under ANSI_QUOTES,NO_BACKSLASH_ESCAPES, the single-quoted value and double-quoted identifier both preserved their literal backslashes.

The temporary container was removed after verification.

Remaining risks

DBX cannot infer different SQL modes for two connections that share the same database type. Users with mixed per-connection modes should keep quoted interpolation disabled or align those session modes. Doris and StarRocks behavior is covered by their upstream SQL mode implementations and the shared regression suite, but this update did not deploy separate Doris or StarRocks servers.

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quoted placeholder 插值仍有一个会改变实际参数值的数据库兼容问题。

  1. apps/desktop/src/lib/sql/sqlParameters.ts:838sqlParameterTextInsideQuote 只转义当前引号分隔符,完全忽略已经传入的 databaseType。在默认 MySQL 字符串语义下,反斜杠仍是转义字符;例如参数值 C:\new 被生成成 select 'C:\new',服务端会把 \n 解释为换行,而不是原始反斜杠和字母 n。针对当前 head 的测试探针已复现。请按数据库字符串规则处理反斜杠,或明确限制不具备可靠转义语义的数据库/SQL mode,并覆盖 Windows 路径、\n\0NO_BACKSLASH_ESCAPES 兼容场景。

Author fix required:这里需要明确不同数据库及 SQL mode 下的转义契约,不能只做通用字符替换。

@LwClick
LwClick force-pushed the codex/feat/sql-quoted-variable-substitution branch from ce9f197 to 7d72657 Compare July 16, 2026 12:25
@LwClick

LwClick commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the requested MySQL compatibility fix in 7d72657d.

The replacement path now uses the active database type. MySQL, Doris, and StarRocks single-quoted values double backslashes under their default string semantics, while an explicit per-database NO_BACKSLASH_ESCAPES setting preserves literal backslashes for sessions using that mode. Quoted identifiers do not receive string-style backslash escaping.

Regression coverage now includes Windows paths, literal \n, literal \0, combined quotes and backslashes, the default MySQL-family path, the NO_BACKSLASH_ESCAPES path, settings normalization, and execution-dialog propagation.

I also verified the generated forms against MySQL 8.0.46 under both session modes with HEX(...); all tested values produced identical bytes. The complete frontend suite, typecheck, lint, build, formatting checks, and the refreshed GitHub frontend CI pass.

@LwClick
LwClick force-pushed the codex/feat/sql-quoted-variable-substitution branch from 7d72657 to 1064880 Compare July 17, 2026 03:31
@LwClick

LwClick commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

I completed another compatibility pass in 1064880f and rebased the single-commit branch onto the latest main.

The MySQL-family quote-mode contract now applies during both occurrence scanning and substitution:

  • Double quotes are treated as strings by default and receive the same backslash preservation as single-quoted strings.
  • An explicit ANSI_QUOTES setting switches double quotes to identifier semantics.
  • NO_BACKSLASH_ESCAPES now controls quote-boundary scanning as well as replacement, including a preceding string that ends in a literal backslash.
  • Both SQL mode settings are propagated through the execution dialog and preview path, while unsupported database types ignore stale overrides.

Regression coverage includes the two newly reproduced failures, default-off behavior, escaped double quotes, all MySQL-family mode combinations, settings normalization, and execution-dialog propagation.

Validation completed successfully:

  • Targeted tests: 4 files, 156 tests
  • Full frontend suite: 447 files, 3,691 tests
  • pnpm typecheck
  • pnpm lint
  • pnpm build
  • Changed-file formatting and git diff --check
  • GitHub frontend, autofill, and change-detection checks

I also executed the generated forms against MySQL 8.0.46 under the default mode, NO_BACKSLASH_ESCAPES, ANSI_QUOTES, and both modes together. Single- and double-quoted values preserved the expected bytes (C:\new = 433A5C6E6577, literal \n = 5C6E), and ANSI double-quoted identifiers preserved literal backslashes. The temporary container was removed after verification.

This is ready for re-review.

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm requesting changes because quoted interpolation is still unsafe for PostgreSQL.

sqlQuoteUsesBackslashEscapes treats every non-MySQL single-quoted region as backslash-escaped, while inserted backslashes are only protected for MySQL-family databases. With PostgreSQL's default standard_conforming_strings=on, an ordinary string ending in a backslash can be scanned incorrectly and produce invalid SQL. The opposite problem occurs for PostgreSQL escape strings: substituting a value containing \n into E'...' can change the parameter bytes by turning it into a newline.

Please make scanning and substitution aware of the PostgreSQL literal form and relevant session semantics, with regression tests for ordinary strings ending in a backslash and E'...' values containing \n. If a reliable contract cannot be provided for a dialect, quoted interpolation should not be offered for that dialect.

@t8y2 t8y2 added area/desktop Desktop application or Tauri shell enhancement New feature or request ui-change Changes user-visible interface, text, or visual assets labels Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/desktop Desktop application or Tauri shell enhancement New feature or request ui-change Changes user-visible interface, text, or visual assets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 变量无法支持引号内的${}替换的问题

2 participants