This is an automated email from the ASF dual-hosted git repository.

westonpace pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow.git


The following commit(s) were added to refs/heads/master by this push:
     new 3c2479f58f ARROW-17736: [C++] Added a fallback name resolution 
mechanism to the Substrait producer. (#14143)
3c2479f58f is described below

commit 3c2479f58f71d0417cadd9d8e51112e2220ac8ee
Author: Weston Pace <[email protected]>
AuthorDate: Mon Sep 26 10:41:43 2022 -1000

    ARROW-17736: [C++] Added a fallback name resolution mechanism to the 
Substrait producer. (#14143)
    
    This will only be used if the URI part of the function reference is empty 
or /.  This is for compatibility with Isthmus which still hasn't decided what 
to use for the URI portion.  Longer term we may remove this.  Or we may leave 
it in as a utility for developers of new functions that don't want to have to 
worry about the URI initially.
    
    Authored-by: Weston Pace <[email protected]>
    Signed-off-by: Weston Pace <[email protected]>
---
 .../arrow/engine/substrait/expression_internal.cc  |  31 +++-
 cpp/src/arrow/engine/substrait/extension_set.cc    |  40 ++++-
 cpp/src/arrow/engine/substrait/extension_set.h     |  16 ++
 cpp/src/arrow/engine/substrait/plan_internal.cc    |   3 +-
 cpp/src/arrow/engine/substrait/plan_internal.h     |   2 +-
 .../arrow/engine/substrait/relation_internal.cc    |  13 +-
 cpp/src/arrow/engine/substrait/serde.cc            |   3 +-
 cpp/src/arrow/engine/substrait/serde_test.cc       | 185 ++++++++++++++++-----
 8 files changed, 239 insertions(+), 54 deletions(-)

diff --git a/cpp/src/arrow/engine/substrait/expression_internal.cc 
b/cpp/src/arrow/engine/substrait/expression_internal.cc
index 0f1c41f14c..9445be6f03 100644
--- a/cpp/src/arrow/engine/substrait/expression_internal.cc
+++ b/cpp/src/arrow/engine/substrait/expression_internal.cc
@@ -37,6 +37,21 @@ using internal::checked_cast;
 
 namespace engine {
 
+namespace {
+
+Id NormalizeFunctionName(Id id) {
+  // Substrait plans encode the types into the function name so it might look 
like
+  // add:opt_i32_i32.  We don't care about  the :opt_i32_i32 so we just trim it
+  std::string_view func_name = id.name;
+  std::size_t colon_index = func_name.find_first_of(':');
+  if (colon_index != std::string_view::npos) {
+    func_name = func_name.substr(0, colon_index);
+  }
+  return {id.uri, func_name};
+}
+
+}  // namespace
+
 Status DecodeArg(const substrait::FunctionArgument& arg, uint32_t idx,
                  SubstraitCall* call, const ExtensionSet& ext_set,
                  const ConversionOptions& conversion_options) {
@@ -112,6 +127,7 @@ Result<SubstraitCall> FromProto(const 
substrait::AggregateFunction& func, bool i
   ARROW_ASSIGN_OR_RAISE(auto output_type_and_nullable,
                         FromProto(func.output_type(), ext_set, 
conversion_options));
   ARROW_ASSIGN_OR_RAISE(Id id, 
ext_set.DecodeFunction(func.function_reference()));
+  id = NormalizeFunctionName(id);
   SubstraitCall call(id, output_type_and_nullable.first, 
output_type_and_nullable.second,
                      is_hash);
   for (int i = 0; i < func.arguments_size(); i++) {
@@ -248,8 +264,19 @@ Result<compute::Expression> FromProto(const 
substrait::Expression& expr,
 
       ARROW_ASSIGN_OR_RAISE(Id function_id,
                             
ext_set.DecodeFunction(scalar_fn.function_reference()));
-      ARROW_ASSIGN_OR_RAISE(ExtensionIdRegistry::SubstraitCallToArrow 
function_converter,
-                            
ext_set.registry()->GetSubstraitCallToArrow(function_id));
+      function_id = NormalizeFunctionName(function_id);
+      ExtensionIdRegistry::SubstraitCallToArrow function_converter;
+
+      if (function_id.uri.empty() || function_id.uri[0] == '/') {
+        // Currently the Substrait project has not aligned on a standard URI 
and often
+        // seems to use /.  In that case we fall back to name-only matching.
+        ARROW_ASSIGN_OR_RAISE(
+            function_converter,
+            
ext_set.registry()->GetSubstraitCallToArrowFallback(function_id.name));
+      } else {
+        ARROW_ASSIGN_OR_RAISE(function_converter,
+                              
ext_set.registry()->GetSubstraitCallToArrow(function_id));
+      }
       ARROW_ASSIGN_OR_RAISE(
           SubstraitCall substrait_call,
           DecodeScalarFunction(function_id, scalar_fn, ext_set, 
conversion_options));
diff --git a/cpp/src/arrow/engine/substrait/extension_set.cc 
b/cpp/src/arrow/engine/substrait/extension_set.cc
index af3d146704..63ba598c82 100644
--- a/cpp/src/arrow/engine/substrait/extension_set.cc
+++ b/cpp/src/arrow/engine/substrait/extension_set.cc
@@ -30,9 +30,6 @@ namespace arrow {
 namespace engine {
 namespace {
 
-// TODO(ARROW-16988): replace this with EXACT_ROUNDTRIP mode
-constexpr bool kExactRoundTrip = true;
-
 struct TypePtrHashEq {
   template <typename Ptr>
   size_t operator()(const Ptr& type) const {
@@ -210,7 +207,8 @@ Status ExtensionSet::AddUri(Id id) {
 Result<ExtensionSet> ExtensionSet::Make(
     std::unordered_map<uint32_t, std::string_view> uris,
     std::unordered_map<uint32_t, Id> type_ids,
-    std::unordered_map<uint32_t, Id> function_ids, const ExtensionIdRegistry* 
registry) {
+    std::unordered_map<uint32_t, Id> function_ids,
+    const ConversionOptions& conversion_options, const ExtensionIdRegistry* 
registry) {
   ExtensionSet set(default_extension_id_registry());
   set.registry_ = registry;
 
@@ -219,7 +217,7 @@ Result<ExtensionSet> ExtensionSet::Make(
     if (maybe_uri_internal) {
       set.uris_[uri.first] = *maybe_uri_internal;
     } else {
-      if (kExactRoundTrip) {
+      if (conversion_options.strictness == 
ConversionStrictness::EXACT_ROUNDTRIP) {
         return Status::Invalid(
             "Plan contained a URI that the extension registry is unaware of: ",
             uri.second);
@@ -249,7 +247,7 @@ Result<ExtensionSet> ExtensionSet::Make(
     if (maybe_id_internal) {
       set.functions_[function_id.first] = *maybe_id_internal;
     } else {
-      if (kExactRoundTrip) {
+      if (conversion_options.strictness == 
ConversionStrictness::EXACT_ROUNDTRIP) {
         return Status::Invalid(
             "Plan contained a function id that the extension registry is 
unaware of: ",
             function_id.second.uri, "#", function_id.second.name);
@@ -532,6 +530,21 @@ struct ExtensionIdRegistryImpl : ExtensionIdRegistry {
     return maybe_converter->second;
   }
 
+  Result<SubstraitCallToArrow> GetSubstraitCallToArrowFallback(
+      std::string_view function_name) const override {
+    for (const auto& converter_item : substrait_to_arrow_) {
+      if (converter_item.first.name == function_name) {
+        return converter_item.second;
+      }
+    }
+    if (parent_) {
+      return parent_->GetSubstraitCallToArrowFallback(function_name);
+    }
+    return Status::NotImplemented(
+        "No conversion function exists to convert the Substrait function ", 
function_name,
+        " to an Arrow call expression");
+  }
+
   Result<SubstraitAggregateToArrow> GetSubstraitAggregateToArrow(
       Id substrait_function_id) const override {
     auto maybe_converter = substrait_to_arrow_agg_.find(substrait_function_id);
@@ -547,6 +560,21 @@ struct ExtensionIdRegistryImpl : ExtensionIdRegistry {
     return maybe_converter->second;
   }
 
+  Result<SubstraitAggregateToArrow> GetSubstraitAggregateToArrowFallback(
+      std::string_view function_name) const override {
+    for (const auto& converter_item : substrait_to_arrow_agg_) {
+      if (converter_item.first.name == function_name) {
+        return converter_item.second;
+      }
+    }
+    if (parent_) {
+      return parent_->GetSubstraitAggregateToArrowFallback(function_name);
+    }
+    return Status::NotImplemented(
+        "No conversion function exists to convert the Substrait aggregate 
function ",
+        function_name, " to an Arrow call expression");
+  }
+
   Result<ArrowToSubstraitCall> GetArrowToSubstraitCall(
       const std::string& arrow_function_name) const override {
     auto maybe_converter = arrow_to_substrait_.find(arrow_function_name);
diff --git a/cpp/src/arrow/engine/substrait/extension_set.h 
b/cpp/src/arrow/engine/substrait/extension_set.h
index 4df8952ff9..e2e09d0d92 100644
--- a/cpp/src/arrow/engine/substrait/extension_set.h
+++ b/cpp/src/arrow/engine/substrait/extension_set.h
@@ -32,6 +32,7 @@
 
 #include "arrow/compute/api_aggregate.h"
 #include "arrow/compute/exec/expression.h"
+#include "arrow/engine/substrait/options.h"
 #include "arrow/engine/substrait/visibility.h"
 #include "arrow/result.h"
 #include "arrow/type_fwd.h"
@@ -253,6 +254,20 @@ class ARROW_ENGINE_EXPORT ExtensionIdRegistry {
   /// \return A converter function or an invalid status if no converter is 
registered
   virtual Result<SubstraitCallToArrow> GetSubstraitCallToArrow(
       Id substrait_function_id) const = 0;
+
+  /// \brief Similar to \see GetSubstraitCallToArrow but only uses the name
+  ///
+  /// There may be multiple functions with the same name and this will return
+  /// the first.  This is slower than GetSubstraitCallToArrow and should only
+  /// be used when the plan does not include a URI (or the URI is "/")
+  virtual Result<SubstraitCallToArrow> GetSubstraitCallToArrowFallback(
+      std::string_view function_name) const = 0;
+
+  /// \brief Similar to \see GetSubstraitAggregateToArrow but only uses the 
name
+  ///
+  /// \see GetSubstraitCallToArrowFallback for details on the fallback behavior
+  virtual Result<SubstraitAggregateToArrow> 
GetSubstraitAggregateToArrowFallback(
+      std::string_view function_name) const = 0;
 };
 
 constexpr std::string_view kArrowExtTypesUri =
@@ -339,6 +354,7 @@ class ARROW_ENGINE_EXPORT ExtensionSet {
       std::unordered_map<uint32_t, std::string_view> uris,
       std::unordered_map<uint32_t, Id> type_ids,
       std::unordered_map<uint32_t, Id> function_ids,
+      const ConversionOptions& conversion_options,
       const ExtensionIdRegistry* = default_extension_id_registry());
 
   const std::unordered_map<uint32_t, std::string_view>& uris() const { return 
uris_; }
diff --git a/cpp/src/arrow/engine/substrait/plan_internal.cc 
b/cpp/src/arrow/engine/substrait/plan_internal.cc
index 81dc6c2103..18915868ee 100644
--- a/cpp/src/arrow/engine/substrait/plan_internal.cc
+++ b/cpp/src/arrow/engine/substrait/plan_internal.cc
@@ -88,6 +88,7 @@ Status AddExtensionSetToPlan(const ExtensionSet& ext_set, 
substrait::Plan* plan)
 }
 
 Result<ExtensionSet> GetExtensionSetFromPlan(const substrait::Plan& plan,
+                                             const ConversionOptions& 
conversion_options,
                                              const ExtensionIdRegistry* 
registry) {
   if (registry == NULLPTR) {
     registry = default_extension_id_registry();
@@ -128,7 +129,7 @@ Result<ExtensionSet> GetExtensionSetFromPlan(const 
substrait::Plan& plan,
   }
 
   return ExtensionSet::Make(std::move(uris), std::move(type_ids), 
std::move(function_ids),
-                            registry);
+                            conversion_options, registry);
 }
 
 Result<std::unique_ptr<substrait::Plan>> PlanToProto(
diff --git a/cpp/src/arrow/engine/substrait/plan_internal.h 
b/cpp/src/arrow/engine/substrait/plan_internal.h
index e1ced549ce..7d6dd37528 100644
--- a/cpp/src/arrow/engine/substrait/plan_internal.h
+++ b/cpp/src/arrow/engine/substrait/plan_internal.h
@@ -50,7 +50,7 @@ Status AddExtensionSetToPlan(const ExtensionSet& ext_set, 
substrait::Plan* plan)
 /// correspond to Substrait's URI/name pairs
 ARROW_ENGINE_EXPORT
 Result<ExtensionSet> GetExtensionSetFromPlan(
-    const substrait::Plan& plan,
+    const substrait::Plan& plan, const ConversionOptions& conversion_options,
     const ExtensionIdRegistry* registry = default_extension_id_registry());
 
 /// \brief Serialize a declaration into a substrait::Plan.
diff --git a/cpp/src/arrow/engine/substrait/relation_internal.cc 
b/cpp/src/arrow/engine/substrait/relation_internal.cc
index 06c4adaba3..2afe177eb1 100644
--- a/cpp/src/arrow/engine/substrait/relation_internal.cc
+++ b/cpp/src/arrow/engine/substrait/relation_internal.cc
@@ -505,9 +505,16 @@ Result<DeclarationInfo> FromProto(const substrait::Rel& 
rel, const ExtensionSet&
           ARROW_ASSIGN_OR_RAISE(
               SubstraitCall aggregate_call,
               FromProto(agg_func, !keys.empty(), ext_set, conversion_options));
-          ARROW_ASSIGN_OR_RAISE(
-              ExtensionIdRegistry::SubstraitAggregateToArrow converter,
-              
ext_set.registry()->GetSubstraitAggregateToArrow(aggregate_call.id()));
+          ExtensionIdRegistry::SubstraitAggregateToArrow converter;
+          if (aggregate_call.id().uri.empty() || aggregate_call.id().uri[0] == 
'/') {
+            ARROW_ASSIGN_OR_RAISE(
+                converter, 
ext_set.registry()->GetSubstraitAggregateToArrowFallback(
+                               aggregate_call.id().name));
+          } else {
+            ARROW_ASSIGN_OR_RAISE(
+                converter,
+                
ext_set.registry()->GetSubstraitAggregateToArrow(aggregate_call.id()));
+          }
           ARROW_ASSIGN_OR_RAISE(compute::Aggregate arrow_agg, 
converter(aggregate_call));
 
           // find aggregate field ids from schema
diff --git a/cpp/src/arrow/engine/substrait/serde.cc 
b/cpp/src/arrow/engine/substrait/serde.cc
index c7792c7c76..b2416aa7ba 100644
--- a/cpp/src/arrow/engine/substrait/serde.cc
+++ b/cpp/src/arrow/engine/substrait/serde.cc
@@ -145,7 +145,8 @@ Result<std::vector<compute::Declaration>> DeserializePlans(
     const ConversionOptions& conversion_options) {
   ARROW_ASSIGN_OR_RAISE(auto plan, ParseFromBuffer<substrait::Plan>(buf));
 
-  ARROW_ASSIGN_OR_RAISE(auto ext_set, GetExtensionSetFromPlan(plan, registry));
+  ARROW_ASSIGN_OR_RAISE(auto ext_set,
+                        GetExtensionSetFromPlan(plan, conversion_options, 
registry));
 
   std::vector<compute::Declaration> sink_decls;
   for (const substrait::PlanRel& plan_rel : plan.relations()) {
diff --git a/cpp/src/arrow/engine/substrait/serde_test.cc 
b/cpp/src/arrow/engine/substrait/serde_test.cc
index de40a53cbc..564d68f360 100644
--- a/cpp/src/arrow/engine/substrait/serde_test.cc
+++ b/cpp/src/arrow/engine/substrait/serde_test.cc
@@ -95,7 +95,10 @@ Result<std::shared_ptr<Table>> GetTableFromPlan(
 
   RETURN_NOT_OK(plan->Validate());
   RETURN_NOT_OK(plan->StartProducing());
-  return arrow::Table::FromRecordBatchReader(sink_reader.get());
+  ARROW_ASSIGN_OR_RAISE(std::shared_ptr<Table> table,
+                        
arrow::Table::FromRecordBatchReader(sink_reader.get()));
+  RETURN_NOT_OK(plan->finished().status());
+  return table;
 }
 
 class NullSinkNodeConsumer : public compute::SinkNodeConsumer {
@@ -194,7 +197,7 @@ void CheckRoundTripResult(const std::shared_ptr<Schema> 
output_schema,
     ASSERT_OK_AND_ASSIGN(output_table, 
output_table->SelectColumns(include_columns));
   }
   ASSERT_OK_AND_ASSIGN(output_table, output_table->CombineChunks());
-  EXPECT_TRUE(expected_table->Equals(*output_table));
+  AssertTablesEqual(*expected_table, *output_table);
 }
 
 TEST(Substrait, SupportedTypes) {
@@ -843,8 +846,13 @@ TEST(Substrait, ExtensionSetFromPlanMissingFunc) {
        {std::shared_ptr<ExtensionIdRegistry>(), MakeExtensionIdRegistry()}) {
     ExtensionIdRegistry* ext_id_reg = sp_ext_id_reg.get();
     ExtensionSet ext_set(ext_id_reg);
-    ASSERT_RAISES(Invalid, DeserializePlans(
-                               *buf, [] { return kNullConsumer; }, ext_id_reg, 
&ext_set));
+    // Since the function is not referenced this plan is ok unless we are 
asking for
+    // strict conversion.
+    ConversionOptions options;
+    options.strictness = ConversionStrictness::EXACT_ROUNDTRIP;
+    ASSERT_RAISES(Invalid,
+                  DeserializePlans(
+                      *buf, [] { return kNullConsumer; }, ext_id_reg, 
&ext_set, options));
   }
 }
 
@@ -921,16 +929,19 @@ TEST(Substrait, ExtensionSetFromPlanRegisterFunc) {
   ExtensionIdRegistry* ext_id_reg = sp_ext_id_reg.get();
   // invalid before registration
   ExtensionSet ext_set_invalid(ext_id_reg);
-  ASSERT_RAISES(Invalid,
-                DeserializePlans(
-                    *buf, [] { return kNullConsumer; }, ext_id_reg, 
&ext_set_invalid));
+  ConversionOptions conversion_options;
+  conversion_options.strictness = ConversionStrictness::EXACT_ROUNDTRIP;
+  ASSERT_RAISES(Invalid, DeserializePlans(
+                             *buf, [] { return kNullConsumer; }, ext_id_reg,
+                             &ext_set_invalid, conversion_options));
   ASSERT_OK(ext_id_reg->AddSubstraitCallToArrow(
       {default_extension_types_uri(), "new_func"}, "multiply"));
   // valid after registration
   ExtensionSet ext_set_valid(ext_id_reg);
-  ASSERT_OK_AND_ASSIGN(auto sink_decls, DeserializePlans(
-                                            *buf, [] { return kNullConsumer; },
-                                            ext_id_reg, &ext_set_valid));
+  ASSERT_OK_AND_ASSIGN(auto sink_decls,
+                       DeserializePlans(
+                           *buf, [] { return kNullConsumer; }, ext_id_reg, 
&ext_set_valid,
+                           conversion_options));
   EXPECT_OK_AND_ASSIGN(Id decoded_add_func_id, 
ext_set_valid.DecodeFunction(42));
   EXPECT_EQ(decoded_add_func_id.uri, kArrowExtTypesUri);
   EXPECT_EQ(decoded_add_func_id.name, "new_func");
@@ -2121,6 +2132,15 @@ TEST(Substrait, BasicPlanRoundTrippingEndToEnd) {
   EXPECT_TRUE(expected_table->Equals(*rnd_trp_table));
 }
 
+/// \brief Create a NamedTableProvider that provides `table` regardless of the 
name
+NamedTableProvider AlwaysProvideSameTable(std::shared_ptr<Table> table) {
+  return [table = std::move(table)](const std::vector<std::string>&) {
+    std::shared_ptr<compute::ExecNodeOptions> options =
+        std::make_shared<compute::TableSourceNodeOptions>(table);
+    return compute::Declaration("table_source", {}, options, "mock_source");
+  };
+}
+
 TEST(Substrait, ProjectRel) {
 #ifdef _WIN32
   GTEST_SKIP() << "ARROW-16392: Substrait File URI not supported for Windows";
@@ -2227,11 +2247,7 @@ TEST(Substrait, ProjectRel) {
     [2, 2, 60, true]
   ])"});
 
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -2350,11 +2366,7 @@ TEST(Substrait, ProjectRelOnFunctionWithEmit) {
       [5, 50, true],
       [2, 60, true]
   ])"});
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -2413,11 +2425,7 @@ TEST(Substrait, ReadRelWithEmit) {
       [4, 20]
   ])"});
 
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -2535,11 +2543,7 @@ TEST(Substrait, FilterRelWithEmit) {
       [6, 2],
       [7, 1]
   ])"});
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -2950,11 +2954,7 @@ TEST(Substrait, AggregateRel) {
       [60, 40]
   ])"});
 
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -3069,11 +3069,7 @@ TEST(Substrait, AggregateRelEmit) {
       [60]
   ])"});
 
-  NamedTableProvider table_provider = [input_table](const 
std::vector<std::string>&) {
-    std::shared_ptr<compute::ExecNodeOptions> options =
-        std::make_shared<compute::TableSourceNodeOptions>(input_table);
-    return compute::Declaration("table_source", {}, options, "mock_source");
-  };
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
 
   ConversionOptions conversion_options;
   conversion_options.named_table_provider = std::move(table_provider);
@@ -3082,5 +3078,114 @@ TEST(Substrait, AggregateRelEmit) {
                        buf, {}, conversion_options);
 }
 
+TEST(Substrait, IsthmusPlan) {
+  // This is a plan generated from Isthmus
+  // isthmus -c "CREATE TABLE T1(foo int)" "SELECT foo + 1 FROM T1"
+  //
+  // The plan had to be modified slightly to introduce the missing enum
+  // argument that isthmus did not put there.
+  std::string substrait_json = R"({
+    "extensionUris": [{
+      "extensionUriAnchor": 1,
+      "uri": "/functions_arithmetic.yaml"
+    }],
+    "extensions": [{
+      "extensionFunction": {
+        "extensionUriReference": 1,
+        "functionAnchor": 0,
+        "name": "add:opt_i32_i32"
+      }
+    }],
+    "relations": [{
+      "root": {
+        "input": {
+          "project": {
+            "common": {
+              "emit": {
+                "outputMapping": [1]
+              }
+            },
+            "input": {
+              "read": {
+                "common": {
+                  "direct": {
+                  }
+                },
+                "baseSchema": {
+                  "names": ["FOO"],
+                  "struct": {
+                    "types": [{
+                      "i32": {
+                        "typeVariationReference": 0,
+                        "nullability": "NULLABILITY_NULLABLE"
+                      }
+                    }],
+                    "typeVariationReference": 0,
+                    "nullability": "NULLABILITY_REQUIRED"
+                  }
+                },
+                "namedTable": {
+                  "names": ["T1"]
+                }
+              }
+            },
+            "expressions": [{
+              "scalarFunction": {
+                "functionReference": 0,
+                "args": [],
+                "outputType": {
+                  "i32": {
+                    "typeVariationReference": 0,
+                    "nullability": "NULLABILITY_NULLABLE"
+                  }
+                },
+                "arguments": [{
+                  "enum": {
+                    "unspecified": {}
+                  }
+                }, {
+                  "value": {
+                    "selection": {
+                      "directReference": {
+                        "structField": {
+                          "field": 0
+                        }
+                      },
+                      "rootReference": {
+                      }
+                    }
+                  }
+                }, {
+                  "value": {
+                    "literal": {
+                      "i32": 1,
+                      "nullable": false,
+                      "typeVariationReference": 0
+                    }
+                  }
+                }]
+              }
+            }]
+          }
+        },
+        "names": ["EXPR$0"]
+      }
+    }],
+    "expectedTypeUrls": []
+  })";
+
+  auto test_schema = schema({field("foo", int32())});
+  auto input_table = TableFromJSON(test_schema, {"[[1], [2], [5]]"});
+  NamedTableProvider table_provider = 
AlwaysProvideSameTable(std::move(input_table));
+  ConversionOptions conversion_options;
+  conversion_options.named_table_provider = std::move(table_provider);
+
+  ASSERT_OK_AND_ASSIGN(auto buf, internal::SubstraitFromJSON("Plan", 
substrait_json));
+
+  auto expected_table = TableFromJSON(test_schema, {"[[2], [3], [6]]"});
+  CheckRoundTripResult(std::move(test_schema), std::move(expected_table),
+                       *compute::default_exec_context(), buf, {}, 
conversion_options);
+}
+
 }  // namespace engine
 }  // namespace arrow

Reply via email to