| 644 | } |
| 645 | |
| 646 | Result<std::shared_ptr<RecordBatchReader>> CallTabularFunction( |
| 647 | const std::string& func_name, const std::vector<Datum>& args, |
| 648 | compute::FunctionRegistry* registry) { |
| 649 | if (args.size() != 0) { |
| 650 | return Status::NotImplemented("non-empty arguments to tabular function"); |
| 651 | } |
| 652 | if (registry == NULLPTR) { |
| 653 | registry = compute::GetFunctionRegistry(); |
| 654 | } |
| 655 | ARROW_ASSIGN_OR_RAISE(auto func, registry->GetFunction(func_name)); |
| 656 | if (func->kind() != compute::Function::SCALAR) { |
| 657 | return Status::Invalid("tabular function of non-scalar kind"); |
| 658 | } |
| 659 | auto arity = func->arity(); |
| 660 | if (arity.num_args != 0 || arity.is_varargs) { |
| 661 | return Status::NotImplemented("tabular function of non-null arity"); |
| 662 | } |
| 663 | auto kernels = |
| 664 | arrow::internal::checked_pointer_cast<compute::ScalarFunction>(func)->kernels(); |
| 665 | if (kernels.size() != 1) { |
| 666 | return Status::NotImplemented("tabular function with non-single kernel"); |
| 667 | } |
| 668 | const compute::ScalarKernel* kernel = kernels[0]; |
| 669 | auto out_type = kernel->signature->out_type(); |
| 670 | if (out_type.kind() != compute::OutputType::FIXED) { |
| 671 | return Status::Invalid("tabular kernel of non-fixed kind"); |
| 672 | } |
| 673 | auto datatype = out_type.type(); |
| 674 | if (datatype->id() != Type::type::STRUCT) { |
| 675 | return Status::Invalid("tabular kernel with non-struct output"); |
| 676 | } |
| 677 | auto struct_type = arrow::internal::checked_cast<StructType*>(datatype.get()); |
| 678 | auto schema = ::arrow::schema(struct_type->fields()); |
| 679 | std::vector<TypeHolder> in_types; |
| 680 | ARROW_ASSIGN_OR_RAISE(auto func_exec, |
| 681 | GetFunctionExecutor(func_name, in_types, NULLPTR, registry)); |
| 682 | auto next_func = [schema, func_exec = std::move( |
| 683 | func_exec)]() -> Result<std::shared_ptr<RecordBatch>> { |
| 684 | std::vector<Datum> args; |
| 685 | // passed_length of -1 or 0 with args.size() of 0 leads to an empty ExecSpanIterator |
| 686 | // in exec.cc and to never invoking the source function, so 1 is passed instead |
| 687 | // TODO: GH-33612: Support batch size in user-defined tabular functions |
| 688 | ARROW_ASSIGN_OR_RAISE(auto datum, func_exec->Execute(args, /*passed_length=*/1)); |
| 689 | if (!datum.is_array()) { |
| 690 | return Status::Invalid("UDF result of non-array kind"); |
| 691 | } |
| 692 | std::shared_ptr<Array> array = datum.make_array(); |
| 693 | if (array->length() == 0) { |
| 694 | return IterationTraits<std::shared_ptr<RecordBatch>>::End(); |
| 695 | } |
| 696 | ARROW_ASSIGN_OR_RAISE(auto batch, RecordBatch::FromStructArray(std::move(array))); |
| 697 | if (!schema->Equals(batch->schema())) { |
| 698 | return Status::Invalid("UDF result with shape not conforming to schema"); |
| 699 | } |
| 700 | return std::move(batch); |
| 701 | }; |
| 702 | return RecordBatchReader::MakeFromIterator(MakeFunctionIterator(std::move(next_func)), |
| 703 | schema); |
nothing calls this directly
no test coverage detected