mirror of https://github.com/ByConity/ByConity
Check access rights via new interface.
This commit is contained in:
parent
9e910222da
commit
2aa58193c8
|
@ -74,7 +74,7 @@ public:
|
|||
static AccessFlags columnLevel();
|
||||
|
||||
private:
|
||||
static constexpr size_t NUM_FLAGS = 64;
|
||||
static constexpr size_t NUM_FLAGS = 128;
|
||||
using Flags = std::bitset<NUM_FLAGS>;
|
||||
Flags flags;
|
||||
|
||||
|
@ -240,9 +240,177 @@ private:
|
|||
|
||||
auto select = std::make_unique<Node>("SELECT", next_flag++, COLUMN_LEVEL);
|
||||
auto insert = std::make_unique<Node>("INSERT", next_flag++, COLUMN_LEVEL);
|
||||
ext::push_back(all, std::move(select), std::move(insert));
|
||||
|
||||
auto update = std::make_unique<Node>("UPDATE", next_flag++, COLUMN_LEVEL);
|
||||
ext::push_back(update->aliases, "ALTER UPDATE");
|
||||
auto delet = std::make_unique<Node>("DELETE", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(all, std::move(select), std::move(insert), std::move(update), std::move(delet));
|
||||
ext::push_back(delet->aliases, "ALTER DELETE");
|
||||
|
||||
auto add_column = std::make_unique<Node>("ADD COLUMN", next_flag++, COLUMN_LEVEL);
|
||||
add_column->aliases.push_back("ALTER ADD COLUMN");
|
||||
auto modify_column = std::make_unique<Node>("MODIFY COLUMN", next_flag++, COLUMN_LEVEL);
|
||||
modify_column->aliases.push_back("ALTER MODIFY COLUMN");
|
||||
auto drop_column = std::make_unique<Node>("DROP COLUMN", next_flag++, COLUMN_LEVEL);
|
||||
drop_column->aliases.push_back("ALTER DROP COLUMN");
|
||||
auto comment_column = std::make_unique<Node>("COMMENT COLUMN", next_flag++, COLUMN_LEVEL);
|
||||
comment_column->aliases.push_back("ALTER COMMENT COLUMN");
|
||||
auto clear_column = std::make_unique<Node>("CLEAR COLUMN", next_flag++, COLUMN_LEVEL);
|
||||
clear_column->aliases.push_back("ALTER CLEAR COLUMN");
|
||||
auto alter_column = std::make_unique<Node>("ALTER COLUMN", std::move(add_column), std::move(modify_column), std::move(drop_column), std::move(comment_column), std::move(clear_column));
|
||||
|
||||
auto alter_order_by = std::make_unique<Node>("ALTER ORDER BY", next_flag++, TABLE_LEVEL);
|
||||
alter_order_by->aliases.push_back("MODIFY ORDER BY");
|
||||
alter_order_by->aliases.push_back("ALTER MODIFY ORDER BY");
|
||||
auto add_index = std::make_unique<Node>("ADD INDEX", next_flag++, TABLE_LEVEL);
|
||||
add_index->aliases.push_back("ALTER ADD INDEX");
|
||||
auto drop_index = std::make_unique<Node>("DROP INDEX", next_flag++, TABLE_LEVEL);
|
||||
drop_index->aliases.push_back("ALTER DROP INDEX");
|
||||
auto materialize_index = std::make_unique<Node>("MATERIALIZE INDEX", next_flag++, TABLE_LEVEL);
|
||||
materialize_index->aliases.push_back("ALTER MATERIALIZE INDEX");
|
||||
auto clear_index = std::make_unique<Node>("CLEAR INDEX", next_flag++, TABLE_LEVEL);
|
||||
clear_index->aliases.push_back("ALTER CLEAR INDEX");
|
||||
auto index = std::make_unique<Node>("INDEX", std::move(alter_order_by), std::move(add_index), std::move(drop_index), std::move(materialize_index), std::move(clear_index));
|
||||
index->aliases.push_back("ALTER INDEX");
|
||||
|
||||
auto add_constraint = std::make_unique<Node>("ADD CONSTRAINT", next_flag++, TABLE_LEVEL);
|
||||
add_constraint->aliases.push_back("ALTER ADD CONSTRAINT");
|
||||
auto drop_constraint = std::make_unique<Node>("DROP CONSTRAINT", next_flag++, TABLE_LEVEL);
|
||||
drop_constraint->aliases.push_back("ALTER DROP CONSTRAINT");
|
||||
auto alter_constraint = std::make_unique<Node>("CONSTRAINT", std::move(add_constraint), std::move(drop_constraint));
|
||||
alter_constraint->aliases.push_back("ALTER CONSTRAINT");
|
||||
|
||||
auto modify_ttl = std::make_unique<Node>("MODIFY TTL", next_flag++, TABLE_LEVEL);
|
||||
modify_ttl->aliases.push_back("ALTER MODIFY TTL");
|
||||
|
||||
auto modify_setting = std::make_unique<Node>("MODIFY SETTING", next_flag++, TABLE_LEVEL);
|
||||
modify_setting->aliases.push_back("ALTER MODIFY SETTING");
|
||||
|
||||
auto move_partition = std::make_unique<Node>("MOVE PARTITION", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(move_partition->aliases, "ALTER MOVE PARTITION", "MOVE PART", "ALTER MOVE PART");
|
||||
auto fetch_partition = std::make_unique<Node>("FETCH PARTITION", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(fetch_partition->aliases, "ALTER FETCH PARTITION");
|
||||
auto freeze_partition = std::make_unique<Node>("FREEZE PARTITION", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(freeze_partition->aliases, "ALTER FREEZE PARTITION");
|
||||
|
||||
auto alter_table = std::make_unique<Node>("ALTER TABLE", std::move(update), std::move(delet), std::move(alter_column), std::move(index), std::move(alter_constraint), std::move(modify_ttl), std::move(modify_setting), std::move(move_partition), std::move(fetch_partition), std::move(freeze_partition));
|
||||
|
||||
auto refresh_view = std::make_unique<Node>("REFRESH VIEW", next_flag++, VIEW_LEVEL);
|
||||
ext::push_back(refresh_view->aliases, "ALTER LIVE VIEW REFRESH");
|
||||
auto modify_view_query = std::make_unique<Node>("MODIFY VIEW QUERY", next_flag++, VIEW_LEVEL);
|
||||
auto alter_view = std::make_unique<Node>("ALTER VIEW", std::move(refresh_view), std::move(modify_view_query));
|
||||
|
||||
auto alter = std::make_unique<Node>("ALTER", std::move(alter_table), std::move(alter_view));
|
||||
ext::push_back(all, std::move(alter));
|
||||
|
||||
auto create_database = std::make_unique<Node>("CREATE DATABASE", next_flag++, DATABASE_LEVEL);
|
||||
ext::push_back(create_database->aliases, "ATTACH DATABASE");
|
||||
auto create_table = std::make_unique<Node>("CREATE TABLE", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(create_table->aliases, "ATTACH TABLE");
|
||||
auto create_view = std::make_unique<Node>("CREATE VIEW", next_flag++, VIEW_LEVEL);
|
||||
ext::push_back(create_view->aliases, "ATTACH VIEW");
|
||||
auto create_dictionary = std::make_unique<Node>("CREATE DICTIONARY", next_flag++, DICTIONARY_LEVEL);
|
||||
ext::push_back(create_dictionary->aliases, "ATTACH DICTIONARY");
|
||||
auto create = std::make_unique<Node>("CREATE", std::move(create_database), std::move(create_table), std::move(create_view), std::move(create_dictionary));
|
||||
ext::push_back(create->aliases, "ATTACH");
|
||||
ext::push_back(all, std::move(create));
|
||||
|
||||
auto create_temporary_table = std::make_unique<Node>("CREATE TEMPORARY TABLE", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(all, std::move(create_temporary_table));
|
||||
|
||||
auto drop_database = std::make_unique<Node>("DROP DATABASE", next_flag++, DATABASE_LEVEL);
|
||||
auto drop_table = std::make_unique<Node>("DROP TABLE", next_flag++, TABLE_LEVEL);
|
||||
auto drop_view = std::make_unique<Node>("DROP VIEW", next_flag++, VIEW_LEVEL);
|
||||
auto drop_dictionary = std::make_unique<Node>("DROP DICTIONARY", next_flag++, DICTIONARY_LEVEL);
|
||||
auto drop = std::make_unique<Node>("DROP", std::move(drop_database), std::move(drop_table), std::move(drop_view), std::move(drop_dictionary));
|
||||
ext::push_back(all, std::move(drop));
|
||||
|
||||
auto detach_database = std::make_unique<Node>("DETACH DATABASE", next_flag++, DATABASE_LEVEL);
|
||||
auto detach_table = std::make_unique<Node>("DETACH TABLE", next_flag++, TABLE_LEVEL);
|
||||
auto detach_view = std::make_unique<Node>("DETACH VIEW", next_flag++, VIEW_LEVEL);
|
||||
auto detach_dictionary = std::make_unique<Node>("DETACH DICTIONARY", next_flag++, DICTIONARY_LEVEL);
|
||||
auto detach = std::make_unique<Node>("DETACH", std::move(detach_database), std::move(detach_table), std::move(detach_view), std::move(detach_dictionary));
|
||||
ext::push_back(all, std::move(detach));
|
||||
|
||||
auto truncate_table = std::make_unique<Node>("TRUNCATE TABLE", next_flag++, TABLE_LEVEL);
|
||||
auto truncate_view = std::make_unique<Node>("TRUNCATE VIEW", next_flag++, VIEW_LEVEL);
|
||||
auto truncate = std::make_unique<Node>("TRUNCATE", std::move(truncate_table), std::move(truncate_view));
|
||||
ext::push_back(all, std::move(truncate));
|
||||
|
||||
auto optimize = std::make_unique<Node>("OPTIMIZE", next_flag++, TABLE_LEVEL);
|
||||
optimize->aliases.push_back("OPTIMIZE TABLE");
|
||||
ext::push_back(all, std::move(optimize));
|
||||
|
||||
auto kill_query = std::make_unique<Node>("KILL QUERY", next_flag++, GLOBAL_LEVEL);
|
||||
auto kill_mutation = std::make_unique<Node>("KILL MUTATION", next_flag++, TABLE_LEVEL);
|
||||
auto kill = std::make_unique<Node>("KILL", std::move(kill_query), std::move(kill_mutation));
|
||||
ext::push_back(all, std::move(kill));
|
||||
|
||||
auto create_user = std::make_unique<Node>("CREATE USER", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(create_user->aliases, "ALTER USER", "DROP USER", "CREATE ROLE", "DROP ROLE", "CREATE POLICY", "ALTER POLICY", "DROP POLICY", "CREATE QUOTA", "ALTER QUOTA", "DROP QUOTA");
|
||||
ext::push_back(all, std::move(create_user));
|
||||
|
||||
auto shutdown = std::make_unique<Node>("SHUTDOWN", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(shutdown->aliases, "SYSTEM SHUTDOWN", "SYSTEM KILL");
|
||||
auto drop_cache = std::make_unique<Node>("DROP CACHE", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(drop_cache->aliases, "SYSTEM DROP CACHE", "DROP DNS CACHE", "SYSTEM DROP DNS CACHE", "DROP MARK CACHE", "SYSTEM DROP MARK CACHE", "DROP UNCOMPRESSED CACHE", "SYSTEM DROP UNCOMPRESSED CACHE", "DROP COMPILED EXPRESSION CACHE", "SYSTEM DROP COMPILED EXPRESSION CACHE");
|
||||
auto reload_config = std::make_unique<Node>("RELOAD CONFIG", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(reload_config->aliases, "SYSTEM RELOAD CONFIG");
|
||||
auto reload_dictionary = std::make_unique<Node>("RELOAD DICTIONARY", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(reload_dictionary->aliases, "SYSTEM RELOAD DICTIONARY", "RELOAD DICTIONARIES", "SYSTEM RELOAD DICTIONARIES", "RELOAD EMBEDDED DICTIONARIES", "SYSTEM RELOAD EMBEDDED DICTIONARIES");
|
||||
auto stop_merges = std::make_unique<Node>("STOP MERGES", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_merges->aliases, "SYSTEM STOP MERGES", "START MERGES", "SYSTEM START MERGES");
|
||||
auto stop_ttl_merges = std::make_unique<Node>("STOP TTL MERGES", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_ttl_merges->aliases, "SYSTEM STOP TTL MERGES", "START TTL MERGES", "SYSTEM START TTL MERGES");
|
||||
auto stop_fetches = std::make_unique<Node>("STOP FETCHES", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_fetches->aliases, "SYSTEM STOP FETCHES", "START FETCHES", "SYSTEM START FETCHES");
|
||||
auto stop_moves = std::make_unique<Node>("STOP MOVES", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_moves->aliases, "SYSTEM STOP MOVES", "START MOVES", "SYSTEM START MOVES");
|
||||
auto stop_distributed_sends = std::make_unique<Node>("STOP DISTRIBUTED SENDS", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_distributed_sends->aliases, "SYSTEM STOP DISTRIBUTED SENDS", "START DISTRIBUTED SENDS", "SYSTEM START DISTRIBUTED SENDS");
|
||||
auto stop_replicated_sends = std::make_unique<Node>("STOP REPLICATED SENDS", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_replicated_sends->aliases, "SYSTEM STOP REPLICATED SENDS", "START REPLICATED SENDS", "SYSTEM START REPLICATED SENDS");
|
||||
auto stop_replication_queues = std::make_unique<Node>("STOP REPLICATION QUEUES", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(stop_replication_queues->aliases, "SYSTEM STOP REPLICATION QUEUES", "START REPLICATION QUEUES", "SYSTEM START REPLICATION QUEUES");
|
||||
auto sync_replica = std::make_unique<Node>("SYNC REPLICA", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(sync_replica->aliases, "SYSTEM SYNC REPLICA");
|
||||
auto restart_replica = std::make_unique<Node>("RESTART REPLICA", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(restart_replica->aliases, "SYSTEM RESTART REPLICA");
|
||||
auto flush_distributed = std::make_unique<Node>("FLUSH DISTRIBUTED", next_flag++, TABLE_LEVEL);
|
||||
ext::push_back(flush_distributed->aliases, "SYSTEM FLUSH DISTRIBUTED");
|
||||
auto flush_logs = std::make_unique<Node>("FLUSH LOGS", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(flush_logs->aliases, "SYSTEM FLUSH LOGS");
|
||||
auto system = std::make_unique<Node>("SYSTEM", std::move(shutdown), std::move(drop_cache), std::move(reload_config), std::move(reload_dictionary), std::move(stop_merges), std::move(stop_ttl_merges), std::move(stop_fetches), std::move(stop_moves), std::move(stop_distributed_sends), std::move(stop_replicated_sends), std::move(stop_replication_queues), std::move(sync_replica), std::move(restart_replica), std::move(flush_distributed), std::move(flush_logs));
|
||||
ext::push_back(all, std::move(system));
|
||||
|
||||
auto dict_get = std::make_unique<Node>("dictGet()", next_flag++, DICTIONARY_LEVEL);
|
||||
dict_get->aliases.push_back("dictHas()");
|
||||
dict_get->aliases.push_back("dictGetHierarchy()");
|
||||
dict_get->aliases.push_back("dictIsIn()");
|
||||
ext::push_back(all, std::move(dict_get));
|
||||
|
||||
auto address_to_line = std::make_unique<Node>("addressToLine()", next_flag++, GLOBAL_LEVEL);
|
||||
auto address_to_symbol = std::make_unique<Node>("addressToSymbol()", next_flag++, GLOBAL_LEVEL);
|
||||
auto demangle = std::make_unique<Node>("demangle()", next_flag++, GLOBAL_LEVEL);
|
||||
auto introspection = std::make_unique<Node>("INTROSPECTION", std::move(address_to_line), std::move(address_to_symbol), std::move(demangle));
|
||||
ext::push_back(introspection->aliases, "INTROSPECTION FUNCTIONS");
|
||||
ext::push_back(all, std::move(introspection));
|
||||
|
||||
auto file = std::make_unique<Node>("file()", next_flag++, GLOBAL_LEVEL);
|
||||
auto url = std::make_unique<Node>("url()", next_flag++, GLOBAL_LEVEL);
|
||||
auto input = std::make_unique<Node>("input()", next_flag++, GLOBAL_LEVEL);
|
||||
auto values = std::make_unique<Node>("values()", next_flag++, GLOBAL_LEVEL);
|
||||
auto numbers = std::make_unique<Node>("numbers()", next_flag++, GLOBAL_LEVEL);
|
||||
auto merge = std::make_unique<Node>("merge()", next_flag++, DATABASE_LEVEL);
|
||||
auto remote = std::make_unique<Node>("remote()", next_flag++, GLOBAL_LEVEL);
|
||||
ext::push_back(remote->aliases, "remoteSecure()", "cluster()");
|
||||
auto mysql = std::make_unique<Node>("mysql()", next_flag++, GLOBAL_LEVEL);
|
||||
auto odbc = std::make_unique<Node>("odbc()", next_flag++, GLOBAL_LEVEL);
|
||||
auto jdbc = std::make_unique<Node>("jdbc()", next_flag++, GLOBAL_LEVEL);
|
||||
auto hdfs = std::make_unique<Node>("hdfs()", next_flag++, GLOBAL_LEVEL);
|
||||
auto s3 = std::make_unique<Node>("s3()", next_flag++, GLOBAL_LEVEL);
|
||||
auto table_functions = std::make_unique<Node>("TABLE FUNCTIONS", std::move(file), std::move(url), std::move(input), std::move(values), std::move(numbers), std::move(merge), std::move(remote), std::move(mysql), std::move(odbc), std::move(jdbc), std::move(hdfs), std::move(s3));
|
||||
ext::push_back(all, std::move(table_functions));
|
||||
|
||||
flags_to_keyword_tree_ = std::make_unique<Node>("ALL", std::move(all));
|
||||
flags_to_keyword_tree_->aliases.push_back("ALL PRIVILEGES");
|
||||
|
|
|
@ -43,6 +43,8 @@ namespace
|
|||
const AccessFlags column_level_flags = AccessFlags::columnLevel();
|
||||
const AccessFlags show_flag = AccessType::SHOW;
|
||||
const AccessFlags exists_flag = AccessType::EXISTS;
|
||||
const AccessFlags create_table_flag = AccessType::CREATE_TABLE;
|
||||
const AccessFlags create_temporary_table_flag = AccessType::CREATE_TEMPORARY_TABLE;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -324,6 +326,9 @@ private:
|
|||
access |= helper.show_flag | helper.exists_flag;
|
||||
else if ((level >= DATABASE_LEVEL) && children)
|
||||
access |= helper.exists_flag;
|
||||
|
||||
if ((level == GLOBAL_LEVEL) && (access & helper.create_table_flag))
|
||||
access |= helper.create_temporary_table_flag;
|
||||
}
|
||||
|
||||
void calculateMinAndMaxAccess()
|
||||
|
|
|
@ -249,7 +249,28 @@ const AccessRights & AccessRightsContext::calculateResultAccess(UInt64 readonly_
|
|||
|
||||
result = granted_to_user;
|
||||
|
||||
/// TODO
|
||||
static const AccessFlags table_ddl = AccessType::CREATE_DATABASE | AccessType::CREATE_TABLE | AccessType::CREATE_VIEW
|
||||
| AccessType::ALTER_TABLE | AccessType::ALTER_VIEW | AccessType::DROP_DATABASE | AccessType::DROP_TABLE | AccessType::DROP_VIEW
|
||||
| AccessType::DETACH_DATABASE | AccessType::DETACH_TABLE | AccessType::DETACH_VIEW | AccessType::TRUNCATE;
|
||||
static const AccessFlags dictionary_ddl = AccessType::CREATE_DICTIONARY | AccessType::DROP_DICTIONARY | AccessType::DETACH_DICTIONARY;
|
||||
static const AccessFlags table_and_dictionary_ddl = table_ddl | dictionary_ddl;
|
||||
static const AccessFlags write_table_access = AccessType::INSERT | AccessType::OPTIMIZE;
|
||||
|
||||
if (readonly_)
|
||||
result.fullRevoke(write_table_access | AccessType::SYSTEM);
|
||||
|
||||
if (readonly_ || !allow_ddl_)
|
||||
result.fullRevoke(table_and_dictionary_ddl);
|
||||
|
||||
if (readonly_ == 1)
|
||||
{
|
||||
/// Table functions are forbidden in readonly mode.
|
||||
/// For example, for readonly = 2 - allowed.
|
||||
result.fullRevoke(AccessType::CREATE_TEMPORARY_TABLE | AccessType::TABLE_FUNCTIONS);
|
||||
}
|
||||
|
||||
if (!allow_introspection_)
|
||||
result.fullRevoke(AccessType::INTROSPECTION);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <Access/AccessRightsElement.h>
|
||||
#include <Dictionaries/IDictionary.h>
|
||||
#include <Common/quoteString.h>
|
||||
|
||||
|
||||
|
@ -49,7 +50,7 @@ String AccessRightsElement::toString() const
|
|||
|
||||
if (any_database)
|
||||
msg += "*.";
|
||||
else if (!database.empty())
|
||||
else if (!database.empty() && (database != IDictionary::NO_DATABASE_TAG))
|
||||
msg += backQuoteIfNeed(database) + ".";
|
||||
|
||||
if (any_table)
|
||||
|
|
|
@ -25,9 +25,130 @@ enum class AccessType
|
|||
INSERT,
|
||||
UPDATE, /// allows to execute ALTER UPDATE
|
||||
DELETE, /// allows to execute ALTER DELETE
|
||||
|
||||
ADD_COLUMN,
|
||||
DROP_COLUMN,
|
||||
MODIFY_COLUMN,
|
||||
COMMENT_COLUMN,
|
||||
CLEAR_COLUMN,
|
||||
ALTER_COLUMN, /// allow to execute ALTER {ADD|DROP|MODIFY...} COLUMN
|
||||
|
||||
ALTER_ORDER_BY,
|
||||
ADD_INDEX,
|
||||
DROP_INDEX,
|
||||
MATERIALIZE_INDEX,
|
||||
CLEAR_INDEX,
|
||||
INDEX, /// allows to execute ALTER ORDER BY or ALTER {ADD|DROP...} INDEX
|
||||
|
||||
ADD_CONSTRAINT,
|
||||
DROP_CONSTRAINT,
|
||||
ALTER_CONSTRAINT, /// allows to execute ALTER {ADD|DROP} CONSTRAINT
|
||||
|
||||
MODIFY_TTL, /// allows to execute ALTER MODIFY TTL
|
||||
MODIFY_SETTING, /// allows to execute ALTER MODIFY SETTING
|
||||
|
||||
MOVE_PARTITION,
|
||||
FETCH_PARTITION,
|
||||
FREEZE_PARTITION,
|
||||
|
||||
ALTER_TABLE, /// allows to execute ALTER TABLE ...
|
||||
|
||||
REFRESH_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH
|
||||
MODIFY_VIEW_QUERY, /// allows to execute ALTER TABLE MODIFY QUERY
|
||||
ALTER_VIEW, /// allows to execute ALTER LIVE VIEW REFRESH, ALTER TABLE MODIFY QUERY
|
||||
|
||||
ALTER, /// allows to execute ALTER {TABLE|LIVE VIEW} ...
|
||||
|
||||
CREATE_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
|
||||
CREATE_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
|
||||
CREATE_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
|
||||
CREATE_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
|
||||
CREATE_TEMPORARY_TABLE, /// allows to create and manipulate temporary tables and views.
|
||||
CREATE, /// allows to execute {CREATE|ATTACH} [TEMPORARY] {DATABASE|TABLE|VIEW|DICTIONARY}
|
||||
|
||||
ATTACH_DATABASE, /// allows to execute {CREATE|ATTACH} DATABASE
|
||||
ATTACH_TABLE, /// allows to execute {CREATE|ATTACH} TABLE
|
||||
ATTACH_VIEW, /// allows to execute {CREATE|ATTACH} VIEW
|
||||
ATTACH_DICTIONARY, /// allows to execute {CREATE|ATTACH} DICTIONARY
|
||||
ATTACH, /// allows to execute {CREATE|ATTACH} {DATABASE|TABLE|VIEW|DICTIONARY}
|
||||
|
||||
DROP_DATABASE,
|
||||
DROP_TABLE,
|
||||
DROP_VIEW,
|
||||
DROP_DICTIONARY,
|
||||
DROP, /// allows to execute DROP {DATABASE|TABLE|VIEW|DICTIONARY}
|
||||
|
||||
DETACH_DATABASE,
|
||||
DETACH_TABLE,
|
||||
DETACH_VIEW,
|
||||
DETACH_DICTIONARY,
|
||||
DETACH, /// allows to execute DETACH {DATABASE|TABLE|VIEW|DICTIONARY}
|
||||
|
||||
TRUNCATE_TABLE,
|
||||
TRUNCATE_VIEW,
|
||||
TRUNCATE, /// allows to execute TRUNCATE {TABLE|VIEW}
|
||||
|
||||
OPTIMIZE, /// allows to execute OPTIMIZE TABLE
|
||||
|
||||
KILL_QUERY, /// allows to kill a query started by another user (anyone can kill his own queries)
|
||||
KILL_MUTATION, /// allows to kill a mutation
|
||||
KILL, /// allows to execute KILL {MUTATION|QUERY}
|
||||
|
||||
CREATE_USER, /// allows to create, alter and drop users, roles, quotas, row policies.
|
||||
ALTER_USER,
|
||||
DROP_USER,
|
||||
CREATE_ROLE,
|
||||
DROP_ROLE,
|
||||
CREATE_POLICY,
|
||||
ALTER_POLICY,
|
||||
DROP_POLICY,
|
||||
CREATE_QUOTA,
|
||||
ALTER_QUOTA,
|
||||
DROP_QUOTA,
|
||||
|
||||
SHUTDOWN,
|
||||
DROP_CACHE,
|
||||
RELOAD_CONFIG,
|
||||
RELOAD_DICTIONARY,
|
||||
STOP_MERGES,
|
||||
STOP_TTL_MERGES,
|
||||
STOP_FETCHES,
|
||||
STOP_MOVES,
|
||||
STOP_DISTRIBUTED_SENDS,
|
||||
STOP_REPLICATED_SENDS,
|
||||
STOP_REPLICATION_QUEUES,
|
||||
SYNC_REPLICA,
|
||||
RESTART_REPLICA,
|
||||
FLUSH_DISTRIBUTED,
|
||||
FLUSH_LOGS,
|
||||
SYSTEM, /// allows to execute SYSTEM {SHUTDOWN|RELOAD CONFIG|...}
|
||||
|
||||
dictGet, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
|
||||
dictHas, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
|
||||
dictGetHierarchy, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
|
||||
dictIsIn, /// allows to execute functions dictGet, dictHas, dictGetHierarchy, dictIsIn
|
||||
|
||||
addressToLine, /// allows to execute function addressToLine
|
||||
addressToSymbol, /// allows to execute function addressToSymbol
|
||||
demangle, /// allows to execute function demangle
|
||||
INTROSPECTION, /// allows to execute functions addressToLine, addressToSymbol, demangle
|
||||
|
||||
file,
|
||||
url,
|
||||
input,
|
||||
values,
|
||||
numbers,
|
||||
merge,
|
||||
remote,
|
||||
mysql,
|
||||
odbc,
|
||||
jdbc,
|
||||
hdfs,
|
||||
s3,
|
||||
TABLE_FUNCTIONS, /// allows to execute any table function
|
||||
};
|
||||
|
||||
constexpr size_t MAX_ACCESS_TYPE = static_cast<size_t>(AccessType::DELETE) + 1;
|
||||
constexpr size_t MAX_ACCESS_TYPE = static_cast<size_t>(AccessType::TABLE_FUNCTIONS) + 1;
|
||||
|
||||
std::string_view toString(AccessType type);
|
||||
|
||||
|
@ -74,6 +195,127 @@ namespace impl
|
|||
ACCESS_TYPE_TO_KEYWORD_CASE(UPDATE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DELETE);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_COLUMN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_COLUMN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_COLUMN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(COMMENT_COLUMN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_COLUMN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_COLUMN);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_ORDER_BY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_INDEX);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_INDEX);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MATERIALIZE_INDEX);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CLEAR_INDEX);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(INDEX);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ADD_CONSTRAINT);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CONSTRAINT);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_CONSTRAINT);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_TTL);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_SETTING);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MOVE_PARTITION);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(FETCH_PARTITION);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(FREEZE_PARTITION);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_TABLE);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(REFRESH_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(MODIFY_VIEW_QUERY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_VIEW);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DATABASE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_DICTIONARY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_TEMPORARY_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DATABASE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH_DICTIONARY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ATTACH);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DATABASE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_DICTIONARY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DATABASE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH_DICTIONARY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DETACH);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_TABLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE_VIEW);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(TRUNCATE);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(OPTIMIZE);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_QUERY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(KILL_MUTATION);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(KILL);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_USER);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_USER);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_USER);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_ROLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_ROLE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_POLICY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_POLICY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_POLICY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(CREATE_QUOTA);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(ALTER_QUOTA);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_QUOTA);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(SHUTDOWN);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(DROP_CACHE);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_CONFIG);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(RELOAD_DICTIONARY);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MERGES);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_TTL_MERGES);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_FETCHES);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_MOVES);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_DISTRIBUTED_SENDS);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATED_SENDS);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(STOP_REPLICATION_QUEUES);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(SYNC_REPLICA);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(RESTART_REPLICA);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_DISTRIBUTED);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(FLUSH_LOGS);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(SYSTEM);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(dictGet);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(dictHas);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(dictGetHierarchy);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(dictIsIn);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(addressToLine);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(addressToSymbol);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(demangle);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(INTROSPECTION);
|
||||
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(file);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(url);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(input);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(values);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(numbers);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(merge);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(remote);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(mysql);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(odbc);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(jdbc);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(hdfs);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(s3);
|
||||
ACCESS_TYPE_TO_KEYWORD_CASE(TABLE_FUNCTIONS);
|
||||
|
||||
#undef ACCESS_TYPE_TO_KEYWORD_CASE
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,22 @@ struct IDictionaryBase : public IExternalLoadable
|
|||
virtual const std::string & getDatabase() const = 0;
|
||||
virtual const std::string & getName() const = 0;
|
||||
virtual const std::string & getFullName() const = 0;
|
||||
|
||||
const std::string & getLoadableName() const override { return getFullName(); }
|
||||
|
||||
/// Specifies that no database is used.
|
||||
/// Sometimes we cannot simply use an empty string for that because an empty string is
|
||||
/// usually replaced with the current database.
|
||||
static constexpr char NO_DATABASE_TAG[] = "<no_database>";
|
||||
|
||||
std::string_view getDatabaseOrNoDatabaseTag() const
|
||||
{
|
||||
const std::string & database = getDatabase();
|
||||
if (!database.empty())
|
||||
return database;
|
||||
return NO_DATABASE_TAG;
|
||||
}
|
||||
|
||||
virtual std::string getTypeName() const = 0;
|
||||
|
||||
virtual size_t getBytesAllocated() const = 0;
|
||||
|
|
|
@ -48,7 +48,6 @@ namespace ErrorCodes
|
|||
extern const int TYPE_MISMATCH;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int DICTIONARY_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/** Functions that use plug-ins (external) dictionaries_loader.
|
||||
|
@ -126,12 +125,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictHas, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatchSimple<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatchSimple<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -301,12 +295,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -487,12 +476,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getFullName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getFullName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -829,12 +813,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -1093,12 +1072,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictGet, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -1670,12 +1644,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictGetHierarchy, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr) &&
|
||||
!executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr) &&
|
||||
|
@ -1839,12 +1808,7 @@ private:
|
|||
|
||||
auto dict = dictionaries_loader.getDictionary(dict_name_col->getValue<String>());
|
||||
const auto dict_ptr = dict.get();
|
||||
|
||||
if (!context.hasDictionaryAccessRights(dict_ptr->getName()))
|
||||
{
|
||||
throw Exception{"For function " + getName() + ", cannot access dictionary "
|
||||
+ dict->getName() + " on database " + context.getCurrentDatabase(), ErrorCodes::DICTIONARY_ACCESS_DENIED};
|
||||
}
|
||||
context.checkAccess(AccessType::dictIsIn, dict_ptr->getDatabaseOrNoDatabaseTag(), dict_ptr->getName());
|
||||
|
||||
if (!executeDispatch<FlatDictionary>(block, arguments, result, dict_ptr)
|
||||
&& !executeDispatch<HashedDictionary>(block, arguments, result, dict_ptr)
|
||||
|
|
|
@ -28,7 +28,6 @@ namespace ErrorCodes
|
|||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int FUNCTION_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
class FunctionAddressToLine : public IFunction
|
||||
|
@ -37,9 +36,7 @@ public:
|
|||
static constexpr auto name = "addressToLine";
|
||||
static FunctionPtr create(const Context & context)
|
||||
{
|
||||
if (!context.getSettingsRef().allow_introspection_functions)
|
||||
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
|
||||
context.checkAccess(AccessType::addressToLine);
|
||||
return std::make_shared<FunctionAddressToLine>();
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ namespace ErrorCodes
|
|||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int FUNCTION_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
class FunctionAddressToSymbol : public IFunction
|
||||
|
@ -28,8 +27,7 @@ public:
|
|||
static constexpr auto name = "addressToSymbol";
|
||||
static FunctionPtr create(const Context & context)
|
||||
{
|
||||
if (!context.getSettingsRef().allow_introspection_functions)
|
||||
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
context.checkAccess(AccessType::addressToSymbol);
|
||||
return std::make_shared<FunctionAddressToSymbol>();
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace ErrorCodes
|
|||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||
extern const int FUNCTION_NOT_ALLOWED;
|
||||
}
|
||||
|
||||
class FunctionDemangle : public IFunction
|
||||
|
@ -26,9 +25,7 @@ public:
|
|||
static constexpr auto name = "demangle";
|
||||
static FunctionPtr create(const Context & context)
|
||||
{
|
||||
if (!context.getSettingsRef().allow_introspection_functions)
|
||||
throw Exception("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
|
||||
context.checkAccess(AccessType::demangle);
|
||||
return std::make_shared<FunctionDemangle>();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,29 +30,23 @@ inline void forEachTable(Context & context, F && f)
|
|||
|
||||
void ActionLocksManager::add(StorageActionBlockType action_type)
|
||||
{
|
||||
forEachTable(global_context, [&] (const StoragePtr & table)
|
||||
{
|
||||
ActionLock action_lock = table->getActionLock(action_type);
|
||||
|
||||
if (!action_lock.expired())
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
storage_locks[table.get()][action_type] = std::move(action_lock);
|
||||
}
|
||||
});
|
||||
forEachTable(global_context, [&](const StoragePtr & table) { add(table, action_type); });
|
||||
}
|
||||
|
||||
void ActionLocksManager::add(const String & database_name, const String & table_name, StorageActionBlockType action_type)
|
||||
{
|
||||
if (auto table = global_context.tryGetTable(database_name, table_name))
|
||||
{
|
||||
ActionLock action_lock = table->getActionLock(action_type);
|
||||
add(table, action_type);
|
||||
}
|
||||
|
||||
if (!action_lock.expired())
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
storage_locks[table.get()][action_type] = std::move(action_lock);
|
||||
}
|
||||
void ActionLocksManager::add(const StoragePtr & table, StorageActionBlockType action_type)
|
||||
{
|
||||
ActionLock action_lock = table->getActionLock(action_type);
|
||||
|
||||
if (!action_lock.expired())
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
storage_locks[table.get()][action_type] = std::move(action_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,12 +61,15 @@ void ActionLocksManager::remove(StorageActionBlockType action_type)
|
|||
void ActionLocksManager::remove(const String & database_name, const String & table_name, StorageActionBlockType action_type)
|
||||
{
|
||||
if (auto table = global_context.tryGetTable(database_name, table_name))
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
remove(table, action_type);
|
||||
}
|
||||
|
||||
if (storage_locks.count(table.get()))
|
||||
storage_locks[table.get()].erase(action_type);
|
||||
}
|
||||
void ActionLocksManager::remove(const StoragePtr & table, StorageActionBlockType action_type)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
if (storage_locks.count(table.get()))
|
||||
storage_locks[table.get()].erase(action_type);
|
||||
}
|
||||
|
||||
void ActionLocksManager::cleanExpired()
|
||||
|
|
|
@ -24,11 +24,13 @@ public:
|
|||
void add(StorageActionBlockType action_type);
|
||||
/// Add new lock for a table if it has not been already added
|
||||
void add(const String & database_name, const String & table_name, StorageActionBlockType action_type);
|
||||
void add(const StoragePtr & table, StorageActionBlockType action_type);
|
||||
|
||||
/// Remove locks for all tables
|
||||
void remove(StorageActionBlockType action_type);
|
||||
/// Removes a lock for a table if it exists
|
||||
void remove(const String & database_name, const String & table_name, StorageActionBlockType action_type);
|
||||
void remove(const StoragePtr & table, StorageActionBlockType action_type);
|
||||
|
||||
/// Removes all locks of non-existing tables
|
||||
void cleanExpired();
|
||||
|
|
|
@ -1242,7 +1242,7 @@ private:
|
|||
};
|
||||
|
||||
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, NameSet && query_databases)
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & context, AccessRightsElements && query_required_access)
|
||||
{
|
||||
/// Remove FORMAT <fmt> and INTO OUTFILE <file> if exists
|
||||
ASTPtr query_ptr = query_ptr_->clone();
|
||||
|
@ -1271,51 +1271,77 @@ BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr_, const Context & cont
|
|||
ClusterPtr cluster = context.getCluster(query->cluster);
|
||||
DDLWorker & ddl_worker = context.getDDLWorker();
|
||||
|
||||
/// Check database access rights, assume that all servers have the same users config
|
||||
NameSet databases_to_access;
|
||||
const String & current_database = context.getCurrentDatabase();
|
||||
|
||||
/// Enumerate hosts which will be used to send query.
|
||||
Cluster::AddressesWithFailover shards = cluster->getShardsAddresses();
|
||||
|
||||
std::vector<HostID> hosts;
|
||||
bool use_shard_default_db = false;
|
||||
bool use_local_default_db = false;
|
||||
for (const auto & shard : shards)
|
||||
{
|
||||
for (const auto & addr : shard)
|
||||
{
|
||||
hosts.emplace_back(addr);
|
||||
}
|
||||
|
||||
/// Expand empty database name to shards' default (o current) database name
|
||||
for (const String & database : query_databases)
|
||||
if (hosts.empty())
|
||||
throw Exception("No hosts defined to execute distributed DDL query", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
/// The current database in a distributed query need to be replaced with either
|
||||
/// the local current database or a shard's default database.
|
||||
bool need_replace_current_database
|
||||
= (std::find_if(
|
||||
query_required_access.begin(),
|
||||
query_required_access.end(),
|
||||
[](const AccessRightsElement & elem) { return elem.isEmptyDatabase(); })
|
||||
!= query_required_access.end());
|
||||
|
||||
if (need_replace_current_database)
|
||||
{
|
||||
bool use_local_default_database = false;
|
||||
Strings shard_default_databases;
|
||||
for (const auto & shard : shards)
|
||||
{
|
||||
for (const auto & addr : shard)
|
||||
{
|
||||
if (database.empty())
|
||||
{
|
||||
bool has_shard_default_db = !addr.default_database.empty();
|
||||
use_shard_default_db |= has_shard_default_db;
|
||||
use_local_default_db |= !has_shard_default_db;
|
||||
databases_to_access.emplace(has_shard_default_db ? addr.default_database : current_database);
|
||||
}
|
||||
if (!addr.default_database.empty())
|
||||
shard_default_databases.push_back(addr.default_database);
|
||||
else
|
||||
databases_to_access.emplace(database);
|
||||
use_local_default_database = true;
|
||||
}
|
||||
}
|
||||
std::sort(shard_default_databases.begin(), shard_default_databases.end());
|
||||
shard_default_databases.erase(std::unique(shard_default_databases.begin(), shard_default_databases.end()), shard_default_databases.end());
|
||||
assert(use_local_default_database || !shard_default_databases.empty());
|
||||
|
||||
if (use_local_default_database && !shard_default_databases.empty())
|
||||
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED);
|
||||
|
||||
if (use_local_default_database)
|
||||
{
|
||||
const String & current_database = context.getCurrentDatabase();
|
||||
AddDefaultDatabaseVisitor visitor(current_database);
|
||||
visitor.visitDDL(query_ptr);
|
||||
|
||||
query_required_access.replaceEmptyDatabase(current_database);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t old_num_elements = query_required_access.size();
|
||||
for (size_t i = 0; i != old_num_elements; ++i)
|
||||
{
|
||||
auto & element = query_required_access[i];
|
||||
if (element.isEmptyDatabase())
|
||||
{
|
||||
element.setDatabase(shard_default_databases[0]);
|
||||
for (size_t j = 1; j != shard_default_databases.size(); ++j)
|
||||
{
|
||||
query_required_access.push_back(element);
|
||||
query_required_access.back().setDatabase(shard_default_databases[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (use_shard_default_db && use_local_default_db)
|
||||
throw Exception("Mixed local default DB and shard default DB in DDL query", ErrorCodes::NOT_IMPLEMENTED);
|
||||
|
||||
if (databases_to_access.empty())
|
||||
throw Exception("No databases to access in distributed DDL query", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
for (const String & database : databases_to_access)
|
||||
context.checkDatabaseAccessRights(database);
|
||||
|
||||
if (use_local_default_db)
|
||||
{
|
||||
AddDefaultDatabaseVisitor visitor(current_database);
|
||||
visitor.visitDDL(query_ptr);
|
||||
}
|
||||
/// Check access rights, assume that all servers have the same users config
|
||||
context.checkAccess(query_required_access);
|
||||
|
||||
DDLLogEntry entry;
|
||||
entry.hosts = std::move(hosts);
|
||||
|
|
|
@ -17,12 +17,13 @@ namespace DB
|
|||
{
|
||||
|
||||
class ASTAlterQuery;
|
||||
class AccessRightsElements;
|
||||
struct DDLLogEntry;
|
||||
struct DDLTask;
|
||||
|
||||
|
||||
/// Pushes distributed DDL query to the queue
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, NameSet && query_databases);
|
||||
BlockIO executeDDLQueryOnCluster(const ASTPtr & query_ptr, const Context & context, AccessRightsElements && query_required_access);
|
||||
|
||||
|
||||
class DDLWorker
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <Interpreters/AddDefaultDatabaseVisitor.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Parsers/ASTAlterQuery.h>
|
||||
#include <Parsers/ASTAssignment.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Storages/AlterCommands.h>
|
||||
#include <Storages/MutationCommands.h>
|
||||
|
@ -36,7 +37,9 @@ BlockIO InterpreterAlterQuery::execute()
|
|||
const auto & alter = query_ptr->as<ASTAlterQuery &>();
|
||||
|
||||
if (!alter.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {alter.database});
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
|
||||
|
||||
context.checkAccess(getRequiredAccess());
|
||||
|
||||
const String & table_name = alter.table;
|
||||
String database_name = alter.database.empty() ? context.getCurrentDatabase() : alter.database;
|
||||
|
@ -113,4 +116,158 @@ BlockIO InterpreterAlterQuery::execute()
|
|||
return {};
|
||||
}
|
||||
|
||||
|
||||
AccessRightsElements InterpreterAlterQuery::getRequiredAccess() const
|
||||
{
|
||||
AccessRightsElements required_access;
|
||||
const auto & alter = query_ptr->as<ASTAlterQuery &>();
|
||||
for (ASTAlterCommand * command_ast : alter.command_list->commands)
|
||||
{
|
||||
auto column_name = [&]() -> String { return getIdentifierName(command_ast->column); };
|
||||
auto column_name_from_col_decl = [&]() -> std::string_view { return command_ast->col_decl->as<ASTColumnDeclaration &>().name; };
|
||||
auto column_names_from_update_assignments = [&]() -> std::vector<std::string_view>
|
||||
{
|
||||
std::vector<std::string_view> column_names;
|
||||
for (const ASTPtr & assignment_ast : command_ast->update_assignments->children)
|
||||
column_names.emplace_back(assignment_ast->as<const ASTAssignment &>().column_name);
|
||||
return column_names;
|
||||
};
|
||||
|
||||
switch (command_ast->type)
|
||||
{
|
||||
case ASTAlterCommand::UPDATE:
|
||||
{
|
||||
required_access.emplace_back(AccessType::UPDATE, alter.database, alter.table, column_names_from_update_assignments());
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::DELETE:
|
||||
{
|
||||
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::ADD_COLUMN:
|
||||
{
|
||||
required_access.emplace_back(AccessType::ADD_COLUMN, alter.database, alter.table, column_name_from_col_decl());
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::DROP_COLUMN:
|
||||
{
|
||||
if (command_ast->clear_column)
|
||||
required_access.emplace_back(AccessType::CLEAR_COLUMN, alter.database, alter.table, column_name());
|
||||
else
|
||||
required_access.emplace_back(AccessType::DROP_COLUMN, alter.database, alter.table, column_name());
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MODIFY_COLUMN:
|
||||
{
|
||||
required_access.emplace_back(AccessType::MODIFY_COLUMN, alter.database, alter.table, column_name_from_col_decl());
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::COMMENT_COLUMN:
|
||||
{
|
||||
required_access.emplace_back(AccessType::COMMENT_COLUMN, alter.database, alter.table, column_name());
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MODIFY_ORDER_BY:
|
||||
{
|
||||
required_access.emplace_back(AccessType::ALTER_ORDER_BY, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::ADD_INDEX:
|
||||
{
|
||||
required_access.emplace_back(AccessType::ADD_INDEX, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::DROP_INDEX:
|
||||
{
|
||||
if (command_ast->clear_index)
|
||||
required_access.emplace_back(AccessType::CLEAR_INDEX, alter.database, alter.table);
|
||||
else
|
||||
required_access.emplace_back(AccessType::DROP_INDEX, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MATERIALIZE_INDEX:
|
||||
{
|
||||
required_access.emplace_back(AccessType::MATERIALIZE_INDEX, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::ADD_CONSTRAINT:
|
||||
{
|
||||
required_access.emplace_back(AccessType::ADD_CONSTRAINT, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::DROP_CONSTRAINT:
|
||||
{
|
||||
required_access.emplace_back(AccessType::DROP_CONSTRAINT, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MODIFY_TTL:
|
||||
{
|
||||
required_access.emplace_back(AccessType::MODIFY_TTL, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MODIFY_SETTING:
|
||||
{
|
||||
required_access.emplace_back(AccessType::MODIFY_SETTING, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::ATTACH_PARTITION:
|
||||
{
|
||||
required_access.emplace_back(AccessType::INSERT, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::DROP_PARTITION: [[fallthrough]];
|
||||
case ASTAlterCommand::DROP_DETACHED_PARTITION:
|
||||
{
|
||||
required_access.emplace_back(AccessType::DELETE, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MOVE_PARTITION:
|
||||
{
|
||||
if ((command_ast->move_destination_type == PartDestinationType::DISK)
|
||||
|| (command_ast->move_destination_type == PartDestinationType::VOLUME))
|
||||
{
|
||||
required_access.emplace_back(AccessType::MOVE_PARTITION, alter.database, alter.table);
|
||||
}
|
||||
else if (command_ast->move_destination_type == PartDestinationType::TABLE)
|
||||
{
|
||||
required_access.emplace_back(AccessType::SELECT | AccessType::DELETE, alter.database, alter.table);
|
||||
required_access.emplace_back(AccessType::INSERT, command_ast->to_database, command_ast->to_table);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::REPLACE_PARTITION:
|
||||
{
|
||||
required_access.emplace_back(AccessType::SELECT, command_ast->from_database, command_ast->from_table);
|
||||
required_access.emplace_back(AccessType::DELETE | AccessType::INSERT, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::FETCH_PARTITION:
|
||||
{
|
||||
required_access.emplace_back(AccessType::FETCH_PARTITION, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::FREEZE_PARTITION: [[fallthrough]];
|
||||
case ASTAlterCommand::FREEZE_ALL:
|
||||
{
|
||||
required_access.emplace_back(AccessType::FREEZE_PARTITION, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::MODIFY_QUERY:
|
||||
{
|
||||
required_access.emplace_back(AccessType::MODIFY_VIEW_QUERY, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::LIVE_VIEW_REFRESH:
|
||||
{
|
||||
required_access.emplace_back(AccessType::REFRESH_VIEW, alter.database, alter.table);
|
||||
break;
|
||||
}
|
||||
case ASTAlterCommand::NO_TYPE: break;
|
||||
}
|
||||
}
|
||||
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class Context;
|
||||
class AccessRightsElements;
|
||||
|
||||
|
||||
/** Allows you add or remove a column in the table.
|
||||
* It also allows you to manipulate the partitions of the MergeTree family tables.
|
||||
|
@ -20,6 +21,8 @@ public:
|
|||
BlockIO execute() override;
|
||||
|
||||
private:
|
||||
AccessRightsElements getRequiredAccess() const;
|
||||
|
||||
ASTPtr query_ptr;
|
||||
|
||||
const Context & context;
|
||||
|
|
|
@ -40,6 +40,7 @@ BlockIO InterpreterCheckQuery::execute()
|
|||
const String & table_name = check.table;
|
||||
String database_name = check.database.empty() ? context.getCurrentDatabase() : check.database;
|
||||
|
||||
context.checkAccess(AccessType::SHOW, database_name, table_name);
|
||||
StoragePtr table = context.getTable(database_name, table_name);
|
||||
auto check_results = table->checkData(query_ptr, context);
|
||||
|
||||
|
|
|
@ -79,9 +79,6 @@ InterpreterCreateQuery::InterpreterCreateQuery(const ASTPtr & query_ptr_, Contex
|
|||
|
||||
BlockIO InterpreterCreateQuery::createDatabase(ASTCreateQuery & create)
|
||||
{
|
||||
if (!create.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
|
||||
|
||||
String database_name = create.database;
|
||||
|
||||
auto guard = context.getDDLGuard(database_name, "");
|
||||
|
@ -538,15 +535,6 @@ void InterpreterCreateQuery::setEngine(ASTCreateQuery & create) const
|
|||
|
||||
BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
||||
{
|
||||
if (!create.cluster.empty())
|
||||
{
|
||||
NameSet databases{create.database};
|
||||
if (!create.to_table.empty())
|
||||
databases.emplace(create.to_database);
|
||||
|
||||
return executeDDLQueryOnCluster(query_ptr, context, std::move(databases));
|
||||
}
|
||||
|
||||
/// Temporary tables are created out of databases.
|
||||
if (create.temporary && !create.database.empty() && !create.is_live_view)
|
||||
throw Exception("Temporary tables cannot be inside a database. You should not specify a database for a temporary table.",
|
||||
|
@ -690,9 +678,6 @@ BlockIO InterpreterCreateQuery::fillTableIfNeeded(const ASTCreateQuery & create)
|
|||
|
||||
BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
|
||||
{
|
||||
if (!create.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {create.database});
|
||||
|
||||
String dictionary_name = create.table;
|
||||
|
||||
if (create.database.empty())
|
||||
|
@ -730,7 +715,11 @@ BlockIO InterpreterCreateQuery::createDictionary(ASTCreateQuery & create)
|
|||
BlockIO InterpreterCreateQuery::execute()
|
||||
{
|
||||
auto & create = query_ptr->as<ASTCreateQuery &>();
|
||||
checkAccess(create);
|
||||
if (!create.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
|
||||
|
||||
context.checkAccess(getRequiredAccess());
|
||||
|
||||
ASTQueryWithOutput::resetOutputASTIfExist(create);
|
||||
|
||||
/// CREATE|ATTACH DATABASE
|
||||
|
@ -743,43 +732,47 @@ BlockIO InterpreterCreateQuery::execute()
|
|||
}
|
||||
|
||||
|
||||
void InterpreterCreateQuery::checkAccess(const ASTCreateQuery & create)
|
||||
AccessRightsElements InterpreterCreateQuery::getRequiredAccess() const
|
||||
{
|
||||
/// Internal queries (initiated by the server itself) always have access to everything.
|
||||
if (internal)
|
||||
return;
|
||||
return {};
|
||||
|
||||
const Settings & settings = context.getSettingsRef();
|
||||
auto readonly = settings.readonly;
|
||||
auto allow_ddl = settings.allow_ddl;
|
||||
AccessRightsElements required_access;
|
||||
const auto & create = query_ptr->as<const ASTCreateQuery &>();
|
||||
|
||||
if (!readonly && allow_ddl)
|
||||
return;
|
||||
|
||||
/// CREATE|ATTACH DATABASE
|
||||
if (!create.database.empty() && create.table.empty())
|
||||
if (create.table.empty())
|
||||
{
|
||||
if (readonly)
|
||||
throw Exception("Cannot create database in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
throw Exception("Cannot create database. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
required_access.emplace_back(AccessType::CREATE_DATABASE, create.database);
|
||||
}
|
||||
String object = "table";
|
||||
|
||||
if (create.is_dictionary)
|
||||
else if (create.is_dictionary)
|
||||
{
|
||||
if (readonly)
|
||||
throw Exception("Cannot create dictionary in readonly mode", ErrorCodes::READONLY);
|
||||
object = "dictionary";
|
||||
required_access.emplace_back(AccessType::CREATE_DICTIONARY, create.database, create.table);
|
||||
}
|
||||
else if (create.is_view || create.is_materialized_view || create.is_live_view)
|
||||
{
|
||||
if (create.temporary)
|
||||
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
|
||||
else
|
||||
{
|
||||
if (create.replace_view)
|
||||
required_access.emplace_back(AccessType::DROP_VIEW | AccessType::CREATE_VIEW, create.database, create.table);
|
||||
else
|
||||
required_access.emplace_back(AccessType::CREATE_VIEW, create.database, create.table);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (create.temporary)
|
||||
required_access.emplace_back(AccessType::CREATE_TEMPORARY_TABLE);
|
||||
else
|
||||
required_access.emplace_back(AccessType::CREATE_TABLE, create.database, create.table);
|
||||
}
|
||||
|
||||
if (create.temporary && readonly >= 2)
|
||||
return;
|
||||
if (!create.to_table.empty())
|
||||
required_access.emplace_back(AccessType::INSERT, create.to_database, create.to_table);
|
||||
|
||||
if (readonly)
|
||||
throw Exception("Cannot create table or dictionary in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
throw Exception("Cannot create " + object + ". DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ private:
|
|||
TableProperties setProperties(ASTCreateQuery & create) const;
|
||||
void validateTableStructure(const ASTCreateQuery & create, const TableProperties & properties) const;
|
||||
void setEngine(ASTCreateQuery & create) const;
|
||||
void checkAccess(const ASTCreateQuery & create);
|
||||
AccessRightsElements getRequiredAccess() const;
|
||||
|
||||
/// Create IStorage and add it to database. If table already exists and IF NOT EXISTS specified, do nothing and return false.
|
||||
bool doCreateTable(const ASTCreateQuery & create, const TableProperties & properties);
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace DB
|
|||
{
|
||||
BlockIO InterpreterCreateQuotaQuery::execute()
|
||||
{
|
||||
context.checkQuotaManagementIsAllowed();
|
||||
const auto & query = query_ptr->as<const ASTCreateQuotaQuery &>();
|
||||
auto & access_control = context.getAccessControlManager();
|
||||
context.checkAccess(query.alter ? AccessType::ALTER_QUOTA : AccessType::CREATE_QUOTA);
|
||||
|
||||
if (query.alter)
|
||||
{
|
||||
|
|
|
@ -11,9 +11,9 @@ namespace DB
|
|||
{
|
||||
BlockIO InterpreterCreateRowPolicyQuery::execute()
|
||||
{
|
||||
context.checkRowPolicyManagementIsAllowed();
|
||||
const auto & query = query_ptr->as<const ASTCreateRowPolicyQuery &>();
|
||||
auto & access_control = context.getAccessControlManager();
|
||||
context.checkAccess(query.alter ? AccessType::ALTER_POLICY : AccessType::CREATE_POLICY);
|
||||
|
||||
if (query.alter)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,9 @@ BlockInputStreamPtr InterpreterDescribeQuery::executeImpl()
|
|||
String table_name;
|
||||
std::tie(database_name, table_name) = IdentifierSemantic::extractDatabaseAndTable(identifier);
|
||||
|
||||
if (!database_name.empty() || !context.isExternalTableExist(table_name))
|
||||
context.checkAccess(AccessType::SHOW, database_name, table_name);
|
||||
|
||||
table = context.getTable(database_name, table_name);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
|
|||
{
|
||||
case Kind::QUOTA:
|
||||
{
|
||||
context.checkQuotaManagementIsAllowed();
|
||||
context.checkAccess(AccessType::DROP_QUOTA);
|
||||
if (query.if_exists)
|
||||
access_control.tryRemove(access_control.find<Quota>(query.names));
|
||||
else
|
||||
|
@ -28,7 +28,7 @@ BlockIO InterpreterDropAccessEntityQuery::execute()
|
|||
}
|
||||
case Kind::ROW_POLICY:
|
||||
{
|
||||
context.checkRowPolicyManagementIsAllowed();
|
||||
context.checkAccess(AccessType::DROP_POLICY);
|
||||
Strings full_names;
|
||||
boost::range::transform(
|
||||
query.row_policies_names, std::back_inserter(full_names),
|
||||
|
|
|
@ -35,11 +35,8 @@ InterpreterDropQuery::InterpreterDropQuery(const ASTPtr & query_ptr_, Context &
|
|||
BlockIO InterpreterDropQuery::execute()
|
||||
{
|
||||
auto & drop = query_ptr->as<ASTDropQuery &>();
|
||||
|
||||
checkAccess(drop);
|
||||
|
||||
if (!drop.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {drop.database});
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
|
||||
|
||||
if (!drop.table.empty())
|
||||
{
|
||||
|
@ -56,8 +53,8 @@ BlockIO InterpreterDropQuery::execute()
|
|||
|
||||
|
||||
BlockIO InterpreterDropQuery::executeToTable(
|
||||
String & database_name_,
|
||||
String & table_name,
|
||||
const String & database_name_,
|
||||
const String & table_name,
|
||||
ASTDropQuery::Kind kind,
|
||||
bool if_exists,
|
||||
bool is_temporary,
|
||||
|
@ -66,7 +63,6 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
if (is_temporary || database_name_.empty())
|
||||
{
|
||||
auto & session_context = context.hasSessionContext() ? context.getSessionContext() : context;
|
||||
|
||||
if (session_context.isExternalTableExist(table_name))
|
||||
return executeToTemporaryTable(table_name, kind);
|
||||
}
|
||||
|
@ -74,7 +70,7 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
if (is_temporary)
|
||||
{
|
||||
if (if_exists)
|
||||
return {};
|
||||
return {};
|
||||
throw Exception("Temporary table " + backQuoteIfNeed(table_name) + " doesn't exist.",
|
||||
ErrorCodes::UNKNOWN_TABLE);
|
||||
}
|
||||
|
@ -83,41 +79,44 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
|
||||
auto ddl_guard = (!no_ddl_lock ? context.getDDLGuard(database_name, table_name) : nullptr);
|
||||
|
||||
DatabaseAndTable database_and_table = tryGetDatabaseAndTable(database_name, table_name, if_exists);
|
||||
auto [database, table] = tryGetDatabaseAndTable(database_name, table_name, if_exists);
|
||||
|
||||
if (database_and_table.first && database_and_table.second)
|
||||
if (database && table)
|
||||
{
|
||||
auto table_id = database_and_table.second->getStorageID();
|
||||
auto table_id = table->getStorageID();
|
||||
if (kind == ASTDropQuery::Kind::Detach)
|
||||
{
|
||||
database_and_table.second->shutdown();
|
||||
context.checkAccess(table->isView() ? AccessType::DETACH_VIEW : AccessType::DETACH_TABLE,
|
||||
database_name, table_name);
|
||||
table->shutdown();
|
||||
/// If table was already dropped by anyone, an exception will be thrown
|
||||
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
|
||||
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
|
||||
/// Drop table from memory, don't touch data and metadata
|
||||
database_and_table.first->detachTable(table_id.table_name);
|
||||
database->detachTable(table_name);
|
||||
}
|
||||
else if (kind == ASTDropQuery::Kind::Truncate)
|
||||
{
|
||||
database_and_table.second->checkTableCanBeDropped();
|
||||
context.checkAccess(table->isView() ? AccessType::TRUNCATE_VIEW : AccessType::TRUNCATE_TABLE,
|
||||
database_name, table_name);
|
||||
table->checkTableCanBeDropped();
|
||||
|
||||
/// If table was already dropped by anyone, an exception will be thrown
|
||||
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
|
||||
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
|
||||
/// Drop table data, don't touch metadata
|
||||
database_and_table.second->truncate(query_ptr, context, table_lock);
|
||||
table->truncate(query_ptr, context, table_lock);
|
||||
}
|
||||
else if (kind == ASTDropQuery::Kind::Drop)
|
||||
{
|
||||
database_and_table.second->checkTableCanBeDropped();
|
||||
context.checkAccess(table->isView() ? AccessType::DROP_VIEW : AccessType::DROP_TABLE,
|
||||
database_name, table_name);
|
||||
table->checkTableCanBeDropped();
|
||||
|
||||
database_and_table.second->shutdown();
|
||||
table->shutdown();
|
||||
/// If table was already dropped by anyone, an exception will be thrown
|
||||
|
||||
auto table_lock = database_and_table.second->lockExclusively(context.getCurrentQueryId());
|
||||
|
||||
const std::string metadata_file_without_extension =
|
||||
database_and_table.first->getMetadataPath()
|
||||
+ escapeForFileName(table_id.table_name);
|
||||
auto table_lock = table->lockExclusively(context.getCurrentQueryId());
|
||||
|
||||
const std::string metadata_file_without_extension = database->getMetadataPath() + escapeForFileName(table_id.table_name);
|
||||
const auto prev_metadata_name = metadata_file_without_extension + ".sql";
|
||||
const auto drop_metadata_name = metadata_file_without_extension + ".sql.tmp_drop";
|
||||
|
||||
|
@ -128,7 +127,7 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
if (Poco::File(prev_metadata_name).exists())
|
||||
Poco::File(prev_metadata_name).renameTo(drop_metadata_name);
|
||||
/// Delete table data
|
||||
database_and_table.second->drop(table_lock);
|
||||
table->drop(table_lock);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
@ -137,11 +136,11 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
throw;
|
||||
}
|
||||
|
||||
String table_data_path_relative = database_and_table.first->getTableDataPath(table_name);
|
||||
String table_data_path_relative = database->getTableDataPath(table_name);
|
||||
|
||||
/// Delete table metadata and table itself from memory
|
||||
database_and_table.first->removeTable(context, table_id.table_name);
|
||||
database_and_table.second->is_dropped = true;
|
||||
database->removeTable(context, table_name);
|
||||
table->is_dropped = true;
|
||||
|
||||
/// If it is not virtual database like Dictionary then drop remaining data dir
|
||||
if (!table_data_path_relative.empty())
|
||||
|
@ -158,8 +157,8 @@ BlockIO InterpreterDropQuery::executeToTable(
|
|||
|
||||
|
||||
BlockIO InterpreterDropQuery::executeToDictionary(
|
||||
String & database_name_,
|
||||
String & dictionary_name,
|
||||
const String & database_name_,
|
||||
const String & dictionary_name,
|
||||
ASTDropQuery::Kind kind,
|
||||
bool if_exists,
|
||||
bool is_temporary,
|
||||
|
@ -187,6 +186,7 @@ BlockIO InterpreterDropQuery::executeToDictionary(
|
|||
if (kind == ASTDropQuery::Kind::Detach)
|
||||
{
|
||||
/// Drop dictionary from memory, don't touch data and metadata
|
||||
context.checkAccess(AccessType::DETACH_DICTIONARY, database_name, dictionary_name);
|
||||
database->detachDictionary(dictionary_name, context);
|
||||
}
|
||||
else if (kind == ASTDropQuery::Kind::Truncate)
|
||||
|
@ -195,12 +195,13 @@ BlockIO InterpreterDropQuery::executeToDictionary(
|
|||
}
|
||||
else if (kind == ASTDropQuery::Kind::Drop)
|
||||
{
|
||||
context.checkAccess(AccessType::DROP_DICTIONARY, database_name, dictionary_name);
|
||||
database->removeDictionary(context, dictionary_name);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind)
|
||||
BlockIO InterpreterDropQuery::executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind)
|
||||
{
|
||||
if (kind == ASTDropQuery::Kind::Detach)
|
||||
throw Exception("Unable to detach temporary table.", ErrorCodes::SYNTAX_ERROR);
|
||||
|
@ -233,7 +234,7 @@ BlockIO InterpreterDropQuery::executeToTemporaryTable(String & table_name, ASTDr
|
|||
return {};
|
||||
}
|
||||
|
||||
BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists)
|
||||
BlockIO InterpreterDropQuery::executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists)
|
||||
{
|
||||
auto ddl_guard = context.getDDLGuard(database_name, "");
|
||||
|
||||
|
@ -245,11 +246,14 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
|
|||
}
|
||||
else if (kind == ASTDropQuery::Kind::Detach)
|
||||
{
|
||||
context.checkAccess(AccessType::DETACH_DATABASE, database_name);
|
||||
context.detachDatabase(database_name);
|
||||
database->shutdown();
|
||||
}
|
||||
else if (kind == ASTDropQuery::Kind::Drop)
|
||||
{
|
||||
context.checkAccess(AccessType::DROP_DATABASE, database_name);
|
||||
|
||||
for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
{
|
||||
String current_table_name = iterator->name();
|
||||
|
@ -289,12 +293,12 @@ BlockIO InterpreterDropQuery::executeToDatabase(String & database_name, ASTDropQ
|
|||
return {};
|
||||
}
|
||||
|
||||
DatabasePtr InterpreterDropQuery::tryGetDatabase(String & database_name, bool if_exists)
|
||||
DatabasePtr InterpreterDropQuery::tryGetDatabase(const String & database_name, bool if_exists)
|
||||
{
|
||||
return if_exists ? context.tryGetDatabase(database_name) : context.getDatabase(database_name);
|
||||
}
|
||||
|
||||
DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists)
|
||||
DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists)
|
||||
{
|
||||
DatabasePtr database = tryGetDatabase(database_name, if_exists);
|
||||
|
||||
|
@ -310,20 +314,38 @@ DatabaseAndTable InterpreterDropQuery::tryGetDatabaseAndTable(String & database_
|
|||
return {};
|
||||
}
|
||||
|
||||
void InterpreterDropQuery::checkAccess(const ASTDropQuery & drop)
|
||||
|
||||
AccessRightsElements InterpreterDropQuery::getRequiredAccessForDDLOnCluster() const
|
||||
{
|
||||
const Settings & settings = context.getSettingsRef();
|
||||
auto readonly = settings.readonly;
|
||||
bool allow_ddl = settings.allow_ddl;
|
||||
AccessRightsElements required_access;
|
||||
const auto & drop = query_ptr->as<const ASTDropQuery &>();
|
||||
|
||||
/// It's allowed to drop temporary tables.
|
||||
if ((!readonly && allow_ddl) || (drop.database.empty() && context.tryGetExternalTable(drop.table) && readonly >= 2))
|
||||
return;
|
||||
if (drop.table.empty())
|
||||
{
|
||||
if (drop.kind == ASTDropQuery::Kind::Detach)
|
||||
required_access.emplace_back(AccessType::DETACH_DATABASE, drop.database);
|
||||
else if (drop.kind == ASTDropQuery::Kind::Drop)
|
||||
required_access.emplace_back(AccessType::DROP_DATABASE, drop.database);
|
||||
}
|
||||
else if (drop.is_dictionary)
|
||||
{
|
||||
if (drop.kind == ASTDropQuery::Kind::Detach)
|
||||
required_access.emplace_back(AccessType::DETACH_DICTIONARY, drop.database, drop.table);
|
||||
else if (drop.kind == ASTDropQuery::Kind::Drop)
|
||||
required_access.emplace_back(AccessType::DROP_DICTIONARY, drop.database, drop.table);
|
||||
}
|
||||
else if (!drop.temporary)
|
||||
{
|
||||
/// It can be view or table.
|
||||
if (drop.kind == ASTDropQuery::Kind::Drop)
|
||||
required_access.emplace_back(AccessType::DROP_TABLE | AccessType::DROP_VIEW, drop.database, drop.table);
|
||||
else if (drop.kind == ASTDropQuery::Kind::Truncate)
|
||||
required_access.emplace_back(AccessType::TRUNCATE_TABLE | AccessType::TRUNCATE_VIEW, drop.database, drop.table);
|
||||
else if (drop.kind == ASTDropQuery::Kind::Detach)
|
||||
required_access.emplace_back(AccessType::DETACH_TABLE | AccessType::DETACH_VIEW, drop.database, drop.table);
|
||||
}
|
||||
|
||||
if (readonly)
|
||||
throw Exception("Cannot drop table in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
throw Exception("Cannot drop table. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,20 +24,20 @@ public:
|
|||
BlockIO execute() override;
|
||||
|
||||
private:
|
||||
void checkAccess(const ASTDropQuery & drop);
|
||||
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
|
||||
ASTPtr query_ptr;
|
||||
Context & context;
|
||||
|
||||
BlockIO executeToDatabase(String & database_name, ASTDropQuery::Kind kind, bool if_exists);
|
||||
BlockIO executeToDatabase(const String & database_name, ASTDropQuery::Kind kind, bool if_exists);
|
||||
|
||||
BlockIO executeToTable(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
|
||||
BlockIO executeToTable(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
|
||||
|
||||
BlockIO executeToDictionary(String & database_name, String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
|
||||
BlockIO executeToDictionary(const String & database_name, const String & table_name, ASTDropQuery::Kind kind, bool if_exists, bool is_temporary, bool no_ddl_lock);
|
||||
|
||||
DatabasePtr tryGetDatabase(String & database_name, bool exists);
|
||||
DatabasePtr tryGetDatabase(const String & database_name, bool exists);
|
||||
|
||||
DatabaseAndTable tryGetDatabaseAndTable(String & database_name, String & table_name, bool if_exists);
|
||||
DatabaseAndTable tryGetDatabaseAndTable(const String & database_name, const String & table_name, bool if_exists);
|
||||
|
||||
BlockIO executeToTemporaryTable(String & table_name, ASTDropQuery::Kind kind);
|
||||
BlockIO executeToTemporaryTable(const String & table_name, ASTDropQuery::Kind kind);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -42,14 +42,27 @@ BlockInputStreamPtr InterpreterExistsQuery::executeImpl()
|
|||
if ((exists_query = query_ptr->as<ASTExistsTableQuery>()))
|
||||
{
|
||||
if (exists_query->temporary)
|
||||
{
|
||||
context.checkAccess(AccessType::EXISTS, "", exists_query->table);
|
||||
result = context.isExternalTableExist(exists_query->table);
|
||||
}
|
||||
else
|
||||
result = context.isTableExist(exists_query->database, exists_query->table);
|
||||
{
|
||||
String database = exists_query->database;
|
||||
if (database.empty())
|
||||
database = context.getCurrentDatabase();
|
||||
context.checkAccess(AccessType::EXISTS, database, exists_query->table);
|
||||
result = context.isTableExist(database, exists_query->table);
|
||||
}
|
||||
}
|
||||
else if ((exists_query = query_ptr->as<ASTExistsDictionaryQuery>()))
|
||||
{
|
||||
if (exists_query->temporary)
|
||||
throw Exception("Temporary dictionaries are not possible.", ErrorCodes::SYNTAX_ERROR);
|
||||
String database = exists_query->database;
|
||||
if (database.empty())
|
||||
database = context.getCurrentDatabase();
|
||||
context.checkAccess(AccessType::EXISTS, database, exists_query->table);
|
||||
result = context.isDictionaryExists(exists_query->database, exists_query->table);
|
||||
}
|
||||
|
||||
|
|
|
@ -74,22 +74,6 @@ namespace ErrorCodes
|
|||
}
|
||||
|
||||
|
||||
static void throwIfNoAccess(Context & context)
|
||||
{
|
||||
if (context.getSettingsRef().readonly)
|
||||
{
|
||||
const auto & client_info = context.getClientInfo();
|
||||
if (client_info.interface == ClientInfo::Interface::HTTP && client_info.http_method == ClientInfo::HTTPMethod::GET)
|
||||
throw Exception("Cannot execute query in readonly mode. "
|
||||
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries.", ErrorCodes::READONLY);
|
||||
else
|
||||
throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
else if (!context.getSettingsRef().allow_ddl)
|
||||
throw Exception("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context & context, QueryProcessingStage::Enum stage)
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::Query);
|
||||
|
@ -108,23 +92,19 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||
else if (query->as<ASTInsertQuery>())
|
||||
{
|
||||
ProfileEvents::increment(ProfileEvents::InsertQuery);
|
||||
/// readonly is checked inside InterpreterInsertQuery
|
||||
bool allow_materialized = static_cast<bool>(context.getSettingsRef().insert_allow_materialized_columns);
|
||||
return std::make_unique<InterpreterInsertQuery>(query, context, allow_materialized);
|
||||
}
|
||||
else if (query->as<ASTCreateQuery>())
|
||||
{
|
||||
/// readonly and allow_ddl are checked inside InterpreterCreateQuery
|
||||
return std::make_unique<InterpreterCreateQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTDropQuery>())
|
||||
{
|
||||
/// readonly and allow_ddl are checked inside InterpreterDropQuery
|
||||
return std::make_unique<InterpreterDropQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTRenameQuery>())
|
||||
{
|
||||
throwIfNoAccess(context);
|
||||
return std::make_unique<InterpreterRenameQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTShowTablesQuery>())
|
||||
|
@ -142,7 +122,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||
}
|
||||
else if (query->as<ASTOptimizeQuery>())
|
||||
{
|
||||
throwIfNoAccess(context);
|
||||
return std::make_unique<InterpreterOptimizeQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTExistsTableQuery>())
|
||||
|
@ -179,7 +158,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||
}
|
||||
else if (query->as<ASTAlterQuery>())
|
||||
{
|
||||
throwIfNoAccess(context);
|
||||
return std::make_unique<InterpreterAlterQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTCheckQuery>())
|
||||
|
@ -192,7 +170,6 @@ std::unique_ptr<IInterpreter> InterpreterFactory::get(ASTPtr & query, Context &
|
|||
}
|
||||
else if (query->as<ASTSystemQuery>())
|
||||
{
|
||||
throwIfNoAccess(context);
|
||||
return std::make_unique<InterpreterSystemQuery>(query, context);
|
||||
}
|
||||
else if (query->as<ASTWatchQuery>())
|
||||
|
|
|
@ -46,7 +46,7 @@ InterpreterInsertQuery::InterpreterInsertQuery(
|
|||
}
|
||||
|
||||
|
||||
StoragePtr InterpreterInsertQuery::getTable(const ASTInsertQuery & query)
|
||||
StoragePtr InterpreterInsertQuery::getTable(ASTInsertQuery & query)
|
||||
{
|
||||
if (query.table_function)
|
||||
{
|
||||
|
@ -57,6 +57,8 @@ StoragePtr InterpreterInsertQuery::getTable(const ASTInsertQuery & query)
|
|||
}
|
||||
|
||||
/// Into what table to write.
|
||||
if (query.database.empty() && !context.isExternalTableExist(query.table))
|
||||
query.database = context.getCurrentDatabase();
|
||||
return context.getTable(query.database, query.table);
|
||||
}
|
||||
|
||||
|
@ -97,15 +99,17 @@ Block InterpreterInsertQuery::getSampleBlock(const ASTInsertQuery & query, const
|
|||
BlockIO InterpreterInsertQuery::execute()
|
||||
{
|
||||
const Settings & settings = context.getSettingsRef();
|
||||
|
||||
const auto & query = query_ptr->as<ASTInsertQuery &>();
|
||||
checkAccess(query);
|
||||
auto & query = query_ptr->as<ASTInsertQuery &>();
|
||||
|
||||
BlockIO res;
|
||||
StoragePtr table = getTable(query);
|
||||
|
||||
StoragePtr table = getTable(query);
|
||||
auto table_lock = table->lockStructureForShare(true, context.getInitialQueryId());
|
||||
|
||||
auto query_sample_block = getSampleBlock(query, table);
|
||||
if (!query.table.empty() && !query.database.empty() /* always allow access to temporary tables */)
|
||||
context.checkAccess(AccessType::INSERT, query.database, query.table, query_sample_block.getNames());
|
||||
|
||||
BlockInputStreams in_streams;
|
||||
size_t out_streams_size = 1;
|
||||
if (query.select)
|
||||
|
@ -128,7 +132,6 @@ BlockIO InterpreterInsertQuery::execute()
|
|||
}
|
||||
|
||||
BlockOutputStreams out_streams;
|
||||
auto query_sample_block = getSampleBlock(query, table);
|
||||
|
||||
for (size_t i = 0; i < out_streams_size; i++)
|
||||
{
|
||||
|
@ -204,19 +207,6 @@ BlockIO InterpreterInsertQuery::execute()
|
|||
}
|
||||
|
||||
|
||||
void InterpreterInsertQuery::checkAccess(const ASTInsertQuery & query)
|
||||
{
|
||||
const Settings & settings = context.getSettingsRef();
|
||||
auto readonly = settings.readonly;
|
||||
|
||||
if (!readonly || (query.database.empty() && context.tryGetExternalTable(query.table) && readonly >= 2))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw Exception("Cannot insert into table in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
|
||||
std::pair<String, String> InterpreterInsertQuery::getDatabaseTable() const
|
||||
{
|
||||
const auto & query = query_ptr->as<ASTInsertQuery &>();
|
||||
|
|
|
@ -32,9 +32,8 @@ public:
|
|||
std::pair<String, String> getDatabaseTable() const;
|
||||
|
||||
private:
|
||||
StoragePtr getTable(const ASTInsertQuery & query);
|
||||
StoragePtr getTable(ASTInsertQuery & query);
|
||||
Block getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table);
|
||||
void checkAccess(const ASTInsertQuery & query);
|
||||
|
||||
ASTPtr query_ptr;
|
||||
const Context & context;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <Interpreters/ProcessList.h>
|
||||
#include <Interpreters/executeQuery.h>
|
||||
#include <Interpreters/CancellationCode.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
|
@ -13,6 +14,7 @@
|
|||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataStreams/OneBlockInputStream.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <thread>
|
||||
#include <iostream>
|
||||
#include <cstddef>
|
||||
|
@ -26,6 +28,7 @@ namespace ErrorCodes
|
|||
extern const int READONLY;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int CANNOT_KILL;
|
||||
extern const int ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,24 +81,32 @@ static QueryDescriptors extractQueriesExceptMeAndCheckAccess(const Block & proce
|
|||
const ColumnString & query_id_col = typeid_cast<const ColumnString &>(*processes_block.getByName("query_id").column);
|
||||
const ColumnString & user_col = typeid_cast<const ColumnString &>(*processes_block.getByName("user").column);
|
||||
const ClientInfo & my_client = context.getProcessListElement()->getClientInfo();
|
||||
std::optional<bool> can_kill_query_started_by_another_user;
|
||||
String query_user;
|
||||
|
||||
for (size_t i = 0; i < num_processes; ++i)
|
||||
{
|
||||
auto query_id = query_id_col.getDataAt(i).toString();
|
||||
auto user = user_col.getDataAt(i).toString();
|
||||
|
||||
if (my_client.current_query_id == query_id && my_client.current_user == user)
|
||||
if ((my_client.current_query_id == query_id_col.getDataAt(i).toString())
|
||||
&& (my_client.current_user == user_col.getDataAt(i).toString()))
|
||||
continue;
|
||||
|
||||
if (context.getSettingsRef().readonly && my_client.current_user != user)
|
||||
auto query_id = query_id_col.getDataAt(i).toString();
|
||||
query_user = user_col.getDataAt(i).toString();
|
||||
|
||||
if (my_client.current_user != query_user)
|
||||
{
|
||||
throw Exception("Readonly user " + my_client.current_user + " attempts to kill query created by " + user,
|
||||
ErrorCodes::READONLY);
|
||||
if (!can_kill_query_started_by_another_user)
|
||||
can_kill_query_started_by_another_user = context.getAccessRights()->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), AccessType::KILL_QUERY);
|
||||
if (!can_kill_query_started_by_another_user.value())
|
||||
continue;
|
||||
}
|
||||
|
||||
res.emplace_back(std::move(query_id), std::move(user), i, false);
|
||||
res.emplace_back(std::move(query_id), std::move(query_user), i, false);
|
||||
}
|
||||
|
||||
if (res.empty() && !query_user.empty())
|
||||
throw Exception("User " + my_client.current_user + " attempts to kill query created by " + query_user, ErrorCodes::ACCESS_DENIED);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -175,7 +186,7 @@ BlockIO InterpreterKillQueryQuery::execute()
|
|||
const auto & query = query_ptr->as<ASTKillQueryQuery &>();
|
||||
|
||||
if (!query.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {"system"});
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
|
||||
|
||||
BlockIO res_io;
|
||||
switch (query.type)
|
||||
|
@ -195,7 +206,6 @@ BlockIO InterpreterKillQueryQuery::execute()
|
|||
if (!query.sync || query.test)
|
||||
{
|
||||
MutableColumns res_columns = header.cloneEmptyColumns();
|
||||
|
||||
for (const auto & query_desc : queries_to_stop)
|
||||
{
|
||||
auto code = (query.test) ? CancellationCode::Unknown : process_list.sendCancelToQuery(query_desc.query_id, query_desc.user, true);
|
||||
|
@ -214,9 +224,6 @@ BlockIO InterpreterKillQueryQuery::execute()
|
|||
}
|
||||
case ASTKillQueryQuery::Type::Mutation:
|
||||
{
|
||||
if (context.getSettingsRef().readonly)
|
||||
throw Exception("Cannot execute query in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
Block mutations_block = getSelectResult("database, table, mutation_id", "system.mutations");
|
||||
if (!mutations_block)
|
||||
return res_io;
|
||||
|
@ -229,11 +236,12 @@ BlockIO InterpreterKillQueryQuery::execute()
|
|||
header.insert(0, {ColumnString::create(), std::make_shared<DataTypeString>(), "kill_status"});
|
||||
|
||||
MutableColumns res_columns = header.cloneEmptyColumns();
|
||||
String database_name, table_name;
|
||||
|
||||
for (size_t i = 0; i < mutations_block.rows(); ++i)
|
||||
{
|
||||
auto database_name = database_col.getDataAt(i).toString();
|
||||
auto table_name = table_col.getDataAt(i).toString();
|
||||
database_name = database_col.getDataAt(i).toString();
|
||||
table_name = table_col.getDataAt(i).toString();
|
||||
auto mutation_id = mutation_id_col.getDataAt(i).toString();
|
||||
|
||||
CancellationCode code = CancellationCode::Unknown;
|
||||
|
@ -243,12 +251,21 @@ BlockIO InterpreterKillQueryQuery::execute()
|
|||
if (!storage)
|
||||
code = CancellationCode::NotFound;
|
||||
else
|
||||
{
|
||||
if (!context.getAccessRights()->isGranted(&Poco::Logger::get("InterpreterKillQueryQuery"), AccessType::KILL_MUTATION, database_name, table_name))
|
||||
continue;
|
||||
code = storage->killMutation(mutation_id);
|
||||
}
|
||||
}
|
||||
|
||||
insertResultRow(i, code, mutations_block, header, res_columns);
|
||||
}
|
||||
|
||||
if (res_columns[0]->empty() && !table_name.empty())
|
||||
throw Exception(
|
||||
"Not allowed to kill mutation on " + backQuoteIfNeed(database_name) + "." + backQuoteIfNeed(table_name),
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
|
||||
res_io.in = std::make_shared<OneBlockInputStream>(header.cloneWithColumns(std::move(res_columns)));
|
||||
|
||||
break;
|
||||
|
@ -265,7 +282,7 @@ Block InterpreterKillQueryQuery::getSelectResult(const String & columns, const S
|
|||
if (where_expression)
|
||||
select_query += " WHERE " + queryToString(where_expression);
|
||||
|
||||
BlockIO block_io = executeQuery(select_query, context, true, QueryProcessingStage::Complete, false, false);
|
||||
BlockIO block_io = executeQuery(select_query, context.getGlobalContext(), true, QueryProcessingStage::Complete, false, false);
|
||||
Block res = block_io.in->read();
|
||||
|
||||
if (res && block_io.in->read())
|
||||
|
@ -274,4 +291,16 @@ Block InterpreterKillQueryQuery::getSelectResult(const String & columns, const S
|
|||
return res;
|
||||
}
|
||||
|
||||
|
||||
AccessRightsElements InterpreterKillQueryQuery::getRequiredAccessForDDLOnCluster() const
|
||||
{
|
||||
const auto & query = query_ptr->as<ASTKillQueryQuery &>();
|
||||
AccessRightsElements required_access;
|
||||
if (query.type == ASTKillQueryQuery::Type::Query)
|
||||
required_access.emplace_back(AccessType::KILL_QUERY);
|
||||
else if (query.type == ASTKillQueryQuery::Type::Mutation)
|
||||
required_access.emplace_back(AccessType::KILL_MUTATION);
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace DB
|
|||
{
|
||||
|
||||
class Context;
|
||||
class AccessRightsElements;
|
||||
|
||||
|
||||
class InterpreterKillQueryQuery : public IInterpreter
|
||||
|
@ -20,6 +21,7 @@ public:
|
|||
BlockIO execute() override;
|
||||
|
||||
private:
|
||||
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
|
||||
Block getSelectResult(const String & columns, const String & table);
|
||||
|
||||
ASTPtr query_ptr;
|
||||
|
|
|
@ -20,11 +20,22 @@ BlockIO InterpreterOptimizeQuery::execute()
|
|||
const auto & ast = query_ptr->as<ASTOptimizeQuery &>();
|
||||
|
||||
if (!ast.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {ast.database});
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
|
||||
|
||||
context.checkAccess(getRequiredAccess());
|
||||
|
||||
StoragePtr table = context.getTable(ast.database, ast.table);
|
||||
table->optimize(query_ptr, ast.partition, ast.final, ast.deduplicate, context);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
AccessRightsElements InterpreterOptimizeQuery::getRequiredAccess() const
|
||||
{
|
||||
const auto & optimize = query_ptr->as<const ASTOptimizeQuery &>();
|
||||
AccessRightsElements required_access;
|
||||
required_access.emplace_back(AccessType::OPTIMIZE, optimize.database, optimize.table);
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class Context;
|
||||
class AccessRightsElements;
|
||||
|
||||
|
||||
/** Just call method "optimize" for table.
|
||||
|
@ -23,9 +23,10 @@ public:
|
|||
BlockIO execute() override;
|
||||
|
||||
private:
|
||||
AccessRightsElements getRequiredAccess() const;
|
||||
|
||||
ASTPtr query_ptr;
|
||||
Context & context;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -36,19 +36,12 @@ struct RenameDescription
|
|||
|
||||
BlockIO InterpreterRenameQuery::execute()
|
||||
{
|
||||
const auto & rename = query_ptr->as<ASTRenameQuery &>();
|
||||
const auto & rename = query_ptr->as<const ASTRenameQuery &>();
|
||||
|
||||
if (!rename.cluster.empty())
|
||||
{
|
||||
NameSet databases;
|
||||
for (const auto & elem : rename.elements)
|
||||
{
|
||||
databases.emplace(elem.from.database);
|
||||
databases.emplace(elem.to.database);
|
||||
}
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccess());
|
||||
|
||||
return executeDDLQueryOnCluster(query_ptr, context, std::move(databases));
|
||||
}
|
||||
context.checkAccess(getRequiredAccess());
|
||||
|
||||
String path = context.getPath();
|
||||
String current_database = context.getCurrentDatabase();
|
||||
|
@ -81,9 +74,10 @@ BlockIO InterpreterRenameQuery::execute()
|
|||
for (const auto & elem : rename.elements)
|
||||
{
|
||||
descriptions.emplace_back(elem, current_database);
|
||||
const auto & description = descriptions.back();
|
||||
|
||||
UniqueTableName from(descriptions.back().from_database_name, descriptions.back().from_table_name);
|
||||
UniqueTableName to(descriptions.back().to_database_name, descriptions.back().to_table_name);
|
||||
UniqueTableName from(description.from_database_name, description.from_table_name);
|
||||
UniqueTableName to(description.to_database_name, description.to_table_name);
|
||||
|
||||
table_guards[from];
|
||||
table_guards[to];
|
||||
|
@ -110,5 +104,16 @@ BlockIO InterpreterRenameQuery::execute()
|
|||
return {};
|
||||
}
|
||||
|
||||
AccessRightsElements InterpreterRenameQuery::getRequiredAccess() const
|
||||
{
|
||||
AccessRightsElements required_access;
|
||||
const auto & rename = query_ptr->as<const ASTRenameQuery &>();
|
||||
for (const auto & elem : rename.elements)
|
||||
{
|
||||
required_access.emplace_back(AccessType::SELECT | AccessType::DROP_TABLE, elem.from.database, elem.from.table);
|
||||
required_access.emplace_back(AccessType::CREATE_TABLE | AccessType::INSERT, elem.to.database, elem.to.table);
|
||||
}
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace DB
|
|||
{
|
||||
|
||||
class Context;
|
||||
class AccessRightsElements;
|
||||
|
||||
|
||||
/** Rename one table
|
||||
|
@ -20,9 +21,10 @@ public:
|
|||
BlockIO execute() override;
|
||||
|
||||
private:
|
||||
AccessRightsElements getRequiredAccess() const;
|
||||
|
||||
ASTPtr query_ptr;
|
||||
Context & context;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -258,6 +258,7 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
|||
auto & query = getSelectQuery();
|
||||
|
||||
ASTPtr table_expression = extractTableExpression(query, 0);
|
||||
String database_name, table_name;
|
||||
|
||||
bool is_table_func = false;
|
||||
bool is_subquery = false;
|
||||
|
@ -295,9 +296,6 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
|||
}
|
||||
else
|
||||
{
|
||||
String database_name;
|
||||
String table_name;
|
||||
|
||||
getDatabaseAndTableNames(query, database_name, table_name, *context);
|
||||
|
||||
if (auto view_source = context->getViewSource())
|
||||
|
@ -433,21 +431,20 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
|||
if (query.prewhere() && !query.where())
|
||||
analysis_result.prewhere_info->need_filter = true;
|
||||
|
||||
if (!table_name.empty() && !database_name.empty() /* always allow access to temporary tables */)
|
||||
context->checkAccess(AccessType::SELECT, database_name, table_name, required_columns);
|
||||
|
||||
/// Remove limits for some tables in the `system` database.
|
||||
if (database_name == "system" && ((table_name == "quotas") || (table_name == "quota_usage") || (table_name == "one")))
|
||||
{
|
||||
options.ignore_quota = true;
|
||||
options.ignore_limits = true;
|
||||
}
|
||||
|
||||
/// Blocks used in expression analysis contains size 1 const columns for constant folding and
|
||||
/// null non-const columns to avoid useless memory allocations. However, a valid block sample
|
||||
/// requires all columns to be of size 0, thus we need to sanitize the block here.
|
||||
sanitizeBlock(result_header);
|
||||
|
||||
/// Remove limits for some tables in the `system` database.
|
||||
if (storage && (table_id.getDatabaseName() == "system"))
|
||||
{
|
||||
String table_name = table_id.getTableName();
|
||||
if ((table_name == "quotas") || (table_name == "quota_usage") || (table_name == "one"))
|
||||
{
|
||||
options.ignore_quota = true;
|
||||
options.ignore_limits = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -459,7 +456,7 @@ void InterpreterSelectQuery::getDatabaseAndTableNames(const ASTSelectQuery & que
|
|||
database_name = db_and_table->database;
|
||||
|
||||
/// If the database is not specified - use the current database.
|
||||
if (database_name.empty() && !context.tryGetTable("", table_name))
|
||||
if (database_name.empty() && !context.isExternalTableExist(table_name))
|
||||
database_name = context.getCurrentDatabase();
|
||||
}
|
||||
else /// If the table is not specified - use the table `system.one`.
|
||||
|
|
|
@ -49,18 +49,23 @@ BlockInputStreamPtr InterpreterShowCreateQuery::executeImpl()
|
|||
if (show_query->temporary)
|
||||
create_query = context.getCreateExternalTableQuery(show_query->table);
|
||||
else
|
||||
{
|
||||
context.checkAccess(AccessType::SHOW, show_query->database, show_query->table);
|
||||
create_query = context.getDatabase(show_query->database)->getCreateTableQuery(context, show_query->table);
|
||||
}
|
||||
}
|
||||
else if ((show_query = query_ptr->as<ASTShowCreateDatabaseQuery>()))
|
||||
{
|
||||
if (show_query->temporary)
|
||||
throw Exception("Temporary databases are not possible.", ErrorCodes::SYNTAX_ERROR);
|
||||
context.checkAccess(AccessType::SHOW, show_query->database);
|
||||
create_query = context.getDatabase(show_query->database)->getCreateDatabaseQuery(context);
|
||||
}
|
||||
else if ((show_query = query_ptr->as<ASTShowCreateDictionaryQuery>()))
|
||||
{
|
||||
if (show_query->temporary)
|
||||
throw Exception("Temporary dictionaries are not possible.", ErrorCodes::SYNTAX_ERROR);
|
||||
context.checkAccess(AccessType::SHOW, show_query->database, show_query->table);
|
||||
create_query = context.getDatabase(show_query->database)->getCreateDictionaryQuery(context, show_query->table);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <Interpreters/TraceLog.h>
|
||||
#include <Interpreters/TextLog.h>
|
||||
#include <Interpreters/MetricLog.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <Storages/StorageDistributed.h>
|
||||
#include <Storages/StorageReplicatedMergeTree.h>
|
||||
|
@ -96,8 +97,29 @@ void executeCommandsAndThrowIfError(Callables && ... commands)
|
|||
}
|
||||
|
||||
|
||||
AccessType getRequiredAccessType(StorageActionBlockType action_type)
|
||||
{
|
||||
if (action_type == ActionLocks::PartsMerge)
|
||||
return AccessType::STOP_MERGES;
|
||||
else if (action_type == ActionLocks::PartsFetch)
|
||||
return AccessType::STOP_FETCHES;
|
||||
else if (action_type == ActionLocks::PartsSend)
|
||||
return AccessType::STOP_REPLICATED_SENDS;
|
||||
else if (action_type == ActionLocks::ReplicationQueue)
|
||||
return AccessType::STOP_REPLICATION_QUEUES;
|
||||
else if (action_type == ActionLocks::DistributedSend)
|
||||
return AccessType::STOP_DISTRIBUTED_SENDS;
|
||||
else if (action_type == ActionLocks::PartsTTLMerge)
|
||||
return AccessType::STOP_TTL_MERGES;
|
||||
else if (action_type == ActionLocks::PartsMove)
|
||||
return AccessType::STOP_MOVES;
|
||||
else
|
||||
throw Exception("Unknown action type: " + std::to_string(action_type), ErrorCodes::LOGICAL_ERROR);
|
||||
}
|
||||
|
||||
|
||||
/// Implements SYSTEM [START|STOP] <something action from ActionLocks>
|
||||
void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlockType action_type, bool start)
|
||||
void startStopAction(Context & context, Poco::Logger * log, ASTSystemQuery & query, StorageActionBlockType action_type, bool start)
|
||||
{
|
||||
auto manager = context.getActionLocksManager();
|
||||
manager->cleanExpired();
|
||||
|
@ -105,7 +127,7 @@ void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlo
|
|||
if (!query.table.empty())
|
||||
{
|
||||
String database = !query.database.empty() ? query.database : context.getCurrentDatabase();
|
||||
|
||||
context.checkAccess(getRequiredAccessType(action_type), database, query.table);
|
||||
if (start)
|
||||
manager->remove(database, query.table, action_type);
|
||||
else
|
||||
|
@ -113,12 +135,22 @@ void startStopAction(Context & context, ASTSystemQuery & query, StorageActionBlo
|
|||
}
|
||||
else
|
||||
{
|
||||
if (start)
|
||||
manager->remove(action_type);
|
||||
else
|
||||
manager->add(action_type);
|
||||
for (auto & elem : context.getDatabases())
|
||||
{
|
||||
for (auto iterator = elem.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
{
|
||||
if (context.getAccessRights()->isGranted(log, getRequiredAccessType(action_type), elem.first, iterator->name()))
|
||||
{
|
||||
if (start)
|
||||
manager->remove(iterator->table(), action_type);
|
||||
else
|
||||
manager->add(iterator->table(), action_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -133,7 +165,7 @@ BlockIO InterpreterSystemQuery::execute()
|
|||
auto & query = query_ptr->as<ASTSystemQuery &>();
|
||||
|
||||
if (!query.cluster.empty())
|
||||
return executeDDLQueryOnCluster(query_ptr, context, {query.database});
|
||||
return executeDDLQueryOnCluster(query_ptr, context, getRequiredAccessForDDLOnCluster());
|
||||
|
||||
using Type = ASTSystemQuery::Type;
|
||||
|
||||
|
@ -151,85 +183,95 @@ BlockIO InterpreterSystemQuery::execute()
|
|||
switch (query.type)
|
||||
{
|
||||
case Type::SHUTDOWN:
|
||||
context.checkAccess(AccessType::SHUTDOWN);
|
||||
if (kill(0, SIGTERM))
|
||||
throwFromErrno("System call kill(0, SIGTERM) failed", ErrorCodes::CANNOT_KILL);
|
||||
break;
|
||||
case Type::KILL:
|
||||
context.checkAccess(AccessType::SHUTDOWN);
|
||||
if (kill(0, SIGKILL))
|
||||
throwFromErrno("System call kill(0, SIGKILL) failed", ErrorCodes::CANNOT_KILL);
|
||||
break;
|
||||
case Type::DROP_DNS_CACHE:
|
||||
context.checkAccess(AccessType::DROP_CACHE);
|
||||
DNSResolver::instance().dropCache();
|
||||
/// Reinitialize clusters to update their resolved_addresses
|
||||
system_context.reloadClusterConfig();
|
||||
break;
|
||||
case Type::DROP_MARK_CACHE:
|
||||
context.checkAccess(AccessType::DROP_CACHE);
|
||||
system_context.dropMarkCache();
|
||||
break;
|
||||
case Type::DROP_UNCOMPRESSED_CACHE:
|
||||
context.checkAccess(AccessType::DROP_CACHE);
|
||||
system_context.dropUncompressedCache();
|
||||
break;
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
case Type::DROP_COMPILED_EXPRESSION_CACHE:
|
||||
context.checkAccess(AccessType::DROP_CACHE);
|
||||
system_context.dropCompiledExpressionCache();
|
||||
break;
|
||||
#endif
|
||||
case Type::RELOAD_DICTIONARY:
|
||||
context.checkAccess(AccessType::RELOAD_DICTIONARY);
|
||||
system_context.getExternalDictionariesLoader().loadOrReload(query.target_dictionary);
|
||||
break;
|
||||
case Type::RELOAD_DICTIONARIES:
|
||||
context.checkAccess(AccessType::RELOAD_DICTIONARY);
|
||||
executeCommandsAndThrowIfError(
|
||||
[&] () { system_context.getExternalDictionariesLoader().reloadAllTriedToLoad(); },
|
||||
[&] () { system_context.getEmbeddedDictionaries().reload(); }
|
||||
);
|
||||
break;
|
||||
case Type::RELOAD_EMBEDDED_DICTIONARIES:
|
||||
context.checkAccess(AccessType::RELOAD_DICTIONARY);
|
||||
system_context.getEmbeddedDictionaries().reload();
|
||||
break;
|
||||
case Type::RELOAD_CONFIG:
|
||||
context.checkAccess(AccessType::RELOAD_CONFIG);
|
||||
system_context.reloadConfig();
|
||||
break;
|
||||
case Type::STOP_MERGES:
|
||||
startStopAction(context, query, ActionLocks::PartsMerge, false);
|
||||
startStopAction(context, log, query, ActionLocks::PartsMerge, false);
|
||||
break;
|
||||
case Type::START_MERGES:
|
||||
startStopAction(context, query, ActionLocks::PartsMerge, true);
|
||||
startStopAction(context, log, query, ActionLocks::PartsMerge, true);
|
||||
break;
|
||||
case Type::STOP_TTL_MERGES:
|
||||
startStopAction(context, query, ActionLocks::PartsTTLMerge, false);
|
||||
startStopAction(context, log, query, ActionLocks::PartsTTLMerge, false);
|
||||
break;
|
||||
case Type::START_TTL_MERGES:
|
||||
startStopAction(context, query, ActionLocks::PartsTTLMerge, true);
|
||||
startStopAction(context, log, query, ActionLocks::PartsTTLMerge, true);
|
||||
break;
|
||||
case Type::STOP_MOVES:
|
||||
startStopAction(context, query, ActionLocks::PartsMove, false);
|
||||
startStopAction(context, log, query, ActionLocks::PartsMove, false);
|
||||
break;
|
||||
case Type::START_MOVES:
|
||||
startStopAction(context, query, ActionLocks::PartsMove, true);
|
||||
startStopAction(context, log, query, ActionLocks::PartsMove, true);
|
||||
break;
|
||||
case Type::STOP_FETCHES:
|
||||
startStopAction(context, query, ActionLocks::PartsFetch, false);
|
||||
startStopAction(context, log, query, ActionLocks::PartsFetch, false);
|
||||
break;
|
||||
case Type::START_FETCHES:
|
||||
startStopAction(context, query, ActionLocks::PartsFetch, true);
|
||||
startStopAction(context, log, query, ActionLocks::PartsFetch, true);
|
||||
break;
|
||||
case Type::STOP_REPLICATED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::PartsSend, false);
|
||||
startStopAction(context, log, query, ActionLocks::PartsSend, false);
|
||||
break;
|
||||
case Type::START_REPLICATED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::PartsSend, true);
|
||||
startStopAction(context, log, query, ActionLocks::PartsSend, true);
|
||||
break;
|
||||
case Type::STOP_REPLICATION_QUEUES:
|
||||
startStopAction(context, query, ActionLocks::ReplicationQueue, false);
|
||||
startStopAction(context, log, query, ActionLocks::ReplicationQueue, false);
|
||||
break;
|
||||
case Type::START_REPLICATION_QUEUES:
|
||||
startStopAction(context, query, ActionLocks::ReplicationQueue, true);
|
||||
startStopAction(context, log, query, ActionLocks::ReplicationQueue, true);
|
||||
break;
|
||||
case Type::STOP_DISTRIBUTED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::DistributedSend, false);
|
||||
startStopAction(context, log, query, ActionLocks::DistributedSend, false);
|
||||
break;
|
||||
case Type::START_DISTRIBUTED_SENDS:
|
||||
startStopAction(context, query, ActionLocks::DistributedSend, true);
|
||||
startStopAction(context, log, query, ActionLocks::DistributedSend, true);
|
||||
break;
|
||||
case Type::SYNC_REPLICA:
|
||||
syncReplica(query);
|
||||
|
@ -246,6 +288,7 @@ BlockIO InterpreterSystemQuery::execute()
|
|||
ErrorCodes::BAD_ARGUMENTS);
|
||||
break;
|
||||
case Type::FLUSH_LOGS:
|
||||
context.checkAccess(AccessType::FLUSH_LOGS);
|
||||
executeCommandsAndThrowIfError(
|
||||
[&] () { if (auto query_log = context.getQueryLog()) query_log->flush(); },
|
||||
[&] () { if (auto part_log = context.getPartLog("")) part_log->flush(); },
|
||||
|
@ -268,7 +311,9 @@ BlockIO InterpreterSystemQuery::execute()
|
|||
|
||||
StoragePtr InterpreterSystemQuery::tryRestartReplica(const String & database_name, const String & table_name, Context & system_context)
|
||||
{
|
||||
context.checkAccess(AccessType::RESTART_REPLICA, database_name, table_name);
|
||||
auto database = system_context.getDatabase(database_name);
|
||||
|
||||
auto table_ddl_guard = system_context.getDDLGuard(database_name, table_name);
|
||||
ASTPtr create_ast;
|
||||
|
||||
|
@ -342,6 +387,7 @@ void InterpreterSystemQuery::syncReplica(ASTSystemQuery & query)
|
|||
String database_name = !query.database.empty() ? query.database : context.getCurrentDatabase();
|
||||
const String & table_name = query.table;
|
||||
|
||||
context.checkAccess(AccessType::SYNC_REPLICA, database_name, table_name);
|
||||
StoragePtr table = context.getTable(database_name, table_name);
|
||||
|
||||
if (auto storage_replicated = dynamic_cast<StorageReplicatedMergeTree *>(table.get()))
|
||||
|
@ -364,6 +410,7 @@ void InterpreterSystemQuery::flushDistributed(ASTSystemQuery & query)
|
|||
{
|
||||
String database_name = !query.database.empty() ? query.database : context.getCurrentDatabase();
|
||||
String & table_name = query.table;
|
||||
context.checkAccess(AccessType::FLUSH_DISTRIBUTED, database_name, table_name);
|
||||
|
||||
if (auto storage_distributed = dynamic_cast<StorageDistributed *>(context.getTable(database_name, table_name).get()))
|
||||
storage_distributed->flushClusterNodesAllData();
|
||||
|
@ -372,4 +419,135 @@ void InterpreterSystemQuery::flushDistributed(ASTSystemQuery & query)
|
|||
}
|
||||
|
||||
|
||||
AccessRightsElements InterpreterSystemQuery::getRequiredAccessForDDLOnCluster() const
|
||||
{
|
||||
const auto & query = query_ptr->as<const ASTSystemQuery &>();
|
||||
using Type = ASTSystemQuery::Type;
|
||||
AccessRightsElements required_access;
|
||||
switch (query.type)
|
||||
{
|
||||
case Type::SHUTDOWN: [[fallthrough]];
|
||||
case Type::KILL:
|
||||
{
|
||||
required_access.emplace_back(AccessType::SHUTDOWN);
|
||||
break;
|
||||
}
|
||||
case Type::DROP_DNS_CACHE: [[fallthrough]];
|
||||
case Type::DROP_MARK_CACHE: [[fallthrough]];
|
||||
#if USE_EMBEDDED_COMPILER
|
||||
case Type::DROP_COMPILED_EXPRESSION_CACHE: [[fallthrough]];
|
||||
#endif
|
||||
case Type::DROP_UNCOMPRESSED_CACHE:
|
||||
{
|
||||
required_access.emplace_back(AccessType::DROP_CACHE);
|
||||
break;
|
||||
}
|
||||
case Type::RELOAD_DICTIONARY: [[fallthrough]];
|
||||
case Type::RELOAD_DICTIONARIES: [[fallthrough]];
|
||||
case Type::RELOAD_EMBEDDED_DICTIONARIES:
|
||||
{
|
||||
required_access.emplace_back(AccessType::RELOAD_DICTIONARY);
|
||||
break;
|
||||
}
|
||||
case Type::RELOAD_CONFIG:
|
||||
{
|
||||
required_access.emplace_back(AccessType::RELOAD_CONFIG);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_MERGES: [[fallthrough]];
|
||||
case Type::START_MERGES:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_MERGES);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_MERGES, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_TTL_MERGES: [[fallthrough]];
|
||||
case Type::START_TTL_MERGES:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_TTL_MERGES);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_TTL_MERGES, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_MOVES: [[fallthrough]];
|
||||
case Type::START_MOVES:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_MOVES);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_MOVES, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_FETCHES: [[fallthrough]];
|
||||
case Type::START_FETCHES:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_FETCHES);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_FETCHES, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_DISTRIBUTED_SENDS: [[fallthrough]];
|
||||
case Type::START_DISTRIBUTED_SENDS:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_DISTRIBUTED_SENDS);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_DISTRIBUTED_SENDS, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_REPLICATED_SENDS: [[fallthrough]];
|
||||
case Type::START_REPLICATED_SENDS:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_REPLICATED_SENDS);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_REPLICATED_SENDS, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_REPLICATION_QUEUES: [[fallthrough]];
|
||||
case Type::START_REPLICATION_QUEUES:
|
||||
{
|
||||
if (query.table.empty())
|
||||
required_access.emplace_back(AccessType::STOP_REPLICATION_QUEUES);
|
||||
else
|
||||
required_access.emplace_back(AccessType::STOP_REPLICATION_QUEUES, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::SYNC_REPLICA:
|
||||
{
|
||||
required_access.emplace_back(AccessType::SYNC_REPLICA, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::RESTART_REPLICA:
|
||||
{
|
||||
required_access.emplace_back(AccessType::RESTART_REPLICA, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::RESTART_REPLICAS:
|
||||
{
|
||||
required_access.emplace_back(AccessType::RESTART_REPLICA);
|
||||
break;
|
||||
}
|
||||
case Type::FLUSH_DISTRIBUTED:
|
||||
{
|
||||
required_access.emplace_back(AccessType::FLUSH_DISTRIBUTED, query.database, query.table);
|
||||
break;
|
||||
}
|
||||
case Type::FLUSH_LOGS:
|
||||
{
|
||||
required_access.emplace_back(AccessType::FLUSH_LOGS);
|
||||
break;
|
||||
}
|
||||
case Type::STOP_LISTEN_QUERIES: break;
|
||||
case Type::START_LISTEN_QUERIES: break;
|
||||
case Type::UNKNOWN: break;
|
||||
case Type::END: break;
|
||||
}
|
||||
return required_access;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace DB
|
|||
{
|
||||
|
||||
class Context;
|
||||
class AccessRightsElements;
|
||||
class ASTSystemQuery;
|
||||
|
||||
class InterpreterSystemQuery : public IInterpreter
|
||||
|
@ -35,6 +36,8 @@ private:
|
|||
void restartReplicas(Context & system_context);
|
||||
void syncReplica(ASTSystemQuery & query);
|
||||
void flushDistributed(ASTSystemQuery & query);
|
||||
|
||||
AccessRightsElements getRequiredAccessForDDLOnCluster() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace DB
|
|||
BlockIO InterpreterUseQuery::execute()
|
||||
{
|
||||
const String & new_database = query_ptr->as<ASTUseQuery &>().database;
|
||||
context.checkAccess(AccessType::EXISTS, new_database);
|
||||
context.getSessionContext().setCurrentDatabase(new_database);
|
||||
return {};
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ BlockIO InterpreterWatchQuery::execute()
|
|||
|
||||
/// List of columns to read to execute the query.
|
||||
Names required_columns = storage->getColumns().getNamesOfPhysical();
|
||||
context.checkAccess(AccessType::SELECT, database, table, required_columns);
|
||||
|
||||
/// Get context settings for this query
|
||||
const Settings & settings = context.getSettingsRef();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <Common/Exception.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <Interpreters/Users.h>
|
||||
#include <Dictionaries/IDictionary.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <Poco/MD5Engine.h>
|
||||
|
||||
|
@ -113,6 +114,15 @@ User::User(const String & name_, const String & config_elem, const Poco::Util::A
|
|||
access.grant(AccessFlags::databaseLevel(), "system"); /// Anyone has access to the "system" database.
|
||||
}
|
||||
|
||||
if (dictionaries)
|
||||
{
|
||||
access.fullRevoke(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
|
||||
for (const String & dictionary : *dictionaries)
|
||||
access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG, dictionary);
|
||||
}
|
||||
else if (databases)
|
||||
access.grant(AccessType::dictGet, IDictionary::NO_DATABASE_TAG);
|
||||
|
||||
if (config.has(config_elem + ".allow_quota_management"))
|
||||
is_quota_management_allowed = config.getBool(config_elem + ".allow_quota_management");
|
||||
if (config.has(config_elem + ".allow_row_policy_management"))
|
||||
|
|
|
@ -93,6 +93,9 @@ public:
|
|||
/// Returns true if the storage receives data from a remote server or servers.
|
||||
virtual bool isRemote() const { return false; }
|
||||
|
||||
/// Returns true if the storage is a view of a table or another view.
|
||||
virtual bool isView() const { return false; }
|
||||
|
||||
/// Returns true if the storage supports queries with the SAMPLE section.
|
||||
virtual bool supportsSampling() const { return false; }
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ friend class LiveViewBlockOutputStream;
|
|||
public:
|
||||
~StorageLiveView() override;
|
||||
String getName() const override { return "LiveView"; }
|
||||
bool isView() const override { return true; }
|
||||
StorageID getSelectTableID() const { return select_table_id; }
|
||||
StorageID getBlocksStorageID() const
|
||||
{
|
||||
|
|
|
@ -17,6 +17,7 @@ class StorageMaterializedView : public ext::shared_ptr_helper<StorageMaterialize
|
|||
friend struct ext::shared_ptr_helper<StorageMaterializedView>;
|
||||
public:
|
||||
std::string getName() const override { return "MaterializedView"; }
|
||||
bool isView() const override { return true; }
|
||||
|
||||
ASTPtr getSelectQuery() const { return select->clone(); }
|
||||
ASTPtr getInnerQuery() const { return inner_query->clone(); }
|
||||
|
|
|
@ -15,6 +15,7 @@ class StorageView : public ext::shared_ptr_helper<StorageView>, public IStorage
|
|||
friend struct ext::shared_ptr_helper<StorageView>;
|
||||
public:
|
||||
std::string getName() const override { return "View"; }
|
||||
bool isView() const override { return true; }
|
||||
|
||||
/// It is passed inside the query and solved at its level.
|
||||
bool supportsSampling() const override { return true; }
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <Processors/Sources/NullSource.h>
|
||||
|
||||
|
@ -62,11 +63,12 @@ public:
|
|||
ColumnPtr databases_,
|
||||
ColumnPtr tables_,
|
||||
Storages storages_,
|
||||
const std::shared_ptr<const AccessRightsContext> & access_rights_,
|
||||
String query_id_)
|
||||
: SourceWithProgress(header_)
|
||||
, columns_mask(std::move(columns_mask_)), max_block_size(max_block_size_)
|
||||
, databases(std::move(databases_)), tables(std::move(tables_)), storages(std::move(storages_))
|
||||
, query_id(std::move(query_id_)), total_tables(tables->size())
|
||||
, query_id(std::move(query_id_)), total_tables(tables->size()), access_rights(access_rights_)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,8 @@ protected:
|
|||
MutableColumns res_columns = getPort().getHeader().cloneEmptyColumns();
|
||||
size_t rows_count = 0;
|
||||
|
||||
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
while (rows_count < max_block_size && db_table_num < total_tables)
|
||||
{
|
||||
const std::string database_name = (*databases)[db_table_num].get<std::string>();
|
||||
|
@ -125,11 +129,16 @@ protected:
|
|||
column_sizes = storage->getColumnSizes();
|
||||
}
|
||||
|
||||
bool check_access_for_columns = check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name);
|
||||
|
||||
for (const auto & column : columns)
|
||||
{
|
||||
if (column.is_virtual)
|
||||
continue;
|
||||
|
||||
if (check_access_for_columns && !access_rights->isGranted(AccessType::SHOW, database_name, table_name, column.name))
|
||||
continue;
|
||||
|
||||
size_t src_index = 0;
|
||||
size_t res_index = 0;
|
||||
|
||||
|
@ -222,6 +231,7 @@ private:
|
|||
String query_id;
|
||||
size_t db_table_num = 0;
|
||||
size_t total_tables;
|
||||
std::shared_ptr<const AccessRightsContext> access_rights;
|
||||
};
|
||||
|
||||
|
||||
|
@ -266,8 +276,7 @@ Pipes StorageSystemColumns::readWithProcessors(
|
|||
/// We are skipping "Lazy" database because we cannot afford initialization of all its tables.
|
||||
/// This should be documented.
|
||||
|
||||
if (context.hasDatabaseAccessRights(database.first)
|
||||
&& database.second->getEngineName() != "Lazy")
|
||||
if (database.second->getEngineName() != "Lazy")
|
||||
database_column_mut->insert(database.first);
|
||||
}
|
||||
|
||||
|
@ -324,7 +333,7 @@ Pipes StorageSystemColumns::readWithProcessors(
|
|||
pipes.emplace_back(std::make_shared<ColumnsSource>(
|
||||
std::move(columns_mask), std::move(header), max_block_size,
|
||||
std::move(filtered_database_column), std::move(filtered_table_column), std::move(storages),
|
||||
context.getCurrentQueryId()));
|
||||
context.getAccessRights(), context.getCurrentQueryId()));
|
||||
|
||||
return pipes;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <Databases/IDatabase.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Storages/System/StorageSystemDatabases.h>
|
||||
|
||||
|
||||
|
@ -19,17 +20,20 @@ NamesAndTypesList StorageSystemDatabases::getNamesAndTypes()
|
|||
|
||||
void StorageSystemDatabases::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const
|
||||
{
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
auto databases = context.getDatabases();
|
||||
for (const auto & database : databases)
|
||||
{
|
||||
if (context.hasDatabaseAccessRights(database.first))
|
||||
{
|
||||
res_columns[0]->insert(database.first);
|
||||
res_columns[1]->insert(database.second->getEngineName());
|
||||
res_columns[2]->insert(context.getPath() + database.second->getDataPath());
|
||||
res_columns[3]->insert(database.second->getMetadataPath());
|
||||
}
|
||||
}
|
||||
if (check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, database.first))
|
||||
continue;
|
||||
|
||||
res_columns[0]->insert(database.first);
|
||||
res_columns[1]->insert(database.second->getEngineName());
|
||||
res_columns[2]->insert(context.getPath() + database.second->getDataPath());
|
||||
res_columns[3]->insert(database.second->getMetadataPath());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <Dictionaries/DictionaryStructure.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/ExternalDictionariesLoader.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Storages/System/StorageSystemDictionaries.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
|
@ -47,6 +48,9 @@ NamesAndTypesList StorageSystemDictionaries::getNamesAndTypes()
|
|||
|
||||
void StorageSystemDictionaries::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & /*query_info*/) const
|
||||
{
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_dictionaries = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
const auto & external_dictionaries = context.getExternalDictionariesLoader();
|
||||
for (const auto & load_result : external_dictionaries.getCurrentLoadResults())
|
||||
{
|
||||
|
@ -68,6 +72,10 @@ void StorageSystemDictionaries::fillData(MutableColumns & res_columns, const Con
|
|||
}
|
||||
}
|
||||
|
||||
if (check_access_for_dictionaries
|
||||
&& !access_rights->isGranted(AccessType::SHOW, database.empty() ? IDictionary::NO_DATABASE_TAG : database, short_name))
|
||||
continue;
|
||||
|
||||
size_t i = 0;
|
||||
res_columns[i++]->insert(database);
|
||||
res_columns[i++]->insert(short_name);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <Interpreters/Context.h>
|
||||
#include <Storages/MergeTree/MergeList.h>
|
||||
#include <Storages/System/StorageSystemMerges.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
|
@ -35,32 +36,35 @@ NamesAndTypesList StorageSystemMerges::getNamesAndTypes()
|
|||
|
||||
void StorageSystemMerges::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo &) const
|
||||
{
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
for (const auto & merge : context.getMergeList().get())
|
||||
{
|
||||
if (context.hasDatabaseAccessRights(merge.database))
|
||||
{
|
||||
size_t i = 0;
|
||||
res_columns[i++]->insert(merge.database);
|
||||
res_columns[i++]->insert(merge.table);
|
||||
res_columns[i++]->insert(merge.elapsed);
|
||||
res_columns[i++]->insert(merge.progress);
|
||||
res_columns[i++]->insert(merge.num_parts);
|
||||
res_columns[i++]->insert(merge.source_part_names);
|
||||
res_columns[i++]->insert(merge.result_part_name);
|
||||
res_columns[i++]->insert(merge.source_part_paths);
|
||||
res_columns[i++]->insert(merge.result_part_path);
|
||||
res_columns[i++]->insert(merge.partition_id);
|
||||
res_columns[i++]->insert(merge.is_mutation);
|
||||
res_columns[i++]->insert(merge.total_size_bytes_compressed);
|
||||
res_columns[i++]->insert(merge.total_size_marks);
|
||||
res_columns[i++]->insert(merge.bytes_read_uncompressed);
|
||||
res_columns[i++]->insert(merge.rows_read);
|
||||
res_columns[i++]->insert(merge.bytes_written_uncompressed);
|
||||
res_columns[i++]->insert(merge.rows_written);
|
||||
res_columns[i++]->insert(merge.columns_written);
|
||||
res_columns[i++]->insert(merge.memory_usage);
|
||||
res_columns[i++]->insert(merge.thread_number);
|
||||
}
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, merge.database, merge.table))
|
||||
continue;
|
||||
|
||||
size_t i = 0;
|
||||
res_columns[i++]->insert(merge.database);
|
||||
res_columns[i++]->insert(merge.table);
|
||||
res_columns[i++]->insert(merge.elapsed);
|
||||
res_columns[i++]->insert(merge.progress);
|
||||
res_columns[i++]->insert(merge.num_parts);
|
||||
res_columns[i++]->insert(merge.source_part_names);
|
||||
res_columns[i++]->insert(merge.result_part_name);
|
||||
res_columns[i++]->insert(merge.source_part_paths);
|
||||
res_columns[i++]->insert(merge.result_part_path);
|
||||
res_columns[i++]->insert(merge.partition_id);
|
||||
res_columns[i++]->insert(merge.is_mutation);
|
||||
res_columns[i++]->insert(merge.total_size_bytes_compressed);
|
||||
res_columns[i++]->insert(merge.total_size_marks);
|
||||
res_columns[i++]->insert(merge.bytes_read_uncompressed);
|
||||
res_columns[i++]->insert(merge.rows_read);
|
||||
res_columns[i++]->insert(merge.bytes_written_uncompressed);
|
||||
res_columns[i++]->insert(merge.rows_written);
|
||||
res_columns[i++]->insert(merge.columns_written);
|
||||
res_columns[i++]->insert(merge.memory_usage);
|
||||
res_columns[i++]->insert(merge.thread_number);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
#include <Storages/MergeTree/MergeTreeMutationStatus.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
|
||||
|
||||
|
@ -36,6 +37,9 @@ NamesAndTypesList StorageSystemMutations::getNamesAndTypes()
|
|||
|
||||
void StorageSystemMutations::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const
|
||||
{
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
/// Collect a set of *MergeTree tables.
|
||||
std::map<String, std::map<String, StoragePtr>> merge_tree_tables;
|
||||
for (const auto & db : context.getDatabases())
|
||||
|
@ -43,10 +47,19 @@ void StorageSystemMutations::fillData(MutableColumns & res_columns, const Contex
|
|||
/// Lazy database can not contain MergeTree tables
|
||||
if (db.second->getEngineName() == "Lazy")
|
||||
continue;
|
||||
if (context.hasDatabaseAccessRights(db.first))
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
if (dynamic_cast<const MergeTreeData *>(iterator->table().get()))
|
||||
merge_tree_tables[db.first][iterator->name()] = iterator->table();
|
||||
|
||||
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
|
||||
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
{
|
||||
if (!dynamic_cast<const MergeTreeData *>(iterator->table().get()))
|
||||
continue;
|
||||
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
|
||||
continue;
|
||||
|
||||
merge_tree_tables[db.first][iterator->name()] = iterator->table();
|
||||
}
|
||||
}
|
||||
|
||||
MutableColumnPtr col_database_mut = ColumnString::create();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <DataStreams/OneBlockInputStream.h>
|
||||
#include <Storages/MergeTree/MergeTreeData.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
|
@ -71,6 +72,9 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
|
|||
MutableColumnPtr engine_column_mut = ColumnString::create();
|
||||
MutableColumnPtr active_column_mut = ColumnUInt8::create();
|
||||
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_tables = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
{
|
||||
Databases databases = context.getDatabases();
|
||||
|
||||
|
@ -80,7 +84,7 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
|
|||
{
|
||||
/// Lazy database can not contain MergeTree tables
|
||||
/// and it's unnecessary to load all tables of Lazy database just to filter all of them.
|
||||
if (context.hasDatabaseAccessRights(database.first) && database.second->getEngineName() != "Lazy")
|
||||
if (database.second->getEngineName() != "Lazy")
|
||||
database_column_mut->insert(database.first);
|
||||
}
|
||||
block_to_filter.insert(ColumnWithTypeAndName(
|
||||
|
@ -114,6 +118,9 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const
|
|||
if (!dynamic_cast<MergeTreeData *>(storage.get()))
|
||||
continue;
|
||||
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name))
|
||||
continue;
|
||||
|
||||
storages[std::make_pair(database_name, iterator->name())] = storage;
|
||||
|
||||
/// Add all combinations of flag 'active'.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <Storages/System/StorageSystemReplicas.h>
|
||||
#include <Storages/StorageReplicatedMergeTree.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <Processors/Sources/SourceFromSingleChunk.h>
|
||||
|
@ -63,6 +64,9 @@ Pipes StorageSystemReplicas::readWithProcessors(
|
|||
{
|
||||
check(column_names);
|
||||
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
/// We collect a set of replicated tables.
|
||||
std::map<String, std::map<String, StoragePtr>> replicated_tables;
|
||||
for (const auto & db : context.getDatabases())
|
||||
|
@ -70,11 +74,14 @@ Pipes StorageSystemReplicas::readWithProcessors(
|
|||
/// Lazy database can not contain replicated tables
|
||||
if (db.second->getEngineName() == "Lazy")
|
||||
continue;
|
||||
if (context.hasDatabaseAccessRights(db.first))
|
||||
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
{
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
if (dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
|
||||
replicated_tables[db.first][iterator->name()] = iterator->table();
|
||||
if (!dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
|
||||
continue;
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
|
||||
continue;
|
||||
replicated_tables[db.first][iterator->name()] = iterator->table();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <Storages/System/StorageSystemReplicationQueue.h>
|
||||
#include <Storages/StorageReplicatedMergeTree.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
|
||||
|
@ -48,6 +49,9 @@ NamesAndTypesList StorageSystemReplicationQueue::getNamesAndTypes()
|
|||
|
||||
void StorageSystemReplicationQueue::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const
|
||||
{
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
std::map<String, std::map<String, StoragePtr>> replicated_tables;
|
||||
for (const auto & db : context.getDatabases())
|
||||
{
|
||||
|
@ -55,11 +59,15 @@ void StorageSystemReplicationQueue::fillData(MutableColumns & res_columns, const
|
|||
if (db.second->getEngineName() == "Lazy")
|
||||
continue;
|
||||
|
||||
if (context.hasDatabaseAccessRights(db.first))
|
||||
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, db.first);
|
||||
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
{
|
||||
for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next())
|
||||
if (dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
|
||||
replicated_tables[db.first][iterator->name()] = iterator->table();
|
||||
if (!dynamic_cast<const StorageReplicatedMergeTree *>(iterator->table().get()))
|
||||
continue;
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, db.first, iterator->name()))
|
||||
continue;
|
||||
replicated_tables[db.first][iterator->name()] = iterator->table();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <Storages/System/StorageSystemTables.h>
|
||||
#include <Storages/VirtualColumnUtils.h>
|
||||
#include <Databases/IDatabase.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/queryToString.h>
|
||||
|
@ -105,6 +106,9 @@ protected:
|
|||
|
||||
MutableColumns res_columns = getPort().getHeader().cloneEmptyColumns();
|
||||
|
||||
const auto access_rights = context.getAccessRights();
|
||||
const bool check_access_for_databases = !access_rights->isGranted(AccessType::SHOW);
|
||||
|
||||
size_t rows_count = 0;
|
||||
while (rows_count < max_block_size)
|
||||
{
|
||||
|
@ -116,7 +120,7 @@ protected:
|
|||
database_name = databases->getDataAt(database_idx).toString();
|
||||
database = context.tryGetDatabase(database_name);
|
||||
|
||||
if (!database || !context.hasDatabaseAccessRights(database_name))
|
||||
if (!database)
|
||||
{
|
||||
/// Database was deleted just now or the user has no access.
|
||||
++database_idx;
|
||||
|
@ -193,6 +197,8 @@ protected:
|
|||
return Chunk(std::move(res_columns), num_rows);
|
||||
}
|
||||
|
||||
const bool check_access_for_tables = check_access_for_databases && !access_rights->isGranted(AccessType::SHOW, database_name);
|
||||
|
||||
if (!tables_it || !tables_it->isValid())
|
||||
tables_it = database->getTablesWithDictionaryTablesIterator(context);
|
||||
|
||||
|
@ -201,8 +207,10 @@ protected:
|
|||
for (; rows_count < max_block_size && tables_it->isValid(); tables_it->next())
|
||||
{
|
||||
auto table_name = tables_it->name();
|
||||
StoragePtr table = nullptr;
|
||||
if (check_access_for_tables && !access_rights->isGranted(AccessType::SHOW, database_name, table_name))
|
||||
continue;
|
||||
|
||||
StoragePtr table = nullptr;
|
||||
TableStructureReadLockHolder lock;
|
||||
|
||||
try
|
||||
|
|
|
@ -62,6 +62,8 @@ StoragePtr ITableFunctionFileLike::executeImpl(const ASTPtr & ast_function, cons
|
|||
if (args.size() == 4)
|
||||
compression_method = args[3]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
context.checkAccess(getRequiredAccessType());
|
||||
|
||||
/// Create table
|
||||
StoragePtr storage = getStorage(filename, format, columns, const_cast<Context &>(context), table_name, compression_method);
|
||||
|
||||
|
|
|
@ -17,5 +17,6 @@ private:
|
|||
StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context, const std::string & table_name) const override;
|
||||
virtual StoragePtr getStorage(
|
||||
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const = 0;
|
||||
virtual AccessType getRequiredAccessType() const = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -60,6 +60,8 @@ StoragePtr ITableFunctionXDBC::executeImpl(const ASTPtr & ast_function, const Co
|
|||
remote_table_name = args[1]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
}
|
||||
|
||||
context.checkAccess(getRequiredAccessType());
|
||||
|
||||
/* Infer external table structure */
|
||||
/// Have to const_cast, because bridges store their commands inside context
|
||||
BridgeHelperPtr helper = createBridgeHelper(const_cast<Context &>(context), context.getSettingsRef().http_receive_timeout.value, connection_string);
|
||||
|
|
|
@ -21,6 +21,8 @@ private:
|
|||
virtual BridgeHelperPtr createBridgeHelper(Context & context,
|
||||
const Poco::Timespan & http_timeout_,
|
||||
const std::string & connection_string_) const = 0;
|
||||
|
||||
virtual AccessType getRequiredAccessType() const = 0;
|
||||
};
|
||||
|
||||
class TableFunctionJDBC : public ITableFunctionXDBC
|
||||
|
@ -39,6 +41,8 @@ private:
|
|||
{
|
||||
return std::make_shared<XDBCBridgeHelper<JDBCBridgeMixin>>(context, http_timeout_, connection_string_);
|
||||
}
|
||||
|
||||
AccessType getRequiredAccessType() const override { return AccessType::jdbc; }
|
||||
};
|
||||
|
||||
class TableFunctionODBC : public ITableFunctionXDBC
|
||||
|
@ -57,5 +61,7 @@ private:
|
|||
{
|
||||
return std::make_shared<XDBCBridgeHelper<ODBCBridgeMixin>>(context, http_timeout_, connection_string_);
|
||||
}
|
||||
|
||||
AccessType getRequiredAccessType() const override { return AccessType::odbc; }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -32,9 +32,6 @@ TableFunctionPtr TableFunctionFactory::get(
|
|||
const std::string & name,
|
||||
const Context & context) const
|
||||
{
|
||||
if (context.getSettings().readonly == 1) /** For example, for readonly = 2 - allowed. */
|
||||
throw Exception("Table functions are forbidden in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
auto res = tryGet(name, context);
|
||||
if (!res)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,11 @@ StoragePtr TableFunctionFile::getStorage(
|
|||
return StorageFile::create(source, global_context.getUserFilesPath(), args);
|
||||
}
|
||||
|
||||
AccessType TableFunctionFile::getRequiredAccessType() const
|
||||
{
|
||||
return AccessType::file;
|
||||
}
|
||||
|
||||
void registerTableFunctionFile(TableFunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<TableFunctionFile>();
|
||||
|
|
|
@ -24,5 +24,6 @@ public:
|
|||
private:
|
||||
StoragePtr getStorage(
|
||||
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const std::string & compression_method) const override;
|
||||
AccessType getRequiredAccessType() const override;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ StoragePtr TableFunctionHDFS::getStorage(
|
|||
compression_method);
|
||||
}
|
||||
|
||||
AccessType TableFunctionHDFS::getRequiredAccessType() const
|
||||
{
|
||||
return AccessType::hdfs;
|
||||
}
|
||||
|
||||
#if USE_HDFS
|
||||
void registerTableFunctionHDFS(TableFunctionFactory & factory)
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ public:
|
|||
private:
|
||||
StoragePtr getStorage(
|
||||
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const override;
|
||||
AccessType getRequiredAccessType() const override;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ StoragePtr TableFunctionInput::executeImpl(const ASTPtr & ast_function, const Co
|
|||
throw Exception("Table function '" + getName() + "' requires exactly 1 argument: structure",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
context.checkAccess(AccessType::input);
|
||||
|
||||
String structure = evaluateConstantExpressionOrIdentifierAsLiteral(args[0], context)->as<ASTLiteral &>().value.safeGet<String>();
|
||||
auto columns = parseColumnsListFromString(structure, context);
|
||||
StoragePtr storage = StorageInput::create(table_name, columns);
|
||||
|
|
|
@ -67,6 +67,8 @@ StoragePtr TableFunctionMerge::executeImpl(const ASTPtr & ast_function, const Co
|
|||
String source_database = args[0]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
String table_name_regexp = args[1]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
context.checkAccess(AccessType::merge, source_database);
|
||||
|
||||
auto res = StorageMerge::create(
|
||||
StorageID(getDatabaseName(), table_name),
|
||||
ColumnsDescription{chooseColumns(source_database, table_name_regexp, context)},
|
||||
|
|
|
@ -55,6 +55,8 @@ StoragePtr TableFunctionMySQL::executeImpl(const ASTPtr & ast_function, const Co
|
|||
std::string user_name = args[3]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
std::string password = args[4]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
context.checkAccess(AccessType::mysql);
|
||||
|
||||
bool replace_query = false;
|
||||
std::string on_duplicate_clause;
|
||||
if (args.size() >= 6)
|
||||
|
|
|
@ -31,6 +31,8 @@ StoragePtr TableFunctionNumbers<multithreaded>::executeImpl(const ASTPtr & ast_f
|
|||
UInt64 offset = arguments.size() == 2 ? evaluateArgument(context, arguments[0]) : 0;
|
||||
UInt64 length = arguments.size() == 2 ? evaluateArgument(context, arguments[1]) : evaluateArgument(context, arguments[0]);
|
||||
|
||||
context.checkAccess(AccessType::numbers);
|
||||
|
||||
auto res = StorageSystemNumbers::create(table_name, multithreaded, length, offset, false);
|
||||
res->startup();
|
||||
return res;
|
||||
|
|
|
@ -132,6 +132,8 @@ StoragePtr TableFunctionRemote::executeImpl(const ASTPtr & ast_function, const C
|
|||
if (arg_num < args.size())
|
||||
throw Exception(help_message, ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
context.checkAccess(AccessType::remote);
|
||||
|
||||
/// ExpressionAnalyzer will be created in InterpreterSelectQuery that will meet these `Identifier` when processing the request.
|
||||
/// We need to mark them as the name of the database or table, because the default value is column.
|
||||
for (auto ast : args)
|
||||
|
|
|
@ -61,6 +61,8 @@ StoragePtr TableFunctionS3::executeImpl(const ASTPtr & ast_function, const Conte
|
|||
else
|
||||
compression_method = "auto";
|
||||
|
||||
context.checkAccess(AccessType::s3);
|
||||
|
||||
ColumnsDescription columns = parseColumnsListFromString(structure, context);
|
||||
|
||||
/// Create table
|
||||
|
|
|
@ -15,6 +15,11 @@ StoragePtr TableFunctionURL::getStorage(
|
|||
return StorageURL::create(uri, StorageID(getDatabaseName(), table_name), format, columns, ConstraintsDescription{}, global_context, compression_method);
|
||||
}
|
||||
|
||||
AccessType TableFunctionURL::getRequiredAccessType() const
|
||||
{
|
||||
return AccessType::url;
|
||||
}
|
||||
|
||||
void registerTableFunctionURL(TableFunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<TableFunctionURL>();
|
||||
|
|
|
@ -20,5 +20,6 @@ public:
|
|||
private:
|
||||
StoragePtr getStorage(
|
||||
const String & source, const String & format, const ColumnsDescription & columns, Context & global_context, const std::string & table_name, const String & compression_method) const override;
|
||||
AccessType getRequiredAccessType() const override;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@ StoragePtr TableFunctionValues::executeImpl(const ASTPtr & ast_function, const C
|
|||
throw Exception("Table function '" + getName() + "' requires 2 or more arguments: structure and values.",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
context.checkAccess(AccessType::values);
|
||||
|
||||
/// Parsing first argument as table structure and creating a sample block
|
||||
std::string structure = args[0]->as<ASTLiteral &>().value.safeGet<String>();
|
||||
|
||||
|
|
Loading…
Reference in New Issue