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 2305085178 [GLUTEN-12597][CORE] Migrate FetchRel offset/count to
expressions (Substrait 0.98) (#12704)
2305085178 is described below
commit 2305085178c23dfb8d389b102e132913cbbdd322
Author: Niels Pardon <[email protected]>
AuthorDate: Wed Aug 19 15:26:34 2026 +0200
[GLUTEN-12597][CORE] Migrate FetchRel offset/count to expressions
(Substrait 0.98) (#12704)
---
.../Parser/RelParsers/FetchRelParser.cpp | 4 +-
.../Parser/RelParsers/SortRelParser.cpp | 2 +-
cpp/velox/substrait/SubstraitToVeloxPlan.cc | 13 ++++---
.../substrait/SubstraitToVeloxPlanValidator.cc | 6 ++-
cpp/velox/substrait/VeloxToSubstraitPlan.cc | 8 ++--
.../apache/gluten/substrait/rel/FetchRelNode.java | 5 ++-
.../substrait/proto/substrait/algebra.proto | 19 ++++++++--
.../apache/gluten/utils/FetchRelProtoSuite.scala | 43 ++++++++++++++++++++++
8 files changed, 81 insertions(+), 19 deletions(-)
diff --git a/cpp-ch/local-engine/Parser/RelParsers/FetchRelParser.cpp
b/cpp-ch/local-engine/Parser/RelParsers/FetchRelParser.cpp
index 1b4c5e037e..bd5b5f1dc3 100644
--- a/cpp-ch/local-engine/Parser/RelParsers/FetchRelParser.cpp
+++ b/cpp-ch/local-engine/Parser/RelParsers/FetchRelParser.cpp
@@ -31,7 +31,9 @@ public:
DB::QueryPlanPtr parse(DB::QueryPlanPtr query_plan, const substrait::Rel &
rel, std::list<const substrait::Rel *> &)
{
const auto & limit = rel.fetch();
- auto limit_step =
std::make_unique<DB::LimitStep>(query_plan->getCurrentHeader(), limit.count(),
limit.offset());
+ size_t count = limit.has_count_expr() ?
limit.count_expr().literal().i64() : 0;
+ size_t offset = limit.has_offset_expr() ?
limit.offset_expr().literal().i64() : 0;
+ auto limit_step =
std::make_unique<DB::LimitStep>(query_plan->getCurrentHeader(), count, offset);
limit_step->setStepDescription("LIMIT");
steps.push_back(limit_step.get());
query_plan->addStep(std::move(limit_step));
diff --git a/cpp-ch/local-engine/Parser/RelParsers/SortRelParser.cpp
b/cpp-ch/local-engine/Parser/RelParsers/SortRelParser.cpp
index ce964da04b..30e7c8db89 100644
--- a/cpp-ch/local-engine/Parser/RelParsers/SortRelParser.cpp
+++ b/cpp-ch/local-engine/Parser/RelParsers/SortRelParser.cpp
@@ -61,7 +61,7 @@ size_t SortRelParser::parseLimit(std::list<const
substrait::Rel *> & rel_stack_)
if (last_rel.has_fetch())
{
const auto & fetch_rel = last_rel.fetch();
- return fetch_rel.count();
+ return fetch_rel.has_count_expr() ?
fetch_rel.count_expr().literal().i64() : 0;
}
return 0;
}
diff --git a/cpp/velox/substrait/SubstraitToVeloxPlan.cc
b/cpp/velox/substrait/SubstraitToVeloxPlan.cc
index 9195861714..37a0a6ec40 100644
--- a/cpp/velox/substrait/SubstraitToVeloxPlan.cc
+++ b/cpp/velox/substrait/SubstraitToVeloxPlan.cc
@@ -1385,12 +1385,13 @@ core::PlanNodePtr
SubstraitToVeloxPlanConverter::toVeloxPlan(const ::substrait::
core::PlanNodePtr SubstraitToVeloxPlanConverter::toVeloxPlan(const
::substrait::FetchRel& fetchRel) {
auto childNode = convertSingleInput<::substrait::FetchRel>(fetchRel);
- return std::make_shared<core::LimitNode>(
- nextPlanNodeId(),
- static_cast<int32_t>(fetchRel.offset()),
- static_cast<int32_t>(fetchRel.count()),
- false /*isPartial*/,
- childNode);
+ int32_t offset = fetchRel.has_offset_expr()
+ ?
static_cast<int32_t>(SubstraitParser::getLiteralValue<int64_t>(fetchRel.offset_expr().literal()))
+ : 0;
+ int32_t count = fetchRel.has_count_expr()
+ ?
static_cast<int32_t>(SubstraitParser::getLiteralValue<int64_t>(fetchRel.count_expr().literal()))
+ : 0;
+ return std::make_shared<core::LimitNode>(nextPlanNodeId(), offset, count,
false /*isPartial*/, childNode);
}
core::PlanNodePtr SubstraitToVeloxPlanConverter::toVeloxPlan(const
::substrait::TopNRel& topNRel) {
diff --git a/cpp/velox/substrait/SubstraitToVeloxPlanValidator.cc
b/cpp/velox/substrait/SubstraitToVeloxPlanValidator.cc
index 0146cae8eb..44313e423f 100644
--- a/cpp/velox/substrait/SubstraitToVeloxPlanValidator.cc
+++ b/cpp/velox/substrait/SubstraitToVeloxPlanValidator.cc
@@ -482,7 +482,11 @@ bool SubstraitToVeloxPlanValidator::validate(const
::substrait::FetchRel& fetchR
}
}
- if (fetchRel.offset() < 0 || fetchRel.count() < 0) {
+ int64_t offset =
+ fetchRel.has_offset_expr() ?
SubstraitParser::getLiteralValue<int64_t>(fetchRel.offset_expr().literal()) : 0;
+ int64_t count =
+ fetchRel.has_count_expr() ?
SubstraitParser::getLiteralValue<int64_t>(fetchRel.count_expr().literal()) : 0;
+ if (offset < 0 || count < 0) {
LOG_VALIDATION_MSG("Offset and count should be valid in FetchRel.");
return false;
}
diff --git a/cpp/velox/substrait/VeloxToSubstraitPlan.cc
b/cpp/velox/substrait/VeloxToSubstraitPlan.cc
index a137872411..f21ba82dab 100644
--- a/cpp/velox/substrait/VeloxToSubstraitPlan.cc
+++ b/cpp/velox/substrait/VeloxToSubstraitPlan.cc
@@ -356,8 +356,8 @@ void VeloxToSubstraitPlanConvertor::toSubstrait(
VELOX_CHECK(!topNNode->isPartial(), "Substrait doesn't support partial topN
yet");
- fetchRel->set_offset(0);
- fetchRel->set_count(topNNode->count());
+ fetchRel->mutable_offset_expr()->mutable_literal()->set_i64(0);
+
fetchRel->mutable_count_expr()->mutable_literal()->set_i64(topNNode->count());
fetchRel->mutable_common()->mutable_direct();
}
@@ -388,8 +388,8 @@ void VeloxToSubstraitPlanConvertor::toSubstrait(
const auto& source = getSingleSource(limitNode);
toSubstrait(arena, source, fetchRel->mutable_input());
- fetchRel->set_offset(limitNode->offset());
- fetchRel->set_count(limitNode->count());
+
fetchRel->mutable_offset_expr()->mutable_literal()->set_i64(limitNode->offset());
+
fetchRel->mutable_count_expr()->mutable_literal()->set_i64(limitNode->count());
VELOX_CHECK(!limitNode->isPartial(), "Substrait doesn't support partial
limit yet");
diff --git
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/rel/FetchRelNode.java
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/rel/FetchRelNode.java
index 91f5a00ce5..f83ef14312 100644
---
a/gluten-substrait/src/main/java/org/apache/gluten/substrait/rel/FetchRelNode.java
+++
b/gluten-substrait/src/main/java/org/apache/gluten/substrait/rel/FetchRelNode.java
@@ -16,6 +16,7 @@
*/
package org.apache.gluten.substrait.rel;
+import org.apache.gluten.substrait.expression.ExpressionBuilder;
import org.apache.gluten.substrait.extensions.AdvancedExtensionNode;
import io.substrait.proto.FetchRel;
@@ -57,8 +58,8 @@ public class FetchRelNode implements RelNode, Serializable {
if (input != null) {
fetchRelBuilder.setInput(input.toProtobuf());
}
- fetchRelBuilder.setOffset(offset);
- fetchRelBuilder.setCount(count);
+
fetchRelBuilder.setOffsetExpr(ExpressionBuilder.makeLongLiteral(offset).toProtobuf());
+
fetchRelBuilder.setCountExpr(ExpressionBuilder.makeLongLiteral(count).toProtobuf());
if (extensionNode != null) {
fetchRelBuilder.setAdvancedExtension(extensionNode.toProtobuf());
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 0e1eefdab0..aa3a5f1978 100644
---
a/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
+++
b/gluten-substrait/src/main/resources/substrait/proto/substrait/algebra.proto
@@ -331,12 +331,23 @@ message NestedLoopJoinRel {
// The relational operator representing LIMIT/OFFSET or TOP type semantics.
message FetchRel {
+ reserved 3, 4;
+ reserved "offset", "count";
+
RelCommon common = 1;
Rel input = 2;
- // the offset expressed in number of records
- int64 offset = 3;
- // the amount of records to return
- int64 count = 4;
+ // Expression evaluated into a non-negative integer specifying the number
+ // of records to skip. An expression evaluating to null is treated as 0.
+ // Evaluating to a negative integer should result in an error.
+ // Recommended type for offset is int64. Unset is treated as 0.
+ Expression offset_expr = 5;
+ // Expression evaluated into a non-negative integer specifying the number
+ // of records to return. An expression evaluating to null signals that ALL
+ // records should be returned.
+ // Evaluating to a negative integer should result in an error.
+ // Recommended type for count is int64. Unset signals that ALL records
+ // should be returned.
+ Expression count_expr = 6;
substrait.extensions.AdvancedExtension advanced_extension = 10;
}
diff --git
a/gluten-substrait/src/test/scala/org/apache/gluten/utils/FetchRelProtoSuite.scala
b/gluten-substrait/src/test/scala/org/apache/gluten/utils/FetchRelProtoSuite.scala
new file mode 100644
index 0000000000..4bcd1101fc
--- /dev/null
+++
b/gluten-substrait/src/test/scala/org/apache/gluten/utils/FetchRelProtoSuite.scala
@@ -0,0 +1,43 @@
+/*
+ * 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.utils
+
+import org.apache.gluten.substrait.SubstraitContext
+import org.apache.gluten.substrait.rel.RelBuilder
+
+import org.scalatest.funsuite.AnyFunSuite
+
+/**
+ * Locks the FetchRel producer contract after the Substrait 0.98 migration.
0.98 removed the scalar
+ * `int64 offset = 3` / `int64 count = 4` fields in favor of `Expression
offset_expr = 5` /
+ * `Expression count_expr = 6`. Gluten's only FetchRel producer feeds literal
`Long`s (from Spark
+ * Limit/Offset), so the producer now wraps each into an i64-literal
`Expression`. This suite pins
+ * that the values land in the new expression carriers as i64 literals.
+ */
+class FetchRelProtoSuite extends AnyFunSuite {
+
+ test("makeFetchRel emits offset/count as i64 literal expressions") {
+ val context = new SubstraitContext
+ val rel = RelBuilder.makeFetchRel(null, 5L, 10L, context, 0L)
+ val fetchRel = rel.toProtobuf.getFetch
+
+ assert(fetchRel.hasOffsetExpr)
+ assert(fetchRel.hasCountExpr)
+ assert(fetchRel.getOffsetExpr.getLiteral.getI64 === 5L)
+ assert(fetchRel.getCountExpr.getLiteral.getI64 === 10L)
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]