Background
Gluten vendors its Substrait .proto files (gluten-substrait/src/main/resources/substrait/proto/substrait/) pinned near Substrait v0.23.0 with local patches, while upstream Substrait is now at 0.98.0. The JVM plan producer and both native consumers (Velox, ClickHouse) regenerate from this single proto source and ship together, and Gluten's Substrait plans are transient (built per query, serialized across JNI, discarded) — so there is no wire-compatibility constraint and the rebase is a pure code migration.
Goal
Bring the vendored proto to official 0.98.0 core (verbatim) plus Gluten's genuinely engine-specific additions grafted at non-colliding numbers, migrating the JVM producer and both native backends in lockstep.
Approach
Land as small, independently-green PRs, one per message family (each keeps protoc + JVM + both native builds compiling). Minimal rebase first — relocate only colliding fork fields and drop dead/decided-drop items; extracting engine-specific forks to formal Substrait extensions is deferred to a follow-up.
Increments
One PR per message family; each keeps protoc + JVM + both native builds green. Progress: 11 merged · Inc 8 to do (ReadRel split into 7a/7b/7c; 7a merged, 7b in review, 7c in review as two stacked PRs) · 1 resolved upstream.
Upstream Substrait convergence PRs
Some kept forks converge by landing the equivalent field/type in Substrait core (substrait-io/substrait) rather than by dropping the fork. These are tracked under the apache-gluten label:
Tracked for later extraction waves (not driving a rebase increment):
Note on Type.Nothing
Following the Substrait community sync (2026-07-29), the earlier plan to drop the Type.Nothing fork is superseded: upstream is adding a dedicated core null type (substrait-io/substrait#1147 — Type.Null at kind field 40). Increment 2 relocates Nothing to field 40 to match, so once that PR releases the fork converges by renaming the message Nothing→Null — no field renumbering, no consumer-mapping changes. Gluten keeps Type.Nothing until then.
Related: upstream consolidation work is tracked under the apache-gluten label in substrait-io/substrait.
Background
Gluten vendors its Substrait
.protofiles (gluten-substrait/src/main/resources/substrait/proto/substrait/) pinned near Substrait v0.23.0 with local patches, while upstream Substrait is now at 0.98.0. The JVM plan producer and both native consumers (Velox, ClickHouse) regenerate from this single proto source and ship together, and Gluten's Substrait plans are transient (built per query, serialized across JNI, discarded) — so there is no wire-compatibility constraint and the rebase is a pure code migration.Goal
Bring the vendored proto to official 0.98.0 core (verbatim) plus Gluten's genuinely engine-specific additions grafted at non-colliding numbers, migrating the JVM producer and both native backends in lockstep.
Approach
Land as small, independently-green PRs, one per message family (each keeps
protoc+ JVM + both native builds compiling). Minimal rebase first — relocate only colliding fork fields and drop dead/decided-drop items; extracting engine-specific forks to formal Substrait extensions is deferred to a follow-up.Increments
One PR per message family; each keeps
protoc+ JVM + both native builds green. Progress: 11 merged · Inc 8 to do (ReadRel split into 7a/7b/7c; 7a merged, 7b in review, 7c in review as two stacked PRs) · 1 resolved upstream.Expression.Enum— merged ([GLUTEN-12597][CORE] Remove unused vendored Substrait proto files and dead proto-based type derivation #12598)plan.proto/extensions.proto→ 0.98 (URI→URN) — merged ([GLUTEN-12597][CORE] Migrate Substrait extension referencing from URI to URN #12604)AdvancedExtension.optimizationsingular → repeated — merged ([GLUTEN-12597][CORE] Make AdvancedExtension.optimization repeated #12642)PrecisionTimestamp*; relocateType.Nothing(kind33→40, kept) — merged ([GLUTEN-12597][CORE] Migrate Substrait type system to 0.98 temporal types #12650)CrossRel→NestedLoopJoinRel(+ join-type enum remap) — merged ([GLUTEN-12597][CORE] Migrate nested loop joins from CrossRel to NestedLoopJoinRel (Substrait 0.98) #12700)WindowRel→ConsistentPartitionWindowRel;WindowType→BoundsType— merged ([GLUTEN-12597][CORE] Migrate WindowRel to ConsistentPartitionWindowRel + BoundsType (Substrait 0.98) #12727)FetchReloffset/count → expressions — merged ([GLUTEN-12597][CORE] Migrate FetchRel offset/count to expressions (Substrait 0.98) #12704)AggregateRel.Grouping→ expression references — merged ([GLUTEN-12597][CORE] Migrate AggregateRel.Grouping to expression references (Substrait 0.98) #12724)ExpandRel.advanced_extension— converged upstream via substrait-io/substrait#1167 (merged, ships 0.100.0); Gluten's graft already matches, no Gluten PR neededTopNRel→Expression count+offset+FetchMode— merged ([GLUTEN-12597][CORE] Migrate TopNRel to Expression count + FetchMode (Substrait 0.98) #12728)WriteRel: relocatebucket_spec(→ 1000); addcreate_mode/advanced_extension;OUTPUT_MODE_MODIFIED_TUPLES→_RECORDS— merged ([GLUTEN-12597][CORE] Migrate WriteRel to Substrait 0.98 (relocate bucket_spec; add create_mode/advanced_extension) #12746)ReadRel(the largest vendored message) — split into three independent slices (disjoint proto regions and consumers, can land in any order):VirtualTable.values→Expression.Nested.Struct expressions— merged ([GLUTEN-12597][CORE] Migrate ReadRel.VirtualTable to Substrait 0.98 (values -> expressions) #12849)TextReadOptions→DelimiterSeparatedTextReadOptions(mapheader/null_valueto the 0.98 fields; graftmax_block_size/empty_as_defaultat 1000+) — PR [GLUTEN-12597][CORE] Migrate ReadRel text read options to Substrait 0.98 (TextReadOptions -> DelimiterSeparatedTextReadOptions) #12917 (in review)read_type→ verbatim 0.98, in two stacked slices:IcebergTable iceberg_table = 9; relocate thestream_kafkagraft off the field-9 collision → 1000 — PR [GLUTEN-12597][CORE] Migrate ReadRel read_type to Substrait 0.98 (add iceberg_table, relocate stream_kafka) #12832 (in review)stream_kafkagraft entirely (makingread_typeverbatim 0.98) and remodel Gluten's ClickHouse Kafka read onto the officialextension_table = 8path: theStreamKafkapayload moves to a new Gluten-ownedkafka.proto, packed intoReadRel.ExtensionTable.detailand discriminated by theAnytype_url (the pattern MergeTree/Range should converge onto — see [CH] Discriminate MergeTree/Range ExtensionTable reads by type_url, not isMergeTree=/isRange= marker strings #12842) — PR [GLUTEN-12597][CORE] Remodel Kafka read onto ReadRel.ExtensionTable (Substrait 0.98) #12841 (in review)Reloneof reconciliation to exact 0.98 layout + dropRelRoot.output_schema— to dodocs/developers/SubstraitModifications.mdwith the finished proto: bump the stated base version v0.23.0 → 0.98.0 and drop entries for messages that ended up verbatim-upstream (e.g.ReadRel.VirtualTable), leaving only the genuinely remaining Gluten deviationsUpstream Substrait convergence PRs
Some kept forks converge by landing the equivalent field/type in Substrait core (
substrait-io/substrait) rather than by dropping the fork. These are tracked under theapache-glutenlabel:advanced_extensiononExpandRel— merged, ships 0.100.0 → drives increment 5c; Gluten's graft already matches verbatimType.Nullfor untyped nulls — open → increment 2 relocatesType.Nothingtokind40 to match; converge by renamingNothing→Nullonce released (see note below)ReadRel.LocalFiles— open → upstreams Gluten'sjsonread-options fork; increment 7b keeps the vendoredjson = 15until this mergesReadRel.LocalFiles— open → upstreams Gluten's partition-columns fork; the field-16 collision with theicebergfork is a post-merge concernsubstrait-io/substrait#1140— docs on typing untyped nulls — closed, superseded by substrait-io/substrait#1147Tracked for later extraction waves (not driving a rebase increment):
GenerateRelfor lateral view / unnest (@EpsilonPrime) — upstreams the pattern of Gluten'sGenerateRelforkNote on
Type.NothingFollowing the Substrait community sync (2026-07-29), the earlier plan to drop the
Type.Nothingfork is superseded: upstream is adding a dedicated corenulltype (substrait-io/substrait#1147 —Type.Nullatkindfield 40). Increment 2 relocatesNothingto field 40 to match, so once that PR releases the fork converges by renaming the messageNothing→Null— no field renumbering, no consumer-mapping changes. Gluten keepsType.Nothinguntil then.Related: upstream consolidation work is tracked under the
apache-glutenlabel insubstrait-io/substrait.