Skip to content

fix(core): an un-aliased dotted column carries its value in core's rows - #425

Merged
fupelaqu merged 2 commits into
mainfrom
feature/SCHEMA-12
Oct 17, 2026
Merged

fupelaqu merged 2 commits into
mainfrom
feature/SCHEMA-12

Conversation

@fupelaqu

Copy link
Copy Markdown
Contributor

Summary

SELECT obj.a FROM t now returns the value of obj.a under the name obj.a on every row core builds, as SELECT obj.a AS k FROM t already did under k. The parent object obj is no longer added as an extra column once its fields have been read.

One implementation in core: the row reader for a projection's un-aliased dotted columns is planned once per stream (or per response) from the projection, and every row route goes through it — plain search (sync and async), scroll / PIT / search_after (sequential, sliced, sorted), _msearch, UNION ALL legs, and the WHERE … IN (SELECT …) subquery. Statements without an un-aliased dotted column keep main's row function itself (a unit test checks the reference identity), so they do no new work per row.

Closes #424

Behaviour changes (release notes)

  1. An un-aliased dotted column carries its value ({obj.a = 4} where main answered {obj.a = null, obj = {a = 4}}). A document holding a flat "obj.a": null beside the nested obj.a reads the nested value, as Elasticsearch does in its doc values. A ranking window orders by that value, and a window partitioned by such a column finds its partition.
  2. The parent object (obj) no longer appears as an extra column through JDBC, the REPL and arrow plain queries, once its fields have been read. A row still carries it when the SELECT list names it, contains *, reads another field of it under an alias, or when it holds values the dotted name cannot read on its own (an array of objects, a flat "obj.a" key with a value).
  3. n IN (SELECT obj.a FROM t LIMIT 100) reads every document's value.

Unchanged: aliased columns, plain fields, computed columns, aggregations and typed-entity reads (searchAs) return main's rows byte for byte; INSERT … SELECT writes main's documents (so materialized-view changelogs are unchanged); no type change; no case-class arity change (no downstream rebuild).

Evidence

  • Unit: core/test 1,259 / 1,259. NestedPathColumnsSpec 15 / 15, which failed 8 / 8 on main's code (every route answered null and leaked obj).
  • Elasticsearch 8.18.3: JavaClientDottedColumnSpec 11 / 11 (with consumer smokes on arrow's, the JDBC driver's and extensions' read paths), JavaClient8ReplGatewayIntegrationSpec 79 / 79, JavaClientInsertByQuerySpec 8 / 8.
  • Main vs branch population on Elasticsearch 8.18.3 (every route × changed shapes × control shapes × document shapes): a cell-level checker derives each expected branch row from main's row: 0 failures, and it catches 7 / 7 planted mutations. No control, aggregation, INSERT or searchAs line moved.
  • Cost (NestedPathCostProbe, 31 × 1,000, main and branch interleaved twice): warm means s1 −1.2 %, s2 +0.7 %, s3 +0.3 %, within run-to-run noise.
  • + compile (2.12 and 2.13) green; the CI lint line passes on a clone of the committed bytes. Elasticsearch 6.8, 7.17 and 9.0 run in CI.

Recorded, not fixed (pre-existing or out of scope)

  • The aliased form obj.a AS k still answers NULL for a document holding a flat "obj.a": null beside the nested value (its lookup is unchanged).
  • Aliased forms keep the leaked parent object, as before.
  • UNNEST reads a deeper inner path (c.addr.city) as NULL; top_hits-based aggregates (ARRAY_AGG, FIRST_VALUE over obj.a) miss nested-shaped values.
  • An alias whose source is also a segment of a dotted column (SELECT id, a AS z, obj.a) leaves obj.a NULL, as on main.
  • A window partitioned by obj.a when obj.a is not selected reads base rows that never requested it; the window fallback without fields builds rows without a projection.
  • Beside a computed column, whether the parent object stays depends on how the expression is written (profile.followers * 2 keeps it, t.profile.followers * 2 drops it); no value is lost either way.
  • Only the first 64 parent objects of a row can be dropped; later ones are kept.

After merge

Core 0.24.0-SNAPSHOT republish: softclient4es-arrow's set-operation work (#198) waits for it.

🤖 Generated with Claude Code

fupelaqu and others added 2 commits October 16, 2026 19:06
…ws (SCHEMA-12)

Story SCHEMA-12. Reviewed: r0:clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…(SCHEMA-12)

Review follow-up, text only: the user-docs bullet states when the parent object is still returned, and three comments drop a premise the story's measurements disproved (an INSERT ... SELECT never writes a null cell).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fupelaqu
fupelaqu marked this pull request as ready for review October 17, 2026 06:13
@fupelaqu
fupelaqu merged commit 886aa2d into main Oct 17, 2026
4 checks passed
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.

An un-aliased dotted column (SELECT obj.a FROM t) answers NULL, plus an extra parent column

1 participant