diff --git a/dbms/programs/server/users.xml b/dbms/programs/server/users.xml
index 48dc6e85b641b7dd83714b12c81838a165054dbd..32ef1d7cdc4a4685599fbde44944675d3746daa4 100644
--- a/dbms/programs/server/users.xml
+++ b/dbms/programs/server/users.xml
@@ -74,6 +74,26 @@
default
+
+
+
+
+
+
+ a = 1
+
+
+
+
+ a + b < 1 or c - d > 5
+
+
+
+
+ c = 1
+
+
+
diff --git a/dbms/src/DataStreams/BlockIO.cpp b/dbms/src/DataStreams/BlockIO.cpp
deleted file mode 100644
index 5666383f345e2a7ff0cbce032eda6c7bd17ab7de..0000000000000000000000000000000000000000
--- a/dbms/src/DataStreams/BlockIO.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include
-#include
-
-namespace DB
-{
-
-BlockIO::~BlockIO() = default;
-BlockIO::BlockIO() = default;
-BlockIO::BlockIO(const BlockIO &) = default;
-
-}
diff --git a/dbms/src/DataStreams/BlockIO.h b/dbms/src/DataStreams/BlockIO.h
index 46db3d27a04cf754e8ba2388dc0f70116c361774..5ebfa45d179cc214d421b7c836a1101156923883 100644
--- a/dbms/src/DataStreams/BlockIO.h
+++ b/dbms/src/DataStreams/BlockIO.h
@@ -11,15 +11,19 @@ class ProcessListEntry;
struct BlockIO
{
+ BlockIO() = default;
+ BlockIO(const BlockIO &) = default;
+ ~BlockIO() = default;
+
+ BlockOutputStreamPtr out;
+ BlockInputStreamPtr in;
+
/** process_list_entry should be destroyed after in and after out,
* since in and out contain pointer to objects inside process_list_entry (query-level MemoryTracker for example),
* which could be used before destroying of in and out.
*/
std::shared_ptr process_list_entry;
- BlockInputStreamPtr in;
- BlockOutputStreamPtr out;
-
/// Callbacks for query logging could be set here.
std::function finish_callback;
std::function exception_callback;
@@ -37,17 +41,11 @@ struct BlockIO
exception_callback();
}
- /// We provide the correct order of destruction.
- void reset()
+ BlockIO & operator= (const BlockIO & rhs)
{
out.reset();
in.reset();
process_list_entry.reset();
- }
-
- BlockIO & operator= (const BlockIO & rhs)
- {
- reset();
process_list_entry = rhs.process_list_entry;
in = rhs.in;
@@ -58,10 +56,6 @@ struct BlockIO
return *this;
}
-
- ~BlockIO();
- BlockIO();
- BlockIO(const BlockIO &);
};
}
diff --git a/dbms/src/Interpreters/Context.cpp b/dbms/src/Interpreters/Context.cpp
index 72e9af80f459106fff03dc5e6266bf7fce76a136..081cea45f4d4404ed0bc7716941efa81840d713f 100644
--- a/dbms/src/Interpreters/Context.cpp
+++ b/dbms/src/Interpreters/Context.cpp
@@ -26,7 +26,7 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
@@ -129,7 +129,7 @@ struct ContextShared
mutable std::optional external_models;
String default_profile_name; /// Default profile name used for default values.
String system_profile_name; /// Profile used by system processes
- std::unique_ptr security_manager; /// Known users.
+ std::unique_ptr users_manager; /// Known users.
Quotas quotas; /// Known quotas for resource use.
mutable UncompressedCachePtr uncompressed_cache; /// The cache of decompressed blocks.
mutable MarkCachePtr mark_cache; /// Cache of marks in compressed files.
@@ -291,7 +291,7 @@ struct ContextShared
private:
void initialize()
{
- security_manager = runtime_components_factory->createSecurityManager();
+ users_manager = runtime_components_factory->createUsersManager();
}
};
@@ -571,7 +571,7 @@ void Context::setUsersConfig(const ConfigurationPtr & config)
{
auto lock = getLock();
shared->users_config = config;
- shared->security_manager->loadFromConfig(*shared->users_config);
+ shared->users_manager->loadFromConfig(*shared->users_config);
shared->quotas.loadFromConfig(*shared->users_config);
}
@@ -581,11 +581,39 @@ ConfigurationPtr Context::getUsersConfig()
return shared->users_config;
}
+bool Context::hasUserProperty(const String & database, const String & table, const String & name) const
+{
+ auto lock = getLock();
+
+ // No user - no properties.
+ if (client_info.current_user.empty())
+ return false;
+
+ const auto & props = shared->users_manager->getUser(client_info.current_user)->table_props;
+
+ auto db = props.find(database);
+ if (db == props.end())
+ return false;
+
+ auto table_props = db->second.find(table);
+ if (table_props == db->second.end())
+ return false;
+
+ return !!table_props->second.count(name);
+}
+
+const String & Context::getUserProperty(const String & database, const String & table, const String & name) const
+{
+ auto lock = getLock();
+ const auto & props = shared->users_manager->getUser(client_info.current_user)->table_props;
+ return props.at(database).at(table).at(name);
+}
+
void Context::calculateUserSettings()
{
auto lock = getLock();
- String profile = shared->security_manager->getUser(client_info.current_user)->profile;
+ String profile = shared->users_manager->getUser(client_info.current_user)->profile;
/// 1) Set default settings (hardcoded values)
/// NOTE: we ignore global_context settings (from which it is usually copied)
@@ -606,7 +634,7 @@ void Context::setUser(const String & name, const String & password, const Poco::
{
auto lock = getLock();
- auto user_props = shared->security_manager->authorizeAndGetUser(name, password, address.host());
+ auto user_props = shared->users_manager->authorizeAndGetUser(name, password, address.host());
client_info.current_user = name;
client_info.current_address = address;
@@ -644,7 +672,7 @@ bool Context::hasDatabaseAccessRights(const String & database_name) const
{
auto lock = getLock();
return client_info.current_user.empty() || (database_name == "system") ||
- shared->security_manager->hasAccessToDatabase(client_info.current_user, database_name);
+ shared->users_manager->hasAccessToDatabase(client_info.current_user, database_name);
}
void Context::checkDatabaseAccessRightsImpl(const std::string & database_name) const
@@ -655,7 +683,7 @@ void Context::checkDatabaseAccessRightsImpl(const std::string & database_name) c
/// All users have access to the database system.
return;
}
- if (!shared->security_manager->hasAccessToDatabase(client_info.current_user, database_name))
+ if (!shared->users_manager->hasAccessToDatabase(client_info.current_user, database_name))
throw Exception("Access denied to database " + database_name + " for user " + client_info.current_user , ErrorCodes::DATABASE_ACCESS_DENIED);
}
diff --git a/dbms/src/Interpreters/Context.h b/dbms/src/Interpreters/Context.h
index 8cd000da28051222bc06ebb37851148aba2678b8..4161c1c51440ec0a1a9b7fc7fef18f1eafe4ea89 100644
--- a/dbms/src/Interpreters/Context.h
+++ b/dbms/src/Interpreters/Context.h
@@ -188,6 +188,10 @@ public:
void setUsersConfig(const ConfigurationPtr & config);
ConfigurationPtr getUsersConfig();
+ // User property is a key-value pair from the configuration entry: users..databases...
+ bool hasUserProperty(const String & database, const String & table, const String & name) const;
+ const String & getUserProperty(const String & database, const String & table, const String & name) const;
+
/// Must be called before getClientInfo.
void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key);
/// Compute and set actual user settings, client_info.current_user should be set
diff --git a/dbms/src/Interpreters/IRuntimeComponentsFactory.h b/dbms/src/Interpreters/IRuntimeComponentsFactory.h
index 9764fc7195a7b2c627405a5c6a8fabb580ac87ea..1577b6b691d1909ec9007716274f31eebf6d123d 100644
--- a/dbms/src/Interpreters/IRuntimeComponentsFactory.h
+++ b/dbms/src/Interpreters/IRuntimeComponentsFactory.h
@@ -2,7 +2,7 @@
#include
#include
-#include
+#include
#include
@@ -16,7 +16,9 @@ namespace DB
class IRuntimeComponentsFactory
{
public:
- virtual std::unique_ptr createSecurityManager() = 0;
+ virtual ~IRuntimeComponentsFactory() = default;
+
+ virtual std::unique_ptr createUsersManager() = 0;
virtual std::unique_ptr createGeoDictionariesLoader() = 0;
@@ -24,8 +26,6 @@ public:
virtual std::unique_ptr createExternalDictionariesConfigRepository() = 0;
virtual std::unique_ptr createExternalModelsConfigRepository() = 0;
-
- virtual ~IRuntimeComponentsFactory() {}
};
}
diff --git a/dbms/src/Interpreters/ISecurityManager.h b/dbms/src/Interpreters/IUsersManager.h
similarity index 89%
rename from dbms/src/Interpreters/ISecurityManager.h
rename to dbms/src/Interpreters/IUsersManager.h
index 6e162034cf51539359ada08fb50a6767e9563f52..340b36a4ad46d3104d4c207f7be94efbc4ef3c7a 100644
--- a/dbms/src/Interpreters/ISecurityManager.h
+++ b/dbms/src/Interpreters/IUsersManager.h
@@ -5,16 +5,18 @@
namespace DB
{
-/** Duties of security manager:
+/** Duties of users manager:
* 1) Authenticate users
* 2) Provide user settings (profile, quota, ACLs)
* 3) Grant access to databases
*/
-class ISecurityManager
+class IUsersManager
{
public:
using UserPtr = std::shared_ptr;
+ virtual ~IUsersManager() = default;
+
virtual void loadFromConfig(const Poco::Util::AbstractConfiguration & config) = 0;
/// Find user and make authorize checks
@@ -28,8 +30,6 @@ public:
/// Check if the user has access to the database.
virtual bool hasAccessToDatabase(const String & user_name, const String & database_name) const = 0;
-
- virtual ~ISecurityManager() {}
};
}
diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.cpp b/dbms/src/Interpreters/InterpreterSelectQuery.cpp
index 182927b7104ae5d7f6ca79f49ea6db8e411c5c12..465a71d88016c0759aecb4e2f39eb90ccac6266c 100644
--- a/dbms/src/Interpreters/InterpreterSelectQuery.cpp
+++ b/dbms/src/Interpreters/InterpreterSelectQuery.cpp
@@ -23,12 +23,13 @@
#include
#include
-#include
-#include
#include
+#include
#include
#include
+#include
#include
+#include
#include
#include
@@ -75,6 +76,60 @@ namespace ErrorCodes
extern const int INVALID_LIMIT_EXPRESSION;
}
+namespace
+{
+
+/// Assumes `storage` is set and the table filter is not empty.
+String generateFilterActions(ExpressionActionsPtr & actions, const StoragePtr & storage, const Context & context, const Names & prerequisite_columns = {})
+{
+ const auto & db_name = storage->getDatabaseName();
+ const auto & table_name = storage->getTableName();
+ const auto & filter_str = context.getUserProperty(db_name, table_name, "filter");
+
+ /// TODO: implement some AST builders for this kind of stuff
+ ASTPtr query_ast = std::make_shared();
+ auto * select_ast = query_ast->as();
+
+ auto expr_list = std::make_shared();
+ select_ast->children.push_back(expr_list);
+ select_ast->select_expression_list = select_ast->children.back();
+
+ auto parseExpression = [] (const String & expr)
+ {
+ ParserExpression expr_parser;
+ return parseQuery(expr_parser, expr, 0);
+ };
+
+ // The first column is our filter expression.
+ expr_list->children.push_back(parseExpression(filter_str));
+
+ /// Keep columns that are required after the filter actions.
+ for (const auto & column_str : prerequisite_columns)
+ expr_list->children.push_back(parseExpression(column_str));
+
+ auto tables = std::make_shared();
+ auto tables_elem = std::make_shared();
+ auto table_expr = std::make_shared();
+ select_ast->children.push_back(tables);
+ select_ast->tables = select_ast->children.back();
+ tables->children.push_back(tables_elem);
+ tables_elem->table_expression = table_expr;
+ tables_elem->children.push_back(table_expr);
+ table_expr->database_and_table_name = createTableIdentifier(db_name, table_name);
+ table_expr->children.push_back(table_expr->database_and_table_name);
+
+ /// Using separate expression analyzer to prevent any possible alias injection
+ auto syntax_result = SyntaxAnalyzer(context).analyze(query_ast, storage->getColumns().getAllPhysical());
+ ExpressionAnalyzer analyzer(query_ast, syntax_result, context);
+ ExpressionActionsChain new_chain(context);
+ analyzer.appendSelect(new_chain, false);
+ actions = new_chain.getLastActions();
+
+ return expr_list->children.at(0)->getColumnName();
+}
+
+} // namespace
+
InterpreterSelectQuery::InterpreterSelectQuery(
const ASTPtr & query_ptr_,
const Context & context_,
@@ -302,7 +357,8 @@ BlockInputStreams InterpreterSelectQuery::executeWithMultipleStreams()
return pipeline.streams;
}
-InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpressions(QueryProcessingStage::Enum from_stage, bool dry_run)
+InterpreterSelectQuery::AnalysisResult
+InterpreterSelectQuery::analyzeExpressions(QueryProcessingStage::Enum from_stage, bool dry_run, const FilterInfoPtr & filter_info)
{
AnalysisResult res;
@@ -318,6 +374,7 @@ InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpression
* throw out unnecessary columns based on the entire query. In unnecessary parts of the query, we will not execute subqueries.
*/
+ bool has_filter = false;
bool has_prewhere = false;
bool has_where = false;
size_t where_step_num;
@@ -350,10 +407,15 @@ InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpression
res.columns_to_remove_after_prewhere = std::move(columns_to_remove);
}
+ else if (has_filter)
+ {
+ /// Can't have prewhere and filter set simultaneously
+ res.filter_info->do_remove_column = chain.steps.at(0).can_remove_required_output.at(0);
+ }
if (has_where)
res.remove_where_filter = chain.steps.at(where_step_num).can_remove_required_output.at(0);
- has_prewhere = has_where = false;
+ has_filter = has_prewhere = has_where = false;
chain.clear();
};
@@ -378,6 +440,26 @@ InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpression
columns_for_final.begin(), columns_for_final.end());
}
+ if (storage && context.hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter"))
+ {
+ has_filter = true;
+
+ /// XXX: aggregated copy-paste from ExpressionAnalyzer::appendSmth()
+ if (chain.steps.empty())
+ {
+ chain.steps.emplace_back(std::make_shared(source_columns, context));
+ }
+ ExpressionActionsChain::Step & step = chain.steps.back();
+
+ // FIXME: assert(filter_info);
+ res.filter_info = filter_info;
+ step.actions = filter_info->actions;
+ step.required_output.push_back(res.filter_info->column_name);
+ step.can_remove_required_output = {true};
+
+ chain.addStep();
+ }
+
if (query_analyzer->appendPrewhere(chain, !res.first_stage, additional_required_columns_after_prewhere))
{
has_prewhere = true;
@@ -445,6 +527,8 @@ InterpreterSelectQuery::AnalysisResult InterpreterSelectQuery::analyzeExpression
}
/// Before executing WHERE and HAVING, remove the extra columns from the block (mostly the aggregation keys).
+ if (res.filter_info)
+ res.filter_info->actions->prependProjectInput();
if (res.has_where)
res.before_where->prependProjectInput();
if (res.has_having)
@@ -491,7 +575,8 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns;
/// PREWHERE optimization
- if (storage)
+ /// Turn off, if the table filter is applied.
+ if (storage && !context.hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter"))
{
if (!dry_run)
from_stage = storage->getQueryProcessingStage(context);
@@ -517,11 +602,23 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
}
AnalysisResult expressions;
+ FilterInfoPtr filter_info;
+
+ /// We need proper `source_header` for `NullBlockInputStream` in dry-run.
+ if (storage && context.hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter"))
+ {
+ filter_info = std::make_shared();
+ filter_info->column_name = generateFilterActions(filter_info->actions, storage, context, required_columns);
+ source_header = storage->getSampleBlockForColumns(filter_info->actions->getRequiredColumns());
+ }
if (dry_run)
{
pipeline.streams.emplace_back(std::make_shared(source_header));
- expressions = analyzeExpressions(QueryProcessingStage::FetchColumns, true);
+ expressions = analyzeExpressions(QueryProcessingStage::FetchColumns, true, filter_info);
+
+ if (storage && expressions.filter_info && expressions.prewhere_info)
+ throw Exception("PREWHERE is not supported if the table is filtered by row-level security expression", ErrorCodes::ILLEGAL_PREWHERE);
if (expressions.prewhere_info)
pipeline.streams.back() = std::make_shared(
@@ -533,12 +630,15 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
if (prepared_input)
pipeline.streams.push_back(prepared_input);
- expressions = analyzeExpressions(from_stage, false);
+ expressions = analyzeExpressions(from_stage, false, filter_info);
if (from_stage == QueryProcessingStage::WithMergeableState &&
options.to_stage == QueryProcessingStage::WithMergeableState)
throw Exception("Distributed on Distributed is not supported", ErrorCodes::NOT_IMPLEMENTED);
+ if (storage && expressions.filter_info && expressions.prewhere_info)
+ throw Exception("PREWHERE is not supported if the table is filtered by row-level security expression", ErrorCodes::ILLEGAL_PREWHERE);
+
/** Read the data from Storage. from_stage - to what stage the request was completed in Storage. */
executeFetchColumns(from_stage, pipeline, expressions.prewhere_info, expressions.columns_to_remove_after_prewhere);
@@ -563,6 +663,18 @@ void InterpreterSelectQuery::executeImpl(Pipeline & pipeline, const BlockInputSt
if (expressions.first_stage)
{
+ if (expressions.filter_info)
+ {
+ pipeline.transform([&](auto & stream)
+ {
+ stream = std::make_shared(
+ stream,
+ expressions.filter_info->actions,
+ expressions.filter_info->column_name,
+ expressions.filter_info->do_remove_column);
+ });
+ }
+
if (expressions.hasJoin())
{
const auto & join = query.join()->table_join->as();
@@ -788,11 +900,26 @@ void InterpreterSelectQuery::executeFetchColumns(
/// Actions to calculate ALIAS if required.
ExpressionActionsPtr alias_actions;
- /// Are ALIAS columns required for query execution?
- auto alias_columns_required = false;
if (storage)
{
+ /// Append columns from the table filter to required
+ if (context.hasUserProperty(storage->getDatabaseName(), storage->getTableName(), "filter"))
+ {
+ auto initial_required_columns = required_columns;
+ ExpressionActionsPtr actions;
+ generateFilterActions(actions, storage, context, initial_required_columns);
+ auto required_columns_from_filter = actions->getRequiredColumns();
+
+ for (const auto & column : required_columns_from_filter)
+ {
+ if (required_columns.end() == std::find(required_columns.begin(), required_columns.end(), column))
+ required_columns.push_back(column);
+ }
+ }
+
+ /// Detect, if ALIAS columns are required for query execution
+ auto alias_columns_required = false;
const ColumnsDescription & storage_columns = storage->getColumns();
for (const auto & column_name : required_columns)
{
@@ -804,25 +931,33 @@ void InterpreterSelectQuery::executeFetchColumns(
}
}
+ /// There are multiple sources of required columns:
+ /// - raw required columns,
+ /// - columns deduced from ALIAS columns,
+ /// - raw required columns from PREWHERE,
+ /// - columns deduced from ALIAS columns from PREWHERE.
+ /// PREWHERE is a special case, since we need to resolve it and pass directly to `IStorage::read()`
+ /// before any other executions.
if (alias_columns_required)
{
- /// Columns required for prewhere actions.
- NameSet required_prewhere_columns;
- /// Columns required for prewhere actions which are aliases in storage.
- NameSet required_prewhere_aliases;
- Block prewhere_actions_result;
+ NameSet required_columns_from_prewhere; /// Set of all (including ALIAS) required columns for PREWHERE
+ NameSet required_aliases_from_prewhere; /// Set of ALIAS required columns for PREWHERE
+
if (prewhere_info)
{
+ /// Get some columns directly from PREWHERE expression actions
auto prewhere_required_columns = prewhere_info->prewhere_actions->getRequiredColumns();
- required_prewhere_columns.insert(prewhere_required_columns.begin(), prewhere_required_columns.end());
- prewhere_actions_result = prewhere_info->prewhere_actions->getSampleBlock();
+ required_columns_from_prewhere.insert(prewhere_required_columns.begin(), prewhere_required_columns.end());
}
- /// We will create an expression to return all the requested columns, with the calculation of the required ALIAS columns.
- ASTPtr required_columns_expr_list = std::make_shared();
- /// Separate expression for columns used in prewhere.
- ASTPtr required_prewhere_columns_expr_list = std::make_shared();
+ /// Expression, that contains all raw required columns
+ ASTPtr required_columns_all_expr = std::make_shared();
+ /// Expression, that contains raw required columns for PREWHERE
+ ASTPtr required_columns_from_prewhere_expr = std::make_shared();
+
+ /// Sort out already known required columns between expressions,
+ /// also populate `required_aliases_from_prewhere`.
for (const auto & column : required_columns)
{
ASTPtr column_expr;
@@ -833,36 +968,47 @@ void InterpreterSelectQuery::executeFetchColumns(
else
column_expr = std::make_shared(column);
- if (required_prewhere_columns.count(column))
+ if (required_columns_from_prewhere.count(column))
{
- required_prewhere_columns_expr_list->children.emplace_back(std::move(column_expr));
+ required_columns_from_prewhere_expr->children.emplace_back(std::move(column_expr));
if (is_alias)
- required_prewhere_aliases.insert(column);
+ required_aliases_from_prewhere.insert(column);
}
else
- required_columns_expr_list->children.emplace_back(std::move(column_expr));
+ required_columns_all_expr->children.emplace_back(std::move(column_expr));
}
- /// Columns which we will get after prewhere execution.
- NamesAndTypesList additional_source_columns;
- /// Add columns which will be added by prewhere (otherwise we will remove them in project action).
- NameSet columns_to_remove(columns_to_remove_after_prewhere.begin(), columns_to_remove_after_prewhere.end());
- for (const auto & column : prewhere_actions_result)
+ /// Columns, which we will get after prewhere and filter executions.
+ NamesAndTypesList required_columns_after_prewhere;
+ NameSet required_columns_after_prewhere_set;
+
+ /// Collect required columns from prewhere expression actions.
+ if (prewhere_info)
{
- if (prewhere_info->remove_prewhere_column && column.name == prewhere_info->prewhere_column_name)
- continue;
+ NameSet columns_to_remove(columns_to_remove_after_prewhere.begin(), columns_to_remove_after_prewhere.end());
+ Block prewhere_actions_result = prewhere_info->prewhere_actions->getSampleBlock();
+
+ /// Populate required columns with the columns, added by PREWHERE actions and not removed afterwards.
+ /// XXX: looks hacky that we already know which columns after PREWHERE we won't need for sure.
+ for (const auto & column : prewhere_actions_result)
+ {
+ if (prewhere_info->remove_prewhere_column && column.name == prewhere_info->prewhere_column_name)
+ continue;
- if (columns_to_remove.count(column.name))
- continue;
+ if (columns_to_remove.count(column.name))
+ continue;
- required_columns_expr_list->children.emplace_back(std::make_shared(column.name));
- additional_source_columns.emplace_back(column.name, column.type);
+ required_columns_all_expr->children.emplace_back(std::make_shared(column.name));
+ required_columns_after_prewhere.emplace_back(column.name, column.type);
+ }
+
+ required_columns_after_prewhere_set
+ = ext::map(required_columns_after_prewhere, [](const auto & it) { return it.name; });
}
- auto additional_source_columns_set = ext::map(additional_source_columns, [] (const auto & it) { return it.name; });
- auto syntax_result = SyntaxAnalyzer(context).analyze(required_columns_expr_list, additional_source_columns, {}, storage);
- alias_actions = ExpressionAnalyzer(required_columns_expr_list, syntax_result, context).getActions(true);
+ auto syntax_result = SyntaxAnalyzer(context).analyze(required_columns_all_expr, required_columns_after_prewhere, {}, storage);
+ alias_actions = ExpressionAnalyzer(required_columns_all_expr, syntax_result, context).getActions(true);
/// The set of required columns could be added as a result of adding an action to calculate ALIAS.
required_columns = alias_actions->getRequiredColumns();
@@ -874,17 +1020,10 @@ void InterpreterSelectQuery::executeFetchColumns(
prewhere_info->remove_prewhere_column = false;
/// Remove columns which will be added by prewhere.
- size_t next_req_column_pos = 0;
- for (size_t i = 0; i < required_columns.size(); ++i)
+ required_columns.erase(std::remove_if(required_columns.begin(), required_columns.end(), [&](const String & name)
{
- if (!additional_source_columns_set.count(required_columns[i]))
- {
- if (next_req_column_pos < i)
- std::swap(required_columns[i], required_columns[next_req_column_pos]);
- ++next_req_column_pos;
- }
- }
- required_columns.resize(next_req_column_pos);
+ return !!required_columns_after_prewhere_set.count(name);
+ }), required_columns.end());
if (prewhere_info)
{
@@ -898,21 +1037,22 @@ void InterpreterSelectQuery::executeFetchColumns(
}
prewhere_info->prewhere_actions = std::move(new_actions);
- auto analyzed_result = SyntaxAnalyzer(context).analyze(required_prewhere_columns_expr_list, storage->getColumns().getAllPhysical());
- prewhere_info->alias_actions =
- ExpressionAnalyzer(required_prewhere_columns_expr_list, analyzed_result, context)
- .getActions(true, false);
+ auto analyzed_result
+ = SyntaxAnalyzer(context).analyze(required_columns_from_prewhere_expr, storage->getColumns().getAllPhysical());
+ prewhere_info->alias_actions
+ = ExpressionAnalyzer(required_columns_from_prewhere_expr, analyzed_result, context).getActions(true, false);
- /// Add columns required by alias actions.
- auto required_aliased_columns = prewhere_info->alias_actions->getRequiredColumns();
- for (auto & column : required_aliased_columns)
+ /// Add (physical?) columns required by alias actions.
+ auto required_columns_from_alias = prewhere_info->alias_actions->getRequiredColumns();
+ Block prewhere_actions_result = prewhere_info->prewhere_actions->getSampleBlock();
+ for (auto & column : required_columns_from_alias)
if (!prewhere_actions_result.has(column))
if (required_columns.end() == std::find(required_columns.begin(), required_columns.end(), column))
required_columns.push_back(column);
- /// Add columns required by prewhere actions.
- for (const auto & column : required_prewhere_columns)
- if (required_prewhere_aliases.count(column) == 0)
+ /// Add physical columns required by prewhere actions.
+ for (const auto & column : required_columns_from_prewhere)
+ if (required_aliases_from_prewhere.count(column) == 0)
if (required_columns.end() == std::find(required_columns.begin(), required_columns.end(), column))
required_columns.push_back(column);
}
@@ -1013,12 +1153,17 @@ void InterpreterSelectQuery::executeFetchColumns(
if (pipeline.streams.empty())
{
- pipeline.streams.emplace_back(std::make_shared(storage->getSampleBlockForColumns(required_columns)));
+ pipeline.streams = {std::make_shared(storage->getSampleBlockForColumns(required_columns))};
if (query_info.prewhere_info)
- pipeline.streams.back() = std::make_shared(
- pipeline.streams.back(), prewhere_info->prewhere_actions,
- prewhere_info->prewhere_column_name, prewhere_info->remove_prewhere_column);
+ pipeline.transform([&](auto & stream)
+ {
+ stream = std::make_shared(
+ stream,
+ prewhere_info->prewhere_actions,
+ prewhere_info->prewhere_column_name,
+ prewhere_info->remove_prewhere_column);
+ });
}
pipeline.transform([&](auto & stream)
@@ -1434,6 +1579,7 @@ void InterpreterSelectQuery::executeLimitBy(Pipeline & pipeline)
}
+// TODO: move to anonymous namespace
bool hasWithTotalsInAnySubqueryInFromClause(const ASTSelectQuery & query)
{
if (query.group_by_with_totals)
diff --git a/dbms/src/Interpreters/InterpreterSelectQuery.h b/dbms/src/Interpreters/InterpreterSelectQuery.h
index 4d8c4a7a39bafa0e0a851ff35b8bcdea740c9a11..1a72a5fb199c3b80761998dad1dc5bab9b7c59cc 100644
--- a/dbms/src/Interpreters/InterpreterSelectQuery.h
+++ b/dbms/src/Interpreters/InterpreterSelectQuery.h
@@ -104,13 +104,13 @@ private:
BlockInputStreamPtr & firstStream() { return streams.at(0); }
template
- void transform(Transform && transform)
+ void transform(Transform && transformation)
{
for (auto & stream : streams)
- transform(stream);
+ transformation(stream);
if (stream_with_non_joined_data)
- transform(stream_with_non_joined_data);
+ transformation(stream_with_non_joined_data);
}
bool hasMoreThanOneStream() const
@@ -154,9 +154,10 @@ private:
SubqueriesForSets subqueries_for_sets;
PrewhereInfoPtr prewhere_info;
+ FilterInfoPtr filter_info;
};
- AnalysisResult analyzeExpressions(QueryProcessingStage::Enum from_stage, bool dry_run);
+ AnalysisResult analyzeExpressions(QueryProcessingStage::Enum from_stage, bool dry_run, const FilterInfoPtr & filter_info);
/** From which table to read. With JOIN, the "left" table is returned.
diff --git a/dbms/src/Interpreters/RuntimeComponentsFactory.h b/dbms/src/Interpreters/RuntimeComponentsFactory.h
index bbadbf68e8bbe329e10a4983731fa3afd66a2da1..4c319911b39f44f7383b1e492afb2333da42fe0b 100644
--- a/dbms/src/Interpreters/RuntimeComponentsFactory.h
+++ b/dbms/src/Interpreters/RuntimeComponentsFactory.h
@@ -1,9 +1,9 @@
#pragma once
#include
-#include
#include
-#include
+#include
+#include
namespace DB
{
@@ -14,9 +14,9 @@ namespace DB
class RuntimeComponentsFactory : public IRuntimeComponentsFactory
{
public:
- std::unique_ptr createSecurityManager() override
+ std::unique_ptr createUsersManager() override
{
- return std::make_unique();
+ return std::make_unique();
}
std::unique_ptr createGeoDictionariesLoader() override
diff --git a/dbms/src/Interpreters/Users.cpp b/dbms/src/Interpreters/Users.cpp
index 11ae604bcd61ae27b68a61a816a4dd350898fd7c..930c36ee84387aab6af4dc44cd988e67c1c06c31 100644
--- a/dbms/src/Interpreters/Users.cpp
+++ b/dbms/src/Interpreters/Users.cpp
@@ -315,6 +315,34 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A
databases.insert(database_name);
}
}
+
+ /// Read properties per "database.table"
+ /// Only tables are expected to have properties, so that all the keys inside "database" are table names.
+ const auto config_databases = config_elem + ".databases";
+ if (config.has(config_databases))
+ {
+ Poco::Util::AbstractConfiguration::Keys database_names;
+ config.keys(config_databases, database_names);
+
+ /// Read tables within databases
+ for (const auto & database : database_names)
+ {
+ const auto config_database = config_databases + "." + database;
+ Poco::Util::AbstractConfiguration::Keys table_names;
+ config.keys(config_database, table_names);
+
+ /// Read table properties
+ for (const auto & table : table_names)
+ {
+ const auto config_filter = config_database + "." + table + ".filter";
+ if (config.has(config_filter))
+ {
+ const auto filter_query = config.getString(config_filter);
+ table_props[database][table]["filter"] = filter_query;
+ }
+ }
+ }
+ }
}
diff --git a/dbms/src/Interpreters/Users.h b/dbms/src/Interpreters/Users.h
index 53d0f798573e258509f7ed28d4ebddddf580e2d8..95dde778b62d01ed8eec99faed5ff3bb02a51bdc 100644
--- a/dbms/src/Interpreters/Users.h
+++ b/dbms/src/Interpreters/Users.h
@@ -2,9 +2,10 @@
#include
-#include
-#include
#include
+#include
+#include
+#include
namespace Poco
@@ -65,6 +66,12 @@ struct User
using DatabaseSet = std::unordered_set;
DatabaseSet databases;
+ /// Table properties.
+ using PropertyMap = std::unordered_map;
+ using TableMap = std::unordered_map;
+ using DatabaseMap = std::unordered_map;
+ DatabaseMap table_props;
+
User(const String & name_, const String & config_elem, const Poco::Util::AbstractConfiguration & config);
};
diff --git a/dbms/src/Interpreters/SecurityManager.cpp b/dbms/src/Interpreters/UsersManager.cpp
similarity index 89%
rename from dbms/src/Interpreters/SecurityManager.cpp
rename to dbms/src/Interpreters/UsersManager.cpp
index 3f784f4366f586605102d068b1344577b4e70eef..d8d6997769d1628402828489b62d90a95c3f2efa 100644
--- a/dbms/src/Interpreters/SecurityManager.cpp
+++ b/dbms/src/Interpreters/UsersManager.cpp
@@ -1,4 +1,5 @@
-#include "SecurityManager.h"
+#include
+
#include
#include
#include
@@ -28,9 +29,9 @@ namespace ErrorCodes
extern const int SUPPORT_IS_DISABLED;
}
-using UserPtr = SecurityManager::UserPtr;
+using UserPtr = UsersManager::UserPtr;
-void SecurityManager::loadFromConfig(const Poco::Util::AbstractConfiguration & config)
+void UsersManager::loadFromConfig(const Poco::Util::AbstractConfiguration & config)
{
Container new_users;
@@ -46,7 +47,7 @@ void SecurityManager::loadFromConfig(const Poco::Util::AbstractConfiguration & c
users = std::move(new_users);
}
-UserPtr SecurityManager::authorizeAndGetUser(
+UserPtr UsersManager::authorizeAndGetUser(
const String & user_name,
const String & password,
const Poco::Net::IPAddress & address) const
@@ -100,7 +101,7 @@ UserPtr SecurityManager::authorizeAndGetUser(
return it->second;
}
-UserPtr SecurityManager::getUser(const String & user_name) const
+UserPtr UsersManager::getUser(const String & user_name) const
{
auto it = users.find(user_name);
@@ -110,7 +111,7 @@ UserPtr SecurityManager::getUser(const String & user_name) const
return it->second;
}
-bool SecurityManager::hasAccessToDatabase(const std::string & user_name, const std::string & database_name) const
+bool UsersManager::hasAccessToDatabase(const std::string & user_name, const std::string & database_name) const
{
auto it = users.find(user_name);
diff --git a/dbms/src/Interpreters/SecurityManager.h b/dbms/src/Interpreters/UsersManager.h
similarity index 78%
rename from dbms/src/Interpreters/SecurityManager.h
rename to dbms/src/Interpreters/UsersManager.h
index d2cc54624733f1c3e6852ffd6aa9d07d749858af..771372e7c9e20e8b4e90e25679d95c62504a9fb7 100644
--- a/dbms/src/Interpreters/SecurityManager.h
+++ b/dbms/src/Interpreters/UsersManager.h
@@ -1,21 +1,17 @@
#pragma once
-#include
+#include
#include