Skip to content

fix: preserve explicit Struct casts and evolved field semantics - #24680

Open
sunchao wants to merge 4 commits into
apache:mainfrom
sunchao:dev/chao/codex/oss-struct-cast-semantics
Open

fix: preserve explicit Struct casts and evolved field semantics#24680
sunchao wants to merge 4 commits into
apache:mainfrom
sunchao:dev/chao/codex/oss-struct-cast-semantics

Conversation

@sunchao

@sunchao sunchao commented Aug 25, 2026

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #24679. Follow-up to #24125 and #24530.

Rationale for this change

When Parquet files have different schemas, DataFusion may need to convert a file's Struct column to the table's logical Struct type. If a query only reads s.x, struct-cast narrowing can save work by converting just x instead of every field in s.

That optimization must preserve the meaning of the original expression. The earlier narrowing rule could hide an explicit cast's error, change the result's nullability, or make an entirely null input fail during decimal conversion.

Selecting one field must not hide an explicit cast error

For s = {x: 1, y: 'bad'}, consider this physical expression:

get_field(CAST(s AS STRUCT<x INT, y INT>), 'x')

The explicit cast asks to convert both fields. It must fail because 'bad' cannot become an integer, even though the caller only uses x. Narrowing it to CAST(get_field(s, 'x') AS INT) instead returns 1: the conversion of y, and its error, have disappeared.

This failure is reproduced directly at the physical-expression adapter boundary. The distinction is whether a cast was inserted to reconcile file and table schemas or was already part of the query; the two can have the same shape but different obligations.

An entirely null Struct should not acquire a decimal-conversion error

Consider a Parquet batch with this schema evolution:

File schema:  s: Struct<x: Utf8>
Table schema: s: Struct<x: Decimal128(10, -1)>
Batch values: s is NULL for every row

DataFusion's whole-Struct conversion returns nulls without converting its children. If the rewrite extracts x first, however, it invokes string-to-decimal conversion on an array of null strings. Arrow rejects the negative scale while setting up that conversion, before looking at any values. A query that should return nulls now fails despite there being no non-null value to convert.

The same problem occurs inside a selected container. For example, evolving s.x from List<Utf8> to List<Decimal128(10, -1)> can fail even when every parent Struct is null. Checking only whether x itself is a decimal misses the conversion inside the List.

This case also reproduces through a Parquet scan with filter pushdown: WHERE get_field(s, 'x') IS NULL should select every row in the batch, not fail while preparing a decimal conversion.

There is a related schema-contract problem. A required child x inside a nullable logical parent s still gives a nullable result for s.x. Rebuilding an expression from the child's Field alone can lose that inherited nullability, including through nested parents.

What changes are included in this PR?

The adapter now distinguishes casts it introduces for schema adaptation from casts already present in the query. It can continue narrowing generated conversions where that is safe while preserving explicit Struct casts and their errors. Narrowing also preserves the original logical result Field, including metadata and nullability; matching scalar types alone do not establish an equivalent result. Cast tracking also avoids quadratic lookup work for large expressions.

For the covered decimal changes, the conversion stays inside its Struct ancestors so DataFusion's existing casting code retains control over the all-null shortcut. The decision looks through container value types too, so selecting a List, Map, or Dictionary does not hide a decimal conversion from the check. The cast target keeps only the selected field path, excluding conversions for unselected siblings. Matching types still narrow normally, and existing container-to-Struct conversion paths are preserved. This extends the existing fix without duplicating Arrow's conversion-validation rules or changing the underlying casting implementation.

The Parquet reader must then be able to evaluate the retained expression after schema adaptation. This matters when the query plan has already delegated a predicate to the reader: retaining a cast must not prevent that filter from executing. The runtime allowance is specifically for field access through a Struct-to-Struct cast of a column; the reader preserves that cast and reads the full source Struct. Planning remains conservative about explicit casts, keeping a residual filter for them.

Are these changes tested?

Regression tests cover explicit cast errors, nullability inherited from parents, decimal conversions on entirely null Structs, unselected sibling conversion errors, and execution through Parquet filters. The adapter and retained-cast read-plan regressions fail without the relevant production fixes. The explicit-cast SQL integration test is a control that also passes on the base; it is not presented as another reproduction of the adapter bug.

The container follow-up extends the all-null regression across the List families, Maps, Dictionaries, and nested containers. It also checks matching-type optimization, chained field access, and successful container-to-Struct conversions. The Parquet regression now exercises both scalar and List decimal changes with filter pushdown enabled and disabled.

For the container follow-up in e79e91236, the targeted Rust and SQL tests and all-feature Clippy used the unchanged tracked Cargo.lock with --locked:

  • Formatting, full-workspace Clippy across all targets and features with warnings denied, and the full repository lint script passed.
  • All 42 adapter tests, 249 Parquet datasource tests, and 227 Parquet integration tests passed: 518 Rust tests in total.
  • Eight doctests passed; five existing doctests were ignored.
  • All 21 SQL logic files selected by struct cast parquet passed.

Earlier in this PR, the extended workspace suite (10,794 Rust tests, 8 ignored), all 505 SQL logic files, and 107 CLI tests also passed. Those broader runs predate the container follow-up; the results above are the checks repeated for this update.

Are there any user-facing changes?

The intended changes preserve explicit-cast errors, logical field metadata and nullability, and the covered all-null decimal behavior. Ordinary field pruning remains enabled. Some evolved-decimal filters may read the full Struct to preserve correctness; this PR does not claim a general performance improvement.

There are no public API or dependency changes. General changes to container-to-Struct conversion semantics, generic get_field behavior under null parents, and masking of encoded arrays remain outside this PR's scope.

AI assistance: Codex generated the implementation, regression tests, and PR text, and performed the stated local checks and source reviews. This does not claim a separate human review.

@github-actions github-actions Bot added core Core DataFusion crate datasource Changes to the datasource crate labels Aug 25, 2026
@codecov-commenter

codecov-commenter commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.25210% with 58 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.46%. Comparing base (6e66a85) to head (e79e912).
⚠️ Report is 18 commits behind head on main.

Files with missing lines Patch % Lines
...usion/physical-expr-adapter/src/schema_rewriter.rs 90.55% 12 Missing and 34 partials ⚠️
...ion/datasource-parquet/src/projection_read_plan.rs 77.55% 5 Missing and 6 partials ⚠️
datafusion/datasource-parquet/src/row_filter.rs 98.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #24680      +/-   ##
==========================================
+ Coverage   81.44%   81.46%   +0.01%     
==========================================
  Files        1118     1119       +1     
  Lines      399602   400981    +1379     
  Branches   399602   400981    +1379     
==========================================
+ Hits       325460   326659    +1199     
- Misses      55146    55200      +54     
- Partials    18996    19122     +126     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sunchao
sunchao marked this pull request as ready for review August 26, 2026 06:10
Keep evolved decimal conversions inside their Struct ancestors while
restricting the cast target to the requested field path. This preserves
all-null shortcuts without evaluating unselected sibling conversions.

Index generated casts by pointer while retaining their Arc allocations,
avoiding repeated linear identity scans for large expressions.

Cover flat and nested decimal access and selective Parquet filters with
pushdown enabled and disabled.

@viirya viirya left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem statement here is unusually clear — three distinct failure modes, each with a concrete reproducer, plus an explicit list of what's out of scope. Having the tests labelled by whether they reproduce the bug or merely act as a control on the base is genuinely useful; that distinction usually has to be reverse-engineered by the reviewer.

I walked all three fixes and they hold up:

  • Gating narrowing on "did the adapter generate this cast" is the right axis. A schema-adaptation cast and a user-written cast are structurally identical, so provenance is the only thing that can separate them, and the obligation genuinely differs.
  • Comparing the full return_field rather than the leaf fields, and using logical_return_field as the cast target, fixes inherited nullability and picks up metadata at the same time.
  • Keeping the conversion inside its Struct ancestors for decimal changes, while retain_field_path trims the target to just the selected path, is the neatest part of this. It preserves Arrow's all-null shortcut without re-implementing Arrow's conversion-validation rules in the rewriter — which would have been a maintenance trap.

I also checked the allow_struct_casts asymmetry rather than assuming it: planning passes false so an explicit cast leaves a residual filter, while the post-adaptation runtime path passes true so an already-delegated predicate stays evaluable. The test asserting both directions is the right way to pin that down.

Approving. My only real feedback is about how much of the pointer-identity scheme's safety argument is written down — details inline, neither blocking.

physical_file_schema: SchemaRef,
// Retain generated casts so their pointer identity remains reliable even
// after a wider cast has been removed from the expression tree.
generated_struct_casts: HashMap<*const (), Arc<dyn PhysicalExpr>>,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This map's soundness rests on three things, and only the second is currently stated:

  1. rewrite() uses expr.transform(...), which is bottom-up, so a Column becomes a generated cast before the parent get_field is visited — that is what makes the child Arc the parent sees the same allocation that was recorded.
  2. The map holds an owned Arc clone, so the allocation cannot be freed and its address recycled (the part the current comment covers).
  3. The rewriter is constructed per rewrite() call, so keys never leak across expressions.

Point 1 is the fragile one: switching to transform_down, or hoisting the rewriter to be reused across expressions, would silently stop narrowing. The failure is fail-closed — it would under-optimise rather than mis-narrow — so this isn't a correctness worry, but it is a silent performance regression that no test would catch. Could the comment name the traversal-order dependency explicitly?

Separately, and only if it isn't invasive: was a structural marker considered instead of pointer identity — threading "this cast was generated" out through the rewrite result, or a thin wrapper type around generated casts? That would remove this whole class of concern rather than documenting around it. If you tried it and it spread too far through the rewriter, saying so in the comment would save the next person from re-litigating it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, clarified in 801bb0e. The comment now documents bottom-up transform traversal, a fresh tracker per rewrite(), and retained Arc ownership. It also explains why provenance stays local instead of adding markers to expression types or threading it through rewrite results. I kept the existing implementation; this follow-up only changes comments.

}

/// Retain a field path without changing its ancestors' metadata or nullability.
fn retain_field_path(field: &FieldRef, path: &[&str]) -> Option<FieldRef> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

retain_field_path and the existing resolve_field_path sit next to each other and read as near-synonyms, but they do different jobs: one resolves a key path to a leaf for inspection, the other rebuilds a trimmed cast target that keeps the ancestors intact. The doc comment explains the what ("retain a field path without changing its ancestors' metadata or nullability"); a clause on why it exists — to exclude unselected siblings from a conversion while keeping the Struct ancestors for Arrow's all-null shortcut — would connect it to the decimal case that motivates it. A name closer to that purpose (trim_cast_target_to_path, say) would also help, though renaming is entirely your call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 801bb0e. The doc comment now explains that the helper trims the cast target to exclude unselected sibling conversions while retaining the Struct ancestors, their metadata and nullability, and the all-null shortcut. I kept the helper name unchanged to keep this follow-up limited to documentation.

@adriangb

Copy link
Copy Markdown
Contributor

Thank you @sunchao . I plan to review this later today.

Document the traversal order, per-rewrite lifetime, and retained Arc ownership
required by generated-cast tracking, including why provenance stays local.

Explain how retaining only the selected cast-target path avoids sibling
conversions while preserving the all-null Struct shortcut.
@comphead

Copy link
Copy Markdown
Contributor

I'll check it this afternoon

@comphead

Copy link
Copy Markdown
Contributor

run benchmarks tpch tpcds

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c5430748701-1999-hd5hq 6.12.85+ #1 SMP Sat Jun 27 09:31:30 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing dev/chao/codex/oss-struct-cast-semantics (801bb0e) to 6e66a85 (merge-base) diff

Run configuration
run benchmark tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c5430748701-2000-h77q7 6.12.85+ #1 SMP Sat Jun 27 09:31:30 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing dev/chao/codex/oss-struct-cast-semantics (801bb0e) to 6e66a85 (merge-base) diff

Run configuration
run benchmark tpcds

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing dev/chao/codex/oss-struct-cast-semantics (801bb0e) to 6e66a85 (merge-base) diff

Run configuration
run benchmark tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and dev_chao_codex_oss-struct-cast-semantics
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query     ┃     HEAD ┃ dev_chao_codex_oss-struct-cast-semantics ┃    Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ QQuery 1  │ 39.07 ms │                                 39.37 ms │ no change │
│ QQuery 2  │ 18.83 ms │                                 19.12 ms │ no change │
│ QQuery 3  │ 29.58 ms │                                 29.34 ms │ no change │
│ QQuery 4  │ 17.98 ms │                                 18.07 ms │ no change │
│ QQuery 5  │ 36.79 ms │                                 36.76 ms │ no change │
│ QQuery 6  │ 16.20 ms │                                 16.44 ms │ no change │
│ QQuery 7  │ 40.90 ms │                                 41.52 ms │ no change │
│ QQuery 8  │ 41.74 ms │                                 41.35 ms │ no change │
│ QQuery 9  │ 50.25 ms │                                 49.82 ms │ no change │
│ QQuery 10 │ 43.14 ms │                                 43.23 ms │ no change │
│ QQuery 11 │ 13.99 ms │                                 14.09 ms │ no change │
│ QQuery 12 │ 23.94 ms │                                 24.32 ms │ no change │
│ QQuery 13 │ 41.77 ms │                                 42.16 ms │ no change │
│ QQuery 14 │ 24.92 ms │                                 24.93 ms │ no change │
│ QQuery 15 │ 31.93 ms │                                 31.59 ms │ no change │
│ QQuery 16 │ 14.24 ms │                                 14.20 ms │ no change │
│ QQuery 17 │ 74.32 ms │                                 75.50 ms │ no change │
│ QQuery 18 │ 60.45 ms │                                 60.91 ms │ no change │
│ QQuery 19 │ 33.79 ms │                                 33.33 ms │ no change │
│ QQuery 20 │ 32.33 ms │                                 32.41 ms │ no change │
│ QQuery 21 │ 57.16 ms │                                 57.90 ms │ no change │
│ QQuery 22 │ 14.63 ms │                                 14.73 ms │ no change │
└───────────┴──────────┴──────────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 757.94ms │
│ Total Time (dev_chao_codex_oss-struct-cast-semantics)   │ 761.11ms │
│ Average Time (HEAD)                                     │  34.45ms │
│ Average Time (dev_chao_codex_oss-struct-cast-semantics) │  34.60ms │
│ Queries Faster                                          │        0 │
│ Queries Slower                                          │        0 │
│ Queries with No Change                                  │       22 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and dev_chao_codex_oss-struct-cast-semantics
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Query     ┃                           HEAD ┃ dev_chao_codex_oss-struct-cast-semantics ┃       Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ QQuery 1  │ 39.07 / 40.39 ±1.13 / 42.23 ms │           39.37 / 40.02 ±0.93 / 41.84 ms │    no change │
│ QQuery 2  │ 18.83 / 19.45 ±0.47 / 20.20 ms │           19.12 / 19.44 ±0.26 / 19.78 ms │    no change │
│ QQuery 3  │ 29.58 / 29.79 ±0.18 / 30.05 ms │           29.34 / 30.10 ±1.17 / 32.42 ms │    no change │
│ QQuery 4  │ 17.98 / 18.68 ±0.73 / 20.04 ms │           18.07 / 18.35 ±0.33 / 18.98 ms │    no change │
│ QQuery 5  │ 36.79 / 37.67 ±1.46 / 40.58 ms │           36.76 / 37.26 ±0.71 / 38.65 ms │    no change │
│ QQuery 6  │ 16.20 / 16.86 ±0.91 / 18.66 ms │           16.44 / 16.70 ±0.30 / 17.26 ms │    no change │
│ QQuery 7  │ 40.90 / 43.32 ±2.66 / 48.30 ms │           41.52 / 46.00 ±2.82 / 49.79 ms │ 1.06x slower │
│ QQuery 8  │ 41.74 / 42.13 ±0.25 / 42.48 ms │           41.35 / 42.23 ±1.21 / 44.59 ms │    no change │
│ QQuery 9  │ 50.25 / 51.18 ±0.73 / 52.26 ms │           49.82 / 51.77 ±1.42 / 53.78 ms │    no change │
│ QQuery 10 │ 43.14 / 44.34 ±1.01 / 46.00 ms │           43.23 / 43.80 ±0.38 / 44.40 ms │    no change │
│ QQuery 11 │ 13.99 / 14.16 ±0.14 / 14.38 ms │           14.09 / 14.21 ±0.10 / 14.34 ms │    no change │
│ QQuery 12 │ 23.94 / 24.62 ±0.36 / 25.00 ms │           24.32 / 24.68 ±0.28 / 25.02 ms │    no change │
│ QQuery 13 │ 41.77 / 42.81 ±1.08 / 44.72 ms │           42.16 / 43.33 ±1.75 / 46.82 ms │    no change │
│ QQuery 14 │ 24.92 / 25.07 ±0.15 / 25.28 ms │           24.93 / 25.07 ±0.11 / 25.23 ms │    no change │
│ QQuery 15 │ 31.93 / 32.24 ±0.18 / 32.44 ms │           31.59 / 32.39 ±0.89 / 34.08 ms │    no change │
│ QQuery 16 │ 14.24 / 14.49 ±0.26 / 14.85 ms │           14.20 / 14.46 ±0.24 / 14.89 ms │    no change │
│ QQuery 17 │ 74.32 / 75.87 ±1.30 / 78.04 ms │           75.50 / 75.97 ±0.49 / 76.87 ms │    no change │
│ QQuery 18 │ 60.45 / 62.43 ±1.68 / 64.64 ms │           60.91 / 62.02 ±1.14 / 63.97 ms │    no change │
│ QQuery 19 │ 33.79 / 34.25 ±0.43 / 34.78 ms │           33.33 / 33.76 ±0.33 / 34.19 ms │    no change │
│ QQuery 20 │ 32.33 / 32.82 ±0.30 / 33.26 ms │           32.41 / 32.64 ±0.16 / 32.80 ms │    no change │
│ QQuery 21 │ 57.16 / 57.77 ±0.41 / 58.44 ms │           57.90 / 58.70 ±0.60 / 59.65 ms │    no change │
│ QQuery 22 │ 14.63 / 15.03 ±0.44 / 15.87 ms │           14.73 / 15.03 ±0.49 / 16.01 ms │    no change │
└───────────┴────────────────────────────────┴──────────────────────────────────────────┴──────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 775.38ms │
│ Total Time (dev_chao_codex_oss-struct-cast-semantics)   │ 777.94ms │
│ Average Time (HEAD)                                     │  35.24ms │
│ Average Time (dev_chao_codex_oss-struct-cast-semantics) │  35.36ms │
│ Queries Faster                                          │        0 │
│ Queries Slower                                          │        1 │
│ Queries with No Change                                  │       21 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Resource Usage

tpch — base (merge-base)

Metric Value
Wall time 5.0s
Peak memory 1.2 GiB
Avg memory 510.4 MiB
CPU user 21.8s
CPU sys 1.9s
Peak spill 0 B

tpch — branch

Metric Value
Wall time 5.0s
Peak memory 1.2 GiB
Avg memory 480.4 MiB
CPU user 22.1s
CPU sys 1.7s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing dev/chao/codex/oss-struct-cast-semantics (801bb0e) to 6e66a85 (merge-base) diff

Run configuration
run benchmark tpcds
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and dev_chao_codex_oss-struct-cast-semantics
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ dev_chao_codex_oss-struct-cast-semantics ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │    5.56 ms │                                  5.72 ms │     no change │
│ QQuery 2  │   81.12 ms │                                 82.10 ms │     no change │
│ QQuery 3  │   29.02 ms │                                 29.48 ms │     no change │
│ QQuery 4  │  493.96 ms │                                494.55 ms │     no change │
│ QQuery 5  │   51.83 ms │                                 51.82 ms │     no change │
│ QQuery 6  │   35.72 ms │                                 36.37 ms │     no change │
│ QQuery 7  │   75.00 ms │                                 74.98 ms │     no change │
│ QQuery 8  │   36.66 ms │                                 36.86 ms │     no change │
│ QQuery 9  │   51.02 ms │                                 50.48 ms │     no change │
│ QQuery 10 │   62.76 ms │                                 63.03 ms │     no change │
│ QQuery 11 │  309.17 ms │                                308.92 ms │     no change │
│ QQuery 12 │   28.58 ms │                                 28.95 ms │     no change │
│ QQuery 13 │  117.70 ms │                                118.12 ms │     no change │
│ QQuery 14 │  419.62 ms │                                415.05 ms │     no change │
│ QQuery 15 │   57.07 ms │                                 57.10 ms │     no change │
│ QQuery 16 │    6.80 ms │                                  6.78 ms │     no change │
│ QQuery 17 │   80.06 ms │                                 79.71 ms │     no change │
│ QQuery 18 │  104.85 ms │                                104.56 ms │     no change │
│ QQuery 19 │   41.15 ms │                                 41.40 ms │     no change │
│ QQuery 20 │   35.79 ms │                                 35.71 ms │     no change │
│ QQuery 21 │   17.52 ms │                                 17.19 ms │     no change │
│ QQuery 22 │   63.75 ms │                                 62.57 ms │     no change │
│ QQuery 23 │  312.01 ms │                                311.29 ms │     no change │
│ QQuery 24 │  196.41 ms │                                195.33 ms │     no change │
│ QQuery 25 │  110.37 ms │                                109.99 ms │     no change │
│ QQuery 26 │   48.49 ms │                                 48.60 ms │     no change │
│ QQuery 27 │    6.01 ms │                                  6.26 ms │     no change │
│ QQuery 28 │   60.40 ms │                                 56.61 ms │ +1.07x faster │
│ QQuery 29 │   96.65 ms │                                 96.80 ms │     no change │
│ QQuery 30 │   32.39 ms │                                 32.27 ms │     no change │
│ QQuery 31 │  112.08 ms │                                110.83 ms │     no change │
│ QQuery 32 │   20.28 ms │                                 20.30 ms │     no change │
│ QQuery 33 │   37.42 ms │                                 37.83 ms │     no change │
│ QQuery 34 │    9.82 ms │                                  9.95 ms │     no change │
│ QQuery 35 │   73.63 ms │                                 72.32 ms │     no change │
│ QQuery 36 │    5.74 ms │                                  5.87 ms │     no change │
│ QQuery 37 │    6.72 ms │                                  6.95 ms │     no change │
│ QQuery 38 │   61.43 ms │                                 61.27 ms │     no change │
│ QQuery 39 │   88.88 ms │                                 88.88 ms │     no change │
│ QQuery 40 │   23.96 ms │                                 23.95 ms │     no change │
│ QQuery 41 │   11.18 ms │                                 11.23 ms │     no change │
│ QQuery 42 │   24.02 ms │                                 23.98 ms │     no change │
│ QQuery 43 │    5.27 ms │                                  5.22 ms │     no change │
│ QQuery 44 │    9.44 ms │                                  9.35 ms │     no change │
│ QQuery 45 │   39.12 ms │                                 37.82 ms │     no change │
│ QQuery 46 │   12.12 ms │                                 11.96 ms │     no change │
│ QQuery 47 │  229.77 ms │                                229.85 ms │     no change │
│ QQuery 48 │   95.56 ms │                                 95.50 ms │     no change │
│ QQuery 49 │   71.13 ms │                                 71.02 ms │     no change │
│ QQuery 50 │   58.79 ms │                                 58.86 ms │     no change │
│ QQuery 51 │   92.96 ms │                                 92.40 ms │     no change │
│ QQuery 52 │   24.36 ms │                                 24.31 ms │     no change │
│ QQuery 53 │   29.10 ms │                                 29.11 ms │     no change │
│ QQuery 54 │   54.00 ms │                                 54.62 ms │     no change │
│ QQuery 55 │   23.53 ms │                                 23.45 ms │     no change │
│ QQuery 56 │   38.81 ms │                                 39.37 ms │     no change │
│ QQuery 57 │  174.88 ms │                                174.18 ms │     no change │
│ QQuery 58 │  112.19 ms │                                110.70 ms │     no change │
│ QQuery 59 │  118.21 ms │                                117.15 ms │     no change │
│ QQuery 60 │   39.63 ms │                                 39.42 ms │     no change │
│ QQuery 61 │   12.16 ms │                                 12.20 ms │     no change │
│ QQuery 62 │   46.75 ms │                                 46.12 ms │     no change │
│ QQuery 63 │   29.41 ms │                                 29.51 ms │     no change │
│ QQuery 64 │  368.37 ms │                                368.69 ms │     no change │
│ QQuery 65 │  123.91 ms │                                124.72 ms │     no change │
│ QQuery 66 │   81.04 ms │                                 82.25 ms │     no change │
│ QQuery 67 │  240.65 ms │                                247.37 ms │     no change │
│ QQuery 68 │   12.06 ms │                                 12.15 ms │     no change │
│ QQuery 69 │   57.19 ms │                                 57.28 ms │     no change │
│ QQuery 70 │  106.64 ms │                                107.51 ms │     no change │
│ QQuery 71 │   35.79 ms │                                 35.67 ms │     no change │
│ QQuery 72 │ 1828.02 ms │                               1830.05 ms │     no change │
│ QQuery 73 │   10.12 ms │                                  9.62 ms │     no change │
│ QQuery 74 │  174.86 ms │                                172.86 ms │     no change │
│ QQuery 75 │  146.25 ms │                                147.09 ms │     no change │
│ QQuery 76 │   35.41 ms │                                 35.42 ms │     no change │
│ QQuery 77 │   61.02 ms │                                 60.90 ms │     no change │
│ QQuery 78 │  216.87 ms │                                215.38 ms │     no change │
│ QQuery 79 │   67.60 ms │                                 65.87 ms │     no change │
│ QQuery 80 │   99.46 ms │                                 98.31 ms │     no change │
│ QQuery 81 │   26.21 ms │                                 25.65 ms │     no change │
│ QQuery 82 │   16.49 ms │                                 16.31 ms │     no change │
│ QQuery 83 │   33.98 ms │                                 34.12 ms │     no change │
│ QQuery 84 │   29.63 ms │                                 29.41 ms │     no change │
│ QQuery 85 │  103.85 ms │                                102.58 ms │     no change │
│ QQuery 86 │   25.55 ms │                                 25.64 ms │     no change │
│ QQuery 87 │   63.35 ms │                                 61.74 ms │     no change │
│ QQuery 88 │   64.45 ms │                                 63.58 ms │     no change │
│ QQuery 89 │   36.03 ms │                                 35.60 ms │     no change │
│ QQuery 90 │   17.33 ms │                                 17.09 ms │     no change │
│ QQuery 91 │   45.72 ms │                                 45.10 ms │     no change │
│ QQuery 92 │   29.93 ms │                                 29.54 ms │     no change │
│ QQuery 93 │   51.91 ms │                                 49.93 ms │     no change │
│ QQuery 94 │   38.45 ms │                                 38.29 ms │     no change │
│ QQuery 95 │   81.47 ms │                                 80.89 ms │     no change │
│ QQuery 96 │   24.23 ms │                                 24.01 ms │     no change │
│ QQuery 97 │   53.31 ms │                                 52.51 ms │     no change │
│ QQuery 98 │   43.57 ms │                                 42.95 ms │     no change │
│ QQuery 99 │   70.89 ms │                                 69.96 ms │     no change │
└───────────┴────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 9381.41ms │
│ Total Time (dev_chao_codex_oss-struct-cast-semantics)   │ 9360.12ms │
│ Average Time (HEAD)                                     │   94.76ms │
│ Average Time (dev_chao_codex_oss-struct-cast-semantics) │   94.55ms │
│ Queries Faster                                          │         1 │
│ Queries Slower                                          │         0 │
│ Queries with No Change                                  │        98 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and dev_chao_codex_oss-struct-cast-semantics
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃ dev_chao_codex_oss-struct-cast-semantics ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │           5.56 / 6.13 ±1.02 / 8.17 ms │              5.72 / 6.29 ±0.96 / 8.20 ms │     no change │
│ QQuery 2  │        81.12 / 81.31 ±0.16 / 81.57 ms │           82.10 / 82.40 ±0.23 / 82.72 ms │     no change │
│ QQuery 3  │        29.02 / 29.34 ±0.27 / 29.77 ms │           29.48 / 29.62 ±0.16 / 29.91 ms │     no change │
│ QQuery 4  │     493.96 / 499.56 ±4.67 / 505.24 ms │        494.55 / 499.91 ±3.84 / 505.41 ms │     no change │
│ QQuery 5  │        51.83 / 52.51 ±0.48 / 53.24 ms │           51.82 / 52.76 ±0.60 / 53.35 ms │     no change │
│ QQuery 6  │        35.72 / 36.31 ±0.57 / 37.29 ms │           36.37 / 36.83 ±0.37 / 37.26 ms │     no change │
│ QQuery 7  │        75.00 / 75.47 ±0.30 / 75.85 ms │           74.98 / 75.42 ±0.30 / 75.78 ms │     no change │
│ QQuery 8  │        36.66 / 38.43 ±2.62 / 43.59 ms │           36.86 / 37.39 ±0.53 / 38.20 ms │     no change │
│ QQuery 9  │        51.02 / 54.55 ±2.30 / 57.33 ms │           50.48 / 53.19 ±2.31 / 57.39 ms │     no change │
│ QQuery 10 │        62.76 / 63.05 ±0.37 / 63.77 ms │           63.03 / 63.36 ±0.41 / 64.14 ms │     no change │
│ QQuery 11 │     309.17 / 311.25 ±2.27 / 315.12 ms │        308.92 / 314.17 ±6.35 / 326.48 ms │     no change │
│ QQuery 12 │        28.58 / 29.16 ±0.69 / 30.41 ms │           28.95 / 29.23 ±0.27 / 29.68 ms │     no change │
│ QQuery 13 │     117.70 / 118.04 ±0.23 / 118.32 ms │        118.12 / 118.67 ±0.44 / 119.19 ms │     no change │
│ QQuery 14 │     419.62 / 421.18 ±2.04 / 425.22 ms │        415.05 / 422.10 ±4.41 / 427.07 ms │     no change │
│ QQuery 15 │        57.07 / 58.22 ±0.97 / 59.48 ms │           57.10 / 58.43 ±1.06 / 60.20 ms │     no change │
│ QQuery 16 │           6.80 / 7.05 ±0.21 / 7.38 ms │              6.78 / 6.90 ±0.22 / 7.35 ms │     no change │
│ QQuery 17 │        80.06 / 81.54 ±2.09 / 85.60 ms │           79.71 / 80.96 ±1.66 / 84.22 ms │     no change │
│ QQuery 18 │     104.85 / 107.25 ±3.02 / 113.03 ms │        104.56 / 105.47 ±0.63 / 106.52 ms │     no change │
│ QQuery 19 │        41.15 / 41.55 ±0.30 / 42.00 ms │           41.40 / 41.96 ±0.44 / 42.59 ms │     no change │
│ QQuery 20 │        35.79 / 36.08 ±0.32 / 36.67 ms │           35.71 / 36.10 ±0.30 / 36.56 ms │     no change │
│ QQuery 21 │        17.52 / 18.00 ±0.62 / 19.17 ms │           17.19 / 17.47 ±0.21 / 17.80 ms │     no change │
│ QQuery 22 │        63.75 / 64.52 ±0.91 / 66.29 ms │           62.57 / 63.86 ±0.93 / 65.15 ms │     no change │
│ QQuery 23 │     312.01 / 314.61 ±2.48 / 319.32 ms │        311.29 / 313.54 ±2.45 / 317.94 ms │     no change │
│ QQuery 24 │     196.41 / 201.61 ±4.63 / 209.49 ms │        195.33 / 197.22 ±1.71 / 200.32 ms │     no change │
│ QQuery 25 │     110.37 / 111.77 ±1.72 / 115.16 ms │        109.99 / 111.16 ±1.08 / 113.20 ms │     no change │
│ QQuery 26 │        48.49 / 48.93 ±0.27 / 49.34 ms │           48.60 / 48.75 ±0.18 / 49.09 ms │     no change │
│ QQuery 27 │           6.01 / 6.22 ±0.23 / 6.68 ms │              6.26 / 6.44 ±0.11 / 6.60 ms │     no change │
│ QQuery 28 │        60.40 / 62.16 ±2.58 / 67.22 ms │           56.61 / 60.56 ±2.20 / 63.39 ms │     no change │
│ QQuery 29 │        96.65 / 97.74 ±0.81 / 99.15 ms │           96.80 / 97.88 ±1.07 / 99.44 ms │     no change │
│ QQuery 30 │        32.39 / 32.88 ±0.49 / 33.72 ms │           32.27 / 32.51 ±0.15 / 32.69 ms │     no change │
│ QQuery 31 │     112.08 / 114.05 ±2.95 / 119.83 ms │        110.83 / 111.26 ±0.27 / 111.55 ms │     no change │
│ QQuery 32 │        20.28 / 20.66 ±0.26 / 20.99 ms │           20.30 / 20.78 ±0.28 / 21.10 ms │     no change │
│ QQuery 33 │        37.42 / 37.82 ±0.23 / 38.11 ms │           37.83 / 37.99 ±0.11 / 38.15 ms │     no change │
│ QQuery 34 │         9.82 / 10.05 ±0.17 / 10.30 ms │            9.95 / 10.17 ±0.17 / 10.41 ms │     no change │
│ QQuery 35 │        73.63 / 74.02 ±0.29 / 74.36 ms │           72.32 / 73.69 ±0.86 / 74.94 ms │     no change │
│ QQuery 36 │           5.74 / 5.86 ±0.15 / 6.14 ms │              5.87 / 6.76 ±1.44 / 9.62 ms │  1.15x slower │
│ QQuery 37 │           6.72 / 6.82 ±0.12 / 7.05 ms │              6.95 / 7.10 ±0.19 / 7.45 ms │     no change │
│ QQuery 38 │        61.43 / 62.83 ±0.71 / 63.33 ms │           61.27 / 62.84 ±1.31 / 65.13 ms │     no change │
│ QQuery 39 │        88.88 / 89.63 ±0.64 / 90.44 ms │           88.88 / 89.64 ±0.62 / 90.38 ms │     no change │
│ QQuery 40 │        23.96 / 24.60 ±0.39 / 25.10 ms │           23.95 / 24.33 ±0.21 / 24.54 ms │     no change │
│ QQuery 41 │        11.18 / 12.79 ±2.78 / 18.32 ms │           11.23 / 11.38 ±0.15 / 11.66 ms │ +1.12x faster │
│ QQuery 42 │        24.02 / 24.49 ±0.40 / 24.90 ms │           23.98 / 25.43 ±1.80 / 28.86 ms │     no change │
│ QQuery 43 │           5.27 / 5.38 ±0.16 / 5.70 ms │              5.22 / 5.32 ±0.15 / 5.61 ms │     no change │
│ QQuery 44 │           9.44 / 9.57 ±0.12 / 9.71 ms │              9.35 / 9.54 ±0.11 / 9.70 ms │     no change │
│ QQuery 45 │        39.12 / 39.97 ±0.67 / 40.57 ms │           37.82 / 39.44 ±1.16 / 41.28 ms │     no change │
│ QQuery 46 │        12.12 / 12.37 ±0.15 / 12.53 ms │           11.96 / 12.28 ±0.28 / 12.65 ms │     no change │
│ QQuery 47 │     229.77 / 235.18 ±3.98 / 239.78 ms │        229.85 / 230.93 ±0.90 / 232.33 ms │     no change │
│ QQuery 48 │        95.56 / 96.40 ±0.56 / 97.17 ms │           95.50 / 96.54 ±1.09 / 98.53 ms │     no change │
│ QQuery 49 │        71.13 / 71.91 ±0.56 / 72.57 ms │           71.02 / 71.72 ±0.46 / 72.21 ms │     no change │
│ QQuery 50 │        58.79 / 59.24 ±0.40 / 59.89 ms │           58.86 / 59.32 ±0.51 / 60.22 ms │     no change │
│ QQuery 51 │       92.96 / 96.18 ±5.14 / 106.44 ms │          92.40 / 95.66 ±3.99 / 103.41 ms │     no change │
│ QQuery 52 │        24.36 / 24.79 ±0.59 / 25.96 ms │           24.31 / 25.23 ±1.37 / 27.94 ms │     no change │
│ QQuery 53 │        29.10 / 29.28 ±0.23 / 29.71 ms │           29.11 / 29.56 ±0.35 / 30.12 ms │     no change │
│ QQuery 54 │        54.00 / 54.78 ±0.69 / 56.06 ms │           54.62 / 54.85 ±0.22 / 55.16 ms │     no change │
│ QQuery 55 │        23.53 / 23.63 ±0.07 / 23.74 ms │           23.45 / 24.03 ±0.55 / 25.00 ms │     no change │
│ QQuery 56 │        38.81 / 41.16 ±2.07 / 44.88 ms │           39.37 / 40.56 ±1.34 / 43.11 ms │     no change │
│ QQuery 57 │     174.88 / 177.82 ±2.05 / 179.98 ms │        174.18 / 177.13 ±3.01 / 182.83 ms │     no change │
│ QQuery 58 │     112.19 / 113.83 ±1.54 / 115.91 ms │        110.70 / 113.47 ±2.35 / 117.63 ms │     no change │
│ QQuery 59 │     118.21 / 119.66 ±1.72 / 122.96 ms │        117.15 / 118.63 ±2.07 / 122.73 ms │     no change │
│ QQuery 60 │        39.63 / 40.29 ±0.41 / 40.76 ms │           39.42 / 40.30 ±0.74 / 41.64 ms │     no change │
│ QQuery 61 │        12.16 / 12.28 ±0.14 / 12.52 ms │           12.20 / 12.40 ±0.27 / 12.94 ms │     no change │
│ QQuery 62 │        46.75 / 46.92 ±0.17 / 47.14 ms │           46.12 / 46.46 ±0.27 / 46.83 ms │     no change │
│ QQuery 63 │        29.41 / 29.58 ±0.16 / 29.87 ms │           29.51 / 29.87 ±0.40 / 30.60 ms │     no change │
│ QQuery 64 │     368.37 / 371.20 ±2.31 / 374.64 ms │        368.69 / 372.32 ±3.10 / 377.23 ms │     no change │
│ QQuery 65 │     123.91 / 126.83 ±3.89 / 134.47 ms │        124.72 / 127.94 ±2.46 / 131.03 ms │     no change │
│ QQuery 66 │        81.04 / 81.69 ±0.46 / 82.36 ms │           82.25 / 82.78 ±0.49 / 83.65 ms │     no change │
│ QQuery 67 │     240.65 / 248.95 ±5.09 / 256.61 ms │        247.37 / 249.22 ±1.77 / 251.37 ms │     no change │
│ QQuery 68 │        12.06 / 12.26 ±0.19 / 12.56 ms │           12.15 / 12.28 ±0.14 / 12.54 ms │     no change │
│ QQuery 69 │        57.19 / 59.57 ±3.38 / 66.15 ms │           57.28 / 60.54 ±4.31 / 68.95 ms │     no change │
│ QQuery 70 │     106.64 / 108.29 ±2.32 / 112.87 ms │        107.51 / 109.99 ±2.96 / 115.74 ms │     no change │
│ QQuery 71 │        35.79 / 36.03 ±0.28 / 36.55 ms │           35.67 / 36.29 ±0.37 / 36.64 ms │     no change │
│ QQuery 72 │ 1828.02 / 1951.75 ±70.87 / 2048.67 ms │    1830.05 / 1886.29 ±75.02 / 2034.55 ms │     no change │
│ QQuery 73 │        10.12 / 10.29 ±0.11 / 10.44 ms │            9.62 / 10.38 ±0.52 / 10.85 ms │     no change │
│ QQuery 74 │     174.86 / 176.66 ±1.79 / 178.85 ms │        172.86 / 176.28 ±1.89 / 178.23 ms │     no change │
│ QQuery 75 │     146.25 / 149.77 ±3.22 / 154.94 ms │        147.09 / 149.68 ±2.99 / 155.53 ms │     no change │
│ QQuery 76 │        35.41 / 35.60 ±0.16 / 35.80 ms │           35.42 / 36.02 ±0.59 / 36.86 ms │     no change │
│ QQuery 77 │        61.02 / 61.66 ±0.53 / 62.35 ms │           60.90 / 61.59 ±0.48 / 62.10 ms │     no change │
│ QQuery 78 │     216.87 / 220.83 ±4.37 / 228.60 ms │        215.38 / 222.91 ±6.26 / 231.60 ms │     no change │
│ QQuery 79 │        67.60 / 68.58 ±0.83 / 69.66 ms │           65.87 / 66.97 ±0.64 / 67.54 ms │     no change │
│ QQuery 80 │      99.46 / 101.01 ±1.52 / 103.88 ms │         98.31 / 100.73 ±2.37 / 104.71 ms │     no change │
│ QQuery 81 │        26.21 / 26.61 ±0.25 / 26.96 ms │           25.65 / 25.95 ±0.21 / 26.21 ms │     no change │
│ QQuery 82 │        16.49 / 19.25 ±5.16 / 29.55 ms │           16.31 / 16.70 ±0.49 / 17.65 ms │ +1.15x faster │
│ QQuery 83 │        33.98 / 35.90 ±2.28 / 40.36 ms │           34.12 / 34.36 ±0.19 / 34.61 ms │     no change │
│ QQuery 84 │        29.63 / 30.36 ±0.61 / 31.48 ms │           29.41 / 29.69 ±0.23 / 29.99 ms │     no change │
│ QQuery 85 │     103.85 / 105.08 ±0.88 / 106.03 ms │        102.58 / 106.91 ±3.63 / 112.90 ms │     no change │
│ QQuery 86 │        25.55 / 26.07 ±0.55 / 27.10 ms │           25.64 / 25.91 ±0.19 / 26.13 ms │     no change │
│ QQuery 87 │        63.35 / 67.72 ±5.22 / 77.93 ms │           61.74 / 62.32 ±0.41 / 62.83 ms │ +1.09x faster │
│ QQuery 88 │        64.45 / 64.80 ±0.27 / 65.05 ms │           63.58 / 66.17 ±4.60 / 75.36 ms │     no change │
│ QQuery 89 │        36.03 / 36.34 ±0.19 / 36.60 ms │           35.60 / 36.42 ±0.68 / 37.36 ms │     no change │
│ QQuery 90 │        17.33 / 17.47 ±0.08 / 17.54 ms │           17.09 / 17.42 ±0.24 / 17.77 ms │     no change │
│ QQuery 91 │        45.72 / 47.89 ±2.52 / 52.36 ms │           45.10 / 45.31 ±0.14 / 45.54 ms │ +1.06x faster │
│ QQuery 92 │        29.93 / 31.15 ±1.02 / 33.02 ms │           29.54 / 29.97 ±0.62 / 31.19 ms │     no change │
│ QQuery 93 │        51.91 / 52.50 ±0.41 / 53.02 ms │           49.93 / 50.55 ±0.33 / 50.92 ms │     no change │
│ QQuery 94 │        38.45 / 39.03 ±0.55 / 39.76 ms │           38.29 / 39.43 ±1.35 / 41.98 ms │     no change │
│ QQuery 95 │        81.47 / 83.48 ±1.73 / 85.59 ms │           80.89 / 81.91 ±0.74 / 82.62 ms │     no change │
│ QQuery 96 │        24.23 / 24.53 ±0.18 / 24.77 ms │           24.01 / 24.28 ±0.30 / 24.80 ms │     no change │
│ QQuery 97 │        53.31 / 54.28 ±1.06 / 55.78 ms │           52.51 / 53.77 ±1.28 / 55.57 ms │     no change │
│ QQuery 98 │        43.57 / 44.12 ±0.48 / 44.85 ms │           42.95 / 43.74 ±1.27 / 46.26 ms │     no change │
│ QQuery 99 │        70.89 / 71.59 ±0.82 / 73.18 ms │           69.96 / 70.58 ±0.37 / 70.98 ms │     no change │
└───────────┴───────────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 9629.39ms │
│ Total Time (dev_chao_codex_oss-struct-cast-semantics)   │ 9538.80ms │
│ Average Time (HEAD)                                     │   97.27ms │
│ Average Time (dev_chao_codex_oss-struct-cast-semantics) │   96.35ms │
│ Queries Faster                                          │         4 │
│ Queries Slower                                          │         1 │
│ Queries with No Change                                  │        94 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Resource Usage

tpcds — base (merge-base)

Metric Value
Wall time 50.0s
Peak memory 2.1 GiB
Avg memory 1.4 GiB
CPU user 209.6s
CPU sys 5.7s
Peak spill 0 B

tpcds — branch

Metric Value
Wall time 50.0s
Peak memory 2.0 GiB
Avg memory 1.3 GiB
CPU user 205.9s
CPU sys 5.4s
Peak spill 0 B

File an issue against this benchmark runner

@comphead comphead left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @sunchao/ One real finding (P2): the decimal all-null fix uses is_decimal() on the top-level leaf type, so it misses decimals nested in List/FixedSizeList/ListView/Map/Dictionary.

Suggested fix direction

Replace the top-level is_decimal() check with a recursive "does this leaf type contain a decimal anywhere" test, so container-wrapped decimals also take the struct-cast (retain_field_path) shape and keep the
all-null shortcut, while non-decimal leaves keep the pushdown-friendly scalar-cast shape.

cast_accesses: Vec<CastColumnAccess>,
/// Whether to collect [`Self::cast_accesses`].
collect_cast_accesses: bool,
/// Allow field access through a retained Struct cast after schema adaptation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps its not very obvious the param allow scope? is it cast to struct, from struct, both?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarified in e79e91236: both the source and target must be Struct types. This flag allows get_field(CAST(struct_column AS Struct(...)), 'field', ...) after schema adaptation; the reader preserves the cast and reads the full source Struct. Planning keeps the flag disabled so explicit casts retain a residual filter.

let return_type = func.return_type();
if DataType::is_nested(return_type) && !self.is_nested_type_supported(return_type)
{
return None;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kind of optional, but when we have multiple early returns maybe its good to debug the reason of None?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, reason logging could help debugging. I'm deferring it to keep this correctness fix focused. Here, None means the retained-Struct-cast special case did not apply; the caller continues with its normal checks and traversal. It does not itself reject pushdown or indicate an execution error.

Comment on lines +324 to +329
let Some((name, rest)) = path.split_first() else {
return Some(Arc::clone(field));
};
let DataType::Struct(fields) = field.data_type() else {
return None;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let Some((name, rest)) = path.split_first() else {
return Some(Arc::clone(field));
};
let DataType::Struct(fields) = field.data_type() else {
return None;
};
let DataType::Struct(fields) = field.data_type() else {
return None;
};
let Some((name, rest)) = path.split_first() else {
return Some(Arc::clone(field));
};

maybe we can swap early returns?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the empty-path check first because it is the recursion's success case, including scalar leaves. Swapping the checks returns None at a decimal leaf, so narrowing is abandoned and the whole Struct cast, including unselected siblings, remains.

I tried the suggested swap: test_narrow_decimal_struct_cast_ignores_siblings failed while converting the unused y = "bad" to Int32. The current order preserves the selected leaf and lets the caller trim the unselected siblings.

@sunchao

sunchao commented Aug 26, 2026

Copy link
Copy Markdown
Member Author

Thanks @comphead, confirmed and fixed in e79e91236. The guard now looks through the selected field's container value types, covering the reported List families, Map, and Dictionary cases while retaining only the selected field path.

The existing regression now covers these containers and deeper nesting. I also added protection for matching-type narrowing, chained field access, and existing container-to-Struct conversions, which must keep their current casting path. The Parquet regression covers List<Utf8> to List<Decimal128(10, -1)> under an entirely null Struct with filter pushdown both enabled and disabled.

Validation passed: 42 adapter tests, 249 Parquet datasource tests, 227 Parquet integration tests, 8 doctests (5 existing ignores), and 21 SQL logic files. Formatting, Clippy across all targets/features with warnings denied, and the full repository lint suite also passed.

This addresses the container-decimal finding. I updated the PR description with the container example and the validation results for this revision.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core Core DataFusion crate datasource Changes to the datasource crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Struct-cast narrowing can suppress cast errors and change field semantics

6 participants