diff --git a/velox/exec/tests/CMakeLists.txt b/velox/exec/tests/CMakeLists.txt index 47517b75e99..38621fd3cf0 100644 --- a/velox/exec/tests/CMakeLists.txt +++ b/velox/exec/tests/CMakeLists.txt @@ -92,7 +92,6 @@ add_executable( GroupedExecutionTest.cpp Main.cpp OperatorUtilsTest.cpp - ParseTypeSignatureTest.cpp PlanBuilderTest.cpp QueryAssertionsTest.cpp TaskTest.cpp diff --git a/velox/exec/tests/FunctionSignatureBuilderTest.cpp b/velox/exec/tests/FunctionSignatureBuilderTest.cpp index f890f6bba22..bda0037f9e2 100644 --- a/velox/exec/tests/FunctionSignatureBuilderTest.cpp +++ b/velox/exec/tests/FunctionSignatureBuilderTest.cpp @@ -115,7 +115,7 @@ TEST_F(FunctionSignatureBuilderTest, typeParamTests) { .returnType("integer") .argumentType("Any(T)") .build(), - "Type 'Any' cannot have parameters"); + "Failed to parse type signature [Any(T)]: syntax error, unexpected LPAREN, expecting YYEOF"); // Variable Arity in argument fails. VELOX_ASSERT_THROW( @@ -124,7 +124,7 @@ TEST_F(FunctionSignatureBuilderTest, typeParamTests) { .returnType("integer") .argumentType("row(..., varchar)") .build(), - "Type doesn't exist: '...'"); + "Failed to parse type signature [row(..., varchar)]: syntax error, unexpected COMMA"); // Type params cant have type params. VELOX_ASSERT_THROW( @@ -134,7 +134,7 @@ TEST_F(FunctionSignatureBuilderTest, typeParamTests) { .returnType("integer") .argumentType("T(M)") .build(), - "Named type cannot have parameters: 'T(M)'"); + "Failed to parse type signature [T(M)]: syntax error, unexpected LPAREN, expecting YYEOF"); } TEST_F(FunctionSignatureBuilderTest, anyInReturn) { diff --git a/velox/exec/tests/ParseTypeSignatureTest.cpp b/velox/exec/tests/ParseTypeSignatureTest.cpp deleted file mode 100644 index 5d65bcfcd54..00000000000 --- a/velox/exec/tests/ParseTypeSignatureTest.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) Facebook, Inc. and its affiliates. - * - * 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 - -#include "velox/expression/FunctionSignature.h" -#include "velox/type/Type.h" - -using namespace facebook::velox; -using namespace facebook::velox::exec; - -namespace { -std::string roundTrip(const std::string& typeSignature) { - return parseTypeSignature(typeSignature).toString(); -} - -void testScalarType(const std::string& typeSignature) { - auto signature = parseTypeSignature(typeSignature); - ASSERT_EQ(signature.baseName(), typeSignature); - ASSERT_EQ(signature.parameters().size(), 0); -} -} // namespace - -TEST(ParseTypeSignatureTest, scalar) { - testScalarType("tinyint"); - testScalarType("smallint"); - testScalarType("integer"); - testScalarType("bigint"); - testScalarType("real"); - testScalarType("double"); - testScalarType("timestamp"); -} - -TEST(ParseTypeSignatureTest, array) { - auto signature = parseTypeSignature("array(bigint)"); - ASSERT_EQ(signature.baseName(), "array"); - ASSERT_EQ(signature.parameters().size(), 1); - - auto param = signature.parameters()[0]; - ASSERT_EQ(param.baseName(), "bigint"); - ASSERT_EQ(param.parameters().size(), 0); -} - -TEST(ParseTypeSignatureTest, map) { - auto signature = parseTypeSignature("map(bigint, double)"); - ASSERT_EQ(signature.baseName(), "map"); - ASSERT_EQ(signature.parameters().size(), 2); - - auto key = signature.parameters()[0]; - ASSERT_EQ(key.baseName(), "bigint"); - ASSERT_EQ(key.parameters().size(), 0); - - auto value = signature.parameters()[1]; - ASSERT_EQ(value.baseName(), "double"); - ASSERT_EQ(value.parameters().size(), 0); -} - -TEST(ParseTypeSignatureTest, roundTrip) { - ASSERT_EQ(roundTrip("bigint"), "bigint"); - - ASSERT_EQ(roundTrip("array(T)"), "array(T)"); - ASSERT_EQ(roundTrip("array(array(T))"), "array(array(T))"); - ASSERT_EQ(roundTrip("array(row(K,V))"), "array(row(K,V))"); - - ASSERT_EQ(roundTrip("map(K,V)"), "map(K,V)"); - - ASSERT_EQ(roundTrip("function(S,R)"), "function(S,R)"); - - // Test a complex type as the second field in a row - ASSERT_EQ( - roundTrip("row(map(K,V),map(bigint,array(double)))"), - "row(map(K,V),map(bigint,array(double)))"); -} - -TEST(ParseTypeSignatureTest, invalidSignatures) { - EXPECT_THROW(parseTypeSignature("array(varchar"), VeloxRuntimeError); - EXPECT_THROW(parseTypeSignature("array(array(T)"), VeloxRuntimeError); -} diff --git a/velox/expression/CMakeLists.txt b/velox/expression/CMakeLists.txt index b36535d9454..00d49d540f6 100644 --- a/velox/expression/CMakeLists.txt +++ b/velox/expression/CMakeLists.txt @@ -12,12 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +add_library(velox_type_signature OBJECT TypeSignature.cpp) + +target_link_libraries(velox_type_signature velox_common_base) + add_library( velox_expression_functions FunctionSignature.cpp SignatureBinder.cpp ReverseSignatureBinder.cpp) -target_link_libraries(velox_expression_functions velox_common_base - velox_type_calculation) +target_link_libraries( + velox_expression_functions velox_common_base velox_type_calculation + velox_type_signature velox_signature_parser) add_library( velox_expression @@ -48,6 +53,7 @@ target_link_libraries( velox_expression_functions velox_functions_util) add_subdirectory(type_calculation) +add_subdirectory(signature_parser) if(${VELOX_BUILD_TESTING}) add_subdirectory(tests) diff --git a/velox/expression/FunctionSignature.cpp b/velox/expression/FunctionSignature.cpp index a9185256f16..7c7d9dd4019 100644 --- a/velox/expression/FunctionSignature.cpp +++ b/velox/expression/FunctionSignature.cpp @@ -51,24 +51,6 @@ const std::vector primitiveTypeNames() { return kPrimitiveTypeNames; } -void toAppend( - const facebook::velox::exec::TypeSignature& signature, - std::string* result) { - result->append(signature.toString()); -} - -std::string TypeSignature::toString() const { - std::ostringstream out; - if (rowFieldName_.has_value()) { - out << *rowFieldName_ << " "; - } - out << baseName_; - if (!parameters_.empty()) { - out << "(" << folly::join(",", parameters_) << ")"; - } - return out.str(); -} - std::string FunctionSignature::argumentsToString() const { std::vector arguments; auto size = argumentTypes_.size(); @@ -110,65 +92,6 @@ size_t findNextComma(const std::string& str, size_t start) { return std::string::npos; } -TypeSignature parseTypeBaseSignature( - const std::string& signature, - std::vector&& parameters, - bool allowNamed) { - if (allowNamed) { - const auto spacePos = signature.find(' '); - - // If there is a space and the name doesn't match a known type, - // e.g. "timestamp with timezone", this is a named row field. - if ((spacePos != std::string::npos) && !hasType(signature)) { - return TypeSignature( - signature.substr(spacePos + 1), - std::move(parameters), - signature.substr(0, spacePos)); - } - } - return TypeSignature(signature, std::move(parameters)); -} - -TypeSignature parseTypeSignatureImpl( - const std::string& signature, - bool allowNamed) { - auto parenPos = signature.find('('); - if (parenPos == std::string::npos) { - return parseTypeBaseSignature(signature, {}, allowNamed); - } - - auto baseName = signature.substr(0, parenPos); - std::vector nestedTypes; - - auto endParenPos = signature.rfind(')'); - VELOX_CHECK( - endParenPos != std::string::npos, - "Couldn't find the closing parenthesis."); - - const bool isRow = (boost::algorithm::to_upper_copy(baseName) == "ROW"); - auto prevPos = parenPos + 1; - auto commaPos = findNextComma(signature, prevPos); - - while (commaPos != std::string::npos) { - auto token = signature.substr(prevPos, commaPos - prevPos); - boost::algorithm::trim(token); - nestedTypes.emplace_back(parseTypeSignatureImpl(token, isRow)); - - prevPos = commaPos + 1; - commaPos = findNextComma(signature, prevPos); - } - - auto token = signature.substr(prevPos, endParenPos - prevPos); - boost::algorithm::trim(token); - nestedTypes.emplace_back(parseTypeSignatureImpl(token, isRow)); - - return parseTypeBaseSignature(baseName, std::move(nestedTypes), allowNamed); -} - -TypeSignature parseTypeSignature(const std::string& signature) { - return parseTypeSignatureImpl(signature, false); -} - namespace { /// Returns true only if 'str' contains digits. bool isPositiveInteger(const std::string& str) { diff --git a/velox/expression/FunctionSignature.h b/velox/expression/FunctionSignature.h index 8b3f063e4eb..e817e59f7e5 100644 --- a/velox/expression/FunctionSignature.h +++ b/velox/expression/FunctionSignature.h @@ -23,6 +23,8 @@ #include #include "velox/common/base/Exceptions.h" +#include "velox/expression/TypeSignature.h" +#include "velox/expression/signature_parser/SignatureParser.h" #include "velox/type/Type.h" namespace facebook::velox::exec { @@ -97,53 +99,6 @@ class SignatureVariable { bool comparableTypesOnly_ = false; }; -// Base type (e.g. map) and optional parameters (e.g. K, V). -class TypeSignature { - public: - /// @param baseName The base name of the type. Could describe a concrete type - /// name (e.g. map, bigint, double), or a variable (e.g. K, V). - /// @param parameters The optional parameters for the type. For example, the - /// signature "map(K, V)" would have two parameters, "K", and "V". All - /// parameters must be of the same ParameterType. - /// @param rowFieldName if this type signature is a field of another parent - /// row type, it can optionally have a name. E.g. `row(id bigint)` would have - /// "id" set as rowFieldName in the "bigint" parameter. - TypeSignature( - std::string baseName, - std::vector parameters, - std::optional rowFieldName = std::nullopt) - : baseName_{std::move(baseName)}, - parameters_{std::move(parameters)}, - rowFieldName_(rowFieldName) {} - - const std::string& baseName() const { - return baseName_; - } - - const std::vector& parameters() const { - return parameters_; - } - - const std::optional& rowFieldName() const { - return rowFieldName_; - } - - std::string toString() const; - - bool operator==(const TypeSignature& rhs) const { - return baseName_ == rhs.baseName_ && parameters_ == rhs.parameters_ && - rowFieldName_ == rhs.rowFieldName_; - } - - private: - const std::string baseName_; - const std::vector parameters_; - - // If this object is a field of another parent row type, it can optionally - // have a name, e.g, `row(id bigint)` - std::optional rowFieldName_; -}; - class FunctionSignature { public: /// @param variables_ Generic type names used in return type @@ -263,21 +218,6 @@ inline void addVariable( } // namespace -/// Parses a string into TypeSignature. The format of the string is type name, -/// optionally followed by type parameters enclosed in parenthesis. -/// -/// Examples: -/// - bigint -/// - double -/// - array(T) -/// - map(K,V) -/// - row(named bigint,array(tinyint),T) -/// - function(S,T,R) -/// -/// Row fields are allowed to be named or anonymous, e.g. "row(foo bigint)" or -/// "row(bigint)" -TypeSignature parseTypeSignature(const std::string& signature); - /// Convenience class for creating FunctionSignature instances. /// /// Example of usage: @@ -328,18 +268,18 @@ class FunctionSignatureBuilder { } FunctionSignatureBuilder& returnType(const std::string& type) { - returnType_.emplace(parseTypeSignature(type)); + returnType_.emplace(*parseTypeSignature(type)); return *this; } FunctionSignatureBuilder& argumentType(const std::string& type) { - argumentTypes_.emplace_back(parseTypeSignature(type)); + argumentTypes_.emplace_back(*parseTypeSignature(type)); constantArguments_.push_back(false); return *this; } FunctionSignatureBuilder& constantArgumentType(const std::string& type) { - argumentTypes_.emplace_back(parseTypeSignature(type)); + argumentTypes_.emplace_back(*parseTypeSignature(type)); constantArguments_.push_back(true); return *this; } @@ -399,25 +339,25 @@ class AggregateFunctionSignatureBuilder { } AggregateFunctionSignatureBuilder& returnType(const std::string& type) { - returnType_.emplace(parseTypeSignature(type)); + returnType_.emplace(*parseTypeSignature(type)); return *this; } AggregateFunctionSignatureBuilder& argumentType(const std::string& type) { - argumentTypes_.emplace_back(parseTypeSignature(type)); + argumentTypes_.emplace_back(*parseTypeSignature(type)); constantArguments_.push_back(false); return *this; } AggregateFunctionSignatureBuilder& constantArgumentType( const std::string& type) { - argumentTypes_.emplace_back(parseTypeSignature(type)); + argumentTypes_.emplace_back(*parseTypeSignature(type)); constantArguments_.push_back(true); return *this; } AggregateFunctionSignatureBuilder& intermediateType(const std::string& type) { - intermediateType_.emplace(parseTypeSignature(type)); + intermediateType_.emplace(*parseTypeSignature(type)); return *this; } diff --git a/velox/expression/TypeSignature.cpp b/velox/expression/TypeSignature.cpp new file mode 100644 index 00000000000..b04bb0ae007 --- /dev/null +++ b/velox/expression/TypeSignature.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/expression/TypeSignature.h" +#include "velox/common/base/Exceptions.h" + +namespace facebook::velox::exec { +void toAppend( + const facebook::velox::exec::TypeSignature& signature, + std::string* result) { + result->append(signature.toString()); +} + +std::string TypeSignature::toString() const { + std::ostringstream out; + if (rowFieldName_.has_value()) { + out << *rowFieldName_ << " "; + } + out << baseName_; + if (!parameters_.empty()) { + out << "(" << folly::join(",", parameters_) << ")"; + } + return out.str(); +} + +} // namespace facebook::velox::exec diff --git a/velox/expression/TypeSignature.h b/velox/expression/TypeSignature.h new file mode 100644 index 00000000000..3dc2144fd36 --- /dev/null +++ b/velox/expression/TypeSignature.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include +#include + +namespace facebook::velox::exec { + +// Base type (e.g. map) and optional parameters (e.g. K, V). +class TypeSignature { + public: + /// @param baseName The base name of the type. Could describe a concrete type + /// name (e.g. map, bigint, double), or a variable (e.g. K, V). + /// @param parameters The optional parameters for the type. For example, the + /// signature "map(K, V)" would have two parameters, "K", and "V". All + /// parameters must be of the same ParameterType. + /// @param rowFieldName if this type signature is a field of another parent + /// row type, it can optionally have a name. E.g. `row(id bigint)` would have + /// "id" set as rowFieldName in the "bigint" parameter. + TypeSignature( + std::string baseName, + std::vector parameters, + std::optional rowFieldName = std::nullopt) + : baseName_{std::move(baseName)}, + parameters_{std::move(parameters)}, + rowFieldName_(rowFieldName) {} + + const std::string& baseName() const { + return baseName_; + } + + const std::vector& parameters() const { + return parameters_; + } + + const std::optional& rowFieldName() const { + return rowFieldName_; + } + + std::string toString() const; + + bool operator==(const TypeSignature& rhs) const { + return baseName_ == rhs.baseName_ && parameters_ == rhs.parameters_ && + rowFieldName_ == rhs.rowFieldName_; + } + + private: + const std::string baseName_; + const std::vector parameters_; + + // If this object is a field of another parent row type, it can optionally + // have a name, e.g, `row(id bigint)` + std::optional rowFieldName_; +}; + +using TypeSignaturePtr = std::shared_ptr; + +} // namespace facebook::velox::exec diff --git a/velox/expression/signature_parser/CMakeLists.txt b/velox/expression/signature_parser/CMakeLists.txt new file mode 100644 index 00000000000..a59d525cb84 --- /dev/null +++ b/velox/expression/signature_parser/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# 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. + +if(${VELOX_BUILD_TESTING}) + add_subdirectory(tests) +endif() + +bison_target( + SignatureParser SignatureParser.yy + ${CMAKE_CURRENT_BINARY_DIR}/SignatureParser.yy.cc + DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/SignatureParser.yy.h + COMPILE_FLAGS "-Werror -Wno-deprecated") + +flex_target( + SignatureParserScanner SignatureParser.ll + ${CMAKE_CURRENT_BINARY_DIR}/Scanner.cpp COMPILE_FLAGS "-Cf --prefix=veloxsp") + +add_flex_bison_dependency(SignatureParserScanner SignatureParser) + +include_directories(${PROJECT_BINARY_DIR}) +include_directories(${FLEX_INCLUDE_DIRS}) +add_library( + velox_signature_parser ${BISON_SignatureParser_OUTPUTS} + ${FLEX_SignatureParserScanner_OUTPUTS} ParseUtil.cpp) +target_link_libraries(velox_signature_parser velox_common_base velox_type) diff --git a/velox/expression/signature_parser/ParseUtil.cpp b/velox/expression/signature_parser/ParseUtil.cpp new file mode 100644 index 00000000000..768e10458e4 --- /dev/null +++ b/velox/expression/signature_parser/ParseUtil.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/expression/signature_parser/ParseUtil.h" +#include +#include "velox/type/Type.h" + +namespace facebook::velox::exec { + +TypeSignaturePtr inferTypeWithSpaces( + const std::vector& words, + bool canHaveFieldName) { + VELOX_CHECK_GE(words.size(), 2); + const auto& fieldName = words[0]; + const auto allWords = folly::join(" ", words); + if (hasType(allWords) || !canHaveFieldName) { + return std::make_shared( + exec::TypeSignature(allWords, {})); + } + return std::make_shared(exec::TypeSignature( + allWords.data() + fieldName.size() + 1, {}, fieldName)); +} + +} // namespace facebook::velox::exec diff --git a/velox/expression/signature_parser/ParseUtil.h b/velox/expression/signature_parser/ParseUtil.h new file mode 100644 index 00000000000..7a66e6419ad --- /dev/null +++ b/velox/expression/signature_parser/ParseUtil.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/expression/TypeSignature.h" + +namespace facebook::velox::exec { + +/// Convert words with spaces to a Velox Type Signature. +/// First check if all the words are a Velox type. +/// If cannotHaveFieldName = false, check if the first word is a field name and +/// the remaining words are a Velox type. +TypeSignaturePtr inferTypeWithSpaces( + const std::vector& words, + bool canHaveFieldName = false); + +} // namespace facebook::velox::exec diff --git a/velox/expression/signature_parser/Scanner.h b/velox/expression/signature_parser/Scanner.h new file mode 100644 index 00000000000..6fcdbaafbaa --- /dev/null +++ b/velox/expression/signature_parser/Scanner.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 +#include +#include +#include + +#include "velox/common/base/Exceptions.h" +#include "velox/expression/TypeSignature.h" + +namespace facebook::velox::exec { + +class Scanner : public yyFlexLexer { + public: + Scanner( + std::istream& arg_yyin, + std::ostream& arg_yyout, + TypeSignaturePtr& outputType, + const std::string_view input) + : yyFlexLexer(&arg_yyin, &arg_yyout), + outputType_(outputType), + input_(input){}; + int lex(Parser::semantic_type* yylval); + + void setTypeSignature(TypeSignaturePtr type) { + outputType_ = std::move(type); + } + + // Store input to print it as part of the error message. + std::string_view input() { + return input_; + } + + private: + TypeSignaturePtr& outputType_; + const std::string_view input_; +}; + +} // namespace facebook::velox::exec diff --git a/velox/expression/signature_parser/SignatureParser.h b/velox/expression/signature_parser/SignatureParser.h new file mode 100644 index 00000000000..ab225e539ba --- /dev/null +++ b/velox/expression/signature_parser/SignatureParser.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 "velox/expression/TypeSignature.h" +#include "velox/type/Type.h" + +namespace facebook::velox::exec { + +/// Parses a string into TypeSignature. The format of the string is type name, +/// optionally followed by type parameters enclosed in parenthesis. +/// +/// Examples: +/// - bigint +/// - double +/// - array(T) +/// - map(K,V) +/// - row(named bigint,array(tinyint),T) +/// - function(S,T,R) +/// +/// Row fields are allowed to be named or anonymous, e.g. "row(foo bigint)" or +/// "row(bigint)" +TypeSignaturePtr parseTypeSignature(const std::string& signature); + +} // namespace facebook::velox::exec diff --git a/velox/expression/signature_parser/SignatureParser.ll b/velox/expression/signature_parser/SignatureParser.ll new file mode 100644 index 00000000000..624cde4035a --- /dev/null +++ b/velox/expression/signature_parser/SignatureParser.ll @@ -0,0 +1,72 @@ +%{ +#include +#include + +#include "velox/expression/signature_parser/SignatureParser.yy.h" // @manual +#include "velox/expression/signature_parser/Scanner.h" +#define YY_DECL int facebook::velox::exec::Scanner::lex(facebook::velox::exec::Parser::semantic_type *yylval) +%} + +%option c++ noyywrap noyylineno nodefault caseless + +A [A|a] +B [B|b] +C [C|c] +D [D|d] +E [E|e] +F [F|f] +G [G|g] +H [H|h] +I [I|i] +J [J|j] +K [K|k] +L [L|l] +M [M|m] +O [O|o] +P [P|p] +R [R|r] +S [S|s] +T [T|t] +U [U|u] +W [W|w] +X [X|x] +Y [Y|y] +Z [Z|z] + +WORD ([[:alnum:]_]*) +QUOTED_ID (['"'][[:alnum:][:space:]_]*['"']) +ROW (ROW|STRUCT) + +%% + +"(" return Parser::token::LPAREN; +")" return Parser::token::RPAREN; +"," return Parser::token::COMMA; +(ARRAY) return Parser::token::ARRAY; +(MAP) return Parser::token::MAP; +(FUNCTION) return Parser::token::FUNCTION; +(DECIMAL) yylval->build(YYText()); return Parser::token::DECIMAL; +{ROW} return Parser::token::ROW; +{WORD} yylval->build(YYText()); return Parser::token::WORD; +{QUOTED_ID} yylval->build(YYText()); return Parser::token::QUOTED_ID; +<> return Parser::token::YYEOF; +. /* no action on unmatched input */ + +%% + +int yyFlexLexer::yylex() { + throw std::runtime_error("Bad call to yyFlexLexer::yylex()"); +} + +#include "velox/expression/signature_parser/SignatureParser.h" + +facebook::velox::exec::TypeSignaturePtr facebook::velox::exec::parseTypeSignature(const std::string& signatureText) + { + std::istringstream is(signatureText); + facebook::velox::exec::TypeSignaturePtr signature; + facebook::velox::exec::Scanner scanner{is, std::cerr, signature, signatureText}; + facebook::velox::exec::Parser parser{ &scanner }; + parser.parse(); + VELOX_CHECK(signature, "Failed to parse signature [{}]", signatureText); + return signature; +} diff --git a/velox/expression/signature_parser/SignatureParser.yy b/velox/expression/signature_parser/SignatureParser.yy new file mode 100644 index 00000000000..999509f09a0 --- /dev/null +++ b/velox/expression/signature_parser/SignatureParser.yy @@ -0,0 +1,98 @@ +%{ +#include +#include "velox/common/base/Exceptions.h" +#include "velox/expression/TypeSignature.h" +#include "velox/expression/signature_parser/ParseUtil.h" +%} +%require "3.0.4" +%language "C++" + +%define parser_class_name {Parser} +%define api.namespace {facebook::velox::exec} +%define api.value.type variant +%parse-param {Scanner* scanner} +%define parse.error verbose + +%code requires +{ + namespace facebook::velox::exec { + class Scanner; + class TypeSignature; + } // namespace facebook::velox::exec +} // %code requires + +%code +{ + #include + #define yylex(x) scanner->lex(x) +} + +%token LPAREN RPAREN COMMA ARRAY MAP ROW FUNCTION +%token WORD VARIABLE QUOTED_ID DECIMAL +%token YYEOF 0 + +%nterm > special_type function_type decimal_type row_type array_type map_type +%nterm > type named_type +%nterm > type_list type_list_opt_names +%nterm > type_with_spaces + +%% + +type_spec : type { scanner->setTypeSignature($1); } + | type_with_spaces { scanner->setTypeSignature(inferTypeWithSpaces($1)); } + | error { yyerrok; } + ; + +type : special_type { $$ = $1; } + | WORD { $$ = std::make_shared(exec::TypeSignature($1, {})); } + ; + +special_type : array_type { $$ = $1; } + | map_type { $$ = $1; } + | row_type { $$ = $1; } + | function_type { $$ = $1; } + | decimal_type { $$ = $1; } + ; + +named_type : QUOTED_ID type { $1.erase(0, 1); $1.pop_back(); $$ = std::make_shared(exec::TypeSignature($2->baseName(), $2->parameters(), $1)); } // Remove the quotes. + | WORD special_type { $$ = std::make_shared(exec::TypeSignature($2->baseName(), $2->parameters(), $1)); } + | type_with_spaces { $$ = inferTypeWithSpaces($1, true); } + ; + +type_with_spaces : type_with_spaces WORD { $1.push_back($2); $$ = std::move($1); } + | WORD WORD { $$.push_back($1); $$.push_back($2); } + ; + +decimal_type : DECIMAL LPAREN WORD COMMA WORD RPAREN { $$ = std::make_shared(exec::TypeSignature($1, { exec::TypeSignature($3, {}), exec::TypeSignature($5, {}) })); } + ; + +type_list : type { $$.push_back(*($1)); } + | type_list COMMA type { $1.push_back(*($3)); $$ = std::move($1); } + ; + +type_list_opt_names : named_type { $$.push_back(*($1)); } + | type_list_opt_names COMMA named_type { $1.push_back(*($3)); $$ = std::move($1); } + | type { $$.push_back(*($1)); } + | type_list_opt_names COMMA type { $1.push_back(*($3)); $$ = std::move($1); } + ; + +row_type : ROW LPAREN type_list_opt_names RPAREN { $$ = std::make_shared("row", $3); } + ; + +array_type : ARRAY LPAREN type RPAREN { $$ = std::make_shared(exec::TypeSignature("array", { *($3) })); } + | ARRAY LPAREN type_with_spaces RPAREN { $$ = std::make_shared(exec::TypeSignature("array", { *inferTypeWithSpaces($3) })); } + ; + +map_type : MAP LPAREN type COMMA type RPAREN { $$ = std::make_shared(exec::TypeSignature("map", {*($3), *($5)})); } + | MAP LPAREN type COMMA type_with_spaces RPAREN { $$ = std::make_shared(exec::TypeSignature("map", {*($3), *inferTypeWithSpaces($5)})); } + | MAP LPAREN type_with_spaces COMMA type RPAREN { $$ = std::make_shared(exec::TypeSignature("map", {*inferTypeWithSpaces($3), *($5)})); } + | MAP LPAREN type_with_spaces COMMA type_with_spaces RPAREN { $$ = std::make_shared(exec::TypeSignature("map", {*inferTypeWithSpaces($3), *inferTypeWithSpaces($5)})); } + ; + +function_type : FUNCTION LPAREN type_list RPAREN { $$ = std::make_shared(exec::TypeSignature("function", {$3})); } + +%% + +void facebook::velox::exec::Parser::error(const std::string& msg) { + VELOX_FAIL("Failed to parse type signature [{}]: {}", scanner->input(), msg); +} diff --git a/velox/expression/signature_parser/tests/CMakeLists.txt b/velox/expression/signature_parser/tests/CMakeLists.txt new file mode 100644 index 00000000000..5bede4ac4cb --- /dev/null +++ b/velox/expression/signature_parser/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) Facebook, Inc. and its affiliates. +# +# 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. + +add_executable(velox_signature_parser_test ParseTypeSignatureTest.cpp) + +add_test(NAME velox_signature_parser_test COMMAND velox_signature_parser_test) + +target_link_libraries(velox_signature_parser_test velox_signature_parser + velox_type_signature gtest gtest_main gmock) diff --git a/velox/expression/signature_parser/tests/ParseTypeSignatureTest.cpp b/velox/expression/signature_parser/tests/ParseTypeSignatureTest.cpp new file mode 100644 index 00000000000..f83dc63b4c1 --- /dev/null +++ b/velox/expression/signature_parser/tests/ParseTypeSignatureTest.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * 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 + +#include "velox/expression/signature_parser/SignatureParser.h" + +using namespace facebook::velox::exec; + +namespace facebook::velox { +namespace { +std::string roundTrip(const std::string& typeSignature) { + return parseTypeSignature(typeSignature)->toString(); +} + +void testScalarType(const std::string& typeSignature) { + auto signature = *parseTypeSignature(typeSignature); + ASSERT_EQ(signature.baseName(), typeSignature); + ASSERT_EQ(signature.parameters().size(), 0); +} + +class CustomType : public VarcharType { + public: + CustomType() = default; + + bool equivalent(const Type& other) const override { + // Pointer comparison works since this type is a singleton. + return this == &other; + } +}; + +static const TypePtr& JSON() { + static const TypePtr instance{new CustomType()}; + return instance; +} + +static const TypePtr& TIMESTAMP_WITH_TIME_ZONE() { + static const TypePtr instance{new CustomType()}; + return instance; +} + +class TypeFactories : public CustomTypeFactories { + public: + TypeFactories(const TypePtr& type) : type_(type) {} + + TypePtr getType() const override { + return type_; + } + + exec::CastOperatorPtr getCastOperator() const override { + return nullptr; + } + + private: + TypePtr type_; +}; + +class ParseTypeSignatureTest : public ::testing::Test { + private: + void SetUp() override { + // Register custom types with and without spaces. + registerCustomType("json", std::make_unique(JSON())); + registerCustomType( + "timestamp with time zone", + std::make_unique(TIMESTAMP_WITH_TIME_ZONE())); + } +}; + +TEST_F(ParseTypeSignatureTest, scalar) { + testScalarType("tinyint"); + testScalarType("smallint"); + testScalarType("integer"); + testScalarType("bigint"); + testScalarType("real"); + testScalarType("double"); + testScalarType("timestamp"); +} + +TEST_F(ParseTypeSignatureTest, decimal) { + { + auto signature = *parseTypeSignature("DECIMAL(a_precision, a_scale)"); + ASSERT_EQ(signature.baseName(), "DECIMAL"); + ASSERT_EQ(signature.parameters().size(), 2); + + auto param1 = signature.parameters()[0]; + ASSERT_EQ(param1.baseName(), "a_precision"); + ASSERT_EQ(param1.parameters().size(), 0); + + auto param2 = signature.parameters()[1]; + ASSERT_EQ(param2.baseName(), "a_scale"); + ASSERT_EQ(param2.parameters().size(), 0); + } + { + auto signature = *parseTypeSignature("decimal(38, a_scale)"); + ASSERT_EQ(signature.baseName(), "decimal"); + auto param1 = signature.parameters()[0]; + ASSERT_EQ(param1.baseName(), "38"); + ASSERT_EQ(param1.parameters().size(), 0); + + auto param2 = signature.parameters()[1]; + ASSERT_EQ(param2.baseName(), "a_scale"); + ASSERT_EQ(param2.parameters().size(), 0); + } +} + +TEST_F(ParseTypeSignatureTest, array) { + { + auto signature = *parseTypeSignature("array(bigint)"); + ASSERT_EQ(signature.baseName(), "array"); + ASSERT_EQ(signature.parameters().size(), 1); + + auto param = signature.parameters()[0]; + ASSERT_EQ(param.baseName(), "bigint"); + ASSERT_EQ(param.parameters().size(), 0); + } + { + auto signature = *parseTypeSignature("array(timestamp with time zone)"); + ASSERT_EQ(signature.baseName(), "array"); + ASSERT_EQ(signature.parameters().size(), 1); + + auto param = signature.parameters()[0]; + ASSERT_EQ(param.baseName(), "timestamp with time zone"); + ASSERT_EQ(param.parameters().size(), 0); + } + { + auto signature = *parseTypeSignature( + "array(row(timestamp timestamp with time zone, decimal(a_scale, 14)))"); + ASSERT_EQ(signature.baseName(), "array"); + ASSERT_EQ(signature.parameters().size(), 1); + + auto param = signature.parameters()[0]; + ASSERT_EQ(param.baseName(), "row"); + ASSERT_EQ(param.parameters().size(), 2); + + auto field1 = param.parameters()[0]; + ASSERT_EQ(field1.baseName(), "timestamp with time zone"); + ASSERT_EQ(field1.rowFieldName(), "timestamp"); + ASSERT_EQ(field1.parameters().size(), 0); + + auto field2 = param.parameters()[1]; + ASSERT_EQ(field2.baseName(), "decimal"); + ASSERT_EQ(field2.parameters().size(), 2); + + auto decimalarg1 = field2.parameters()[0]; + ASSERT_EQ(decimalarg1.baseName(), "a_scale"); + ASSERT_EQ(decimalarg1.parameters().size(), 0); + + auto decimalarg2 = field2.parameters()[1]; + ASSERT_EQ(decimalarg2.baseName(), "14"); + ASSERT_EQ(decimalarg2.parameters().size(), 0); + } +} + +TEST_F(ParseTypeSignatureTest, map) { + auto signature = *parseTypeSignature("map(interval day to second, double)"); + ASSERT_EQ(signature.baseName(), "map"); + ASSERT_EQ(signature.parameters().size(), 2); + + auto key = signature.parameters()[0]; + ASSERT_EQ(key.baseName(), "interval day to second"); + ASSERT_EQ(key.parameters().size(), 0); + + auto value = signature.parameters()[1]; + ASSERT_EQ(value.baseName(), "double"); + ASSERT_EQ(value.parameters().size(), 0); +} + +TEST_F(ParseTypeSignatureTest, row) { + { + auto signature = *parseTypeSignature("row(v)"); + ASSERT_EQ(signature.baseName(), "row"); + ASSERT_EQ(signature.parameters().size(), 1); + } + + // test quoted row fields. + { + auto signature = *parseTypeSignature("row(\"12col0\" v, l)"); + ASSERT_EQ(signature.baseName(), "row"); + ASSERT_EQ(signature.parameters().size(), 2); + ASSERT_FALSE(signature.rowFieldName().has_value()); + auto field1 = signature.parameters()[0]; + ASSERT_EQ(field1.baseName(), "v"); + ASSERT_EQ(field1.rowFieldName(), "12col0"); + ASSERT_EQ(field1.parameters().size(), 0); + + auto field2 = signature.parameters()[1]; + ASSERT_EQ(field2.baseName(), "l"); + ASSERT_EQ(field2.parameters().size(), 0); + } + + { + auto signature = *parseTypeSignature( + "row(\"field 0\" array(json), \"field 1\" row(bla varchar))"); + ASSERT_EQ(signature.baseName(), "row"); + ASSERT_EQ(signature.parameters().size(), 2); + ASSERT_FALSE(signature.rowFieldName().has_value()); + auto field0 = signature.parameters()[0]; + ASSERT_EQ(field0.baseName(), "array"); + ASSERT_EQ(field0.rowFieldName(), "field 0"); + ASSERT_EQ(field0.parameters().size(), 1); + auto arrayElement = field0.parameters()[0]; + ASSERT_EQ(arrayElement.baseName(), "json"); + + auto field1 = signature.parameters()[1]; + ASSERT_EQ(field1.baseName(), "row"); + ASSERT_EQ(field1.rowFieldName(), "field 1"); + ASSERT_EQ(field1.parameters().size(), 1); + + auto rowfield = field1.parameters()[0]; + ASSERT_EQ(rowfield.baseName(), "varchar"); + ASSERT_EQ(rowfield.rowFieldName(), "bla"); + ASSERT_EQ(rowfield.parameters().size(), 0); + } +} + +TEST_F(ParseTypeSignatureTest, roundTrip) { + ASSERT_EQ(roundTrip("bigint"), "bigint"); + + ASSERT_EQ(roundTrip("array(T)"), "array(T)"); + ASSERT_EQ(roundTrip("array(array(T))"), "array(array(T))"); + ASSERT_EQ(roundTrip("array(row(K,V))"), "array(row(K,V))"); + + ASSERT_EQ(roundTrip("map(K,V)"), "map(K,V)"); + + ASSERT_EQ(roundTrip("function(S,R)"), "function(S,R)"); + + // Test a complex type as the second field in a row + ASSERT_EQ( + roundTrip("row(map(K,V),map(bigint,array(double)))"), + "row(map(K,V),map(bigint,array(double)))"); +} + +TEST_F(ParseTypeSignatureTest, invalidSignatures) { + EXPECT_THROW(*parseTypeSignature("array(varchar"), VeloxRuntimeError); + EXPECT_THROW(*parseTypeSignature("array(array(T)"), VeloxRuntimeError); +} + +} // namespace +} // namespace facebook::velox diff --git a/velox/expression/tests/SignatureBinderTest.cpp b/velox/expression/tests/SignatureBinderTest.cpp index 3d0ea5214c3..428d99e5b49 100644 --- a/velox/expression/tests/SignatureBinderTest.cpp +++ b/velox/expression/tests/SignatureBinderTest.cpp @@ -700,7 +700,7 @@ TEST(SignatureBinderTest, tryResolveTypeNullOutput) { auto assertNullResult = [&](const std::string& argument) { ASSERT_EQ( exec::SignatureBinder::tryResolveType( - exec::parseTypeSignature(argument), {}, {}), + *exec::parseTypeSignature(argument), {}, {}), nullptr); }; @@ -755,7 +755,7 @@ TEST(SignatureBinderTest, logicalType) { testSignatureBinder(signature, {INTERVAL_DAY_TIME()}, BIGINT()); } - // Logical type as an return type. + // Logical type as a return type. { auto signature = exec::FunctionSignatureBuilder() .returnType("interval day to second") diff --git a/velox/type/parser/ParserUtil.cpp b/velox/type/parser/ParserUtil.cpp index 136e49a74d9..b7fa36ec27b 100644 --- a/velox/type/parser/ParserUtil.cpp +++ b/velox/type/parser/ParserUtil.cpp @@ -41,18 +41,15 @@ std::pair> inferTypeWithSpaces( std::vector& words, bool cannotHaveFieldName = false) { VELOX_CHECK_GE(words.size(), 2); - std::string fieldName = words[0]; - std::string typeName = words[1]; - for (int i = 2; i < words.size(); ++i) { - typeName = fmt::format("{} {}", typeName, words[i]); - } - auto allWords = fmt::format("{} {}", fieldName, typeName); + const auto& fieldName = words[0]; + const auto allWords = folly::join(" ", words); // Fail if cannotHaveFieldName = true. auto type = typeFromString(allWords, cannotHaveFieldName); if (type) { return std::make_pair("", type); } - return std::make_pair(fieldName, typeFromString(typeName)); + return std::make_pair( + fieldName, typeFromString(allWords.substr(fieldName.size() + 1))); } } // namespace facebook::velox diff --git a/velox/type/parser/tests/TypeParserTest.cpp b/velox/type/parser/tests/TypeParserTest.cpp index da6b9915534..16052a01821 100644 --- a/velox/type/parser/tests/TypeParserTest.cpp +++ b/velox/type/parser/tests/TypeParserTest.cpp @@ -58,7 +58,7 @@ class TypeFactories : public CustomTypeFactories { TypePtr type_; }; -class TestTypeSignature : public ::testing::Test { +class TypeParserTest : public ::testing::Test { private: void SetUp() override { // Register custom types with and without spaces. @@ -69,25 +69,25 @@ class TestTypeSignature : public ::testing::Test { } }; -TEST_F(TestTypeSignature, booleanType) { +TEST_F(TypeParserTest, booleanType) { ASSERT_EQ(*parseType("boolean"), *BOOLEAN()); } -TEST_F(TestTypeSignature, integerType) { +TEST_F(TypeParserTest, integerType) { ASSERT_EQ(*parseType("int"), *INTEGER()); ASSERT_EQ(*parseType("integer"), *INTEGER()); } -TEST_F(TestTypeSignature, varcharType) { +TEST_F(TypeParserTest, varcharType) { ASSERT_EQ(*parseType("varchar"), *VARCHAR()); ASSERT_EQ(*parseType("varchar(4)"), *VARCHAR()); } -TEST_F(TestTypeSignature, varbinary) { +TEST_F(TypeParserTest, varbinary) { ASSERT_EQ(*parseType("varbinary"), *VARBINARY()); } -TEST_F(TestTypeSignature, arrayType) { +TEST_F(TypeParserTest, arrayType) { ASSERT_EQ(*parseType("array(bigint)"), *ARRAY(BIGINT())); ASSERT_EQ(*parseType("array(int)"), *ARRAY(INTEGER())); @@ -104,7 +104,7 @@ TEST_F(TestTypeSignature, arrayType) { ASSERT_EQ(*parseType("array(DECIMAL(10,5))"), *ARRAY(DECIMAL(10, 5))); } -TEST_F(TestTypeSignature, mapType) { +TEST_F(TypeParserTest, mapType) { ASSERT_EQ(*parseType("map(bigint,bigint)"), *MAP(BIGINT(), BIGINT())); ASSERT_EQ( @@ -135,7 +135,7 @@ TEST_F(TestTypeSignature, mapType) { *MAP(DECIMAL(10, 5), DECIMAL(20, 4))); } -TEST_F(TestTypeSignature, invalidType) { +TEST_F(TypeParserTest, invalidType) { VELOX_ASSERT_THROW( parseType("blah()"), "Failed to parse type [blah]. Type not registered."); @@ -151,7 +151,7 @@ TEST_F(TestTypeSignature, invalidType) { "Failed to parse type [rowxxx]. Type not registered."); } -TEST_F(TestTypeSignature, rowType) { +TEST_F(TypeParserTest, rowType) { ASSERT_EQ( *parseType("row(a bigint,b varchar,c real)"), *ROW({"a", "b", "c"}, {BIGINT(), VARCHAR(), REAL()})); @@ -220,7 +220,7 @@ TEST_F(TestTypeSignature, rowType) { ASSERT_EQ(*parseType("row(col iNt)"), *ROW({"col"}, {INTEGER()})); } -TEST_F(TestTypeSignature, typesWithSpaces) { +TEST_F(TypeParserTest, typesWithSpaces) { // Type is not registered. VELOX_ASSERT_THROW( parseType("row(time time with time zone)"), @@ -261,7 +261,7 @@ TEST_F(TestTypeSignature, typesWithSpaces) { "Failed to parse type [timestamp timestamp with time zone]. Type not registered."); } -TEST_F(TestTypeSignature, intervalYearToMonthType) { +TEST_F(TypeParserTest, intervalYearToMonthType) { ASSERT_EQ( *parseType("row(interval interval year to month)"), *ROW({"interval"}, {INTERVAL_YEAR_MONTH()})); @@ -270,7 +270,7 @@ TEST_F(TestTypeSignature, intervalYearToMonthType) { *parseType("row(interval year to month)"), *ROW({INTERVAL_YEAR_MONTH()})); } -TEST_F(TestTypeSignature, functionType) { +TEST_F(TypeParserTest, functionType) { ASSERT_EQ( *parseType("function(bigint,bigint,bigint)"), *FUNCTION({BIGINT(), BIGINT()}, BIGINT())); @@ -279,7 +279,7 @@ TEST_F(TestTypeSignature, functionType) { *FUNCTION({BIGINT(), ARRAY(VARCHAR())}, VARCHAR())); } -TEST_F(TestTypeSignature, decimalType) { +TEST_F(TypeParserTest, decimalType) { ASSERT_EQ(*parseType("decimal(10, 5)"), *DECIMAL(10, 5)); ASSERT_EQ(*parseType("decimal(20,10)"), *DECIMAL(20, 10));