blob: 923841c0bf9a7db998fada933b10f8106cd13d57 [file] [log] [blame]
// 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.
#include <sstream>
#include <string>
#include <vector>
#include "gandiva/expr_validator.h"
namespace gandiva {
Status ExprValidator::Validate(const ExpressionPtr& expr) {
ARROW_RETURN_IF(expr == nullptr,
Status::ExpressionValidationError("Expression cannot be null"));
Node& root = *expr->root();
ARROW_RETURN_NOT_OK(root.Accept(*this));
// Ensure root's return type match the expression return type. Type
// support validation is not required because root type is already supported.
ARROW_RETURN_IF(!root.return_type()->Equals(*expr->result()->type()),
Status::ExpressionValidationError("Return type of root node ",
root.return_type()->ToString(),
" does not match that of expression ",
expr->result()->type()->ToString()));
return Status::OK();
}
Status ExprValidator::Visit(const FieldNode& node) {
auto llvm_type = types_->IRType(node.return_type()->id());
ARROW_RETURN_IF(llvm_type == nullptr,
Status::ExpressionValidationError("Field ", node.field()->name(),
" has unsupported data type ",
node.return_type()->name()));
// Ensure that field is found in schema
auto field_in_schema_entry = field_map_.find(node.field()->name());
ARROW_RETURN_IF(field_in_schema_entry == field_map_.end(),
Status::ExpressionValidationError("Field ", node.field()->name(),
" not in schema."));
// Ensure that that the found field match.
FieldPtr field_in_schema = field_in_schema_entry->second;
ARROW_RETURN_IF(!field_in_schema->Equals(node.field()),
Status::ExpressionValidationError(
"Field definition in schema ", field_in_schema->ToString(),
" different from field in expression ", node.field()->ToString()));
return Status::OK();
}
Status ExprValidator::Visit(const FunctionNode& node) {
auto desc = node.descriptor();
FunctionSignature signature(desc->name(), desc->params(), desc->return_type());
const NativeFunction* native_function = registry_.LookupSignature(signature);
ARROW_RETURN_IF(native_function == nullptr,
Status::ExpressionValidationError("Function ", signature.ToString(),
" not supported yet. "));
for (auto& child : node.children()) {
ARROW_RETURN_NOT_OK(child->Accept(*this));
}
return Status::OK();
}
Status ExprValidator::Visit(const IfNode& node) {
ARROW_RETURN_NOT_OK(node.condition()->Accept(*this));
ARROW_RETURN_NOT_OK(node.then_node()->Accept(*this));
ARROW_RETURN_NOT_OK(node.else_node()->Accept(*this));
auto if_node_ret_type = node.return_type();
auto then_node_ret_type = node.then_node()->return_type();
auto else_node_ret_type = node.else_node()->return_type();
// Then-branch return type must match.
ARROW_RETURN_IF(!if_node_ret_type->Equals(*then_node_ret_type),
Status::ExpressionValidationError(
"Return type of if ", if_node_ret_type->ToString(), " and then ",
then_node_ret_type->ToString(), " not matching."));
// Else-branch return type must match.
ARROW_RETURN_IF(!if_node_ret_type->Equals(*else_node_ret_type),
Status::ExpressionValidationError(
"Return type of if ", if_node_ret_type->ToString(), " and else ",
else_node_ret_type->ToString(), " not matching."));
return Status::OK();
}
Status ExprValidator::Visit(const LiteralNode& node) {
auto llvm_type = types_->IRType(node.return_type()->id());
ARROW_RETURN_IF(llvm_type == nullptr,
Status::ExpressionValidationError("Value ", ToString(node.holder()),
" has unsupported data type ",
node.return_type()->name()));
return Status::OK();
}
Status ExprValidator::Visit(const BooleanNode& node) {
ARROW_RETURN_IF(
node.children().size() < 2,
Status::ExpressionValidationError("Boolean expression has ", node.children().size(),
" children, expected atleast two"));
for (auto& child : node.children()) {
const auto bool_type = arrow::boolean();
const auto ret_type = child->return_type();
ARROW_RETURN_IF(!ret_type->Equals(bool_type),
Status::ExpressionValidationError(
"Boolean expression has a child with return type ",
ret_type->ToString(), ", expected return type boolean"));
ARROW_RETURN_NOT_OK(child->Accept(*this));
}
return Status::OK();
}
/*
* Validate the following
*
* 1. Non empty list of constants to search in.
* 2. Expression returns of the same type as the constants.
*/
Status ExprValidator::Visit(const InExpressionNode<int32_t>& node) {
return ValidateInExpression(node.values().size(), node.eval_expr()->return_type(),
arrow::int32());
}
Status ExprValidator::Visit(const InExpressionNode<int64_t>& node) {
return ValidateInExpression(node.values().size(), node.eval_expr()->return_type(),
arrow::int64());
}
Status ExprValidator::Visit(const InExpressionNode<std::string>& node) {
return ValidateInExpression(node.values().size(), node.eval_expr()->return_type(),
arrow::utf8());
}
Status ExprValidator::ValidateInExpression(size_t number_of_values,
DataTypePtr in_expr_return_type,
DataTypePtr type_of_values) {
ARROW_RETURN_IF(number_of_values == 0,
Status::ExpressionValidationError(
"IN Expression needs a non-empty constant list to match."));
ARROW_RETURN_IF(!in_expr_return_type->Equals(type_of_values),
Status::ExpressionValidationError(
"Evaluation expression for IN clause returns ", in_expr_return_type,
" values are of type", type_of_values));
return Status::OK();
}
} // namespace gandiva