fix(row-panel): emit Map['key'] subscript for Map columns in mergePath#2357
fix(row-panel): emit Map['key'] subscript for Map columns in mergePath#2357alex-fedotyev wants to merge 5 commits into
Conversation
mergePath converted numeric path segments to 1-based array subscripts ([N+1]) regardless of whether the parent column was a Map or an Array. On a Map(String, String) column this produced SQL like LogAttributes[2], which ClickHouse rejects with "Illegal types of arguments: Map(String, String), UInt8 for function arrayElement". The grid row expand view failed for any row whose attribute path included a numeric-looking key under a Map column. The function now accepts a mapColumns argument alongside jsonColumns. For Map-typed parents, all sub-keys render as string-key subscripts (Map['1']) regardless of whether the key looks numeric. Array columns keep their existing 1-based array-index behavior. The three call sites (useAutoCompleteOptions, DBRowJsonViewer via DBRowOverviewPanel and DBRowDataPanel, DBSearchPageFilters) now thread Map-column names from the result-set metadata or source schema. A new useMapColumns hook mirrors useJsonColumns. A new getMapColumnNames helper mirrors getJSONColumnNames for callers that work directly off ResponseJSON meta. Fixes HDX-4369.
🦋 Changeset detectedLatest commit: f4a1ac7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
🔵 Tier 2 — Low RiskSmall, isolated change with no API route or data model modifications. Why this tier:
Review process: AI review + quick human skim (target: 5–15 min). Reviewer validates AI assessment and checks for domain-specific concerns. Stats
|
E2E Test Results✅ All tests passed • 191 passed • 3 skipped • 1234s
Tests ran across 4 shards in parallel. |
Deep Review✅ No critical issues found. The Map-column branch in 🟡 P2 -- recommended
🔵 P3 nitpicks (5)
Reviewers (4): correctness, security, kieran-typescript, testing. Testing gaps:
|
Resolve two conflicts:
1. packages/app/src/components/DBRowOverviewPanel.tsx
Compose origin/main's empty-section guard with HEAD's mapColumns
prop. Event Attributes is now wrapped in
{Object.keys(filteredEventAttributes).length > 0 && (...)} (from
origin/main, mirroring the same treatment of Resource Attributes
that auto-merged) and the DBRowJsonViewer receives
mapColumns={mapColumns} (from HEAD, the HDX-4369 fix). Both
changes are additive and do not interact.
2. packages/app/src/__tests__/utils.test.ts
Keep both describe blocks. HEAD added describe('mergePath', ...)
for the HDX-4369 Map-subscript test cases; origin/main added
describe('getColorFromCSSToken', ...) for the chart-token tests.
Place mergePath first (own PR's contribution), then the color
helper block.
Regression: packages/app/src/components/__tests__/DBRowDataPanel.test.ts
gains a getMapColumnNames suite. The helper is new on HEAD; without
the resolution composing it through, no test would exercise it
against the merged base. The new cases cover bare-Map and Map(K, V)
matching, undefined-meta fallback, and the no-classify-as-Map
guarantee for JSON columns.
The merge-into-main resolution at cf88054 introduced the import as three lines; prettier wants one. CI lint was failing on this single formatting issue.
Thread mapColumns into toClickHouseKeyExpression so dot-form filterState keys with numeric-looking sub-keys (e.g. LogAttributes.1) render as LogAttributes['1'] on the Load-more SQL path. parseMapFieldName already guarantees the base column is a Map, so the third mergePath argument is just the parsed baseName. Escape backslash and single-quote inside Map and string-key subscripts in mergePath. Keys carry user-controlled text; an unescaped quote produces malformed SQL like Map['it's']. Switch the Map-column derivation in useAutoCompleteOptions from raw type-string matching to the canonical jsType === JSDataType.Map check. convertCHDataTypeToJSType unwraps LowCardinality(...) and Nullable(...) wrappers before classifying, so the new check covers wrapped Map types that the raw pattern was missing. Extract deriveMapColumnsFromFields as an exported helper so a regression here trips a unit test. Annotate the mergePath call in buildJSONExtractQuery with an invariant comment: the helper only runs under a parsed-JSON root, so mapColumns is intentionally omitted. Future callers that point it at a Map column will need to thread mapColumns through. Tests: - New mergePath SQL-escaping cases (single quote, backslash, both). - New toClickHouseKeyExpression cases for numeric-looking Map sub-keys. - New deriveMapColumnsFromFields cases covering wrapped Map types, nested fields, non-Map columns, and empty input.
|
Pushed Same-bug-class sibling in SQL escaping in
Deferred: the three Verification:
|
The deep-review pass on the previous revision flagged a residual leak of HDX-4369 through the JSON-extract path. When a Map column has a sub-value that is itself a JSON-parseable string, HyperJson promotes the child context to `isInParsedJson=true` with parsedJsonRootPath=[MapCol, key]. The inner mergePath call in buildJSONExtractQuery did not receive mapColumns, so a numeric-looking sub-key fell through the default branch and emitted Map[N+1] array syntax. ClickHouse rejects that with the same "Illegal types of arguments: Map(String, String), UInt8 for function arrayElement" the row-panel "expand" path was already failing on. Thread mapColumns through buildJSONExtractQuery so the four line actions in the parsed-JSON arm (Add to Filters, Search, Chart, Toggle Column) render Map['1'] instead of Map[2]. The four call sites already had mapColumns in scope via the DBRowJsonViewer prop and the getLineActions dep array. Rewrote the comment on buildJSONExtractQuery to describe what parsedJsonRootPath[0] actually is (JSON column OR Map column with a parsed-JSON child) instead of asserting an invariant the gate does not enforce. Tests in DBRowJsonViewer.test.tsx: - emits Map['1'] for Map column with numeric sub-key holding parsed JSON - emits Map['42'] for deeply nested Map column with numeric sub-key - keeps non-numeric Map sub-key unchanged when mapColumns is threaded - falls back to array index when mapColumns is empty (pins the pre-HDX-4369 default for the non-Map case, e.g. an Array(JSON))
|
Pushed 1. Closed the deep-review P2 on
Four new tests in 2. PR body now carries the production-impact context. I traced HDX-4369 back to its production session data; the patterns match a sidebar-attribute click on the search view and a TraceId-filtered search with a Map-bracket field in the selected columns. Both stacks resolve to the ClickHouse query client in the chunked Verification on this revision:
|
Summary
mergePathinpackages/app/src/utils.tsconverted numeric path segments to 1-based array subscripts ([N+1]) regardless of whether the parent column was a Map or an Array. On aMap(String, String)column this produced SQL likeLogAttributes[2], which ClickHouse rejects withIllegal types of arguments: Map(String, String), UInt8 for function arrayElement. The grid row "expand" view failed for any row whose attribute path included a numeric-looking key under a Map column.The function now accepts a
mapColumnsargument alongsidejsonColumns. For Map-typed parents, all sub-keys render as string-key subscripts (Map['1']) regardless of whether the key looks numeric. Array columns keep their existing 1-based array-index behavior.Confirmed impact
I traced the original report back to production session data. Two sessions in the 10-day window covered by HDX-4369 hit the exact ClickHouse error above. Both stack traces resolve to the ClickHouse query client in the chunked
_app-*.jsbundle, consistent with the bug class: an autocomplete / sidebar interaction on the search page produces a query that subscripts a Map column with a numeric token.Reproduction patterns observed:
whereclause set, user clicks an attribute in the filter sidebar; autocomplete fetch fires and ClickHouse rejects with thearrayElementtype error.TraceId = '...'with a Map-bracket field in the selected columns; same error.Low absolute frequency in the sampled window, but the failure class matches a broader pattern of user-reported "grid expand keeps failing" symptoms, so the cheap fix is worth shipping. Full per-session detail (timestamps, user identities, session IDs, source IDs, exact URLs) is on the Linear ticket.
Adjacent failures in the same RUM window look superficially similar but are NOT addressed by this PR: two cases of subscripting a String column (e.g.
SomeStringCol['...']) and one case of subscripting a JSON column. Different bug class, different fix; flagging for context.What this PR changes
mergePathnow accepts a thirdmapColumnsargument. The three direct call sites thread it through:useAutoCompleteOptionsderivesmapColumnsfrom the field list via the newderiveMapColumnsFromFieldshelper, matching on the canonicalJSDataType.Mapso wrapped Map types (LowCardinality(Map(...)),Nullable(Map(...))) are detected too.DBRowJsonVieweraccepts an optionalmapColumnsprop, threaded byDBRowDataPanelandDBRowOverviewPanelfrom the result-set metadata via a newgetMapColumnNameshelper.DBSearchPageFiltersuses a newuseMapColumnshook (mirrorsuseJsonColumns).The same-bug-class sibling in
DBSearchPageFilters/utils.ts(toClickHouseKeyExpression, the "Load more" SQL path) is fixed in this PR by threading the parsed base name asmapColumnsintomergePath.parseMapFieldNamealready proves the base column is a Map.mergePath's Map and string-key branches now escape backslash and single-quote before interpolating into single-quoted SQL strings, so a Map sub-key likeit'sproducesMap['it\'s']instead of malformed SQL.The deep-review pass on the previous revision flagged a residual leak through
buildJSONExtractQuery. When a Map column sub-value is itself a JSON-parseable string,HyperJsonpromotes the child context toisInParsedJson=truewithparsedJsonRootPath=[MapCol, key]. The innermergePathcall inbuildJSONExtractQuerydid not receivemapColumns, so a numeric sub-key fell through the default branch and re-introducedMap[N+1]. The latest commit threadsmapColumnsthroughbuildJSONExtractQueryand its four call sites inDBRowJsonViewer(Add to Filters, Search, Chart, Toggle Column).Explained test case for the reviewer
The shortest path from "this PR's claim" to "this PR's fix" is one test in
packages/app/src/__tests__/utils.test.ts:On
mainthis test fails withLogAttributes[2]. That's the exact subscript that produced the ClickHouse error in the production sessions. After this PR the test passes becausemergePathconsultsmapColumns(the third argument) and renders the string-key subscript instead of the array-index subscript.The same shape applies to the other layers:
Each one is a regression test for a specific surface where a Map column meets a numeric-looking sub-key. If the threading drops at any call site, the corresponding test trips.
Test plan
packages/app/src/__tests__/utils.test.tscovering:mergePath(['LogAttributes', '1'], [], ['LogAttributes'])returnsLogAttributes['1'], notLogAttributes[2]packages/app/src/components/DBSearchPageFilters/utils.test.tscovering:LogAttributes.1->LogAttributes['1']packages/app/src/hooks/__tests__/useAutoCompleteOptions.test.tsxcoveringderiveMapColumnsFromFields:jsType(LowCardinality(Map(...)),Nullable(Map(...)))packages/app/src/components/DBRowJsonViewer.test.tsxcoveringbuildJSONExtractQuerywithmapColumns:JSONExtractString(LogAttributes['1'], 'foo')mapColumnsfalls back to array-index (regression, covers Array(JSON))yarn jestfor affected files (208 passed)make ci-lintcleanyarn tsc --noEmitcleanDeferred follow-up
packages/api/src/controllers/ai.tshas threemergePathcalls with the same bug class on the AI Summary / AI Assistant backend path. Fixing it crosses into a second package and would push this PR to Tier 3, so I split it out: #2367 (tracked as HDX-4402).Fixes HDX-4369.