From 55994afe0c647cfb2483a7bb46a4d70274fe3e62 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Thu, 28 Apr 2022 22:09:29 +0200 Subject: [PATCH 1/3] Fix evaluateConstantExpression for subqueries --- .../UserDefinedSQLFunctionFactory.cpp | 2 +- .../evaluateConstantExpression.cpp | 19 ++++++++++++++++++ .../test_executable_table_function/test.py | 20 +++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp index afe51de99a4..698faac5fab 100644 --- a/src/Interpreters/UserDefinedSQLFunctionFactory.cpp +++ b/src/Interpreters/UserDefinedSQLFunctionFactory.cpp @@ -48,7 +48,7 @@ void UserDefinedSQLFunctionFactory::registerFunction(ContextPtr context, const S if (if_not_exists) return; - throw Exception(ErrorCodes::CANNOT_DROP_FUNCTION, "User defined executable function '{}'", function_name); + throw Exception(ErrorCodes::FUNCTION_ALREADY_EXISTS, "User defined executable function '{}' already exists", function_name); } std::lock_guard lock(mutex); diff --git a/src/Interpreters/evaluateConstantExpression.cpp b/src/Interpreters/evaluateConstantExpression.cpp index 6b47db0f0d9..735676a9590 100644 --- a/src/Interpreters/evaluateConstantExpression.cpp +++ b/src/Interpreters/evaluateConstantExpression.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,19 @@ std::pair> evaluateConstantExpression(co return std::make_pair(literal->value, applyVisitor(FieldToDataType(), literal->value)); NamesAndTypesList source_columns = {{ "_dummy", std::make_shared() }}; + auto ast = node->clone(); + + if (ASTSubquery * subquery = ast->as()) + { + /** For subqueries getColumnName if there are no alias will return __subquery_ + 'hash'. + * If there is alias getColumnName for subquery will return alias. + * In result block name of subquery after QueryAliasesVisitor pass will be _subquery1. + * We specify alias for subquery, because we need to get column from result block. + */ + ast->setAlias("constant_expression"); + } + ReplaceQueryParameterVisitor param_visitor(context->getQueryParameters()); param_visitor.visit(ast); @@ -46,6 +59,12 @@ std::pair> evaluateConstantExpression(co String name = ast->getColumnName(); auto syntax_result = TreeRewriter(context).analyze(ast, source_columns); + + /// AST potentially could be transformed to literal during TreeRewriter analyze. + /// For example if we have SQL user defined function that return literal AS subquery. + if (ASTLiteral * literal = ast->as()) + return std::make_pair(literal->value, applyVisitor(FieldToDataType(), literal->value)); + ExpressionActionsPtr expr_for_constant_folding = ExpressionAnalyzer(ast, syntax_result, context).getConstActions(); /// There must be at least one column in the block so that it knows the number of rows. diff --git a/tests/integration/test_executable_table_function/test.py b/tests/integration/test_executable_table_function/test.py index 868e056993b..22c1530c9b2 100644 --- a/tests/integration/test_executable_table_function/test.py +++ b/tests/integration/test_executable_table_function/test.py @@ -92,6 +92,26 @@ def test_executable_function_input_python(started_cluster): == "Key 0\nKey 1\nKey 2\n" ) + query = ( + "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT 'value String'), {source})" + ) + assert node.query(query.format(source="(SELECT 1)")) == "Key 1\n" + assert ( + node.query(query.format(source="(SELECT id FROM test_data_table)")) + == "Key 0\nKey 1\nKey 2\n" + ) + + node.query("CREATE FUNCTION test_function AS () -> 'value String';") + query = ( + "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT test_function()), {source})" + ) + assert node.query(query.format(source="(SELECT 1)")) == "Key 1\n" + assert ( + node.query(query.format(source="(SELECT id FROM test_data_table)")) + == "Key 0\nKey 1\nKey 2\n" + ) + node.query("DROP FUNCTION test_function;") + def test_executable_function_input_sum_python(started_cluster): skip_test_msan(node) From 0e04c2cbc81919b4d1fdfa3bc7c664ad1f94de8d Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Fri, 29 Apr 2022 11:16:20 +0200 Subject: [PATCH 2/3] Fix style check --- tests/integration/test_executable_table_function/test.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/integration/test_executable_table_function/test.py b/tests/integration/test_executable_table_function/test.py index 22c1530c9b2..e3ac11eef87 100644 --- a/tests/integration/test_executable_table_function/test.py +++ b/tests/integration/test_executable_table_function/test.py @@ -92,9 +92,7 @@ def test_executable_function_input_python(started_cluster): == "Key 0\nKey 1\nKey 2\n" ) - query = ( - "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT 'value String'), {source})" - ) + query = "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT 'value String'), {source})" assert node.query(query.format(source="(SELECT 1)")) == "Key 1\n" assert ( node.query(query.format(source="(SELECT id FROM test_data_table)")) @@ -102,9 +100,7 @@ def test_executable_function_input_python(started_cluster): ) node.query("CREATE FUNCTION test_function AS () -> 'value String';") - query = ( - "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT test_function()), {source})" - ) + query = "SELECT * FROM executable('input.py', 'TabSeparated', (SELECT test_function()), {source})" assert node.query(query.format(source="(SELECT 1)")) == "Key 1\n" assert ( node.query(query.format(source="(SELECT id FROM test_data_table)")) From 2377106370058b8c125e3bed2426d53341adf6b4 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Mon, 2 May 2022 14:27:00 +0200 Subject: [PATCH 3/3] Fix build --- src/Interpreters/evaluateConstantExpression.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/evaluateConstantExpression.cpp b/src/Interpreters/evaluateConstantExpression.cpp index 735676a9590..f5ad0337629 100644 --- a/src/Interpreters/evaluateConstantExpression.cpp +++ b/src/Interpreters/evaluateConstantExpression.cpp @@ -41,7 +41,7 @@ std::pair> evaluateConstantExpression(co auto ast = node->clone(); - if (ASTSubquery * subquery = ast->as()) + if (ast->as() != nullptr) { /** For subqueries getColumnName if there are no alias will return __subquery_ + 'hash'. * If there is alias getColumnName for subquery will return alias.