Skip to content

[absorbed into #630 Phase 5] extract ClickHouse SQL quoting and type-expression grammar #635

Description

@BorisTyshkevich

Part of #630.

Depends on: #634

Goal

Move the reusable pure ClickHouse language helpers already maintained by SQL Browser into @altinity/clickhouse-http:

  1. SQL string-literal and identifier quoting;
  2. generic ClickHouse type-expression AST parsing, wrapper analysis, canonicalization, and enum inspection.

This is a pure-code ownership move. Do not move SQL Browser formatting, parameter-control UX, result-view policy, or product-specific SQL helpers.

SQL quoting API

Move the behavior currently implemented by src/core/format.ts's ClickHouse-specific quoting helpers into the package and expose public equivalents:

export function quoteStringLiteral(value: unknown): string;
export function quoteIdentifier(name: unknown): string;
export function qualifyIdentifier(...parts: unknown[]): string;

Required behavior is the current SQL Browser behavior:

String literals

  • convert input with String(...);
  • escape \ first using ClickHouse backslash escaping;
  • escape ' so the result cannot break out of the string literal;
  • wrap in single quotes;
  • preserve Unicode and empty strings.

Identifiers

  • bare identifier regex remains [A-Za-z_][A-Za-z0-9_]*;
  • bare names remain unquoted for readability;
  • every other name is backtick-quoted;
  • \ and backticks are escaped using the current ClickHouse identifier rules;
  • dots inside one identifier part are data and must not be treated as qualification separators.

Qualification

  • take already-separated identifier parts;
  • drop nullish/empty parts exactly as today;
  • quote each retained part independently;
  • join with ..

Reconcile #634's private query-id quoting so killQuery() uses the new public string-literal helper. There must be one quoting implementation after this PR.

SQL Browser may keep temporary named re-exports (sqlString, quoteIdent, qualifyIdent) from src/core/format.ts for compatibility, but those names must delegate to package functions rather than retain code.

Do not move unrelated src/core/format.ts behavior such as row/byte display formatting, detectSqlFormat, prepareExportSql, withTrailingFormat, schema-mutating classification, query naming, or editor-specific helpers.

ClickHouse type-expression package API

Move the generic AST/parser implementation currently in src/core/clickhouse-type.ts into the package.

The package must own the canonical types representing:

  • type nodes;
  • literal string/number arguments;
  • Tuple members;
  • Enum members;
  • wrapper/modifier analysis.

Expose the current generic capabilities, at minimum:

parseClickHouseType(input): TypeNode | null
analyzeTypeModifiers(node): TypeModifiers
canonicalType(input): string
enumMembers(node): EnumMember[] | null
unwrapNullable(node)
unwrapLowCardinality(node)
unwrapValueTransparentWrappers(node)

If the current module exports additional generic predicates used by multiple consumers, move them only when their semantics describe ClickHouse types rather than a SQL Browser UI/product decision. Keep the package public surface minimal and document every moved export.

Grammar/behavior to preserve

The move must preserve the existing parser's tested semantics, including:

  • nested type expressions;
  • whitespace handling;
  • argument lists;
  • numeric literal arguments;
  • quoted string literal arguments;
  • backtick/double-quote/single-quote token handling where currently supported;
  • ClickHouse backslash and doubled-delimiter decoding;
  • $tag$...$tag$ heredoc handling already supported by the parser;
  • Array;
  • Nullable;
  • LowCardinality;
  • Tuple, including named and positional members and the current mixed-member rejection;
  • Enum8 / Enum16, explicit and implicit codes;
  • nested wrapper analysis and wrapper-order validity;
  • the current LowCardinality(Enum*) invalidity rule;
  • malformed/unbalanced input returning null or the current fail-closed result rather than throwing;
  • canonicalization preserving quoted-content meaning while normalizing syntax as currently tested.

Do not broaden the grammar speculatively in this issue. The extraction contract is current behavior plus current tests.

SQL Browser integration

Repoint existing consumers to package exports while preserving their own policy layers:

  • src/core/param-type.ts continues to own parameter compatibility/policy projection, but consumes the package AST/parser;
  • KPI, dashboard-variable, and other current type consumers use the same package parser rather than a compatibility copy;
  • src/core/clickhouse-type.ts may be a short compatibility re-export during the PR if needed to keep migration atomic, but the final state of this unit must have no independent parser implementation under SQL Browser source;
  • SQL Browser-specific functions such as multiSelectElementType, field/control decisions, recent-value behavior, or feature-specific supported-type policy stay in SQL Browser unless they are already demonstrably generic ClickHouse type semantics.

The package must not import sql-spans.ts, quoted-span.ts, or other SQL Browser files. If the type parser depends on generic lexical helpers currently under src/core, move the smallest reusable lexical primitives it needs into the package in this PR and repoint SQL Browser consumers to that single implementation. Do not duplicate the scanner to satisfy the boundary.

Tests

SQL quoting

Port/retain tests for:

  • empty string;
  • ';
  • \;
  • trailing backslash;
  • backslash + quote combinations;
  • Unicode;
  • bare identifiers;
  • identifiers with spaces, dots, dashes, backticks, and backslashes;
  • qualification with empty/nullish parts;
  • killQuery() using the same quoting implementation.

Type parser

The complete existing clickhouse-type test corpus must remain green after migration and should execute against package exports directly.

Add package-boundary tests proving no SQL Browser source import is required.

Run affected param-type, KPI, variable-option, serializer, and any other type-consumer tests to catch a projection drift.

Run the full repository gate:

npm run check:types
npm run check:arch
npm run check:schemas
npm run check:examples
npm test
npm run build

No browser e2e is required solely for this pure move unless the implementation changes package/workspace runtime wiring in a way not already covered by #632.

Acceptance criteria

  • ClickHouse SQL string quoting has one implementation in @altinity/clickhouse-http.
  • ClickHouse identifier quoting/qualification has one package implementation.
  • killQuery() uses the shared public quoting helper.
  • Generic ClickHouse type AST/parser/canonicalization has one package implementation.
  • Existing parser behavior and test corpus are preserved.
  • SQL Browser param-type and UI/product policy remain SQL Browser-owned but use package type primitives.
  • Any lexical helper needed by the parser is single-owned rather than copied across the package boundary.
  • src/core/format.ts retains only SQL Browser formatting/policy plus compatibility delegates where temporarily necessary.
  • src/core/clickhouse-type.ts contains no independent parser implementation at completion of this unit.
  • Package remains dependency-free at runtime and imports no SQL Browser source.
  • Full repository gate passes.

Non-goals

  • Moving detectSqlFormat, stripTrailingTrivia, withTrailingFormat, prepareExportSql, query-name inference, display formatting, or schema-mutating classification.
  • Moving parameter field/control policy or Dashboard variable UX.
  • Adding a SQL parser, formatter, query builder, or AST for full SQL statements.
  • Expanding type grammar beyond behavior already required/tested by SQL Browser.
  • Changing persisted parameter/type representations.
  • Rewriting authentication/application services.

Agent execution notes

Before planning, read #630#634, src/core/format.ts, src/core/clickhouse-type.ts, src/core/param-type.ts, the parser/format tests, and every import of the helpers being moved. This is a subtractive unit: move implementation and tests to the package, repoint all real consumers, then delete the former implementation. Do not leave two scanners/parsers/quoters behind a compatibility facade.

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorRestructuring without user-facing behavior changetech-debt

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions