Analyzer improve JOIN with constants

This commit is contained in:
Maksim Kita 2022-11-10 19:17:10 +01:00
parent c92e91b54b
commit 4ce261dfd7
4 changed files with 84 additions and 4 deletions

View File

@ -227,7 +227,11 @@ QueryPlan buildQueryPlanForJoinNode(QueryTreeNodePtr join_tree_node,
JoinClausesAndActions join_clauses_and_actions; JoinClausesAndActions join_clauses_and_actions;
JoinKind join_kind = join_node.getKind(); JoinKind join_kind = join_node.getKind();
auto join_constant = tryExtractConstantFromJoinNode(join_tree_node); std::optional<bool> join_constant;
if (join_node.getStrictness() == JoinStrictness::All)
join_constant = tryExtractConstantFromJoinNode(join_tree_node);
if (join_constant) if (join_constant)
{ {
/** If there is JOIN with always true constant, we transform it to cross. /** If there is JOIN with always true constant, we transform it to cross.

View File

@ -20,6 +20,7 @@
#include <Functions/FunctionsConversion.h> #include <Functions/FunctionsConversion.h>
#include <Functions/CastOverloadResolver.h> #include <Functions/CastOverloadResolver.h>
#include <Analyzer/FunctionNode.h>
#include <Analyzer/TableNode.h> #include <Analyzer/TableNode.h>
#include <Analyzer/TableFunctionNode.h> #include <Analyzer/TableFunctionNode.h>
#include <Analyzer/JoinNode.h> #include <Analyzer/JoinNode.h>
@ -76,6 +77,23 @@ void JoinClause::dump(WriteBuffer & buffer) const
if (!right_filter_condition_nodes.empty()) if (!right_filter_condition_nodes.empty())
buffer << " right_condition_nodes: " + dump_dag_nodes(right_filter_condition_nodes); buffer << " right_condition_nodes: " + dump_dag_nodes(right_filter_condition_nodes);
if (!asof_conditions.empty())
{
buffer << " asof_conditions: ";
size_t asof_conditions_size = asof_conditions.size();
for (size_t i = 0; i < asof_conditions_size; ++i)
{
const auto & asof_condition = asof_conditions[i];
buffer << "key_index: " << asof_condition.key_index;
buffer << "inequality: " << toString(asof_condition.asof_inequality);
if (i + 1 != asof_conditions_size)
buffer << ',';
}
}
} }
String JoinClause::dump() const String JoinClause::dump() const
@ -249,9 +267,7 @@ void buildJoinClause(ActionsDAGPtr join_expression_dag,
join_node); join_node);
if (!expression_side_optional) if (!expression_side_optional)
throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION, expression_side_optional = JoinTableSide::Right;
"JOIN {} with constants is not supported",
join_node.formatASTForErrorMessage());
auto expression_side = *expression_side_optional; auto expression_side = *expression_side_optional;
join_clause.addCondition(expression_side, join_expressions_actions_node); join_clause.addCondition(expression_side, join_expressions_actions_node);
@ -277,6 +293,22 @@ JoinClausesAndActions buildJoinClausesAndActions(const ColumnsWithTypeAndName &
for (const auto & node : join_expression_actions_nodes) for (const auto & node : join_expression_actions_nodes)
join_expression_dag_input_nodes.insert(&node); join_expression_dag_input_nodes.insert(&node);
auto * function_node = join_node.getJoinExpression()->as<FunctionNode>();
if (!function_node)
throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION,
"JOIN {} join expression expected function",
join_node.formatASTForErrorMessage());
/** It is possible to have constant value in JOIN ON section, that we need to ignore during DAG construction.
* If we do not ignore it, this function will be replaced by underlying constant.
* For example ASOF JOIN does not support JOIN with constants, and we should process it like ordinary JOIN.
*
* Example: SELECT * FROM (SELECT 1 AS id, 1 AS value) AS t1 ASOF LEFT JOIN (SELECT 1 AS id, 1 AS value) AS t2
* ON (t1.id = t2.id) AND 1 != 1 AND (t1.value >= t1.value);
*/
auto constant_value = function_node->getConstantValueOrNull();
function_node->performConstantFolding({});
PlannerActionsVisitor join_expression_visitor(planner_context); PlannerActionsVisitor join_expression_visitor(planner_context);
auto join_expression_dag_node_raw_pointers = join_expression_visitor.visit(join_expression_actions, join_node.getJoinExpression()); auto join_expression_dag_node_raw_pointers = join_expression_visitor.visit(join_expression_actions, join_node.getJoinExpression());
if (join_expression_dag_node_raw_pointers.size() != 1) if (join_expression_dag_node_raw_pointers.size() != 1)
@ -284,6 +316,8 @@ JoinClausesAndActions buildJoinClausesAndActions(const ColumnsWithTypeAndName &
"JOIN {} ON clause contains multiple expressions", "JOIN {} ON clause contains multiple expressions",
join_node.formatASTForErrorMessage()); join_node.formatASTForErrorMessage());
function_node->performConstantFolding(std::move(constant_value));
const auto * join_expressions_actions_root_node = join_expression_dag_node_raw_pointers[0]; const auto * join_expressions_actions_root_node = join_expression_dag_node_raw_pointers[0];
if (!join_expressions_actions_root_node->function) if (!join_expressions_actions_root_node->function)
throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION, throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION,

View File

@ -0,0 +1,15 @@
1 1
--
--
--
1 2
--
1 1 1 1
--
1 1 0 0
--
0
0
0
0
0

View File

@ -0,0 +1,27 @@
SET allow_experimental_analyzer = 1;
SELECT * FROM (SELECT 1 AS id) AS t1 INNER JOIN (SELECT 1 AS id) AS t2 ON t1.id = t2.id AND 1;
SELECT '--';
SELECT * FROM (SELECT 1 AS id) AS t1 INNER JOIN (SELECT 2 AS id) AS t2 ON t1.id = t2.id AND 1;
SELECT '--';
SELECT * FROM (SELECT 1 AS id) AS t1 INNER JOIN (SELECT 1 AS id) AS t2 ON t1.id = t2.id AND 0;
SELECT '--';
SELECT * FROM (SELECT 1 AS id) AS t1 INNER JOIN (SELECT 2 AS id) AS t2 ON t1.id = t2.id OR 1;
SELECT '--';
SELECT * FROM (SELECT 1 AS id, 1 AS value) AS t1 ASOF LEFT JOIN (SELECT 1 AS id, 1 AS value) AS t2 ON (t1.id = t2.id) AND 1 == 1 AND (t1.value >= t2.value);
SELECT '--';
SELECT * FROM (SELECT 1 AS id, 1 AS value) AS t1 ASOF LEFT JOIN (SELECT 1 AS id, 1 AS value) AS t2 ON (t1.id = t2.id) AND 1 != 1 AND (t1.value >= t2.value);
SELECT '--';
SELECT b.dt FROM (SELECT NULL > NULL AS pk, 1 AS dt FROM numbers(5)) AS a ASOF LEFT JOIN (SELECT NULL AS pk, 1 AS dt) AS b ON (a.pk = b.pk) AND 1 != 1 AND (a.dt >= b.dt);