diff --git a/presto-native-execution/presto_cpp/main/CMakeLists.txt b/presto-native-execution/presto_cpp/main/CMakeLists.txt index b00fee32927b6..687cacdff50da 100644 --- a/presto-native-execution/presto_cpp/main/CMakeLists.txt +++ b/presto-native-execution/presto_cpp/main/CMakeLists.txt @@ -51,6 +51,7 @@ target_link_libraries( presto_http presto_operators presto_velox_conversion + presto_expression_optimizer velox_abfs velox_aggregates velox_caching diff --git a/presto-native-execution/presto_cpp/main/PrestoServer.cpp b/presto-native-execution/presto_cpp/main/PrestoServer.cpp index 1f316144bbfce..8b5d7c679a90b 100644 --- a/presto-native-execution/presto_cpp/main/PrestoServer.cpp +++ b/presto-native-execution/presto_cpp/main/PrestoServer.cpp @@ -1546,6 +1546,13 @@ void PrestoServer::registerSidecarEndpoints() { proxygen::ResponseHandler* downstream) { http::sendOkResponse(downstream, getFunctionsMetadata()); }); + httpServer_->registerPost( + "/v1/expressions", + [&](proxygen::HTTPMessage* message, + const std::vector>& body, + proxygen::ResponseHandler* downstream) { + optimizeExpressions(*message, body, downstream); + }); httpServer_->registerPost( "/v1/velox/plan", [server = this]( @@ -1594,4 +1601,24 @@ protocol::NodeStatus PrestoServer::fetchNodeStatus() { return nodeStatus; } +void PrestoServer::optimizeExpressions( + const proxygen::HTTPMessage& message, + const std::vector>& body, + proxygen::ResponseHandler* downstream) { + json::array_t inputRowExpressions = + json::parse(util::extractMessageBody(body)); + auto rowExpressionOptimizer = + std::make_unique(); + auto result = rowExpressionOptimizer->optimize( + message.getHeaders(), inputRowExpressions); + if (result.second) { + VELOX_USER_CHECK( + result.first.is_array(), + "Output json should be an array of row expressions"); + http::sendOkResponse(downstream, result.first); + } else { + http::sendErrorResponse(downstream, result.first); + } +} + } // namespace facebook::presto diff --git a/presto-native-execution/presto_cpp/main/PrestoServer.h b/presto-native-execution/presto_cpp/main/PrestoServer.h index 615d6bfb910d9..8fa7d58b6dd2f 100644 --- a/presto-native-execution/presto_cpp/main/PrestoServer.h +++ b/presto-native-execution/presto_cpp/main/PrestoServer.h @@ -25,6 +25,7 @@ #include "presto_cpp/main/PeriodicHeartbeatManager.h" #include "presto_cpp/main/PrestoExchangeSource.h" #include "presto_cpp/main/PrestoServerOperations.h" +#include "presto_cpp/main/types/RowExpressionOptimizer.h" #include "presto_cpp/main/types/VeloxPlanValidator.h" #include "velox/common/caching/AsyncDataCache.h" #include "velox/common/memory/MemoryAllocator.h" @@ -217,6 +218,11 @@ class PrestoServer { protocol::NodeStatus fetchNodeStatus(); + void optimizeExpressions( + const proxygen::HTTPMessage& message, + const std::vector>& body, + proxygen::ResponseHandler* downstream); + void populateMemAndCPUInfo(); // Periodically yield tasks if there are tasks queued. diff --git a/presto-native-execution/presto_cpp/main/types/CMakeLists.txt b/presto-native-execution/presto_cpp/main/types/CMakeLists.txt index c17e136e9984d..c110bb51ef422 100644 --- a/presto-native-execution/presto_cpp/main/types/CMakeLists.txt +++ b/presto-native-execution/presto_cpp/main/types/CMakeLists.txt @@ -34,6 +34,15 @@ add_library(presto_velox_conversion OBJECT VeloxPlanConversion.cpp) target_link_libraries(presto_velox_conversion velox_type) +add_library(presto_expression_converter RowExpressionConverter.cpp) + +target_link_libraries(presto_expression_converter presto_type_converter + presto_types presto_protocol) + +add_library(presto_expression_optimizer RowExpressionOptimizer.cpp) + +target_link_libraries(presto_expression_optimizer presto_expression_converter) + if(PRESTO_ENABLE_TESTING) add_subdirectory(tests) endif() diff --git a/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.cpp b/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.cpp index 2c2b2a3c5ea00..7e0d51ad9659d 100644 --- a/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.cpp +++ b/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.cpp @@ -33,32 +33,10 @@ std::string toJsonString(const T& value) { } std::string mapScalarFunction(const std::string& name) { - static const std::unordered_map kFunctionNames = { - // Operator overrides: com.facebook.presto.common.function.OperatorType - {"presto.default.$operator$add", "presto.default.plus"}, - {"presto.default.$operator$between", "presto.default.between"}, - {"presto.default.$operator$divide", "presto.default.divide"}, - {"presto.default.$operator$equal", "presto.default.eq"}, - {"presto.default.$operator$greater_than", "presto.default.gt"}, - {"presto.default.$operator$greater_than_or_equal", "presto.default.gte"}, - {"presto.default.$operator$is_distinct_from", - "presto.default.distinct_from"}, - {"presto.default.$operator$less_than", "presto.default.lt"}, - {"presto.default.$operator$less_than_or_equal", "presto.default.lte"}, - {"presto.default.$operator$modulus", "presto.default.mod"}, - {"presto.default.$operator$multiply", "presto.default.multiply"}, - {"presto.default.$operator$negation", "presto.default.negate"}, - {"presto.default.$operator$not_equal", "presto.default.neq"}, - {"presto.default.$operator$subtract", "presto.default.minus"}, - {"presto.default.$operator$subscript", "presto.default.subscript"}, - // Special form function overrides. - {"presto.default.in", "in"}, - }; - std::string lowerCaseName = boost::to_lower_copy(name); - auto it = kFunctionNames.find(lowerCaseName); - if (it != kFunctionNames.end()) { + auto it = kPrestoOperatorMap.find(lowerCaseName); + if (it != kPrestoOperatorMap.end()) { return it->second; } @@ -102,6 +80,15 @@ std::string getFunctionName(const protocol::SqlFunctionId& functionId) { } // namespace +const std::unordered_map& veloxToPrestoOperatorMap() { + static std::unordered_map veloxToPrestoOperatorMap = + {{"cast", "presto.default.$operator$cast"}}; + for (const auto& entry : kPrestoOperatorMap) { + veloxToPrestoOperatorMap[entry.second] = entry.first; + } + return veloxToPrestoOperatorMap; +} + velox::variant VeloxExprConverter::getConstantValue( const velox::TypePtr& type, const protocol::Block& block) const { diff --git a/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.h b/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.h index f63a84ec35ad2..d7ae5975df1f8 100644 --- a/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.h +++ b/presto-native-execution/presto_cpp/main/types/PrestoToVeloxExpr.h @@ -20,6 +20,30 @@ namespace facebook::presto { +static const std::unordered_map kPrestoOperatorMap = { + // Operator overrides: com.facebook.presto.common.function.OperatorType + {"presto.default.$operator$add", "presto.default.plus"}, + {"presto.default.$operator$between", "presto.default.between"}, + {"presto.default.$operator$divide", "presto.default.divide"}, + {"presto.default.$operator$equal", "presto.default.eq"}, + {"presto.default.$operator$greater_than", "presto.default.gt"}, + {"presto.default.$operator$greater_than_or_equal", "presto.default.gte"}, + {"presto.default.$operator$is_distinct_from", + "presto.default.distinct_from"}, + {"presto.default.$operator$less_than", "presto.default.lt"}, + {"presto.default.$operator$less_than_or_equal", "presto.default.lte"}, + {"presto.default.$operator$modulus", "presto.default.mod"}, + {"presto.default.$operator$multiply", "presto.default.multiply"}, + {"presto.default.$operator$negation", "presto.default.negate"}, + {"presto.default.$operator$not_equal", "presto.default.neq"}, + {"presto.default.$operator$subtract", "presto.default.minus"}, + {"presto.default.$operator$subscript", "presto.default.subscript"}, + // Special form function overrides. + {"presto.default.in", "in"}, +}; + +const std::unordered_map& veloxToPrestoOperatorMap(); + class VeloxExprConverter { public: VeloxExprConverter(velox::memory::MemoryPool* pool, TypeParser* typeParser) diff --git a/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.cpp b/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.cpp new file mode 100644 index 0000000000000..edb667b855608 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.cpp @@ -0,0 +1,388 @@ +/* + * Licensed 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 "presto_cpp/main/types/RowExpressionConverter.h" +#include "presto_cpp/main/types/PrestoToVeloxExpr.h" +#include "velox/expression/FieldReference.h" + +using namespace facebook::presto; +using namespace facebook::velox; + +namespace facebook::presto::expression { + +namespace { +const std::string kConstant = "constant"; +const std::string kBoolean = "boolean"; +const std::string kVariable = "variable"; +const std::string kCall = "call"; +const std::string kStatic = "$static"; +const std::string kSpecial = "special"; +const std::string kCoalesce = "COALESCE"; +const std::string kRowConstructor = "ROW_CONSTRUCTOR"; +const std::string kSwitch = "SWITCH"; +const std::string kWhen = "WHEN"; + +protocol::TypeSignature getTypeSignature(const TypePtr& type) { + std::string typeSignature; + if (type->parameters().empty()) { + typeSignature = type->toString(); + boost::algorithm::to_lower(typeSignature); + } else if (type->isDecimal()) { + typeSignature = type->toString(); + } else { + std::string complexTypeString; + std::vector childTypes; + if (type->isRow()) { + complexTypeString = "row"; + childTypes = asRowType(type)->children(); + } else if (type->isArray()) { + complexTypeString = "array"; + childTypes = type->asArray().children(); + } else if (type->isMap()) { + complexTypeString = "map"; + const auto mapType = type->asMap(); + childTypes = {mapType.keyType(), mapType.valueType()}; + } else { + VELOX_USER_FAIL("Invalid type {}", type->toString()); + } + + typeSignature = complexTypeString + "("; + if (!childTypes.empty()) { + auto numChildren = childTypes.size(); + for (auto i = 0; i < numChildren - 1; i++) { + typeSignature += fmt::format("{},", getTypeSignature(childTypes[i])); + } + typeSignature += getTypeSignature(childTypes[numChildren - 1]); + } + typeSignature += ")"; + } + + return typeSignature; +} + +json toVariableReferenceExpression( + const std::shared_ptr& field) { + protocol::VariableReferenceExpression vexpr; + vexpr.name = field->name(); + vexpr._type = kVariable; + vexpr.type = getTypeSignature(field->type()); + json result; + protocol::to_json(result, vexpr); + + return result; +} + +bool isPrestoSpecialForm(const std::string& name) { + static const std::unordered_set kPrestoSpecialForms = { + "if", + "null_if", + "switch", + "when", + "is_null", + "coalesce", + "in", + "and", + "or", + "dereference", + "row_constructor", + "bind"}; + return kPrestoSpecialForms.count(name) != 0; +} + +json getWhenSpecialForm( + const exec::ExprPtr& input, + const json::array_t& whenArgs) { + json when; + when["@type"] = kSpecial; + when["form"] = kWhen; + when["arguments"] = whenArgs; + when["returnType"] = getTypeSignature(input->type()); + return when; +} +} // namespace + +std::string RowExpressionConverter::getValueBlock(const VectorPtr& vector) { + std::ostringstream output; + serde_->serializeSingleColumn(vector, nullptr, pool_, &output); + const auto serialized = output.str(); + const auto serializedSize = serialized.size(); + return encoding::Base64::encode(serialized.c_str(), serializedSize); +} + +std::shared_ptr +RowExpressionConverter::getConstantRowExpression( + const std::shared_ptr& constantExpr) { + protocol::ConstantExpression cexpr; + cexpr.type = getTypeSignature(constantExpr->type()); + cexpr.valueBlock.data = getValueBlock(constantExpr->value()); + return std::make_shared(cexpr); +} + +SwitchFormArguments RowExpressionConverter::getSimpleSwitchFormArgs( + const exec::SwitchExpr* switchExpr, + const json::array_t& inputArgs) { + SwitchFormArguments result; + // The switch case's 'expression' in simple form of the conditional cannot be + // inferred from Velox since it could evaluate all 'when' clauses to true or + // false, so we get it from the input json. + result.arguments.emplace_back(inputArgs[0]); + const auto& switchInputs = switchExpr->inputs(); + const auto numInputs = switchInputs.size(); + + for (auto i = 0; i < numInputs - 1; i += 2) { + json::array_t whenArgs; + const vector_size_t argsIdx = i / 2 + 1; + const auto& caseValue = switchInputs[i + 1]; + json::array_t inputWhenArgs = inputArgs[argsIdx].at("arguments"); + + if (switchInputs[i]->isConstant()) { + auto constantExpr = + std::dynamic_pointer_cast(switchInputs[i]); + if (auto constVector = + constantExpr->value()->as>()) { + // If this is the first switch case that evaluates to true, return the + // expression corresponding to this case. + if (constVector->valueAt(0) && result.arguments.size() == 1) { + return { + true, + veloxToPrestoRowExpression(caseValue, inputWhenArgs[1]), + json::array()}; + } else { + // Skip switch cases that evaluate to false. + continue; + } + } else { + whenArgs.emplace_back(getConstantRowExpression(constantExpr)); + } + } else { + VELOX_USER_CHECK(!switchInputs[i]->inputs().empty()); + const auto& matchExpr = switchInputs[i]->inputs().back(); + whenArgs.emplace_back( + veloxToPrestoRowExpression(matchExpr, inputWhenArgs[0])); + } + + whenArgs.emplace_back( + veloxToPrestoRowExpression(caseValue, inputWhenArgs[1])); + result.arguments.emplace_back( + getWhenSpecialForm(switchInputs[i + 1], whenArgs)); + } + + // Else clause. + if (numInputs % 2 != 0) { + result.arguments.emplace_back(veloxToPrestoRowExpression( + switchInputs[numInputs - 1], inputArgs.back())); + } + return result; +} + +SwitchFormArguments RowExpressionConverter::getSwitchSpecialFormArgs( + const exec::SwitchExpr* switchExpr, + const json& input) { + json::array_t inputArgs = input["arguments"]; + // The searched form of the conditional expression needs to be handled + // differently from the simple form. The searched form can be detected by the + // presence of a boolean value in the first argument. This default boolean + // argument is not present in the Velox switch expression, so it is added to + // the arguments of output switch expression unchanged. + if (inputArgs[0].at("@type") == kConstant && + inputArgs[0].at("type") == kBoolean) { + SwitchFormArguments result; + const auto& switchInputs = switchExpr->inputs(); + const auto numInputs = switchInputs.size(); + result.arguments = {inputArgs[0]}; + for (auto i = 0; i < numInputs - 1; i += 2) { + const vector_size_t argsIdx = i / 2 + 1; + json::array_t inputWhenArgs = inputArgs[argsIdx].at("arguments"); + json::array_t whenArgs; + whenArgs.emplace_back( + veloxToPrestoRowExpression(switchInputs[i], inputWhenArgs[0])); + whenArgs.emplace_back( + veloxToPrestoRowExpression(switchInputs[i + 1], inputWhenArgs[1])); + result.arguments.emplace_back( + getWhenSpecialForm(switchInputs[i + 1], whenArgs)); + } + + // Else clause. + if (numInputs % 2 != 0) { + result.arguments.emplace_back(veloxToPrestoRowExpression( + switchInputs[numInputs - 1], inputArgs.back())); + } + return result; + } + + return getSimpleSwitchFormArgs(switchExpr, inputArgs); +} + +json RowExpressionConverter::getSpecialForm( + const exec::ExprPtr& expr, + const json& input) { + json result; + result["@type"] = kSpecial; + result["returnType"] = getTypeSignature(expr->type()); + auto form = expr->name(); + // Presto requires the field form to be in upper case. + std::transform(form.begin(), form.end(), form.begin(), ::toupper); + result["form"] = form; + + // Arguments for switch expression include special form expression 'when' + // so they are constructed separately. If the switch expression evaluation + // found a case that always evaluates to true, the field 'isSimplified' in the + // result will be true and the field 'caseExpression' contains the value + // corresponding to the simplified switch case. Otherwise, 'isSimplified' will + // be false and the field 'arguments' will contain the 'when' clauses needed + // by switch SpecialFormExpression in Presto. + if (form == kSwitch) { + auto switchExpr = dynamic_cast(expr.get()); + auto switchResult = getSwitchSpecialFormArgs(switchExpr, input); + if (switchResult.isSimplified) { + return switchResult.caseExpression; + } else { + result["arguments"] = switchResult.arguments; + } + } else { + json::array_t inputArguments = input["arguments"]; + auto exprInputs = expr->inputs(); + const auto numInputs = exprInputs.size(); + if (form == kCoalesce) { + VELOX_USER_CHECK_LE(numInputs, inputArguments.size()); + } else { + VELOX_USER_CHECK_EQ(numInputs, inputArguments.size()); + } + result["arguments"] = json::array(); + for (auto i = 0; i < numInputs; i++) { + result["arguments"].push_back( + veloxToPrestoRowExpression(exprInputs[i], inputArguments[i])); + } + } + + return result; +} + +json RowExpressionConverter::getRowConstructorSpecialForm( + std::shared_ptr& constantExpr) { + json result; + result["@type"] = kSpecial; + result["form"] = kRowConstructor; + result["returnType"] = getTypeSignature(constantExpr->type()); + auto value = constantExpr->value(); + auto* constVector = value->as>(); + auto* rowVector = constVector->valueVector()->as(); + auto type = asRowType(constantExpr->type()); + auto size = rowVector->children().size(); + + protocol::ConstantExpression cexpr; + json j; + result["arguments"] = json::array(); + for (auto i = 0; i < size; i++) { + cexpr.type = getTypeSignature(type->childAt(i)); + cexpr.valueBlock.data = getValueBlock(rowVector->childAt(i)); + protocol::to_json(j, cexpr); + result["arguments"].push_back(j); + } + return result; +} + +json RowExpressionConverter::toConstantRowExpression( + const exec::ExprPtr& expr) { + auto constantExpr = std::dynamic_pointer_cast(expr); + VELOX_USER_CHECK_NOT_NULL(constantExpr); + // Constant velox expressions of ROW type map to ROW_CONSTRUCTOR special form + // expression in Presto. + if (expr->type()->isRow()) { + return getRowConstructorSpecialForm(constantExpr); + } + + json result; + auto constantRowExpr = getConstantRowExpression(constantExpr); + protocol::to_json(result, constantRowExpr); + return result; +} + +json RowExpressionConverter::toCallRowExpression( + const exec::ExprPtr& expr, + const json& input) { + json result; + result["@type"] = kCall; + protocol::Signature signature; + std::string exprName = expr->name(); + if (veloxToPrestoOperatorMap().find(exprName) != + veloxToPrestoOperatorMap().end()) { + exprName = veloxToPrestoOperatorMap().at(exprName); + } + signature.name = exprName; + result["displayName"] = exprName; + signature.kind = protocol::FunctionKind::SCALAR; + signature.typeVariableConstraints = {}; + signature.longVariableConstraints = {}; + signature.returnType = getTypeSignature(expr->type()); + + std::vector argumentTypes; + auto exprInputs = expr->inputs(); + auto numArgs = exprInputs.size(); + argumentTypes.reserve(numArgs); + for (auto i = 0; i < numArgs; i++) { + argumentTypes.emplace_back(getTypeSignature(exprInputs[i]->type())); + } + signature.argumentTypes = argumentTypes; + signature.variableArity = false; + + protocol::BuiltInFunctionHandle builtInFunctionHandle; + builtInFunctionHandle._type = kStatic; + builtInFunctionHandle.signature = signature; + result["functionHandle"] = builtInFunctionHandle; + result["returnType"] = getTypeSignature(expr->type()); + result["arguments"] = json::array(); + for (const auto& exprInput : exprInputs) { + result["arguments"].push_back(veloxToPrestoRowExpression(exprInput, input)); + } + + return result; +} + +json RowExpressionConverter::veloxToPrestoRowExpression( + const exec::ExprPtr& expr, + const json& input) { + if (expr->isConstant()) { + if (expr->inputs().empty()) { + return toConstantRowExpression(expr); + } else { + // Expressions such as 'divide(0, 0)' are not constant folded during + // compilation in velox, since they would throw an exception (Divide by + // zero here) during evaluation. So the input expression is returned + // unchanged in such cases. + return input; + } + } + + if (auto field = + std::dynamic_pointer_cast(expr)) { + return toVariableReferenceExpression(field); + } + + if (expr->isSpecialForm() || expr->vectorFunction()) { + // Check if special form expression or call expression. + auto exprName = expr->name(); + boost::algorithm::to_lower(exprName); + if (isPrestoSpecialForm(exprName)) { + return getSpecialForm(expr, input); + } else { + return toCallRowExpression(expr, input); + } + } + + VELOX_NYI( + "Conversion of Velox Expr {} to Presto RowExpression is not supported", + expr->toString()); +} + +} // namespace facebook::presto::expression diff --git a/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.h b/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.h new file mode 100644 index 0000000000000..1868694939a74 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/RowExpressionConverter.h @@ -0,0 +1,129 @@ +/* + * Licensed 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. + */ +#pragma once + +#include "presto_cpp/external/json/nlohmann/json.hpp" +#include "presto_cpp/presto_protocol/presto_protocol.h" +#include "velox/expression/ConstantExpr.h" +#include "velox/expression/Expr.h" +#include "velox/expression/SwitchExpr.h" +#include "velox/serializers/PrestoSerializer.h" + +using namespace facebook::velox; + +namespace facebook::presto::expression { + +/// When 'isSimplified' is true, the cases (or arguments) in switch expression +/// have been simplified when the expression was constant folded in Velox. In +/// this case, the expression corresponding to the first switch case that always +/// evaluates to true is returned in 'caseExpression'. +/// When 'isSimplified' is false, the cases in switch expression have not been +/// simplified, and the switch expression arguments required by Presto are +/// returned in 'arguments'. +struct SwitchFormArguments { + bool isSimplified = false; + json caseExpression; + json::array_t arguments; +}; + +/// Helper class to convert Velox expression of type exec::Expr to its +/// corresponding Presto expression of type protocol::RowExpression. The +/// function veloxToPrestoRowExpression is used in RowExpressionOptimizer to +/// convert the constant folded velox expression of type exec::Expr to a Presto +/// expression of type protocol::RowExpression: +/// 1. A constant velox expression of type exec::ConstantExpr without any inputs +/// is converted to Presto expression of type protocol::ConstantExpression. +/// If the velox constant expression is of ROW type, it is converted to a +/// Presto expression of type protocol::SpecialFormExpression (with Form as +/// ROW_CONSTRUCTOR). +/// 2. Velox expression representing a variable is of type exec::FieldReference, +/// it is converted to a Presto expression of type +/// protocol::VariableReferenceExpression. +/// 3. Special form expressions and expressions with a vector function in Velox +/// can map either to a Presto protocol::SpecialFormExpression or to a Presto +/// protocol::CallExpression. Based on the expression name, we decide whether +/// the velox expression is converted to a Presto expression of type +/// protocol::SpecialFormExpression or of type protocol::CallExpression. +/// +/// The function getConstantRowExpression is used in RowExpressionOptimizer to +/// convert a velox constant expression of type exec::ConstantExpr to a Presto +/// constant expression of type protocol::ConstantExpression. +class RowExpressionConverter { + public: + explicit RowExpressionConverter(memory::MemoryPool* pool) : pool_(pool) {} + + /// Converts a velox constant expression of type exec::ConstantExpr to a + /// Presto constant expression of type protocol::ConstantExpression. + std::shared_ptr getConstantRowExpression( + const std::shared_ptr& constantExpr); + + /// Converts the velox expression of type exec::Expr to a Presto expression of + /// type protocol::RowExpression serialized as json. Called from the function + /// optimize in RowExpressionOptimizer with two arguments: + /// 1. expr: The constant folded velox expression. + /// 2. inputRowExpr: The input Presto expression to RowExpressionOptimizer + /// of type protocol::RowExpression (serialized as json), before it is + /// optimized and constant folded. + json veloxToPrestoRowExpression( + const exec::ExprPtr& expr, + const json& inputRowExpr); + + private: + /// ValueBlock in Presto expression of type protocol::ConstantExpression + /// requires only the column from the serialized PrestoPage without the page + /// header. This function is used to serialize a velox Vector to ValueBlock. + std::string getValueBlock(const VectorPtr& vector); + + /// Helper function to get arguments for switch special form expression when + /// the CASE expression is of 'simple' form (please refer to the documentation + /// at: https://prestodb.io/docs/current/functions/conditional.html#case). + SwitchFormArguments getSimpleSwitchFormArgs( + const exec::SwitchExpr* switchExpr, + const json::array_t& inputArgs); + + /// Helper function to get arguments for Presto special form expression of + /// type protocol::SpecialFormExpression with SWITCH form from a velox switch + /// expression of type exec::SwitchExpr. + SwitchFormArguments getSwitchSpecialFormArgs( + const exec::SwitchExpr* switchExpr, + const json& input); + + /// Helper function to construct a Presto special form expression of type + /// protocol::SpecialFormExpression (serialized as json) from a velox + /// expression of type exec::Expr. + json getSpecialForm(const exec::ExprPtr& expr, const json& input); + + /// Helper function to construct a Presto special form expression of type + /// protocol::SpecialFormExpression with ROW_CONSTRUCTOR form (serialized as + /// json) from a velox constant expression of type exec::ConstantExpr. + json getRowConstructorSpecialForm( + std::shared_ptr& constantExpr); + + /// Helper function to construct a Presto constant expression of type + /// protocol::ConstantExpression (serialized as json) from a velox expression + /// of type exec::Expr. When the velox expression is of ROW type, the helper + /// function getRowConstructorSpecialForm is called to return a Presto + /// protocol::SpecialFormExpression. + json toConstantRowExpression(const exec::ExprPtr& expr); + + /// Helper function to construct a Presto call expression of type + /// protocol::CallExpression (serialized as json) from a velox expression + /// of type exec::Expr. + json toCallRowExpression(const exec::ExprPtr& expr, const json& input); + + memory::MemoryPool* pool_; + const std::unique_ptr serde_ = + std::make_unique(); +}; +} // namespace facebook::presto::expression diff --git a/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.cpp b/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.cpp new file mode 100644 index 0000000000000..08209972ca569 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.cpp @@ -0,0 +1,331 @@ +/* + * Licensed 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 "presto_cpp/main/types/RowExpressionOptimizer.h" +#include "velox/expression/ExprCompiler.h" + +using namespace facebook::presto; +using namespace facebook::velox; + +namespace facebook::presto::expression { + +namespace { + +const std::string kTimezoneHeader = "X-Presto-Time-Zone"; +const std::string kOptimizerLevelHeader = "X-Presto-Expression-Optimizer-Level"; +const std::string kEvaluated = "EVALUATED"; + +template +std::shared_ptr getConstantExpr( + const TypePtr& type, + const DecodedVector& decoded, + memory::MemoryPool* pool) { + if constexpr ( + KIND == TypeKind::ROW || KIND == TypeKind::UNKNOWN || + KIND == TypeKind::ARRAY || KIND == TypeKind::MAP) { + VELOX_USER_FAIL("Invalid result type {}", type->toString()); + } else { + using T = typename TypeTraits::NativeType; + auto constVector = std::make_shared>( + pool, decoded.size(), decoded.isNullAt(0), type, decoded.valueAt(0)); + return std::make_shared(constVector); + } +} +} // namespace + +exec::ExprPtr RowExpressionOptimizer::compileExpression( + const std::shared_ptr& inputRowExpr) { + auto typedExpr = veloxExprConverter_.toVeloxExpr(inputRowExpr); + exec::ExprSet exprSet{{typedExpr}, execCtx_.get()}; + auto compiledExprs = + exec::compileExpressions({typedExpr}, execCtx_.get(), &exprSet, true); + return compiledExprs[0]; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeAndSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr) { + auto left = specialFormExpr->arguments[0]; + auto right = specialFormExpr->arguments[1]; + auto leftExpr = compileExpression(left); + bool isLeftNull = false; + bool leftValue = false; + + if (auto constantExpr = + std::dynamic_pointer_cast(leftExpr)) { + isLeftNull = constantExpr->value()->isNullAt(0); + if (!isLeftNull) { + if (auto constVector = + constantExpr->value()->as>()) { + if (!constVector->valueAt(0)) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } else { + leftValue = true; + } + } + } + } + + auto rightExpr = compileExpression(right); + if (auto constantExpr = + std::dynamic_pointer_cast(rightExpr)) { + if (constantExpr->value()->isNullAt(0) && (isLeftNull || leftValue)) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } + if (auto constVector = constantExpr->value()->as>()) { + if (constVector->valueAt(0)) { + return left; + } else if (isLeftNull || leftValue) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } + } + } + + return specialFormExpr; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeIfSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr) { + auto condition = specialFormExpr->arguments[0]; + auto expr = compileExpression(condition); + + if (auto constantExpr = + std::dynamic_pointer_cast(expr)) { + if (auto constVector = constantExpr->value()->as>()) { + if (constVector->valueAt(0)) { + return specialFormExpr->arguments[1]; + } + return specialFormExpr->arguments[2]; + } + } + + return specialFormExpr; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeIsNullSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr) { + auto expr = compileExpression(specialFormExpr); + if (auto constantExpr = + std::dynamic_pointer_cast(expr)) { + auto resultExpr = std::make_shared( + std::make_shared>( + pool_.get(), + 1, + false, + BOOLEAN(), + constantExpr->value()->isNullAt(0))); + return rowExpressionConverter_.getConstantRowExpression(resultExpr); + } + + return specialFormExpr; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeOrSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr) { + auto left = specialFormExpr->arguments[0]; + auto right = specialFormExpr->arguments[1]; + auto leftExpr = compileExpression(left); + bool isLeftNull = false; + bool leftValue = true; + + if (auto constantExpr = + std::dynamic_pointer_cast(leftExpr)) { + isLeftNull = constantExpr->value()->isNullAt(0); + if (!isLeftNull) { + if (auto constVector = + constantExpr->value()->as>()) { + if (constVector->valueAt(0)) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } else { + leftValue = false; + } + } + } + } + + auto rightExpr = compileExpression(right); + if (auto constantExpr = + std::dynamic_pointer_cast(rightExpr)) { + if (constantExpr->value()->isNullAt(0) && (isLeftNull || !leftValue)) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } + if (auto constVector = constantExpr->value()->as>()) { + if (!constVector->valueAt(0)) { + return left; + } else if (isLeftNull || !leftValue) { + return rowExpressionConverter_.getConstantRowExpression(constantExpr); + } + } + } + + return specialFormExpr; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeCoalesceSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr) { + auto argsNoNulls = specialFormExpr->arguments; + argsNoNulls.erase( + std::remove_if( + argsNoNulls.begin(), + argsNoNulls.end(), + [&](const auto& arg) { + auto compiledExpr = compileExpression(arg); + if (auto constantExpr = + std::dynamic_pointer_cast( + compiledExpr)) { + return constantExpr->value()->isNullAt(0); + } + return false; + }), + argsNoNulls.end()); + + if (argsNoNulls.empty()) { + return specialFormExpr->arguments[0]; + } + specialFormExpr->arguments = argsNoNulls; + return specialFormExpr; +} + +RowExpressionPtr RowExpressionOptimizer::optimizeSpecialForm( + const std::shared_ptr& specialFormExpr) { + switch (specialFormExpr->form) { + case protocol::Form::IF: + return optimizeIfSpecialForm(specialFormExpr); + case protocol::Form::NULL_IF: + VELOX_USER_FAIL("NULL_IF specialForm not supported"); + break; + case protocol::Form::IS_NULL: + return optimizeIsNullSpecialForm(specialFormExpr); + case protocol::Form::AND: + return optimizeAndSpecialForm(specialFormExpr); + case protocol::Form::OR: + return optimizeOrSpecialForm(specialFormExpr); + case protocol::Form::COALESCE: + return optimizeCoalesceSpecialForm(specialFormExpr); + case protocol::Form::IN: + case protocol::Form::DEREFERENCE: + case protocol::Form::SWITCH: + case protocol::Form::WHEN: + case protocol::Form::ROW_CONSTRUCTOR: + case protocol::Form::BIND: + default: + break; + } + + return specialFormExpr; +} + +json RowExpressionOptimizer::evaluateNonDeterministicConstantExpr( + const exec::ExprPtr& expr, + exec::ExprSet& exprSet) { + VELOX_USER_CHECK(!expr->isDeterministic()); + std::vector compiledExprInputTypes; + std::vector compiledExprInputs; + for (const auto& exprInput : expr->inputs()) { + VELOX_USER_CHECK( + exprInput->isConstant(), + "Inputs to non-deterministic expression to be evaluated must be constant"); + const auto inputAsConstExpr = + std::dynamic_pointer_cast(exprInput); + compiledExprInputs.emplace_back(inputAsConstExpr->value()); + compiledExprInputTypes.emplace_back(exprInput->type()); + } + + const auto inputVector = std::make_shared( + pool_.get(), + ROW(std::move(compiledExprInputTypes)), + nullptr, + 1, + compiledExprInputs); + exec::EvalCtx evalCtx(execCtx_.get(), &exprSet, inputVector.get()); + std::vector results(1); + SelectivityVector rows(1); + exprSet.eval(rows, evalCtx, results); + auto res = results.front(); + DecodedVector decoded(*res, rows); + VELOX_USER_CHECK(decoded.size() == 1); + const auto constExpr = VELOX_DYNAMIC_TYPE_DISPATCH( + getConstantExpr, res->typeKind(), res->type(), decoded, pool_.get()); + return rowExpressionConverter_.getConstantRowExpression(constExpr); +} + +json::array_t RowExpressionOptimizer::optimizeExpressions( + const json::array_t& input, + const std::string& optimizerLevel) { + const auto numExpr = input.size(); + json::array_t output = json::array(); + for (auto i = 0; i < numExpr; i++) { + std::shared_ptr inputRowExpr = input[i]; + if (const auto special = + std::dynamic_pointer_cast( + inputRowExpr)) { + inputRowExpr = optimizeSpecialForm(special); + } + auto typedExpr = veloxExprConverter_.toVeloxExpr(inputRowExpr); + exec::ExprSet exprSet{{typedExpr}, execCtx_.get()}; + auto compiledExprs = + exec::compileExpressions({typedExpr}, execCtx_.get(), &exprSet, true); + auto compiledExpr = compiledExprs[0]; + json resultJson; + + if (optimizerLevel == kEvaluated) { + if (compiledExpr->isConstant()) { + resultJson = rowExpressionConverter_.veloxToPrestoRowExpression( + compiledExpr, input[i]); + } else { + // Velox does not evaluate expressions that are non-deterministic during + // compilation with constant folding enabled. Presto might require such + // non-deterministic expressions to be evaluated as well, this would be + // indicated by the header field 'X-Presto-Expression-Optimizer-Level' + // in the http request made to the native sidecar. When this field is + // set to 'EVALUATED', non-deterministic expressions with constant + // inputs are also evaluated. + resultJson = + evaluateNonDeterministicConstantExpr(compiledExpr, exprSet); + } + } else { + resultJson = rowExpressionConverter_.veloxToPrestoRowExpression( + compiledExpr, input[i]); + } + + output.push_back(resultJson); + } + return output; +} + +std::pair RowExpressionOptimizer::optimize( + const proxygen::HTTPHeaders& httpHeaders, + const json::array_t& input) { + try { + auto timezone = httpHeaders.getSingleOrEmpty(kTimezoneHeader); + auto optimizerLevel = httpHeaders.getSingleOrEmpty(kOptimizerLevelHeader); + std::unordered_map config( + {{core::QueryConfig::kSessionTimezone, timezone}, + {core::QueryConfig::kAdjustTimestampToTimezone, "true"}}); + auto queryCtx = + core::QueryCtx::create(nullptr, core::QueryConfig{std::move(config)}); + execCtx_ = std::make_unique(pool_.get(), queryCtx.get()); + + return {optimizeExpressions(input, optimizerLevel), true}; + } catch (const VeloxUserError& e) { + VLOG(1) << "VeloxUserError during expression evaluation: " << e.what(); + return {e.what(), false}; + } catch (const VeloxException& e) { + VLOG(1) << "VeloxException during expression evaluation: " << e.what(); + return {e.what(), false}; + } catch (const std::exception& e) { + VLOG(1) << "std::exception during expression evaluation: " << e.what(); + return {e.what(), false}; + } +} + +} // namespace facebook::presto::expression diff --git a/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.h b/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.h new file mode 100644 index 0000000000000..9e6761b3aa179 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/RowExpressionOptimizer.h @@ -0,0 +1,105 @@ +/* + * Licensed 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. + */ +#pragma once + +#include +#include "presto_cpp/main/types/PrestoToVeloxExpr.h" +#include "presto_cpp/main/types/RowExpressionConverter.h" + +using namespace facebook::velox; + +namespace facebook::presto::expression { + +using RowExpressionPtr = std::shared_ptr; +using SpecialFormExpressionPtr = + std::shared_ptr; + +/// Helper class to optimize Presto expressions of type protocol::RowExpression +/// sent along the REST endpoint 'v1/expressions' in the sidecar. +class RowExpressionOptimizer { + public: + explicit RowExpressionOptimizer() + : pool_(memory::MemoryManager::getInstance()->addLeafPool( + "RowExpressionOptimizer")), + veloxExprConverter_(pool_.get(), &typeParser_), + rowExpressionConverter_(RowExpressionConverter(pool_.get())) {} + + /// Optimizes all expressions from the input json array. If the expression + /// optimization fails for any of the input expressions, the second value in + /// the returned pair will be false and the returned json would contain the + /// exception. Otherwise, the returned json will be an array of optimized row + /// expressions. + std::pair optimize( + const proxygen::HTTPHeaders& httpHeaders, + const json::array_t& input); + + private: + /// Converts protocol::RowExpression into a velox expression with constant + /// folding enabled during velox expression compilation. + exec::ExprPtr compileExpression(const RowExpressionPtr& inputRowExpr); + + /// Evaluates special form expression AND when the inputs can be constant + /// folded to true, false, or NULL. + RowExpressionPtr optimizeAndSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Evaluates special form expression IF when the condition can be constant + /// folded to true or false. + RowExpressionPtr optimizeIfSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Evaluates special form expression IS_NULL when the input can be constant + /// folded. + RowExpressionPtr optimizeIsNullSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Evaluates special form expression OR when the inputs can be constant + /// folded to true, false, or NULL. + RowExpressionPtr optimizeOrSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Optimizes special form expression COALESCE by removing NULLs from the + /// argument list. + RowExpressionPtr optimizeCoalesceSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Optimizes special form expressions using optimization rules borrowed from + /// Presto function visitSpecialForm() in RowExpressionInterpreter.java. + RowExpressionPtr optimizeSpecialForm( + const SpecialFormExpressionPtr& specialFormExpr); + + /// Evaluates non-deterministic expressions with constant inputs. + json evaluateNonDeterministicConstantExpr( + const exec::ExprPtr& expr, + exec::ExprSet& exprSet); + + /// Optimizes and constant folds each expression from input json array and + /// returns an array of expressions that are optimized and constant folded. + /// Each expression in the input array is optimized with helper functions + /// optimizeSpecialForm (applicable only for special form expressions) and + /// optimizeExpression. The optimized expression is also evaluated if the + /// optimization level in the header of http request made to 'v1/expressions' + /// is 'EVALUATED'. optimizeExpression uses RowExpressionConverter to convert + /// Velox expression(s) to their corresponding Presto RowExpression(s). + json::array_t optimizeExpressions( + const json::array_t& input, + const std::string& optimizationLevel); + + const std::shared_ptr pool_; + std::unique_ptr execCtx_; + TypeParser typeParser_; + VeloxExprConverter veloxExprConverter_; + RowExpressionConverter rowExpressionConverter_; +}; +} // namespace facebook::presto::expression diff --git a/presto-native-execution/presto_cpp/main/types/tests/CMakeLists.txt b/presto-native-execution/presto_cpp/main/types/tests/CMakeLists.txt index 28f73aff40b80..1d560f75749d6 100644 --- a/presto-native-execution/presto_cpp/main/types/tests/CMakeLists.txt +++ b/presto-native-execution/presto_cpp/main/types/tests/CMakeLists.txt @@ -131,3 +131,36 @@ target_link_libraries( velox_exec_test_lib GTest::gtest GTest::gtest_main) + +add_executable(presto_expression_optimizer_test RowExpressionOptimizerTest.cpp) + +add_test( + NAME presto_expression_optimizer_test + COMMAND presto_expression_optimizer_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries( + presto_expression_optimizer_test + presto_expression_optimizer + presto_http + presto_type_test_utils + velox_exec_test_lib + velox_parse_utils + GTest::gtest + GTest::gtest_main) + +add_executable(presto_expression_converter_test RowExpressionConverterTest.cpp) + +add_test( + NAME presto_expression_converter_test + COMMAND presto_expression_converter_test + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries( + presto_expression_converter_test + presto_expression_converter + presto_http + presto_type_test_utils + velox_exec_test_lib + GTest::gtest + GTest::gtest_main) diff --git a/presto-native-execution/presto_cpp/main/types/tests/RowExpressionConverterTest.cpp b/presto-native-execution/presto_cpp/main/types/tests/RowExpressionConverterTest.cpp new file mode 100644 index 0000000000000..d0d781be20aab --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/RowExpressionConverterTest.cpp @@ -0,0 +1,161 @@ +/* + * Licensed 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 "presto_cpp/main/types/RowExpressionConverter.h" +#include +#include "presto_cpp/main/common/tests/test_json.h" +#include "presto_cpp/main/http/tests/HttpTestBase.h" +#include "presto_cpp/main/types/tests/TestUtils.h" +#include "velox/expression/CastExpr.h" +#include "velox/expression/ConjunctExpr.h" +#include "velox/expression/FieldReference.h" +#include "velox/functions/prestosql/registration/RegistrationFunctions.h" +#include "velox/parse/TypeResolver.h" +#include "velox/vector/VectorStream.h" +#include "velox/vector/tests/utils/VectorTestBase.h" + +using namespace facebook::presto; +using namespace facebook::velox; + +// RowExpressionConverter is used only by RowExpressionOptimizer so unit tests +// are only added for simple expressions here. End-to-end tests to validate +// Velox expression to Presto RowExpression conversion for different types of +// expressions can be found in TestDelegatingExpressionOptimizer.java in +// presto-native-sidecar-plugin. +class RowExpressionConverterTest + : public ::testing::Test, + public facebook::velox::test::VectorTestBase { + protected: + static void SetUpTestCase() { + memory::MemoryManager::testingSetInstance({}); + } + + void SetUp() override { + rowExpressionConverter_ = + std::make_unique(pool_.get()); + } + + void testFile( + const std::vector& inputs, + const std::string& testName, + std::vector inputRowExpressions = {}) { + json::array_t expected = json::parse( + slurp(facebook::presto::test::utils::getDataPath(testName))); + auto numExpr = inputs.size(); + for (auto i = 0; i < numExpr; i++) { + json inputRowExpr = + inputRowExpressions.empty() ? json() : inputRowExpressions.at(i); + json result = rowExpressionConverter_->veloxToPrestoRowExpression( + inputs[i], inputRowExpr); + EXPECT_EQ(result, expected[i]); + } + } + + std::unique_ptr rowExpressionConverter_; +}; + +TEST_F(RowExpressionConverterTest, constant) { + auto inputs = std::vector{ + std::make_shared(makeConstant(true, 1)), + std::make_shared(makeConstant(12, 1)), + std::make_shared(makeConstant(123, 1)), + std::make_shared(makeConstant(1234, 1)), + std::make_shared(makeConstant(12345, 1)), + std::make_shared( + makeConstant(std::numeric_limits::max(), 1)), + std::make_shared(makeConstant(123.456, 1)), + std::make_shared(makeConstant(1234.5678, 1)), + std::make_shared( + makeConstant(std::numeric_limits::quiet_NaN(), 1)), + std::make_shared(makeConstant("abcd", 1)), + std::make_shared( + makeNullConstant(TypeKind::BIGINT, 1)), + std::make_shared( + makeNullConstant(TypeKind::VARCHAR, 1))}; + testFile(inputs, "ConstantExpressions.json"); +} + +TEST_F(RowExpressionConverterTest, rowConstructor) { + auto inputs = std::vector{ + std::make_shared(makeConstantRow( + ROW({BIGINT(), REAL(), VARCHAR()}), + variant::row( + {static_cast(123456), + static_cast(12.345), + "abc"}), + 1)), + std::make_shared(makeConstantRow( + ROW({REAL(), VARCHAR(), INTEGER()}), + variant::row( + {std::numeric_limits::quiet_NaN(), + "", + std::numeric_limits::max()}), + 1))}; + testFile(inputs, "RowConstructorExpressions.json"); +} + +TEST_F(RowExpressionConverterTest, variable) { + auto inputs = std::vector{ + std::make_shared( + VARCHAR(), std::vector{}, "c0"), + std::make_shared( + ARRAY(BIGINT()), std::vector{}, "c1"), + std::make_shared( + MAP(INTEGER(), REAL()), std::vector{}, "c2"), + std::make_shared( + ROW({SMALLINT(), VARCHAR(), DOUBLE()}), + std::vector{}, + "c3"), + std::make_shared( + MAP(ARRAY(TINYINT()), BOOLEAN()), std::vector{}, "c4"), + }; + testFile(inputs, "VariableExpressions.json"); +} + +TEST_F(RowExpressionConverterTest, special) { + auto castExpr = std::make_shared(); + auto orExpr = std::make_shared(false); + auto andExpr = std::make_shared(true); + auto inputs = std::vector{ + castExpr->constructSpecialForm( + VARCHAR(), + {std::make_shared( + BIGINT(), std::vector{}, "c0")}, + false, + core::QueryConfig({})), + orExpr->constructSpecialForm( + BOOLEAN(), + {std::make_shared( + BOOLEAN(), std::vector{}, "c1"), + std::make_shared(makeConstant(true, 1))}, + false, + core::QueryConfig({})), + andExpr->constructSpecialForm( + BOOLEAN(), + {castExpr->constructSpecialForm( + BOOLEAN(), + {std::make_shared( + INTEGER(), std::vector{}, "c2")}, + false, + core::QueryConfig({})), + std::make_shared( + BOOLEAN(), std::vector{}, "c3")}, + false, + core::QueryConfig({}))}; + + json rowExprWithTwoInputs; + rowExprWithTwoInputs["arguments"] = json::array({json(), json()}); + auto inputRowExpressions = + std::vector{json(), rowExprWithTwoInputs, rowExprWithTwoInputs}; + testFile(inputs, "CallAndSpecialFormExpressions.json", inputRowExpressions); +} diff --git a/presto-native-execution/presto_cpp/main/types/tests/RowExpressionOptimizerTest.cpp b/presto-native-execution/presto_cpp/main/types/tests/RowExpressionOptimizerTest.cpp new file mode 100644 index 0000000000000..71a15de0cc35c --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/RowExpressionOptimizerTest.cpp @@ -0,0 +1,95 @@ +/* + * Licensed 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 "presto_cpp/main/types/RowExpressionOptimizer.h" +#include +#include +#include "presto_cpp/main/common/tests/test_json.h" +#include "presto_cpp/main/http/tests/HttpTestBase.h" +#include "presto_cpp/main/types/tests/TestUtils.h" +#include "velox/expression/FieldReference.h" +#include "velox/expression/RegisterSpecialForm.h" +#include "velox/functions/prestosql/registration/RegistrationFunctions.h" +#include "velox/parse/TypeResolver.h" +#include "velox/vector/VectorStream.h" +#include "velox/vector/tests/utils/VectorTestBase.h" + +using namespace facebook::presto; + +// RowExpressionOptimizerTest only tests for basic expression optimization. +// End-to-end tests for different types of expressions can be found in +// TestDelegatingExpressionOptimizer.java in presto-native-sidecar-plugin. +class RowExpressionOptimizerTest + : public ::testing::Test, + public facebook::velox::test::VectorTestBase { + protected: + static void SetUpTestCase() { + memory::MemoryManager::testingSetInstance({}); + } + + void SetUp() override { + parse::registerTypeResolver(); + functions::prestosql::registerAllScalarFunctions("presto.default."); + exec::registerFunctionCallToSpecialForms(); + rowExpressionOptimizer_ = + std::make_unique(); + } + + void testFile(const std::string& testName) { + auto input = slurp(facebook::presto::test::utils::getDataPath( + fmt::format("{}Input.json", testName))); + json::array_t inputExpressions = json::parse(input); + proxygen::HTTPMessage httpMessage; + httpMessage.getHeaders().set( + "X-Presto-Time-Zone", "America/Bahia_Banderas"); + httpMessage.getHeaders().set( + "X-Presto-Expression-Optimizer-Level", "OPTIMIZED"); + auto result = rowExpressionOptimizer_->optimize( + httpMessage.getHeaders(), inputExpressions); + + EXPECT_EQ(result.second, true); + json resultExpressions = result.first; + EXPECT_EQ(resultExpressions.is_array(), true); + auto expected = slurp(facebook::presto::test::utils::getDataPath( + fmt::format("{}Expected.json", testName))); + json::array_t expectedExpressions = json::parse(expected); + auto numExpressions = resultExpressions.size(); + EXPECT_EQ(numExpressions, expectedExpressions.size()); + for (auto i = 0; i < numExpressions; i++) { + EXPECT_EQ(resultExpressions.at(i), expectedExpressions.at(i)); + } + } + + std::unique_ptr rowExpressionOptimizer_; +}; + +TEST_F(RowExpressionOptimizerTest, simple) { + // Files SimpleExpressions{Input|Expected}.json contain the input and expected + // JSON representing the RowExpressions resulting from the following queries: + // select 1 + 2; + // select abs(-11) + ceil(cast(3.4 as double)) + floor(cast(5.6 as double)); + // select 2 between 1 and 3; + // Simple expression evaluation with constant folding is verified here. + testFile("SimpleExpressions"); +} + +TEST_F(RowExpressionOptimizerTest, specialFormRewrites) { + // Files SpecialExpressions{Input|Expected}.json contain the input and + // expected JSON representing the RowExpressions resulting from the following + // queries: + // select if(1 < 2, 2, 3); + // select (1 < 2) and (2 < 3); + // select (1 < 2) or (2 < 3); + // Special form expression rewrites are verified here. + testFile("SpecialFormExpressions"); +} diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/CallAndSpecialFormExpressions.json b/presto-native-execution/presto_cpp/main/types/tests/data/CallAndSpecialFormExpressions.json new file mode 100644 index 0000000000000..990a19c127103 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/CallAndSpecialFormExpressions.json @@ -0,0 +1,83 @@ +[ + { + "@type": "call", + "arguments": [ + { + "@type": "variable", + "name": "c0", + "type": "bigint" + } + ], + "displayName": "presto.default.$operator$cast", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "bigint" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$cast", + "returnType": "varchar", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "varchar" + }, + { + "@type": "special", + "arguments": [ + { + "@type": "variable", + "name": "c1", + "type": "boolean" + }, + { + "@type": "constant", + "type": "boolean", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAE=" + } + ], + "form": "OR", + "returnType": "boolean" + }, + { + "@type": "special", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "variable", + "name": "c2", + "type": "integer" + } + ], + "displayName": "presto.default.$operator$cast", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$cast", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + }, + { + "@type": "variable", + "name": "c3", + "type": "boolean" + } + ], + "form": "AND", + "returnType": "boolean" + } +] \ No newline at end of file diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/ConstantExpressions.json b/presto-native-execution/presto_cpp/main/types/tests/data/ConstantExpressions.json new file mode 100644 index 0000000000000..f0c2a178603da --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/ConstantExpressions.json @@ -0,0 +1,62 @@ +[ + { + "@type": "constant", + "type": "boolean", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAE=" + }, + { + "@type": "constant", + "type": "tinyint", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAw=" + }, + { + "@type": "constant", + "type": "smallint", + "valueBlock": "CwAAAFNIT1JUX0FSUkFZAQAAAAB7AA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAA0gQAAA==" + }, + { + "@type": "constant", + "type": "bigint", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAADkwAAAAAAAA" + }, + { + "@type": "constant", + "type": "bigint", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAP////////9/" + }, + { + "@type": "constant", + "type": "real", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAeen2Qg==" + }, + { + "@type": "constant", + "type": "double", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAK36XG1FSpNA" + }, + { + "@type": "constant", + "type": "double", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAAAAAAAAAPh/" + }, + { + "@type": "constant", + "type": "varchar", + "valueBlock": "DgAAAFZBUklBQkxFX1dJRFRIAQAAAAQAAAAABAAAAGFiY2Q=" + }, + { + "@type": "constant", + "type": "bigint", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAaA=" + }, + { + "@type": "constant", + "type": "varchar", + "valueBlock": "DgAAAFZBUklBQkxFX1dJRFRIAQAAAAAAAAABhQAAAAA=" + } +] \ No newline at end of file diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/RowConstructorExpressions.json b/presto-native-execution/presto_cpp/main/types/tests/data/RowConstructorExpressions.json new file mode 100644 index 0000000000000..2255e900d737e --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/RowConstructorExpressions.json @@ -0,0 +1,46 @@ +[ + { + "@type": "special", + "arguments": [ + { + "@type": "constant", + "type": "bigint", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAEDiAQAAAAAA" + }, + { + "@type": "constant", + "type": "real", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAH4VFQQ==" + }, + { + "@type": "constant", + "type": "varchar", + "valueBlock": "DgAAAFZBUklBQkxFX1dJRFRIAQAAAAMAAAAAAwAAAGFiYw==" + } + ], + "form": "ROW_CONSTRUCTOR", + "returnType": "row(bigint,real,varchar)" + }, + { + "@type": "special", + "arguments": [ + { + "@type": "constant", + "type": "real", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAADAfw==" + }, + { + "@type": "constant", + "type": "varchar", + "valueBlock": "DgAAAFZBUklBQkxFX1dJRFRIAQAAAAAAAAAAAAAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAA////fw==" + } + ], + "form": "ROW_CONSTRUCTOR", + "returnType": "row(real,varchar,integer)" + } +] \ No newline at end of file diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsExpected.json b/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsExpected.json new file mode 100644 index 0000000000000..e9b014fecd3b2 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsExpected.json @@ -0,0 +1,17 @@ +[ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAwAAAA==" + }, + { + "@type": "constant", + "type": "double", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAAAAAAAAAADRA" + }, + { + "@type": "constant", + "type": "boolean", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAE=" + } +] diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsInput.json b/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsInput.json new file mode 100644 index 0000000000000..dd2a0497703eb --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/SimpleExpressionsInput.json @@ -0,0 +1,278 @@ +[ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAQAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + } + ], + "displayName": "ADD", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$add", + "returnType": "integer", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "integer" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAACwAAAA==" + } + ], + "displayName": "NEGATION", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$negation", + "returnType": "integer", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "integer" + } + ], + "displayName": "abs", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.abs", + "returnType": "integer", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "integer" + } + ], + "displayName": "CAST", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$cast", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "decimal(2,1)", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAACIAAAAAAAAA" + } + ], + "displayName": "CAST", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "decimal(2,1)" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$cast", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + } + ], + "displayName": "ceil", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "double" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.ceil", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + } + ], + "displayName": "ADD", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "double", + "double" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$add", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "decimal(2,1)", + "valueBlock": "CgAAAExPTkdfQVJSQVkBAAAAADgAAAAAAAAA" + } + ], + "displayName": "CAST", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "decimal(2,1)" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$cast", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + } + ], + "displayName": "floor", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "double" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.floor", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + } + ], + "displayName": "ADD", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "double", + "double" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$add", + "returnType": "double", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "double" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAQAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAwAAAA==" + } + ], + "displayName": "BETWEEN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$between", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + } +] diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsExpected.json b/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsExpected.json new file mode 100644 index 0000000000000..2ce6acb1ab46e --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsExpected.json @@ -0,0 +1,17 @@ +[ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + }, + { + "@type": "constant", + "type": "boolean", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAE=" + }, + { + "@type": "constant", + "type": "boolean", + "valueBlock": "CgAAAEJZVEVfQVJSQVkBAAAAAAE=" + } +] diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsInput.json b/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsInput.json new file mode 100644 index 0000000000000..77802722541b0 --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/SpecialFormExpressionsInput.json @@ -0,0 +1,193 @@ +[ + { + "@type": "special", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAQAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + } + ], + "displayName": "LESS_THAN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$less_than", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAwAAAA==" + } + ], + "form": "IF", + "returnType": "integer" + }, + { + "@type": "special", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAQAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + } + ], + "displayName": "LESS_THAN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$less_than", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAwAAAA==" + } + ], + "displayName": "LESS_THAN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$less_than", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + } + ], + "form": "AND", + "returnType": "boolean" + }, + { + "@type": "special", + "arguments": [ + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAQAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + } + ], + "displayName": "LESS_THAN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$less_than", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + }, + { + "@type": "call", + "arguments": [ + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAgAAAA==" + }, + { + "@type": "constant", + "type": "integer", + "valueBlock": "CQAAAElOVF9BUlJBWQEAAAAAAwAAAA==" + } + ], + "displayName": "LESS_THAN", + "functionHandle": { + "@type": "$static", + "signature": { + "argumentTypes": [ + "integer", + "integer" + ], + "kind": "SCALAR", + "longVariableConstraints": [], + "name": "presto.default.$operator$less_than", + "returnType": "boolean", + "typeVariableConstraints": [], + "variableArity": false + } + }, + "returnType": "boolean" + } + ], + "form": "OR", + "returnType": "boolean" + } +] diff --git a/presto-native-execution/presto_cpp/main/types/tests/data/VariableExpressions.json b/presto-native-execution/presto_cpp/main/types/tests/data/VariableExpressions.json new file mode 100644 index 0000000000000..0ec32816a41ac --- /dev/null +++ b/presto-native-execution/presto_cpp/main/types/tests/data/VariableExpressions.json @@ -0,0 +1,27 @@ +[ + { + "@type": "variable", + "name": "c0", + "type": "varchar" + }, + { + "@type": "variable", + "name": "c1", + "type": "array(bigint)" + }, + { + "@type": "variable", + "name": "c2", + "type": "map(integer,real)" + }, + { + "@type": "variable", + "name": "c3", + "type": "row(smallint,varchar,double)" + }, + { + "@type": "variable", + "name": "c4", + "type": "map(array(tinyint),boolean)" + } +] \ No newline at end of file