Skip to content

fix(datagrid): type filter values from the table's columns before its first rows load - #2737

Merged
datlechin merged 1 commit into
mainfrom
fix/filter-literal-types-before-first-load
Sep 11, 2026
Merged

fix(datagrid): type filter values from the table's columns before its first rows load#2737
datlechin merged 1 commit into
mainfrom
fix/filter-literal-types-before-first-load

Conversation

@datlechin

Copy link
Copy Markdown
Member

A filter value on a text column went out as a number when the table's first page had not loaded yet. With code = 0123, a table that opened with that filter applied sent WHERE code = 0123 instead of WHERE code = '0123'. PostgreSQL rejects that, and MySQL compares numerically, so '00123' matches as well. A foreign key jump into the current tab did the same. The Cloudflare R2 SQL review in #2030 found this, because R2 SQL has no implicit conversions and fails the query outright.

Cause

Filter SQL took its column types only from the tab's loaded rows. Before any rows arrived, FilterSQLGenerator had no types and guessed from the value's text. The schema cache (SchemaColumnStore) held column names and primary keys but no types. The first-load step fetched the schema only for a default sort, hidden columns or a restored page, never for filters. The foreign key jump into the current tab skipped the first-load step altogether and built its SQL from the empty row buffer.

Fix

  • The schema cache carries types. SchemaColumnStore.Entry is now a struct that also holds each column's ColumnType. The type comes from ColumnTypeClassifier, the same classifier the result path, MCP browse_table and the foreign key lookup use.
  • One answer to "which columns, with which types". MainContentCoordinator.queryColumns(for:) returns the loaded rows' columns and types once rows exist, and the schema's before that.
    • The generator pairs names and types by position. So if any column name is missing from the schema, it returns no types at all rather than a shorter list that would shift every later type onto the wrong column.
    • Filter apply, query rebuild, the filter preview, the exact count and the phase-2 count all go through it.
  • First load waits for the schema when filters are applied, then rebuilds the query from it.
  • Foreign key jump into the current tab now takes the same path as every other retarget: clear the rows, rebuild, and load through first-load. It no longer builds and runs its own untyped SQL.
  • ColumnTypeClassifier strips trailing UNSIGNED, SIGNED and ZEROFILL. MySQL's catalog spells a column INT UNSIGNED or INT(10) UNSIGNED ZEROFILL, while its result metadata says INT. Without this, the catalog spelling fell through to text, and this fix would have started quoting values on common unsigned id columns on first load.

Behaviour changes

  • A table that opens with applied filters makes one metadata fetch before its first query. The result is cached per table per window. Before, that only happened for a default sort, hidden columns or a restored page.
  • A foreign key jump into the current tab now applies the default sort and hidden-column scoping, the same as a foreign key target opened in a new tab.

Verification

  • New suite FilterTypingBeforeFirstLoadTests:
    • a text column quotes 0123 on first load, and TRUE stays 'TRUE';
    • an integer column keeps 123 unquoted;
    • a schema that cannot be fetched still dispatches the first load;
    • applied filters make the first load wait for the schema;
    • the filter preview is typed before rows arrive;
    • loaded rows win over the schema.
  • FKNavigationTests: a jump into the current tab types 0123 from the target table's schema.
  • SchemaColumnStoreTests: entries built from fetched columns, aligned types, and a column name the table does not have.
  • ColumnTypeClassifierTests: INT UNSIGNED and the other trailing attributes.
  • 15 suites, 404 of 404 cases pass.
  • With the schema-type fallback and the attribute stripping reverted, 7 of the new cases fail as expected.
  • swiftlint --strict on the 16 changed paths: 0 violations. That includes one import-order finding that was already in ColumnTypeClassifierTests.

Still untyped: a first load with an empty buffer and no schema. That only happens when an unfiltered first load failed, the metadata fetch failed, or the engine's fetchColumns returns nothing. The value is then guessed from its text, as before.

No UI automation: the defect is in the SQL the first load sends, which the unit suites check directly, and a UI test would need a server-backed table with a text column holding numeric-looking values.

https://claude.ai/code/session_01JKFSBk6YwDemnkbQnyc2xz

@datlechin
datlechin merged commit 103ff05 into main Sep 11, 2026
8 checks passed
@datlechin
datlechin deleted the fix/filter-literal-types-before-first-load branch September 11, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant