Fix parsing of SETTINGS clause of the INSERT ... SELECT ... SETTINGS query

Before this patch the following query ignores the settings for INSERT:

   insert into test_parallel_insert select * from numbers_mt(65535*2) settings max_insert_threads=10

And the reason is that SETTINGS was parsed by the SELECT parser.
Fix this by push down the SETTINGS from the SELECT to INSERT.

Also note that since INSERT parser does not use ParserQueryWithOutput the
following works:

   insert into test_parallel_insert select * from numbers_mt(65535*2) format Null settings max_insert_threads=10
This commit is contained in:
Azat Khuzhin 2020-11-25 21:32:52 +03:00
parent 36dc23668d
commit 3f67e320dd
6 changed files with 119 additions and 0 deletions

View File

@ -0,0 +1,64 @@
#include <Common/SettingsChanges.h>
#include <Parsers/InsertQuerySettingsPushDownVisitor.h>
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTSetQuery.h>
#include <Parsers/ASTSubquery.h>
#include <iterator>
#include <algorithm>
namespace DB
{
bool InsertQuerySettingsPushDownMatcher::needChildVisit(ASTPtr & node, const ASTPtr & child)
{
if (node->as<ASTSelectWithUnionQuery>())
return true;
if (node->as<ASTSubquery>())
return true;
if (child->as<ASTSelectQuery>())
return true;
return false;
}
void InsertQuerySettingsPushDownMatcher::visit(ASTPtr & ast, Data & data)
{
if (auto * select_query = ast->as<ASTSelectQuery>())
visit(*select_query, ast, data);
}
void InsertQuerySettingsPushDownMatcher::visit(ASTSelectQuery & select_query, ASTPtr &, Data & data)
{
ASTPtr select_settings_ast = select_query.settings();
if (!select_settings_ast)
return;
auto & insert_settings_ast = data.insert_settings_ast;
if (!insert_settings_ast)
{
insert_settings_ast = select_settings_ast->clone();
return;
}
SettingsChanges & select_settings = select_settings_ast->as<ASTSetQuery &>().changes;
SettingsChanges & insert_settings = insert_settings_ast->as<ASTSetQuery &>().changes;
for (auto & setting : select_settings)
{
auto it = std::find_if(insert_settings.begin(), insert_settings.end(), [&](auto & select_setting)
{
return select_setting.name == setting.name;
});
if (it == insert_settings.end())
insert_settings.push_back(setting);
else
{
/// Do not ovewrite setting that was passed for INSERT
/// by settings that was passed for SELECT
}
}
}
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <Parsers/IAST.h>
#include <Interpreters/InDepthNodeVisitor.h>
namespace DB
{
class ASTSelectQuery;
struct SettingChange;
class SettingsChanges;
/// Pushdown SETTINGS clause to the INSERT from the SELECT query:
/// (since SETTINGS after SELECT will be parsed by the SELECT parser.)
///
/// NOTE: INSERT ... SELECT ... FORMAT Null SETTINGS max_insert_threads=10 works even w/o push down,
/// since ParserInsertQuery does not ParserInsertQuery.
class InsertQuerySettingsPushDownMatcher
{
public:
using Visitor = InDepthNodeVisitor<InsertQuerySettingsPushDownMatcher, true>;
struct Data
{
ASTPtr & insert_settings_ast;
};
static bool needChildVisit(ASTPtr & node, const ASTPtr & child);
static void visit(ASTPtr & ast, Data & data);
private:
static void visit(ASTSelectQuery &, ASTPtr &, Data &);
};
using InsertQuerySettingsPushDownVisitor = InsertQuerySettingsPushDownMatcher::Visitor;
}

View File

@ -9,6 +9,7 @@
#include <Parsers/ParserWatchQuery.h>
#include <Parsers/ParserInsertQuery.h>
#include <Parsers/ParserSetQuery.h>
#include <Parsers/InsertQuerySettingsPushDownVisitor.h>
#include <Common/typeid_cast.h>
@ -127,6 +128,14 @@ bool ParserInsertQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
return false;
}
if (select)
{
/// Copy SETTINGS from the INSERT ... SELECT ... SETTINGS
InsertQuerySettingsPushDownVisitor::Data visitor_data{settings_ast};
InsertQuerySettingsPushDownVisitor(visitor_data).visit(select);
}
if (format)
{
Pos last_token = pos;

View File

@ -67,6 +67,7 @@ SRCS(
ExpressionListParsers.cpp
IAST.cpp
IParserBase.cpp
InsertQuerySettingsPushDownVisitor.cpp
Lexer.cpp
MySQL/ASTAlterCommand.cpp
MySQL/ASTAlterQuery.cpp

View File

@ -0,0 +1,8 @@
drop table if exists data_01593;
create table data_01593 (key Int) engine=MergeTree() order by key partition by key;
insert into data_01593 select * from numbers_mt(10);
-- TOO_MANY_PARTS error
insert into data_01593 select * from numbers_mt(10) settings max_partitions_per_insert_block=1; -- { serverError 252 }
-- settings for INSERT is prefered
insert into data_01593 select * from numbers_mt(10) settings max_partitions_per_insert_block=1 settings max_partitions_per_insert_block=100;