From b73c8993fc316297399455f0e87225e41e148173 Mon Sep 17 00:00:00 2001 From: Fangchen Li Date: Fri, 24 Jul 2026 20:32:27 -0700 Subject: [PATCH] GH-50634: [C++][Engine] Fix Substrait consumer silently dropping modern FetchRel/AggregateRel fields Arrow's Substrait consumer read several fields through deprecated accessors from a vendored substrait proto pinned to v0.44.0, so values sent by current producers (which use the newer forms introduced by ~v0.63.0) were silently dropped and defaulted, producing wrong results with no error. FetchRel (LIMIT/OFFSET): count/offset became oneofs (count_mode/offset_mode) and current producers (e.g. DataFusion, substrait-rs 0.63.0) emit the count_expr/offset_expr expression arms, leaving the scalar fields unset. The consumer read the default 0, turning every `LIMIT n` into `LIMIT 0` (empty result) and dropping OFFSET (skipping no rows). AggregateRel (GROUP BY): grouping expressions moved from the deprecated inline `Grouping.grouping_expressions` to `Grouping.expression_references` indexing the AggregateRel-level `grouping_expressions`. Reading only the deprecated field dropped the group keys, collapsing every row into a single group. - Bump the vendored substrait proto from v0.44.0 to v0.63.0 (matching DataFusion's pinned substrait-rs), where these newer forms were introduced while the deprecated arms remain for backward compatibility. - Rewrite the FetchRel consumer to read the count_mode/offset_mode oneofs: evaluate count_expr/offset_expr to a constant int64 literal, fall back to the deprecated scalar arm, treat an unset/null count as ALL and an unset/null offset as 0. A no-op fetch (ALL, offset 0) is skipped so it does not impose the fetch node's input-ordering requirement on plans that never limit output. - Read AggregateRel grouping keys via expression_references into the AggregateRel-level grouping_expressions, falling back to the deprecated inline field for older producers. - Update the JoinRel consumer for the renamed JoinType enum values (JOIN_TYPE_SEMI/ANTI -> JOIN_TYPE_LEFT_SEMI/LEFT_ANTI) surfaced by the bump. - Add regression tests for the count_expr/offset_expr arms, unset-count = ALL, and grouping via expression_references. Co-Authored-By: Claude Opus 4.8 --- cpp/cmake_modules/ThirdpartyToolchain.cmake | 18 +- .../engine/substrait/relation_internal.cc | 136 +++++++- cpp/src/arrow/engine/substrait/serde_test.cc | 303 ++++++++++++++++++ .../engine/substrait/test_plan_builder.cc | 4 + .../packagefiles/substrait/meson.build | 16 +- cpp/subprojects/substrait.wrap | 8 +- cpp/thirdparty/versions.txt | 4 +- 7 files changed, 461 insertions(+), 28 deletions(-) diff --git a/cpp/cmake_modules/ThirdpartyToolchain.cmake b/cpp/cmake_modules/ThirdpartyToolchain.cmake index 895b88118e45..5af1e8225fc7 100644 --- a/cpp/cmake_modules/ThirdpartyToolchain.cmake +++ b/cpp/cmake_modules/ThirdpartyToolchain.cmake @@ -2251,14 +2251,11 @@ if(ARROW_WITH_GOOGLE_CLOUD_CPP endif() if(ARROW_WITH_PROTOBUF) - if(ARROW_FLIGHT_SQL) - # Flight SQL uses proto3 optionals, which require 3.12 or later. + if(ARROW_FLIGHT_SQL OR ARROW_SUBSTRAIT) + # Flight SQL and Substrait use proto3 optionals, which require 3.12 or later. # 3.12.0-3.14.0: need --experimental_allow_proto3_optional # 3.15.0-: don't need --experimental_allow_proto3_optional set(ARROW_PROTOBUF_REQUIRED_VERSION "3.12.0") - elseif(ARROW_SUBSTRAIT) - # Substrait protobuf files use proto3 syntax - set(ARROW_PROTOBUF_REQUIRED_VERSION "3.0.0") else() set(ARROW_PROTOBUF_REQUIRED_VERSION "2.6.1") endif() @@ -2399,6 +2396,13 @@ macro(build_substrait) endif() endif() + set(SUBSTRAIT_PROTOC_ARGS) + if(Protobuf_VERSION VERSION_LESS 3.15) + # 3.12.0-3.14.0 require this flag to accept the proto3 optional fields used by + # the vendored Substrait protos; 3.15.0+ accept them by default. + list(APPEND SUBSTRAIT_PROTOC_ARGS "--experimental_allow_proto3_optional") + endif() + set(SUBSTRAIT_SOURCES) set(SUBSTRAIT_PROTO_GEN_ALL) foreach(SUBSTRAIT_PROTO ${SUBSTRAIT_PROTOS}) @@ -2418,7 +2422,7 @@ macro(build_substrait) endif() add_custom_command(OUTPUT "${SUBSTRAIT_PROTO_GEN}.cc" "${SUBSTRAIT_PROTO_GEN}.h" COMMAND ${ARROW_PROTOBUF_PROTOC} ${SUBSTRAIT_PROTOC_INCLUDES} - "--cpp_out=${SUBSTRAIT_CPP_DIR}" + ${SUBSTRAIT_PROTOC_ARGS} "--cpp_out=${SUBSTRAIT_CPP_DIR}" "${SUBSTRAIT_LOCAL_DIR}/proto/substrait/${SUBSTRAIT_PROTO}.proto" DEPENDS ${PROTO_DEPENDS} substrait_ep) @@ -2440,7 +2444,7 @@ macro(build_substrait) add_custom_command(OUTPUT "${ARROW_SUBSTRAIT_PROTO_GEN}.cc" "${ARROW_SUBSTRAIT_PROTO_GEN}.h" COMMAND ${ARROW_PROTOBUF_PROTOC} ${ARROW_SUBSTRAIT_PROTOC_INCLUDES} - "--cpp_out=${SUBSTRAIT_CPP_DIR}" + ${SUBSTRAIT_PROTOC_ARGS} "--cpp_out=${SUBSTRAIT_CPP_DIR}" "${ARROW_SUBSTRAIT_PROTOS_DIR}/substrait/${ARROW_SUBSTRAIT_PROTO}.proto" DEPENDS ${PROTO_DEPENDS} substrait_ep) diff --git a/cpp/src/arrow/engine/substrait/relation_internal.cc b/cpp/src/arrow/engine/substrait/relation_internal.cc index d8d77fa800cd..7e286484b66d 100644 --- a/cpp/src/arrow/engine/substrait/relation_internal.cc +++ b/cpp/src/arrow/engine/substrait/relation_internal.cc @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -32,6 +34,7 @@ #include "arrow/acero/exec_plan.h" #include "arrow/acero/options.h" #include "arrow/compute/api_aggregate.h" +#include "arrow/compute/cast.h" #include "arrow/compute/expression.h" #include "arrow/compute/kernel.h" #include "arrow/dataset/dataset.h" @@ -380,6 +383,31 @@ struct SortBehavior { } }; +// Evaluates a FetchRel offset_expr/count_expr to a constant int64. Acero needs a +// constant, so only literals are accepted; a null literal returns nullopt (unset). +Result> FetchArgFromExpr( + const substrait::Expression& expr, std::string_view arg_name, + const ExtensionSet& ext_set, const ConversionOptions& conversion_options) { + ARROW_ASSIGN_OR_RAISE(compute::Expression bound, + FromProto(expr, ext_set, conversion_options)); + const Datum* literal = bound.literal(); + if (literal == nullptr || !literal->is_scalar()) { + return Status::NotImplemented( + "substrait::FetchRel ", arg_name, + " is not a constant literal; Acero requires a constant offset/count"); + } + const Scalar& scalar = *literal->scalar(); + if (!scalar.is_valid) { + return std::nullopt; + } + if (!is_integer(scalar.type->id())) { + return Status::Invalid("substrait::FetchRel ", arg_name, + " must be an integer literal, got ", scalar.type->ToString()); + } + ARROW_ASSIGN_OR_RAISE(Datum casted, compute::Cast(*literal, int64())); + return checked_cast(*casted.scalar()).value; +} + } // namespace Result FromProto(const substrait::Rel& rel, const ExtensionSet& ext_set, @@ -691,10 +719,10 @@ Result FromProto(const substrait::Rel& rel, const ExtensionSet& case substrait::JoinRel::JOIN_TYPE_RIGHT: join_type = acero::JoinType::RIGHT_OUTER; break; - case substrait::JoinRel::JOIN_TYPE_SEMI: + case substrait::JoinRel::JOIN_TYPE_LEFT_SEMI: join_type = acero::JoinType::LEFT_SEMI; break; - case substrait::JoinRel::JOIN_TYPE_ANTI: + case substrait::JoinRel::JOIN_TYPE_LEFT_ANTI: join_type = acero::JoinType::LEFT_ANTI; break; default: @@ -779,8 +807,73 @@ Result FromProto(const substrait::Rel& rel, const ExtensionSet& ARROW_ASSIGN_OR_RAISE(auto input, FromProto(fetch.input(), ext_set, conversion_options)); - int64_t offset = fetch.offset(); - int64_t count = fetch.count(); + // Unset offset is 0. The scalar `offset` is deprecated for `offset_expr` + // but still read for older producers. + int64_t offset = 0; + switch (fetch.offset_mode_case()) { + case substrait::FetchRel::kOffset: + // GH-44954: the scalar `offset` field is [[deprecated]] in the proto. + ARROW_SUPPRESS_DEPRECATION_WARNING + offset = fetch.offset(); + ARROW_UNSUPPRESS_DEPRECATION_WARNING + break; + case substrait::FetchRel::kOffsetExpr: { + ARROW_ASSIGN_OR_RAISE(auto value, + FetchArgFromExpr(fetch.offset_expr(), "offset", ext_set, + conversion_options)); + offset = value.value_or(0); + break; + } + case substrait::FetchRel::OFFSET_MODE_NOT_SET: + break; + } + if (offset < 0) { + return Status::Invalid("substrait::FetchRel offset must be non-negative, got ", + offset); + } + + // Unset count, a null count_expr, or the deprecated scalar -1 all mean ALL. + // Acero has no unbounded fetch, so ALL uses int64 max (FetchCounter tracks + // skipped and returned rows separately, so this cannot overflow). + constexpr int64_t kFetchAll = std::numeric_limits::max(); + int64_t count = kFetchAll; + switch (fetch.count_mode_case()) { + case substrait::FetchRel::kCount: { + // The deprecated scalar field uses -1 to signal ALL. + ARROW_SUPPRESS_DEPRECATION_WARNING + int64_t scalar_count = fetch.count(); + ARROW_UNSUPPRESS_DEPRECATION_WARNING + if (scalar_count >= 0) { + count = scalar_count; + } else if (scalar_count != -1) { + return Status::Invalid( + "substrait::FetchRel count must be non-negative or -1 (ALL), got ", + scalar_count); + } + break; + } + case substrait::FetchRel::kCountExpr: { + ARROW_ASSIGN_OR_RAISE( + auto value, + FetchArgFromExpr(fetch.count_expr(), "count", ext_set, conversion_options)); + if (value.has_value()) { + if (*value < 0) { + return Status::Invalid( + "substrait::FetchRel count must be non-negative, got ", *value); + } + count = *value; + } + break; + } + case substrait::FetchRel::COUNT_MODE_NOT_SET: + break; + } + + // Skip a no-op fetch (keep everything) so it does not impose the fetch + // node's input-ordering requirement on plans that never limit their output. + if (count == kFetchAll && offset == 0) { + return ProcessEmit(fetch, input, input.output_schema); + } acero::Declaration fetch_dec{ "fetch", {input.declaration}, acero::FetchNodeOptions(offset, count)}; @@ -855,12 +948,35 @@ Result FromProto(const substrait::Rel& rel, const ExtensionSet& std::vector keys; if (aggregate.groupings_size() > 0) { const substrait::AggregateRel::Grouping& group = aggregate.groupings(0); - int grouping_expr_size = group.grouping_expressions_size(); - keys.reserve(grouping_expr_size); - for (int exp_id = 0; exp_id < grouping_expr_size; exp_id++) { - ARROW_ASSIGN_OR_RAISE( - compute::Expression expr, - FromProto(group.grouping_expressions(exp_id), ext_set, conversion_options)); + // Current producers reference grouping expressions by index into the + // AggregateRel-level `grouping_expressions`; older ones inline them in the + // deprecated `Grouping.grouping_expressions`. Reading only the deprecated + // field drops the group keys, collapsing every row into one group. + std::vector grouping_exprs; + if (group.expression_references_size() > 0) { + grouping_exprs.reserve(group.expression_references_size()); + for (uint32_t ref : group.expression_references()) { + if (ref >= static_cast(aggregate.grouping_expressions_size())) { + return Status::Invalid( + "substrait::AggregateRel grouping expression reference ", ref, + " is out of bounds (", aggregate.grouping_expressions_size(), + " grouping expressions)"); + } + grouping_exprs.push_back(&aggregate.grouping_expressions(ref)); + } + } else { + // GH-44954: the inline `Grouping.grouping_expressions` is [[deprecated]]. + ARROW_SUPPRESS_DEPRECATION_WARNING + grouping_exprs.reserve(group.grouping_expressions_size()); + for (const auto& grouping_expr : group.grouping_expressions()) { + grouping_exprs.push_back(&grouping_expr); + } + ARROW_UNSUPPRESS_DEPRECATION_WARNING + } + keys.reserve(grouping_exprs.size()); + for (const substrait::Expression* grouping_expr : grouping_exprs) { + ARROW_ASSIGN_OR_RAISE(compute::Expression expr, + FromProto(*grouping_expr, ext_set, conversion_options)); const FieldRef* field_ref = expr.field_ref(); if (field_ref) { keys.emplace_back(std::move(*field_ref)); diff --git a/cpp/src/arrow/engine/substrait/serde_test.cc b/cpp/src/arrow/engine/substrait/serde_test.cc index 4de5480ef3cd..d680b3c648e3 100644 --- a/cpp/src/arrow/engine/substrait/serde_test.cc +++ b/cpp/src/arrow/engine/substrait/serde_test.cc @@ -2085,6 +2085,82 @@ TEST(Substrait, AggregateBasic) { EXPECT_EQ(agg_options.aggregates[0].function, "hash_sum"); } +// Grouping keys referenced by index (expression_references into the +// AggregateRel-level grouping_expressions) must be resolved; otherwise the keys +// are dropped and the aggregate degrades from a grouped hash_sum to a global sum. +TEST(Substrait, AggregateGroupingExpressionReferences) { + ASSERT_OK_AND_ASSIGN(auto buf, + internal::SubstraitFromJSON("Plan", R"({ + "version": { "major_number": 9999, "minor_number": 9999, "patch_number": 9999 }, + "relations": [{ + "rel": { + "aggregate": { + "input": { + "read": { + "base_schema": { + "names": ["A", "B", "C"], + "struct": { + "types": [{ "i32": {} }, { "i32": {} }, { "i32": {} }] + } + }, + "local_files": { + "items": [ + { "uri_file": "file:///tmp/dat.parquet", "parquet": {} } + ] + } + } + }, + "groupings": [{ + "expressionReferences": [0] + }], + "groupingExpressions": [{ + "selection": { + "directReference": { "structField": { "field": 0 } } + } + }], + "measures": [{ + "measure": { + "functionReference": 0, + "arguments": [{ + "value": { + "selection": { + "directReference": { "structField": { "field": 1 } } + } + } + }], + "sorts": [], + "phase": "AGGREGATION_PHASE_INITIAL_TO_RESULT", + "outputType": { "i64": {} } + } + }] + } + } + }], + "extensionUris": [{ + "extension_uri_anchor": 0, + "uri": "https://github.com/substrait-io/substrait/blob/main/extensions/functions_arithmetic.yaml" + }], + "extensions": [{ + "extension_function": { + "extension_uri_reference": 0, + "function_anchor": 0, + "name": "sum" + } + }], + })", + /*ignore_unknown_fields=*/false)); + + ASSERT_OK_AND_ASSIGN(auto sink_decls, + DeserializePlans(*buf, [] { return kNullConsumer; })); + const auto& agg_rel = std::get(sink_decls[0].inputs[0]); + const auto& agg_options = + checked_cast(*agg_rel.options); + + EXPECT_EQ(agg_rel.factory_name, "aggregate"); + ASSERT_EQ(agg_options.keys.size(), 1); + EXPECT_EQ(agg_options.aggregates[0].function, "hash_sum"); +} + TEST(Substrait, AggregateInvalidRel) { ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", R"({ @@ -5582,6 +5658,233 @@ TEST(Substrait, SortAndFetch) { CheckRoundTripResult(std::move(output_table), buf, {}, conversion_options); } +// Current producers (e.g. DataFusion) express a FetchRel limit through the +// count_expr/offset_expr arms; the consumer must evaluate them instead of reading +// the unset scalar fields (which default to 0, i.e. LIMIT 0). +TEST(Substrait, FetchViaCountAndOffsetExpr) { + std::string substrait_json = R"({ + "version": { + "major_number": 9999, + "minor_number": 9999, + "patch_number": 9999 + }, + "relations": [ + { + "rel": { + "fetch": { + "input": { + "sort": { + "input": { + "read": { + "base_schema": { + "names": ["A"], + "struct": { + "types": [{"i32": {}}] + } + }, + "namedTable": { + "names": ["table"] + } + } + }, + "sorts": [ + { + "expr": { + "selection": { + "directReference": { + "structField": {"field": 0} + }, + "rootReference": {} + } + }, + "direction": "SORT_DIRECTION_ASC_NULLS_FIRST" + } + ] + } + }, + "offset_expr": {"literal": {"i64": "2"}}, + "count_expr": {"literal": {"i64": "3"}} + } + } + } + ], + "extension_uris": [], + "extensions": [] +})"; + + ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", substrait_json)); + auto test_schema = schema({field("A", int32())}); + + auto input_table = TableFromJSON(test_schema, {R"([ + [null], [5], [null], [null], [3], [9], [4] + ])"}); + + // Sort A ascending, nulls first, yields [null, null, null, 3, 4, 5, 9]. + // offset_expr=2, count_expr=3 grabs [null, 3, 4]. + auto output_table = TableFromJSON(test_schema, {R"([ + [null], [3], [4] + ])"}); + + ConversionOptions conversion_options; + conversion_options.named_table_provider = + AlwaysProvideSameTable(std::move(input_table)); + + CheckRoundTripResult(std::move(output_table), buf, {}, conversion_options); +} + +// An unset `count_mode` means "return ALL rows" (not `LIMIT 0`). With no offset +// either, the FetchRel is a no-op that must pass every row through. +TEST(Substrait, FetchUnsetCountReturnsAllRows) { + std::string substrait_json = R"({ + "version": { + "major_number": 9999, + "minor_number": 9999, + "patch_number": 9999 + }, + "relations": [ + { + "rel": { + "fetch": { + "input": { + "read": { + "base_schema": { + "names": ["A"], + "struct": { + "types": [{"i32": {}}] + } + }, + "namedTable": { + "names": ["table"] + } + } + } + } + } + } + ], + "extension_uris": [], + "extensions": [] +})"; + + ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", substrait_json)); + auto test_schema = schema({field("A", int32())}); + + auto input_table = TableFromJSON(test_schema, {R"([ + [1], [2], [3], [4] + ])"}); + auto output_table = TableFromJSON(test_schema, {R"([ + [1], [2], [3], [4] + ])"}); + + ConversionOptions conversion_options; + conversion_options.named_table_provider = + AlwaysProvideSameTable(std::move(input_table)); + + CheckRoundTripResult(std::move(output_table), buf, {}, conversion_options); +} + +// offset_expr in isolation, with an unset count (ALL). Also covers the "offset +// but no limit" path (a fetch node with an unbounded count). +TEST(Substrait, FetchOffsetExprWithUnsetCount) { + std::string substrait_json = R"({ + "version": { + "major_number": 9999, + "minor_number": 9999, + "patch_number": 9999 + }, + "relations": [ + { + "rel": { + "fetch": { + "input": { + "sort": { + "input": { + "read": { + "base_schema": { + "names": ["A"], + "struct": { + "types": [{"i32": {}}] + } + }, + "namedTable": { + "names": ["table"] + } + } + }, + "sorts": [ + { + "expr": { + "selection": { + "directReference": { + "structField": {"field": 0} + }, + "rootReference": {} + } + }, + "direction": "SORT_DIRECTION_ASC_NULLS_FIRST" + } + ] + } + }, + "offset_expr": {"literal": {"i64": "2"}} + } + } + } + ], + "extension_uris": [], + "extensions": [] +})"; + + ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", substrait_json)); + auto test_schema = schema({field("A", int32())}); + + auto input_table = TableFromJSON(test_schema, {R"([ + [1], [2], [3], [4], [5] + ])"}); + + // Sort ascending yields [1, 2, 3, 4, 5]; offset_expr=2 skips the first two and, + // with no count, returns all remaining rows. + auto output_table = TableFromJSON(test_schema, {R"([ + [3], [4], [5] + ])"}); + + ConversionOptions conversion_options; + conversion_options.named_table_provider = + AlwaysProvideSameTable(std::move(input_table)); + + CheckRoundTripResult(std::move(output_table), buf, {}, conversion_options); +} + +// A count/offset expression must evaluate to an integer literal; a non-integer +// literal is out of contract and must be rejected rather than silently coerced. +TEST(Substrait, FetchNonIntegerCountExprRejected) { + ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", R"({ + "version": { "major_number": 9999, "minor_number": 9999, "patch_number": 9999 }, + "relations": [{ + "rel": { + "fetch": { + "input": { + "read": { + "base_schema": { + "names": ["A"], + "struct": { "types": [{ "i32": {} }] } + }, + "local_files": { + "items": [ { "uri_file": "file:///tmp/dat.parquet", "parquet": {} } ] + } + } + }, + "count_expr": { "literal": { "fp64": 3.0 } } + } + } + }], + "extension_uris": [], + "extensions": [] + })")); + + ASSERT_RAISES(Invalid, DeserializePlans(*buf, [] { return kNullConsumer; })); +} + TEST(Substrait, MixedSort) { std::string substrait_json = R"({ "version": { diff --git a/cpp/src/arrow/engine/substrait/test_plan_builder.cc b/cpp/src/arrow/engine/substrait/test_plan_builder.cc index a8302145f548..e2a3463d0347 100644 --- a/cpp/src/arrow/engine/substrait/test_plan_builder.cc +++ b/cpp/src/arrow/engine/substrait/test_plan_builder.cc @@ -132,7 +132,11 @@ Result> CreateAgg(Id function_id, if (!keys.empty()) { substrait::AggregateRel::Grouping* grouping = agg->add_groupings(); for (int key : keys) { + // GH-44954: the inline `Grouping.grouping_expressions` is [[deprecated]]; this + // test helper deliberately exercises the deprecated form. + ARROW_SUPPRESS_DEPRECATION_WARNING substrait::Expression* key_expr = grouping->add_grouping_expressions(); + ARROW_UNSUPPRESS_DEPRECATION_WARNING CreateDirectReference(key, key_expr); } } diff --git a/cpp/subprojects/packagefiles/substrait/meson.build b/cpp/subprojects/packagefiles/substrait/meson.build index c0f4716be73f..483f2661fd43 100644 --- a/cpp/subprojects/packagefiles/substrait/meson.build +++ b/cpp/subprojects/packagefiles/substrait/meson.build @@ -36,14 +36,20 @@ else endif substrait_protos_dir = meson.current_source_dir() / 'proto' +protoc_arguments = [ + '--proto_path=@0@'.format(substrait_protos_dir), + '--cpp_out=@0@@1@'.format(proto_visibility, '@BUILD_DIR@'), + '@INPUT@', +] +# 3.12.0-3.14.0 require this flag to accept the proto3 optional fields used by +# the Substrait protos; 3.15.0+ accept them by default. +if protobuf_dep.version().version_compare('<3.15') + protoc_arguments = ['--experimental_allow_proto3_optional'] + protoc_arguments +endif gen = generator( protoc, output: ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'], - arguments: [ - '--proto_path=@0@'.format(substrait_protos_dir), - '--cpp_out=@0@@1@'.format(proto_visibility, '@BUILD_DIR@'), - '@INPUT@', - ], + arguments: protoc_arguments, ) protos = [ 'substrait/algebra.proto', diff --git a/cpp/subprojects/substrait.wrap b/cpp/subprojects/substrait.wrap index 66708dccc207..b1fca70f2d19 100644 --- a/cpp/subprojects/substrait.wrap +++ b/cpp/subprojects/substrait.wrap @@ -16,10 +16,10 @@ # under the License. [wrap-file] -source_url = https://github.com/substrait-io/substrait/archive/v0.44.0.tar.gz -source_filename = substrait-0.44.0.tar.gz -source_hash = f989a862f694e7dbb695925ddb7c4ce06aa6c51aca945105c075139aed7e55a2 -directory = substrait-0.44.0 +source_url = https://github.com/substrait-io/substrait/archive/v0.63.0.tar.gz +source_filename = substrait-0.63.0.tar.gz +source_hash = 14aabf828d54fbae38aa90f2c10006952c0040feff8f813cabad5bc13d6fbcfe +directory = substrait-0.63.0 patch_directory=substrait [provide] diff --git a/cpp/thirdparty/versions.txt b/cpp/thirdparty/versions.txt index c0fbaaab12f8..2272f53f8f15 100644 --- a/cpp/thirdparty/versions.txt +++ b/cpp/thirdparty/versions.txt @@ -106,8 +106,8 @@ ARROW_SIMDJSON_BUILD_VERSION=v4.6.4 ARROW_SIMDJSON_BUILD_SHA256_CHECKSUM=b091107844fe928158c5c2265c20360fff312889ddf7ebc4528a0f0f8f2ff9cd ARROW_SNAPPY_BUILD_VERSION=1.2.2 ARROW_SNAPPY_BUILD_SHA256_CHECKSUM=90f74bc1fbf78a6c56b3c4a082a05103b3a56bb17bca1a27e052ea11723292dc -ARROW_SUBSTRAIT_BUILD_VERSION=v0.44.0 -ARROW_SUBSTRAIT_BUILD_SHA256_CHECKSUM=f989a862f694e7dbb695925ddb7c4ce06aa6c51aca945105c075139aed7e55a2 +ARROW_SUBSTRAIT_BUILD_VERSION=v0.63.0 +ARROW_SUBSTRAIT_BUILD_SHA256_CHECKSUM=14aabf828d54fbae38aa90f2c10006952c0040feff8f813cabad5bc13d6fbcfe ARROW_S2N_TLS_BUILD_VERSION=v1.7.0 ARROW_S2N_TLS_BUILD_SHA256_CHECKSUM=a6e8228e238239bb3c17b1eda3ed702bcbb2eaebc792eac4d754cc5619b0ea06 ARROW_THRIFT_BUILD_VERSION=0.22.0