fix(datagrid): filters on PostgreSQL non-text columns and MongoDB collections named like db methods - #2625
Merged
datlechin merged 1 commit intoSep 3, 2026
Conversation
…lections named like db methods Claude-Session: https://claude.ai/code/session_019aSCJ4CVmua6eqveqtSRYx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four defects in the way the filter panel and the browse query are built for PostgreSQL and MongoDB, found while auditing both paths side by side.
PostgreSQL: pattern filters on a non-text column fail on the server
Root cause.
FilterSQLGeneratoremits"col" ILIKE '%x%',"col" ~ 'x'andLOWER("col")on whatever column the row names. MySQL and SQLite coerce the operand; PostgreSQL does not, and answersoperator does not exist: uuid ~~* unknown(likewise forinteger,timestamptz, an enum,jsonb,text[]).ColumnTypeClassifierfilesuuidand every unknown type under.text, so the generator could not even tell a uuid from a varchar, andallowsCaseFoldinghappily wrapped it inLOWER(). A Contains filter on a uuid primary key, the default operator with its default ignore-case setting, hits this every time.Fix.
SQLDialectDescriptorgainstextCastTypeName, nil by default. A dialect that names one (PostgreSQL setsTEXT) has every column that is not character data cast withCAST(col AS TEXT)beforeLIKE,ILIKE, a regex, a case fold, and the= ''half of Is empty. A plain=keeps the column's own type and its index.ColumnTypeSQLQuoting.isCharacterTypereads the raw type name behind a.textcolumn, which is the only placeuuidandvarcharstill differ. Cockroach, Redshift and PGlite inherit the descriptor.The field is added through a new
SQLDialectDescriptorinitializer overload; the previous full initializer keeps its signature and is now@_disfavoredOverload, so an already-built plugin keeps loading.withCaseSensitivityStylecarries the field, which is how the Redshift variant gets it.PostgreSQL:
.arraywas treated as textColumnTypeSQLQuoting.isKnownTextLikelisted.array, so Is empty produced"tags" = ''(malformed array literal) and ignore-case producedLOWER("tags"). Arrays are now outside the text-like set: Is empty isIS NULL, and Contains searches the array's text form through the cast above.MongoDB: a collection named like a
dbmethod cannot be openedRoot cause.
MongoDBQueryBuilderandMongoDBStatementGeneratorspelled the collection asdb.statsordb["stats"]. Both go through theDBproxy inMongoScriptPrelude, whosegettrap returns a prototype member before it falls back to a collection, sostats,version,getName,toString,constructorand the rest come back as functions and.find()throwsdb.stats.find is not a function. mongosh resolvesdb.statsthe same way, so the MQL export had the same bug in its output.Fix.
MongoCollectionAccessorin PluginKit is the one place the accessor is spelled:db.<name>for a plain identifier that nodbmethod shadows,db.getCollection("<name>")for everything else. The query builder, the statement generator and the MQL export use it.QuerySqlParser.extractTableNamelearns thegetCollectionform so the edited grid still resolves its collection. A test scansMongoScriptPrelude.sourcefor everyDB.prototype.<name>and asserts the accessor refuses each, so a method added to the shell later cannot reopen this.MongoDB: a raw filter in shell syntax loses the row count
Root cause. A raw filter row such as
{status: "active", _id: ObjectId("…")}is spliced intofind(...), which evaluates it as JavaScript and works.countDocumentshands the same text tobson_new_from_json, which rejects it, and the coordinator swallows the error, so the count disappears while the rows show.Fix.
MongoDBRawFilterNormalizerevaluates__ejson((<raw>))once in aJSContextloaded with the shell's own prelude, with the host stubbed so only the load-timecurrentDatabasecall is answered. BothbuildFilteredQueryanddocumentCounton the driver build through it, sofindandcountDocumentsreceive the same canonical Extended JSON. Text the shell cannot evaluate is kept as typed, which leavesfindreporting the syntax error as before. The text is JavaScript and JavaScriptCore cannot interrupt it, so the engine runs on its own queue with a 2 second deadline and is abandoned when it misses it, the same shapeMongoScriptRuntimeuses.Tests
FilterSQLGeneratorColumnTypeTests: casting on uuid, integer, enum, timestamp, array, unknown type, a dialect without a cast, Is empty, In list on uuid and on integers.ColumnTypeSQLQuotingTests:isCharacterTypeover Postgres, MySQL, Oracle and ClickHouse spellings; arrays outside the text-like set.SQLDialectDescriptorTests: default and carry-through oftextCastTypeName.MongoDBQueryBuilderTests: shadowed names throughgetCollection, parser round trip, prelude scan, raw filter normalization and its fallback.MongoDBStatementGeneratorTests,MQLExportHelpersTests,QueryExecutorTests: accessor and parser expectations.Verification
All through
.claude/skills/fix-issue/scripts/verify.shon Xcode 27.0 beta (27A5252f), signing disabled locally the way CI does.generate: PASS.build TablePro: PASS.testover the 24 suites that own a changed type (FilterSQLGenerator*, ColumnTypeSQLQuoting, SQLDialectDescriptor, TableQueryBuilderFilter, QueryExecutor, ExtractTableName, MongoShellParser, MongoDB QueryBuilder / NestedFilter / StatementGenerator / WriteBackType, MQLExportHelpers, ClickHouseDialect, PluginMetadataRegistryVariant, ForeignKey*Query, FilterMove, FilterCaseSensitivityPersistence, DocumentStoreCaseSensitivity, SQLRowToStatementConverter, VisibleColumnProjection): PASS, 650 cases executed, 650 passed.build MongoDBDriver(registry-only, so the app scheme never compiles it): PASS.plugins(AllPlugins) stops onDamengDriverfor a missingNative/DamengBridge/lib/libdameng_bridge.a, an environment gap unrelated to this change; every other target compiled.abi main: additive.MongoCollectionAccessoris new,textCastTypeNameis a new field on a non-frozen struct, the previous fullSQLDialectDescriptorinitializer keeps its symbol and the new one is a separate overload. No PluginKit version bump.linton every changed file: the source files are clean. The five findings are onmainalready, in the import order and opening-brace whitespace ofColumnTypeSQLQuotingTestsandFilterSQLGeneratorColumnTypeTests, which.swiftlint.ymlnever runs over.{a: (function(){for(;;){}})()}in a scratch SwiftPM harness:nilafter 2.0 s, and the next document serialized normally. That probe is not committed, because it leaves a spinning thread in the test host.Not in this change
FilterSQLGeneratorinPackages/TableProCore/Sources/TableProQueryhas no column types, so it cannot decide when to cast and keeps the bareILIKE. It needs the grid's types plumbed through first.TEXT, which there isVARCHAR(256). A pattern filter on a value longer than 256 characters is truncated before matching, and aSUPERcolumn still refuses the cast. Both already failed before this change.MongoDBQueryBuilderaccepts a raw filter on{and}alone, so{a:1}), evil(), ({b:2}runs statements in the normalizer. The same text already runs in the shell runtime with a live host bridge on thefindpath, so nothing is widened; the normalizer's context has no host.conditionparameter becomes a raw filter row throughapplyURLFilter, which on MongoDB reaches the shell runtime withoutSQLBoundaryValidator. Pre-existing and unrelated to the four fixes, flagged by the security pass.https://claude.ai/code/session_019aSCJ4CVmua6eqveqtSRYx