| 113 | } |
| 114 | |
| 115 | arrow::Status RunSubstraitConsumer(int argc, char** argv) { |
| 116 | // Plans arrive at the consumer serialized in a Buffer, using the binary protobuf |
| 117 | // serialization of a substrait Plan |
| 118 | auto maybe_serialized_plan = GetSubstraitFromServer(argv[1]).result(); |
| 119 | ARROW_RETURN_NOT_OK(maybe_serialized_plan.status()); |
| 120 | ARROW_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Buffer> serialized_plan, |
| 121 | std::move(maybe_serialized_plan)); |
| 122 | |
| 123 | // Print the received plan to stdout as JSON |
| 124 | ARROW_ASSIGN_OR_RAISE(auto plan_json, |
| 125 | eng::internal::SubstraitToJSON("Plan", *serialized_plan)); |
| 126 | |
| 127 | std::cout << std::string(50, '#') << " received substrait::Plan:" << std::endl; |
| 128 | std::cout << plan_json << std::endl; |
| 129 | |
| 130 | // The data sink(s) for plans is/are implicit in substrait plans, but explicit in |
| 131 | // Arrow. Therefore, deserializing a plan requires a factory for consumers: each |
| 132 | // time the root of a substrait relation tree is deserialized, an Arrow consumer is |
| 133 | // constructed into which its batches will be piped. |
| 134 | std::vector<std::shared_ptr<ac::SinkNodeConsumer>> consumers; |
| 135 | std::function<std::shared_ptr<ac::SinkNodeConsumer>()> consumer_factory = [&] { |
| 136 | // All batches produced by the plan will be fed into IgnoringConsumers: |
| 137 | auto tag = consumers.size(); |
| 138 | consumers.emplace_back(new IgnoringConsumer{tag}); |
| 139 | return consumers.back(); |
| 140 | }; |
| 141 | |
| 142 | // Deserialize each relation tree in the substrait plan to an Arrow compute Declaration |
| 143 | arrow::Result<std::vector<ac::Declaration>> maybe_decls = |
| 144 | eng::DeserializePlans(*serialized_plan, consumer_factory); |
| 145 | ARROW_RETURN_NOT_OK(maybe_decls.status()); |
| 146 | ARROW_ASSIGN_OR_RAISE(std::vector<ac::Declaration> decls, std::move(maybe_decls)); |
| 147 | |
| 148 | // It's safe to drop the serialized plan; we don't leave references to its memory |
| 149 | serialized_plan.reset(); |
| 150 | |
| 151 | // Construct an empty plan (note: configure Function registry and ThreadPool here) |
| 152 | ARROW_ASSIGN_OR_RAISE(std::shared_ptr<ac::ExecPlan> plan, ac::ExecPlan::Make()); |
| 153 | |
| 154 | // Add decls to plan (note: configure ExecNode registry before this point) |
| 155 | for (const ac::Declaration& decl : decls) { |
| 156 | ARROW_RETURN_NOT_OK(decl.AddToPlan(plan.get()).status()); |
| 157 | } |
| 158 | |
| 159 | // Validate the plan and print it to stdout |
| 160 | ARROW_RETURN_NOT_OK(plan->Validate()); |
| 161 | std::cout << std::string(50, '#') << " produced arrow::ExecPlan:" << std::endl; |
| 162 | std::cout << plan->ToString() << std::endl; |
| 163 | |
| 164 | // Start the plan... |
| 165 | std::cout << std::string(50, '#') << " consuming batches:" << std::endl; |
| 166 | plan->StartProducing(); |
| 167 | |
| 168 | // ... and wait for it to finish |
| 169 | ARROW_RETURN_NOT_OK(plan->finished().status()); |
| 170 | return arrow::Status::OK(); |
| 171 | } |
| 172 |
no test coverage detected