Skip to content

fix(datagrid): quote filter values by the column's declared type - #2029

Merged
datlechin merged 1 commit into
mainfrom
fix/filter-column-type-quoting
Aug 6, 2026
Merged

fix(datagrid): quote filter values by the column's declared type#2029
datlechin merged 1 commit into
mainfrom
fix/filter-column-type-quoting

Conversation

@datlechin

Copy link
Copy Markdown
Member

Filtering a varchar column code with = equals and the value 68 produced:

SELECT * FROM `customers` WHERE `code` = 68 LIMIT 100 OFFSET 0;

It should produce `code` = '68'. Typing 68a was already correct.

Root cause

FilterSQLGenerator.escapeValue() picked the SQL literal type from the shape of the typed string, never from the column:

if Int(trimmed) != nil || Double(trimmed) != nil { return trimmed }

On MySQL varchar_col = 68 coerces the column to a number on every row, so it matches the wrong rows ('68abc', ' 68', '0068') and the index goes unused. On ClickHouse and Trino it is a hard error.

The same function also promoted a typed NULL to the SQL keyword and TRUE/FALSE to booleans with no type gate, which made the literal text "NULL" in a text column impossible to filter for.

The rule

Quoting now follows the column's type category, with the value's shape used only as a secondary check inside an already-numeric column. This is the pattern InClauseConverter already used correctly, now extracted so both share one definition.

Column type 68 NULL TRUE
text, enum, set '68' 'NULL' 'TRUE'
integer, decimal 68 IS NULL 'TRUE'
boolean '68' IS NULL dialect literal
date, json, blob, spatial '68' IS NULL 'TRUE'
unknown 68 IS NULL dialect literal

Always quoting was rejected: Trino and BigQuery reject int_col = '68', and ClickHouse silently drops the atom from index pruning on an out of range numeric string. The unknown case keeps today's heuristic for the same reason.

Quoting enum columns matters on its own: MySQL and ClickHouse compare enums by ordinal, so status = 1 selected the first member rather than the member named "1".

Scope

The type data was already in TableRows.columnTypes, index aligned with columns and in scope at every call site. It just was not forwarded. FilterSQLGenerator gained defaulted columns:/columnTypes: parameters, so existing construction sites compile unchanged.

MSSQL, Oracle, BigQuery and SurrealDB build their own filter queries and each reimplemented the same bug. They now go through a shared renderer in PluginKit.

TableFilter gains no field, so nothing new is persisted and saved filters keep working.

PluginKit ABI

scripts/check-pluginkit-abi.sh origin/main reports additions only, no removed symbols, so no currentPluginKitVersion bump. Needs the abi-additive label. The four registry plugins need a re-release to pick up their side of the fix; until then they keep the old behavior rather than failing to load.

Also changed

  • IN/NOT IN stripped the token NULL from every list regardless of column type, so code IN ('68a', 'NULL') on a text column lost the real string. Fixed by the same per element rendering.
  • IS EMPTY emitted (col IS NULL OR col = '') for every type. The = '' half is a type mismatch error on PostgreSQL, ClickHouse, MSSQL, Oracle and Trino, so a non text column now emits only col IS NULL. This changes the rows returned by a saved IS EMPTY filter on a non text column, where the previous result was already wrong or erroring.
  • A decimal column used Double(value) != nil, which accepts 0x1F, nan and infinity. It now uses PluginKit's existing strict PluginNumericLiteral.isValid.

Not covered

Foreign key navigation builds its filter before the target table's rows load, so no column types exist yet and it falls back to the heuristic. The iOS copy of the generator is unchanged; it has no column type pipeline to thread.

Tests

FilterSQLGeneratorColumnTypeTests covers the reported case first, plus a guard that a genuinely numeric column keeps an unquoted literal, the enum ordinal case, NULL/TRUE gating, the unknown type fallback, duplicate and missing column names, and the IN/BETWEEN element paths. ColumnTypeSQLQuotingTests pins the literal shapes. One existing test title claimed a general rule that no longer holds and was renamed; its assertion is unchanged because it passes no column type.

https://claude.ai/code/session_01PaouzGXduVq1dr5SBCgVH8

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin added the abi-additive PluginKit ABI diff reviewed as additive; no version bump needed label Aug 6, 2026
@datlechin
datlechin force-pushed the fix/filter-column-type-quoting branch from 58316fe to e7c9a48 Compare August 6, 2026 16:30
@datlechin
datlechin merged commit 74a1f54 into main Aug 6, 2026
1 of 3 checks passed
@datlechin
datlechin deleted the fix/filter-column-type-quoting branch August 6, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

abi-additive PluginKit ABI diff reviewed as additive; no version bump needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant