This is an automated email from the ASF dual-hosted git repository.
zhouyuan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gluten.git
The following commit(s) were added to refs/heads/main by this push:
new fb588bc4c9 [GLUTEN-12597][CORE] Migrate Rel oneof to Substrait 0.98
(park GenerateRel/WindowGroupLimitRel) (#13210)
fb588bc4c9 is described below
commit fb588bc4c965db7895e3fc2c781a62a9fb6cbd01
Author: Niels Pardon <[email protected]>
AuthorDate: Fri Oct 23 14:50:16 2026 +0200
[GLUTEN-12597][CORE] Migrate Rel oneof to Substrait 0.98 (park
GenerateRel/WindowGroupLimitRel) (#13210)
---
.../local-engine/Parser/SerializedPlanParser.cpp | 10 +++
docs/developers/SubstraitModifications.md | 5 ++
.../substrait/proto/substrait/algebra.proto | 96 ++++++++++++++++++---
.../gluten/substrait/rel/RelProtoSuite.scala | 99 ++++++++++++++++++++++
4 files changed, 198 insertions(+), 12 deletions(-)
diff --git a/cpp-ch/local-engine/Parser/SerializedPlanParser.cpp
b/cpp-ch/local-engine/Parser/SerializedPlanParser.cpp
index 594484adae..556e24d0ad 100644
--- a/cpp-ch/local-engine/Parser/SerializedPlanParser.cpp
+++ b/cpp-ch/local-engine/Parser/SerializedPlanParser.cpp
@@ -52,6 +52,7 @@
#include <Storages/SubstraitSource/SubstraitFileSource.h>
#include <Storages/SubstraitSource/SubstraitFileSourceStep.h>
#include <google/protobuf/wrappers.pb.h>
+#include <magic_enum.hpp>
#include <Common/BlockTypeUtils.h>
#include <Common/DebugUtils.h>
#include <Common/Exception.h>
@@ -60,6 +61,15 @@
#include <Common/logger_useful.h>
#include <Common/typeid_cast.h>
+/// Gluten's own relations sit at 1000+ in the Rel oneof, past magic_enum's
default reflection range,
+/// so widen it to keep their metric names (e.g. kGenerate, kWindowGroupLimit).
+template <>
+struct magic_enum::customize::enum_range<substrait::Rel::RelTypeCase>
+{
+ static constexpr int min = 0;
+ static constexpr int max = 1001;
+};
+
namespace DB
{
namespace Setting
diff --git a/docs/developers/SubstraitModifications.md
b/docs/developers/SubstraitModifications.md
index 3299c5f198..e99cd818a2 100644
--- a/docs/developers/SubstraitModifications.md
+++ b/docs/developers/SubstraitModifications.md
@@ -33,6 +33,11 @@ changed `Unbounded` in `WindowFunction` into
`Unbounded_Preceding` and `Unbounde
* Added `output_schema` in
RelRoot([#1901](https://github.com/apache/gluten/pull/1901)).
* Added `ExpandRel`([#1361](https://github.com/apache/gluten/pull/1361)).
* Added `GenerateRel`([#574](https://github.com/apache/gluten/pull/574)).
+* Rebased the `Rel` oneof onto upstream `v0.98.0`: every upstream relation now
sits at its upstream number
+(`exchange` 15, `expand` 16, `window` 17, `ddl` 20, `reference` 21, `update`
22, `lateral_join` 24), with
+`LateralJoinRel`, `UpdateRel`, the top-level `NamedTable` and
`DdlRel.common`/`advanced_extension` vendored
+verbatim, and `ReferenceRel` moved out of `AggregateFunction` to the top
level. Gluten's `GenerateRel` and
+`WindowGroupLimitRel` moved to 1000 and 1001.
* Added `PartitionColumn` in
`LocalFiles`([#2405](https://github.com/apache/gluten/pull/2405)).
* Added `WriteRel` ([#3690](https://github.com/apache/gluten/pull/3690)).
* Added `TopNRel` ([#5409](https://github.com/apache/gluten/pull/5409)).
diff --git
a/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
b/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
index a698b297b6..562593ba91 100644
---
a/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
+++
b/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
@@ -346,6 +346,40 @@ message JoinRel {
substrait.extensions.AdvancedExtension advanced_extension = 10;
}
+// The binary lateral JOIN relational operator left-join-right. This is
+// semantically identical to JoinRel, except the right input is evaluated once
+// per row of the left input and may reference fields from the current left row
+// via OuterReference rel_reference.
+//
+// LateralJoinRel must set RelCommon.rel_anchor so the right input can
+// reference fields of the current left row.
+message LateralJoinRel {
+ RelCommon common = 1;
+ // The independent input whose rows drive evaluation of the lateral join.
+ Rel left = 2;
+
+ // The dependent input evaluated once per left row. It may reference fields
+ // from the current left row using OuterReference.rel_reference pointing to
+ // this LateralJoinRel's RelCommon.rel_anchor.
+ Rel right = 3;
+ // A boolean condition evaluated over the left and right inputs that
+ // determines whether a pair of records is a match.
+ Expression expression = 4;
+ // An optional boolean filter applied to each output record after
+ // join-type-specific output formation is complete.
+ // Semantically equivalent to placing a FilterRel directly above this join.
+ Expression post_join_filter = 5;
+
+ // Only INNER and left-oriented join types are valid for lateral joins:
+ // INNER, LEFT, LEFT_SEMI, LEFT_ANTI, LEFT_SINGLE, LEFT_MARK.
+ // RIGHT-oriented join types and OUTER are not valid because the right input
+ // is evaluated in the context of each left row and has no independent
+ // existence.
+ JoinRel.JoinType type = 6;
+
+ substrait.extensions.AdvancedExtension advanced_extension = 10;
+}
+
// Cartesian product relational operator of two tables (left and right)
message CrossRel {
RelCommon common = 1;
@@ -691,22 +725,29 @@ message Rel {
AggregateRel aggregate = 4;
SortRel sort = 5;
JoinRel join = 6;
+ LateralJoinRel lateral_join = 24;
ProjectRel project = 7;
SetRel set = 8;
ExtensionSingleRel extension_single = 9;
ExtensionMultiRel extension_multi = 10;
ExtensionLeafRel extension_leaf = 11;
CrossRel cross = 12;
- //Physical relations
+ ReferenceRel reference = 21;
+ WriteRel write = 19;
+ DdlRel ddl = 20;
+ UpdateRel update = 22;
+ // Physical relations
HashJoinRel hash_join = 13;
MergeJoinRel merge_join = 14;
- ExpandRel expand = 15;
- ConsistentPartitionWindowRel window = 16;
- GenerateRel generate = 17;
- WriteRel write = 19;
- TopNRel top_n = 23;
- WindowGroupLimitRel windowGroupLimit = 20;
NestedLoopJoinRel nested_loop_join = 18;
+ ConsistentPartitionWindowRel window = 17;
+ ExchangeRel exchange = 15;
+ ExpandRel expand = 16;
+ TopNRel top_n = 23;
+ // Gluten additions, numbered from 1000 so future upstream relations
+ // cannot collide with them.
+ GenerateRel generate = 1000;
+ WindowGroupLimitRel windowGroupLimit = 1001;
}
}
@@ -747,6 +788,7 @@ message DdlRel {
// The body of the CREATE VIEW
Rel view_definition = 7;
+ RelCommon common = 8;
enum DdlObject {
DDL_OBJECT_UNSPECIFIED = 0;
@@ -769,6 +811,9 @@ message DdlRel {
// An operation that removes an object from the system (without throwing
an exception if the object did not exist)
DDL_OP_DROP_IF_EXIST = 5;
}
+
+ substrait.extensions.AdvancedExtension advanced_extension = 9;
+
//TODO add PK/constraints/indexes/etc..?
}
@@ -853,6 +898,33 @@ message WriteRel {
}
}
+// The operator that modifies the columns of a table
+message UpdateRel {
+ oneof update_type {
+ NamedTable named_table = 1;
+ }
+
+ NamedStruct table_schema = 2; // The full schema of the named_table
+ Expression condition = 3; // condition to be met for the update to be
applied on a record
+
+ // The list of transformations to apply to the columns of the named_table
+ repeated TransformExpression transformations = 4;
+
+ message TransformExpression {
+ Expression transformation = 1; // the transformation to apply
+ int32 column_target = 2; // index of the column to apply the
transformation to
+ }
+
+ substrait.extensions.AdvancedExtension advanced_extension = 5;
+}
+
+// A base table. The list of string is used to represent namespacing (e.g.,
mydb.mytable).
+// This assumes shared catalog between systems exchanging a message.
+message NamedTable {
+ repeated string names = 1;
+ substrait.extensions.AdvancedExtension advanced_extension = 10;
+}
+
// The hash equijoin join operator will build a hash table out of the right
input based on a set of join keys.
// It will then probe that hash table for incoming inputs, finding matches.
message HashJoinRel {
@@ -1681,10 +1753,10 @@ message AggregateFunction {
// Use only distinct values in the aggregation calculation.
AGGREGATION_INVOCATION_DISTINCT = 2;
}
+}
- // This rel is used to create references,
- // in case we refer to a RelRoot field names will be ignored
- message ReferenceRel {
- int32 subtree_ordinal = 1;
- }
+// This rel is used to create references,
+// in case we refer to a RelRoot field names will be ignored
+message ReferenceRel {
+ int32 subtree_ordinal = 1;
}
diff --git
a/gluten-substrait/src/test/scala/org/apache/gluten/substrait/rel/RelProtoSuite.scala
b/gluten-substrait/src/test/scala/org/apache/gluten/substrait/rel/RelProtoSuite.scala
new file mode 100644
index 0000000000..ff8e172163
--- /dev/null
+++
b/gluten-substrait/src/test/scala/org/apache/gluten/substrait/rel/RelProtoSuite.scala
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.gluten.substrait.rel
+
+import com.google.protobuf.Descriptors.Descriptor
+import io.substrait.proto.{AggregateFunction, DdlRel, Rel}
+import org.scalatest.funsuite.AnyFunSuite
+
+import scala.collection.JavaConverters._
+
+/**
+ * Pins the wire tags of the vendored `Rel.rel_type` oneof. Producer and
consumer share one schema,
+ * so a renumber round-trips cleanly through the generated classes and cannot
be caught by
+ * exercising them; these assert on the descriptors instead.
+ *
+ * Gluten's own relations live in the 1000 graft range so that relations added
by upstream cannot
+ * collide with them.
+ */
+class RelProtoSuite extends AnyFunSuite {
+
+ private def assertFieldNumbers(descriptor: Descriptor, expected: (String,
Int)*): Unit =
+ expected.foreach {
+ case (name, number) =>
+ val field = descriptor.findFieldByName(name)
+ assert(field != null, s"${descriptor.getName} has no field named
$name")
+ assert(field.getNumber === number, s"${descriptor.getName} field $name
changed its number")
+ }
+
+ private val standardRelations = Seq(
+ "read" -> 1,
+ "filter" -> 2,
+ "fetch" -> 3,
+ "aggregate" -> 4,
+ "sort" -> 5,
+ "join" -> 6,
+ "project" -> 7,
+ "set" -> 8,
+ "extension_single" -> 9,
+ "extension_multi" -> 10,
+ "extension_leaf" -> 11,
+ "cross" -> 12,
+ "hash_join" -> 13,
+ "merge_join" -> 14,
+ "exchange" -> 15,
+ "expand" -> 16,
+ "window" -> 17,
+ "nested_loop_join" -> 18,
+ "write" -> 19,
+ "ddl" -> 20,
+ "reference" -> 21,
+ "update" -> 22,
+ "top_n" -> 23,
+ "lateral_join" -> 24
+ )
+
+ private val glutenRelations = Seq("generate" -> 1000, "windowGroupLimit" ->
1001)
+
+ test("the standard relations carry their expected tags") {
+ assertFieldNumbers(Rel.getDescriptor, standardRelations: _*)
+ }
+
+ test("Gluten-local relations live in the 1000 graft range") {
+ assertFieldNumbers(Rel.getDescriptor, glutenRelations: _*)
+ }
+
+ test("rel_type holds exactly the standard and Gluten-local relations") {
+ val rel = Rel.getDescriptor
+ val oneof = rel.getOneofs.asScala.find(_.getName == "rel_type")
+ assert(oneof.isDefined, "Rel has no rel_type oneof")
+ val actual = oneof.get.getFields.asScala.map(f => f.getName ->
f.getNumber).toSet
+ assert(actual === (standardRelations ++ glutenRelations).toSet)
+ }
+
+ test("ReferenceRel is a top-level message") {
+ val reference = Rel.getDescriptor.findFieldByName("reference")
+ assert(reference.getMessageType.getFullName === "substrait.ReferenceRel")
+ assert(
+ AggregateFunction.getDescriptor.findNestedTypeByName("ReferenceRel") ===
null,
+ "ReferenceRel must not be nested inside AggregateFunction")
+ }
+
+ test("DdlRel carries common and advanced_extension") {
+ assertFieldNumbers(DdlRel.getDescriptor, "common" -> 8,
"advanced_extension" -> 9)
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]