PREWHERE can be used now by user without row filtering.

This commit is contained in:
Vitaly Baranov 2020-04-09 00:10:00 +03:00
parent d992e408d8
commit 4d93577791
5 changed files with 146 additions and 97 deletions

View File

@ -1,7 +1,5 @@
#include <Access/EnabledRowPolicies.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTExpressionList.h>
#include <boost/smart_ptr/make_shared.hpp>
#include <Parsers/makeASTForLogicalFunction.h>
#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm/copy.hpp>
@ -35,19 +33,17 @@ ASTPtr EnabledRowPolicies::getCondition(const String & database, const String &
ASTPtr EnabledRowPolicies::getCondition(const String & database, const String & table_name, ConditionType type, const ASTPtr & extra_condition) const
{
ASTPtr main_condition = getCondition(database, table_name, type);
if (!main_condition)
return extra_condition;
if (!extra_condition)
return main_condition;
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "and";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children.push_back(main_condition);
exp_list->children.push_back(extra_condition);
return function;
ASTPtr condition = getCondition(database, table_name, type);
if (condition && extra_condition)
condition = makeASTForLogicalAnd({condition, extra_condition});
else if (!condition)
condition = extra_condition;
bool value;
if (tryGetLiteralBool(condition.get(), value) && value)
condition = nullptr; /// The condition is always true, no need to check it.
return condition;
}

View File

@ -1,97 +1,19 @@
#include <Access/RowPolicyCache.h>
#include <Access/EnabledRowPolicies.h>
#include <Access/AccessControlManager.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ExpressionListParsers.h>
#include <Parsers/parseQuery.h>
#include <Parsers/makeASTForLogicalFunction.h>
#include <Common/Exception.h>
#include <Common/quoteString.h>
#include <ext/range.h>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
namespace DB
{
namespace
{
bool tryGetLiteralBool(const IAST & ast, bool & value)
{
try
{
if (const ASTLiteral * literal = ast.as<ASTLiteral>())
{
value = !literal->value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), literal->value);
return true;
}
return false;
}
catch (...)
{
return false;
}
}
ASTPtr applyFunctionAND(ASTs arguments)
{
bool const_arguments = true;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(*argument, b))
return false;
const_arguments &= b;
return true;
});
if (!const_arguments)
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "and";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
ASTPtr applyFunctionOR(ASTs arguments)
{
bool const_arguments = false;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(*argument, b))
return false;
const_arguments |= b;
return true;
});
if (const_arguments)
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "or";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
using ConditionType = RowPolicy::ConditionType;
constexpr size_t MAX_CONDITION_TYPE = RowPolicy::MAX_CONDITION_TYPE;
@ -111,10 +33,16 @@ namespace
ASTPtr getResult() &&
{
/// Process permissive conditions.
restrictions.push_back(applyFunctionOR(std::move(permissions)));
restrictions.push_back(makeASTForLogicalOr(std::move(permissions)));
/// Process restrictive conditions.
return applyFunctionAND(std::move(restrictions));
auto condition = makeASTForLogicalAnd(std::move(restrictions));
bool value;
if (tryGetLiteralBool(condition.get(), value) && value)
condition = nullptr; /// The condition is always true, no need to check it.
return condition;
}
private:

View File

@ -0,0 +1,103 @@
#include <Parsers/makeASTForLogicalFunction.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTExpressionList.h>
#include <boost/range/algorithm_ext/erase.hpp>
namespace DB
{
ASTPtr makeASTForLogicalNot(ASTPtr argument)
{
bool b;
if (tryGetLiteralBool(argument.get(), b))
return std::make_shared<ASTLiteral>(Field{UInt8(!b)});
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "not";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children.push_back(argument);
return function;
}
ASTPtr makeASTForLogicalAnd(ASTs && arguments)
{
bool partial_result = true;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(argument.get(), b))
return false;
partial_result &= b;
return true;
});
if (!partial_result)
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "and";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
ASTPtr makeASTForLogicalOr(ASTs && arguments)
{
bool partial_result = false;
boost::range::remove_erase_if(arguments, [&](const ASTPtr & argument) -> bool
{
bool b;
if (!tryGetLiteralBool(argument.get(), b))
return false;
partial_result |= b;
return true;
});
if (partial_result)
return std::make_shared<ASTLiteral>(Field{UInt8(1)});
if (arguments.empty())
return std::make_shared<ASTLiteral>(Field{UInt8(0)});
if (arguments.size() == 1)
return arguments[0];
auto function = std::make_shared<ASTFunction>();
auto exp_list = std::make_shared<ASTExpressionList>();
function->name = "or";
function->arguments = exp_list;
function->children.push_back(exp_list);
exp_list->children = std::move(arguments);
return function;
}
bool tryGetLiteralBool(const IAST * ast, bool & value)
{
if (!ast)
return false;
try
{
if (const ASTLiteral * literal = ast->as<ASTLiteral>())
{
value = !literal->value.isNull() && applyVisitor(FieldVisitorConvertToNumber<bool>(), literal->value);
return true;
}
return false;
}
catch (...)
{
return false;
}
}
}

View File

@ -0,0 +1,19 @@
#pragma once
#include <Parsers/IAST_fwd.h>
namespace DB
{
/// Makes an AST calculating NOT argument.
ASTPtr makeASTForLogicalNot(ASTPtr argument);
/// Makes an AST calculating argument1 AND argument2 AND ... AND argumentN.
ASTPtr makeASTForLogicalAnd(ASTs && arguments);
/// Makes an AST calculating argument1 OR argument2 OR ... OR argumentN.
ASTPtr makeASTForLogicalOr(ASTs && arguments);
/// Tries to extract a literal bool from AST.
bool tryGetLiteralBool(const IAST * ast, bool & value);
}

View File

@ -113,6 +113,9 @@ def test_prewhere_not_supported():
assert expected_error in instance.query_and_get_error("SELECT * FROM mydb.filtered_table2 PREWHERE 1")
assert expected_error in instance.query_and_get_error("SELECT * FROM mydb.filtered_table3 PREWHERE 1")
# However PREWHERE should still work for user without filtering.
assert instance.query("SELECT * FROM mydb.filtered_table1 PREWHERE 1", user="another") == "0\t0\n0\t1\n1\t0\n1\t1\n"
def test_single_table_name():
copy_policy_xml('tag_with_table_name.xml')