Skip to content

CAST/CONVERT target-type surface: SIGNED/UNSIGNED (Tableau MySQL dialect), DECIMAL(p,s), CHAR(n), TEXT, CONVERT USING all rejected #275

Description

@fupelaqu

Found 2026-09-04 while specifying Epic 21 (Core SQL Front Door), by running the real app.softnetwork.elastic.sql.parser.Parser compiled from main (ac54a079).

Measured — all reject

REJECT  string matching regex (?i)(binary|var…   | SELECT CAST(amount AS SIGNED) FROM t
REJECT  string matching regex (?i)(binary|var…   | SELECT CAST(amount AS UNSIGNED) FROM t
REJECT  string matching regex (?i)(binary|var…   | SELECT CAST(amount AS NUMERIC) FROM t
REJECT  string matching regex (?i)(binary|var…   | SELECT CAST(name AS TEXT) FROM t
REJECT  ) expected but ( found                   | SELECT CAST(amount AS DECIMAL(10,2)) FROM t
REJECT  ) expected but ( found                   | SELECT CAST(name AS CHAR(10)) FROM t
REJECT  end of input expected                    | SELECT CONVERT(name USING utf8) FROM t

The failing production is the target-type regex, not the operand.

Why it matters

CAST(x AS SIGNED) / AS UNSIGNED is what Tableau's MySQL dialect generates — the dialect Epic 19 captured 89 of its 94 corpus rejections from. Parameterised types (DECIMAL(p,s), CHAR(n)) and the alias names (NUMERIC, TEXT) are standard across MySQL / PostgreSQL / SQL-92 generators.

Note — this supersedes a false filing

#267 claimed the operand surface rejected bare literals (CAST('125' AS BIGINT)). That is false and was refuted by measurement — all four spellings accept literals today. The operand surface was never the gap; this one is. #267 is being closed as not-reproducible; this issue is not a re-purposing of that number.

Scope

The conversion target-type production in sql/src/main/scala/app/softnetwork/elastic/sql/parser/. Each accepted type needs an explicit ES-type mapping decision — an alias that silently resolves to the wrong numeric type, or a parameterised precision that looks honoured but is ignored, is a #205-family silent wrong answer. Assert the emitted Painless directly, never via .sql alone.

⚠️ Do not widen the operand productions while in here: identifierWithValue already reaches them via identifierWithIntervalFunction, and inserting an alternative before it breaks CAST('2025-01-01' + INTERVAL 1 DAY AS DATE) (measured) — the alternation commits to the first succeeding alternative and a later sequence failure never backtracks.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions