diff --git a/Catalog/DdlCommandExecutor.cpp b/Catalog/DdlCommandExecutor.cpp index a70125bae1..18010d0ce3 100644 --- a/Catalog/DdlCommandExecutor.cpp +++ b/Catalog/DdlCommandExecutor.cpp @@ -387,6 +387,31 @@ ExecutionResult DdlCommandExecutor::execute() { auto rename_user_stmt = Parser::RenameUserStmt(extractPayload(*ddl_data_)); rename_user_stmt.execute(*session_ptr_); return result; + } else if (ddl_command_ == "CREATE_ROLE") { + auto create_role_stmt = Parser::CreateRoleStmt(extractPayload(*ddl_data_)); + create_role_stmt.execute(*session_ptr_); + return result; + } else if (ddl_command_ == "DROP_ROLE") { + auto drop_role_stmt = Parser::DropRoleStmt(extractPayload(*ddl_data_)); + drop_role_stmt.execute(*session_ptr_); + return result; + } else if (ddl_command_ == "GRANT_ROLE") { + auto grant_role_stmt = Parser::GrantRoleStmt(extractPayload(*ddl_data_)); + grant_role_stmt.execute(*session_ptr_); + return result; + } else if (ddl_command_ == "REVOKE_ROLE") { + auto revoke_role_stmt = Parser::RevokeRoleStmt(extractPayload(*ddl_data_)); + revoke_role_stmt.execute(*session_ptr_); + return result; + } else if (ddl_command_ == "GRANT_PRIVILEGE") { + auto grant_privilege_stmt = Parser::GrantPrivilegesStmt(extractPayload(*ddl_data_)); + grant_privilege_stmt.execute(*session_ptr_); + return result; + } else if (ddl_command_ == "REVOKE_PRIVILEGE") { + auto revoke_privileges_stmt = + Parser::RevokePrivilegesStmt(extractPayload(*ddl_data_)); + revoke_privileges_stmt.execute(*session_ptr_); + return result; } // the following commands require a global unique lock until proper table locking has @@ -447,11 +472,30 @@ DistributedExecutionDetails DdlCommandExecutor::getDistributedExecutionDetails() ddl_command_ == "CREATE_TABLE" || ddl_command_ == "DROP_TABLE" || ddl_command_ == "RENAME_TABLE" || ddl_command_ == "ALTER_TABLE" || ddl_command_ == "CREATE_DB" || ddl_command_ == "DROP_DB" || - ddl_command_ == "RENAME_DB" || ddl_command_ == "CREATE_USER" || - ddl_command_ == "DROP_USER" || ddl_command_ == "ALTER_USER" || - ddl_command_ == "RENAME_USER") { + ddl_command_ == "RENAME_DB") { + // commands execution_details.execution_location = ExecutionLocation::ALL_NODES; execution_details.aggregation_type = AggregationType::NONE; + } else if (ddl_command_ == "CREATE_USER" || ddl_command_ == "DROP_USER" || + ddl_command_ == "ALTER_USER" || ddl_command_ == "RENAME_USER" || + ddl_command_ == "CREATE_ROLE" || ddl_command_ == "DROP_ROLE" || + ddl_command_ == "GRANT_ROLE" || ddl_command_ == "REVOKE_ROLE") { + // group user/role/db commands + execution_details.execution_location = ExecutionLocation::ALL_NODES; + execution_details.aggregation_type = AggregationType::NONE; + } else if (ddl_command_ == "GRANT_PRIVILEGE" || ddl_command_ == "REVOKE_PRIVILEGE") { + auto& ddl_payload = extractPayload(*ddl_data_); + CHECK(ddl_payload.HasMember("type")); + const std::string& targetType = ddl_payload["type"].GetString(); + if (targetType == "DASHBOARD") { + // dashboard commands should run on Aggregator alone + execution_details.execution_location = ExecutionLocation::AGGREGATOR_ONLY; + execution_details.aggregation_type = AggregationType::NONE; + } else { + execution_details.execution_location = ExecutionLocation::ALL_NODES; + execution_details.aggregation_type = AggregationType::NONE; + } + } else if (ddl_command_ == "SHOW_TABLE_DETAILS") { execution_details.execution_location = ExecutionLocation::LEAVES_ONLY; execution_details.aggregation_type = AggregationType::UNION; diff --git a/Parser/ParserNode.cpp b/Parser/ParserNode.cpp index 52297b6832..aa66791f8a 100644 --- a/Parser/ParserNode.cpp +++ b/Parser/ParserNode.cpp @@ -4861,6 +4861,11 @@ void CopyTableStmt::execute(const Catalog_Namespace::SessionInfo& session, } // namespace Parser // CREATE ROLE payroll_dept_role; +CreateRoleStmt::CreateRoleStmt(const rapidjson::Value& payload) { + CHECK(payload.HasMember("role")); + role_ = std::make_unique(json_str(payload["role"])); +} + void CreateRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { const auto& currentUser = session.get_currentUser(); if (!currentUser.isSuper) { @@ -4872,6 +4877,11 @@ void CreateRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { } // DROP ROLE payroll_dept_role; +DropRoleStmt::DropRoleStmt(const rapidjson::Value& payload) { + CHECK(payload.HasMember("role")); + role_ = std::make_unique(json_str(payload["role"])); +} + void DropRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { const auto& currentUser = session.get_currentUser(); if (!currentUser.isSuper) { @@ -5071,6 +5081,32 @@ static void verifyObject(Catalog_Namespace::Catalog& sessionCatalog, } // GRANT SELECT/INSERT/CREATE ON TABLE payroll_table TO payroll_dept_role; +GrantPrivilegesStmt::GrantPrivilegesStmt(const rapidjson::Value& payload) { + CHECK(payload.HasMember("type")); + type_ = std::make_unique(json_str(payload["type"])); + + CHECK(payload.HasMember("target")); + target_ = std::make_unique(json_str(payload["target"])); + + if (payload.HasMember("privileges")) { + CHECK(payload["privileges"].IsArray()); + for (auto& privilege : payload["privileges"].GetArray()) { + auto r = json_str(privilege); + // privilege was a StringLiteral + // and is wrapped with quotes which need to get removed + boost::algorithm::trim_if(r, boost::is_any_of(" \"'`")); + privileges_.emplace_back(r); + } + } + if (payload.HasMember("grantees")) { + CHECK(payload["grantees"].IsArray()); + for (auto& grantee : payload["grantees"].GetArray()) { + std::string g = json_str(grantee); + grantees_.emplace_back(g); + } + } +} + void GrantPrivilegesStmt::execute(const Catalog_Namespace::SessionInfo& session) { auto& catalog = session.getCatalog(); const auto& currentUser = session.get_currentUser(); @@ -5108,6 +5144,32 @@ void GrantPrivilegesStmt::execute(const Catalog_Namespace::SessionInfo& session) } // REVOKE SELECT/INSERT/CREATE ON TABLE payroll_table FROM payroll_dept_role; +RevokePrivilegesStmt::RevokePrivilegesStmt(const rapidjson::Value& payload) { + CHECK(payload.HasMember("type")); + type_ = std::make_unique(json_str(payload["type"])); + + CHECK(payload.HasMember("target")); + target_ = std::make_unique(json_str(payload["target"])); + + if (payload.HasMember("privileges")) { + CHECK(payload["privileges"].IsArray()); + for (auto& privilege : payload["privileges"].GetArray()) { + auto r = json_str(privilege); + // privilege was a StringLiteral + // and is wrapped with quotes which need to get removed + boost::algorithm::trim_if(r, boost::is_any_of(" \"'`")); + privileges_.emplace_back(r); + } + } + if (payload.HasMember("grantees")) { + CHECK(payload["grantees"].IsArray()); + for (auto& grantee : payload["grantees"].GetArray()) { + std::string g = json_str(grantee); + grantees_.emplace_back(g); + } + } +} + void RevokePrivilegesStmt::execute(const Catalog_Namespace::SessionInfo& session) { auto& catalog = session.getCatalog(); const auto& currentUser = session.get_currentUser(); @@ -5241,6 +5303,23 @@ void ShowPrivilegesStmt::execute(const Catalog_Namespace::SessionInfo& session) } // GRANT payroll_dept_role TO joe; +GrantRoleStmt::GrantRoleStmt(const rapidjson::Value& payload) { + if (payload.HasMember("roles")) { + CHECK(payload["roles"].IsArray()); + for (auto& role : payload["roles"].GetArray()) { + std::string r = json_str(role); + roles_.emplace_back(r); + } + } + if (payload.HasMember("grantees")) { + CHECK(payload["grantees"].IsArray()); + for (auto& grantee : payload["grantees"].GetArray()) { + std::string g = json_str(grantee); + grantees_.emplace_back(g); + } + } +} + void GrantRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { const auto& currentUser = session.get_currentUser(); if (!currentUser.isSuper) { @@ -5256,7 +5335,24 @@ void GrantRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { SysCatalog::instance().grantRoleBatch(get_roles(), get_grantees()); } -// REVOKE payroll_dept_role FROM joe;get_users +// REVOKE payroll_dept_role FROM joe; +RevokeRoleStmt::RevokeRoleStmt(const rapidjson::Value& payload) { + if (payload.HasMember("roles")) { + CHECK(payload["roles"].IsArray()); + for (auto& role : payload["roles"].GetArray()) { + std::string r = json_str(role); + roles_.emplace_back(r); + } + } + if (payload.HasMember("grantees")) { + CHECK(payload["grantees"].IsArray()); + for (auto& grantee : payload["grantees"].GetArray()) { + std::string g = json_str(grantee); + grantees_.emplace_back(g); + } + } +} + void RevokeRoleStmt::execute(const Catalog_Namespace::SessionInfo& session) { const auto& currentUser = session.get_currentUser(); if (!currentUser.isSuper) { @@ -6028,6 +6124,24 @@ void execute_calcite_ddl( } else if (ddl_command == "RENAME_USER") { auto rename_user_stmt = Parser::RenameUserStmt(payload); rename_user_stmt.execute(*session_ptr); + } else if (ddl_command == "CREATE_ROLE") { + auto create_role_stmt = Parser::CreateRoleStmt(payload); + create_role_stmt.execute(*session_ptr); + } else if (ddl_command == "DROP_ROLE") { + auto drop_role_stmt = Parser::DropRoleStmt(payload); + drop_role_stmt.execute(*session_ptr); + } else if (ddl_command == "GRANT_ROLE") { + auto grant_role_stmt = Parser::GrantRoleStmt(payload); + grant_role_stmt.execute(*session_ptr); + } else if (ddl_command == "REVOKE_ROLE") { + auto revoke_role_stmt = Parser::RevokeRoleStmt(payload); + revoke_role_stmt.execute(*session_ptr); + } else if (ddl_command == "GRANT_PRIVILEGE") { + auto grant_privilege_stmt = Parser::GrantPrivilegesStmt(payload); + grant_privilege_stmt.execute(*session_ptr); + } else if (ddl_command == "REVOKE_PRIVILEGE") { + auto revoke_privilege_stmt = Parser::RevokePrivilegesStmt(payload); + revoke_privilege_stmt.execute(*session_ptr); } else { throw std::runtime_error("Unsupported DDL command"); } diff --git a/Parser/ParserNode.h b/Parser/ParserNode.h index 78978be4f3..82ccc00dd8 100644 --- a/Parser/ParserNode.h +++ b/Parser/ParserNode.h @@ -1542,6 +1542,7 @@ class CopyTableStmt : public DDLStmt { class CreateRoleStmt : public DDLStmt { public: CreateRoleStmt(std::string* r) : role_(r) {} + CreateRoleStmt(const rapidjson::Value& payload); const std::string& get_role() const { return *role_; } void execute(const Catalog_Namespace::SessionInfo& session) override; @@ -1556,6 +1557,7 @@ class CreateRoleStmt : public DDLStmt { class DropRoleStmt : public DDLStmt { public: DropRoleStmt(std::string* r) : role_(r) {} + DropRoleStmt(const rapidjson::Value& payload); const std::string& get_role() const { return *role_; } void execute(const Catalog_Namespace::SessionInfo& session) override; @@ -1583,21 +1585,22 @@ class GrantPrivilegesStmt : public DDLStmt { std::string* t, std::string* o, std::list* g) - : object_type_(t), object_(o) { - parser_slistval_to_vector(p, privs_); + : type_(t), target_(o) { + parser_slistval_to_vector(p, privileges_); parser_slistval_to_vector(g, grantees_); } + GrantPrivilegesStmt(const rapidjson::Value& payload); - const std::vector& get_privs() const { return privs_; } - const std::string& get_object_type() const { return *object_type_; } - const std::string& get_object() const { return *object_; } + const std::vector& get_privs() const { return privileges_; } + const std::string& get_object_type() const { return *type_; } + const std::string& get_object() const { return *target_; } const std::vector& get_grantees() const { return grantees_; } void execute(const Catalog_Namespace::SessionInfo& session) override; private: - std::vector privs_; - std::unique_ptr object_type_; - std::unique_ptr object_; + std::vector privileges_; + std::unique_ptr type_; + std::unique_ptr target_; std::vector grantees_; }; @@ -1611,21 +1614,22 @@ class RevokePrivilegesStmt : public DDLStmt { std::string* t, std::string* o, std::list* g) - : object_type_(t), object_(o) { - parser_slistval_to_vector(p, privs_); + : type_(t), target_(o) { + parser_slistval_to_vector(p, privileges_); parser_slistval_to_vector(g, grantees_); } + RevokePrivilegesStmt(const rapidjson::Value& payload); - const std::vector& get_privs() const { return privs_; } - const std::string& get_object_type() const { return *object_type_; } - const std::string& get_object() const { return *object_; } + const std::vector& get_privs() const { return privileges_; } + const std::string& get_object_type() const { return *type_; } + const std::string& get_object() const { return *target_; } const std::vector& get_grantees() const { return grantees_; } void execute(const Catalog_Namespace::SessionInfo& session) override; private: - std::vector privs_; - std::unique_ptr object_type_; - std::unique_ptr object_; + std::vector privileges_; + std::unique_ptr type_; + std::unique_ptr target_; std::vector grantees_; }; @@ -1658,6 +1662,8 @@ class GrantRoleStmt : public DDLStmt { parser_slistval_to_vector(r, roles_); parser_slistval_to_vector(g, grantees_); } + GrantRoleStmt(const rapidjson::Value& payload); + const std::vector& get_roles() const { return roles_; } const std::vector& get_grantees() const { return grantees_; } void execute(const Catalog_Namespace::SessionInfo& session) override; @@ -1677,6 +1683,8 @@ class RevokeRoleStmt : public DDLStmt { parser_slistval_to_vector(r, roles_); parser_slistval_to_vector(g, grantees_); } + RevokeRoleStmt(const rapidjson::Value& payload); + const std::vector& get_roles() const { return roles_; } const std::vector& get_grantees() const { return grantees_; } void execute(const Catalog_Namespace::SessionInfo& session) override; diff --git a/Parser/ParserWrapper.cpp b/Parser/ParserWrapper.cpp index 239764e461..856aea1bcd 100644 --- a/Parser/ParserWrapper.cpp +++ b/Parser/ParserWrapper.cpp @@ -157,7 +157,7 @@ ParserWrapper::ParserWrapper(std::string query_string) { is_legacy_ddl_ = true; } } else { - boost::regex create_regex{R"(CREATE\s+(TABLE|VIEW|DATABASE|USER).*)", + boost::regex create_regex{R"(CREATE\s+(TABLE|ROLE|VIEW|DATABASE|USER).*)", boost::regex::extended | boost::regex::icase}; if (g_enable_calcite_ddl_parser && boost::regex_match(query_string, create_regex)) { @@ -188,9 +188,10 @@ ParserWrapper::ParserWrapper(std::string query_string) { return; } } else if (ddl == "DROP") { - boost::regex drop_regex{R"(DROP\s+(TABLE|VIEW|DATABASE|USER).*)", + boost::regex drop_regex{R"(DROP\s+(TABLE|ROLE|VIEW|DATABASE|USER).*)", boost::regex::extended | boost::regex::icase}; - if (g_enable_calcite_ddl_parser && boost::regex_match(query_string, drop_regex)) { + if (g_enable_calcite_ddl_parser && + (boost::regex_match(query_string, drop_regex))) { is_calcite_ddl_ = true; is_legacy_ddl_ = false; return; @@ -220,6 +221,24 @@ ParserWrapper::ParserWrapper(std::string query_string) { is_legacy_ddl_ = false; return; } + } else if (ddl == "GRANT") { + boost::regex grant_regex{R"(GRANT.*)", + boost::regex::extended | boost::regex::icase}; + if (g_enable_calcite_ddl_parser && + boost::regex_match(query_string, grant_regex)) { + is_calcite_ddl_ = true; + is_legacy_ddl_ = false; + return; + } + } else if (ddl == "REVOKE") { + boost::regex revoke_regex{R"(REVOKE.*)", + boost::regex::extended | boost::regex::icase}; + if (g_enable_calcite_ddl_parser && + boost::regex_match(query_string, revoke_regex)) { + is_calcite_ddl_ = true; + is_legacy_ddl_ = false; + return; + } } // ctas may look like ddl, but is neither legacy_dll nor calcite_ddl if (!is_ctas) { diff --git a/Parser/Parser_wnd_pregen.cpp b/Parser/Parser_wnd_pregen.cpp index 3e07308572..33357d9cd1 100644 --- a/Parser/Parser_wnd_pregen.cpp +++ b/Parser/Parser_wnd_pregen.cpp @@ -1405,7 +1405,7 @@ static const char* const yytname[] = {"$", "restore_table_statement", "create_role_statement", "drop_role_statement", - "grant_privileges_statement", + "grant_privilege_statement", "revoke_privileges_statement", "grant_role_statement", "revoke_role_statement", diff --git a/Parser/parser.y b/Parser/parser.y index fe6d405421..7faa12fcd1 100644 --- a/Parser/parser.y +++ b/Parser/parser.y @@ -141,14 +141,8 @@ sql: /* schema { $$ = $1; } */ | drop_column_statement { $$ = $1; } | alter_table_param_statement { $$ = $1; } | copy_table_statement { $$ = $1; } - | create_role_statement { $$ = $1; } - | drop_role_statement { $$ = $1; } - | grant_privileges_statement { $$ = $1; } - | revoke_privileges_statement { $$ = $1; } - | grant_role_statement { $$ = $1; } | optimize_table_statement { $$ = $1; } | validate_system_statement { $$ = $1; } - | revoke_role_statement { $$ = $1; } | dump_table_statement { $$ = $1; } | restore_table_statement { $$ = $1; } ; @@ -328,43 +322,6 @@ restore_table_statement: } ; -create_role_statement: - CREATE ROLE rolename - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new CreateRoleStmt(($3)->release())); - } - ; -drop_role_statement: - DROP ROLE rolename - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new DropRoleStmt(($3)->release())); - } - ; -grant_privileges_statement: - GRANT privileges ON privileges_target_type privileges_target TO grantees - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new GrantPrivilegesStmt(($2)->release(), ($4)->release(), ($5)->release(), ($7)->release())); - } - ; -revoke_privileges_statement: - REVOKE privileges ON privileges_target_type privileges_target FROM grantees - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new RevokePrivilegesStmt(($2)->release(), ($4)->release(), ($5)->release(), ($7)->release())); - } - ; -grant_role_statement: - GRANT rolenames TO grantees - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new GrantRoleStmt(($2)->release(), ($4)->release())); - } - ; -revoke_role_statement: - REVOKE rolenames FROM grantees - { - $$ = TrackedPtr::make(lexer.parsed_node_tokens_, new RevokeRoleStmt(($2)->release(), ($4)->release())); - } - ; - optimize_table_statement: OPTIMIZE TABLE opt_table opt_with_option_list { diff --git a/Tests/DBObjectPrivilegesTest.cpp b/Tests/DBObjectPrivilegesTest.cpp index 241acba55d..6c93c4adea 100644 --- a/Tests/DBObjectPrivilegesTest.cpp +++ b/Tests/DBObjectPrivilegesTest.cpp @@ -353,7 +353,12 @@ class InvalidGrantSyntax : public DBHandlerTestFixture {}; TEST_F(InvalidGrantSyntax, InvalidGrantSyntax) { std::string error_message; - error_message = "Syntax error at: ON"; + error_message = + "SQL Error: Encountered \"ON\" at line 1, column 23.\nWas expecting one of:\n " + "\"ALTER\" ...\n \"CREATE\" ...\n \"DELETE\" ...\n \"DROP\" ...\n " + "\"INSERT\" ...\n \"SELECT\" ...\n \"TRUNCATE\" ...\n \"UPDATE\" ...\n " + "\"USAGE\" ...\n \"VIEW\" ...\n \"ACCESS\" ...\n \"EDIT\" ...\n " + "\"SERVER\" ...\n \"ALL\" ...\n "; queryAndAssertException("GRANT SELECT, INSERT, ON TABLE tbl TO Arsenal, Juventus;", error_message); @@ -382,10 +387,10 @@ TEST(UserRoles, InvalidGrantsRevokesTest) { EXPECT_THROW(run_ddl_statement("GRANT Antazin to Antazin;"), std::runtime_error); EXPECT_THROW(run_ddl_statement("REVOKE Antazin from Antazin;"), std::runtime_error); - EXPECT_THROW(run_ddl_statement("GRANT Antazin to Max;"), std::runtime_error); - EXPECT_THROW(run_ddl_statement("REVOKE Antazin from Max;"), std::runtime_error); - EXPECT_THROW(run_ddl_statement("GRANT Max to Antazin;"), std::runtime_error); - EXPECT_THROW(run_ddl_statement("REVOKE Max from Antazin;"), std::runtime_error); + EXPECT_THROW(run_ddl_statement("GRANT Antazin to \"Max\";"), std::runtime_error); + EXPECT_THROW(run_ddl_statement("REVOKE Antazin from \"Max\";"), std::runtime_error); + EXPECT_THROW(run_ddl_statement("GRANT \"Max\" to Antazin;"), std::runtime_error); + EXPECT_THROW(run_ddl_statement("REVOKE \"Max\" from Antazin;"), std::runtime_error); run_ddl_statement("DROP USER Antazin;"); run_ddl_statement("DROP USER \"Max\";"); @@ -403,20 +408,21 @@ TEST(UserRoles, ValidNames) { EXPECT_NO_THROW( run_ddl_statement("CREATE USER \"vasya-vasya\" (password = 'password');")); EXPECT_NO_THROW(run_ddl_statement("CREATE ROLE developer;")); - EXPECT_NO_THROW(run_ddl_statement("CREATE ROLE developer-backend;")); - EXPECT_NO_THROW(run_ddl_statement("CREATE ROLE developer-backend-rendering;")); - EXPECT_NO_THROW(run_ddl_statement("GRANT developer-backend-rendering TO vasya;")); + EXPECT_NO_THROW(run_ddl_statement("CREATE ROLE \"developer-backend\";")); + EXPECT_NO_THROW(run_ddl_statement("CREATE ROLE \"developer-backend-rendering\";")); + EXPECT_NO_THROW(run_ddl_statement("GRANT \"developer-backend-rendering\" TO vasya;")); + EXPECT_NO_THROW(run_ddl_statement( + "GRANT \"developer-backend\" TO \"vasya ivanov@vasya.ivanov.com\";")); + EXPECT_NO_THROW(run_ddl_statement("GRANT developer TO \"vasya.vasya@vasya.com\";")); EXPECT_NO_THROW( - run_ddl_statement("GRANT developer-backend TO \"vasya ivanov@vasya.ivanov.com\";")); - EXPECT_NO_THROW(run_ddl_statement("GRANT developer TO vasya.vasya@vasya.com;")); - EXPECT_NO_THROW(run_ddl_statement("GRANT developer-backend-rendering TO vasya-vasya;")); + run_ddl_statement("GRANT \"developer-backend-rendering\" TO \"vasya-vasya\";")); EXPECT_NO_THROW(run_ddl_statement("DROP USER vasya;")); EXPECT_NO_THROW(run_ddl_statement("DROP USER \"vasya.vasya@vasya.com\";")); EXPECT_NO_THROW(run_ddl_statement("DROP USER \"vasya ivanov@vasya.ivanov.com\";")); EXPECT_NO_THROW(run_ddl_statement("DROP USER \"vasya-vasya\";")); EXPECT_NO_THROW(run_ddl_statement("DROP ROLE developer;")); - EXPECT_NO_THROW(run_ddl_statement("DROP ROLE developer-backend;")); - EXPECT_NO_THROW(run_ddl_statement("DROP ROLE developer-backend-rendering;")); + EXPECT_NO_THROW(run_ddl_statement("DROP ROLE \"developer-backend\";")); + EXPECT_NO_THROW(run_ddl_statement("DROP ROLE \"developer-backend-rendering\";")); } TEST(UserRoles, RoleHierarchies) { diff --git a/java/calcite/src/main/codegen/config.fmpp b/java/calcite/src/main/codegen/config.fmpp index 5e15f88db4..c57f8d808f 100644 --- a/java/calcite/src/main/codegen/config.fmpp +++ b/java/calcite/src/main/codegen/config.fmpp @@ -59,6 +59,12 @@ data: { "com.mapd.parser.extension.ddl.SqlShowQueries" "com.mapd.parser.extension.ddl.SqlShowDiskCacheUsage" "com.mapd.parser.extension.ddl.SqlKillQuery" + "com.mapd.parser.extension.ddl.SqlCreateRole" + "com.mapd.parser.extension.ddl.SqlDropRole" + "com.mapd.parser.extension.ddl.SqlGrantRole" + "com.mapd.parser.extension.ddl.SqlRevokeRole" + "com.mapd.parser.extension.ddl.SqlGrantPrivilege" + "com.mapd.parser.extension.ddl.SqlRevokePrivilege" "com.mapd.parser.extension.ddl.omnisql.*" "java.util.Map" "java.util.HashMap" @@ -84,9 +90,14 @@ data: { "DICTIONARY" # Non-reserved keywords (keywords that do not start a command and may therefore be used as regular text elsewhere in the parser. must add to nonReservedKeywordsToAdd below) + "ACCESS" "CACHE" + "DASHBOARD" "DATABASES" "DISK" + "DETAILS" + "EDIT" + "EDITOR" "MAPPING" "OWNER" "QUERY" @@ -97,7 +108,6 @@ data: { "STORED" "TABLES" "VIRTUAL" - "DETAILS" ] # List of keywords from "keywords" section that are not reserved. @@ -425,9 +435,14 @@ data: { # List of non-reserved keywords to add; # items in this list become non-reserved nonReservedKeywordsToAdd: [ + "ACCESS" "CACHE" + "DASHBOARD" "DATABASES" "DISK" + "DETAILS" + "EDIT" + "EDITOR" "MAPPING" "OWNER" "QUERY" @@ -438,7 +453,6 @@ data: { "STORED" "TABLES" "VIRTUAL" - "DETAILS" ] # List of non-reserved keywords to remove; @@ -470,6 +484,8 @@ data: { "SqlShowDiskCacheUsage(span())" "SqlKillQuery(span())" "SqlCustomDrop(span())" + "SqlGrant(span())" + "SqlRevoke(span())" ] # List of methods for parsing custom literals. @@ -504,6 +520,7 @@ data: { "SqlCreateTable" "SqlCreateUser" "SqlCreateView" + "SqlCreateRole" ] # List of methods for parsing extensions to "DROP" calls. diff --git a/java/calcite/src/main/codegen/includes/ddlParser.ftl b/java/calcite/src/main/codegen/includes/ddlParser.ftl index e36063c93e..a1de4fff6f 100644 --- a/java/calcite/src/main/codegen/includes/ddlParser.ftl +++ b/java/calcite/src/main/codegen/includes/ddlParser.ftl @@ -66,6 +66,24 @@ SqlNodeList TableElementList() : } } +SqlNodeList idList() : +{ + final Span s; + SqlIdentifier id; + final List list = new ArrayList(); +} +{ + { s = span(); } + id = CompoundIdentifier() { list.add(id); } + ( + + id = CompoundIdentifier() { list.add(id); } + )* + { + return new SqlNodeList(list, s.end(this)); + } +} + // Parse an optional data type encoding, default is NONE. Pair OmniSciEncodingOpt() : { @@ -385,7 +403,6 @@ void TableElement(List list) : final SqlNode defval; final SqlNode constraint; SqlIdentifier name = null; - final SqlNodeList columnList; final Span s = Span.of(); final ColumnStrategy strategy; } @@ -553,13 +570,15 @@ SqlDdl SqlCustomDrop(Span s) : | LOOKAHEAD(1) drop = SqlDropView(s) | - LOOKAHEAD(1) drop = SqlDropServer(s, replace) + LOOKAHEAD(1) drop = SqlDropServer(s) | - LOOKAHEAD(1) drop = SqlDropForeignTable(s, replace) + LOOKAHEAD(1) drop = SqlDropForeignTable(s) | - LOOKAHEAD(2) drop = SqlDropUserMapping(s, replace) + LOOKAHEAD(2) drop = SqlDropUserMapping(s) | LOOKAHEAD(2) drop = SqlDropUser(s) + | + LOOKAHEAD(2) drop = SqlDropRole(s) ) { return drop; @@ -918,3 +937,259 @@ SqlDdl SqlAlterUser(Span s) : } } +/* + * Create a role using the following syntax: + * + * CREATE ROLE + * + */ +SqlCreate SqlCreateRole(Span s, boolean replace) : +{ + final SqlIdentifier role; +} +{ + role = CompoundIdentifier() + { + return new SqlCreateRole(s.end(this), role); + } +} + +/* + * Drop a role using the following syntax: + * + * DROP ROLE + * + */ +SqlDrop SqlDropRole(Span s) : +{ + final SqlIdentifier role; +} +{ + role = CompoundIdentifier() + { + return new SqlDropRole(s.end(this), role.toString()); + } +} + +/* + * Base class for: + * + * Grant using the following syntax: + * + * GRANT ... + * + */ +SqlDdl SqlGrant(Span si) : +{ + Span s; + SqlNodeList nodeList; + final SqlDdl grant; +} +{ + { s = span(); } + ( + nodeList = privilegeList() + grant = SqlGrantPrivilege(s, nodeList) + | + nodeList = idList() + grant = SqlGrantRole(s, nodeList) + ) + { + return grant; + } +} + +/* + * Base class for: + * + * Revoke using the following syntax: + * + * REVOKE ... + * + */ +SqlDdl SqlRevoke(Span si) : +{ + Span s; + SqlNodeList nodeList; + final SqlDdl revoke; +} +{ + { s = span(); } + ( + nodeList = privilegeList() + revoke = SqlRevokePrivilege(s, nodeList) + | + nodeList = idList() + revoke = SqlRevokeRole(s, nodeList) + ) + { + return revoke; + } +} + +/* + * Grant a role using the following syntax: + * + * (GRANT ) TO + * + */ +SqlDdl SqlGrantRole(Span s, SqlNodeList roleList) : +{ + SqlNodeList granteeList; +} +{ + + granteeList = idList() + + { return new SqlGrantRole(s.end(this), roleList, granteeList); } +} + +/* + * Revoke a role using the following syntax: + * + * (REVOKE ) FROM + * + */ +SqlDdl SqlRevokeRole(Span s, SqlNodeList roleList) : +{ + SqlNodeList granteeList; +} +{ + + granteeList = idList() + + { return new SqlRevokeRole(s.end(this), roleList, granteeList); } +} + +SqlNode Privilege(Span s) : +{ + String type; +} +{ + ( + LOOKAHEAD(2) { type = "SERVER USAGE"; } + | LOOKAHEAD(2) { type = "ALTER SERVER"; } + | LOOKAHEAD(2) { type = "CREATE SERVER"; } + | LOOKAHEAD(2) { type = "CREATE TABLE"; } + | LOOKAHEAD(2) { type = "CREATE VIEW"; } + | LOOKAHEAD(2) { type = "SELECT"; } + | { type = "TRUNCATE"; } + | { type = "UPDATE"; } + | { type = "USAGE"; } + | { type = "VIEW"; } + ) + { return SqlLiteral.createCharString(type, s.end(this)); } +} + +SqlNodeList privilegeList() : +{ + Span s; + SqlNode privilege; + final List list = new ArrayList(); +} +{ + { s = span(); } + privilege = Privilege(s) { list.add(privilege); } + ( + + privilege = Privilege(s) { list.add(privilege); } + )* + { + return new SqlNodeList(list, s.end(this)); + } +} + +String PrivilegeType() : +{ + String type; +} +{ + ( + { type = "DATABASE"; } + |
{ type = "TABLE"; } + | { type = "DASHBOARD"; } + | { type = "VIEW"; } + | { type = "SERVER"; } + ) + { return type; } +} + +String PrivilegeTarget() : +{ + final SqlIdentifier target; + final Integer iTarget; + final String result; +} +{ + ( + target = CompoundIdentifier() { result = target.toString(); } + | + iTarget = IntLiteral() { result = iTarget.toString(); } + ) + { return result; } +} + +/* + * Grant privileges using the following syntax: + * + * (GRANT privileges) ON privileges_target_type privileges_target TO grantees + * + */ +SqlDdl SqlGrantPrivilege(Span s, SqlNodeList privileges) : +{ + final String type; + final String target; + final SqlNodeList grantees; +} +{ + + type = PrivilegeType() + target = PrivilegeTarget() + + grantees = idList() + { + return new SqlGrantPrivilege(s.end(this), privileges, type, target, grantees); + } +} + +/* + * Revoke privileges using the following syntax: + * + * (REVOKE ) ON FROM ; + * + */ +SqlDdl SqlRevokePrivilege(Span s, SqlNodeList privileges) : +{ + final String type; + final String target; + final SqlNodeList grantees; +} +{ + + type = PrivilegeType() + target = PrivilegeTarget() + + grantees = idList() + { + return new SqlRevokePrivilege(s.end(this), privileges, type, target, grantees); + } +} + + + diff --git a/java/calcite/src/main/codegen/includes/foreignServerParser.ftl b/java/calcite/src/main/codegen/includes/foreignServerParser.ftl index 6c2cb70eb3..850765da52 100644 --- a/java/calcite/src/main/codegen/includes/foreignServerParser.ftl +++ b/java/calcite/src/main/codegen/includes/foreignServerParser.ftl @@ -114,7 +114,7 @@ SqlDdl SqlAlterServer(Span s) : * * DROP SERVER [ IF EXISTS ] */ -SqlDrop SqlDropServer(Span s, boolean replace) : +SqlDrop SqlDropServer(Span s) : { final boolean ifExists; final SqlIdentifier serverName; diff --git a/java/calcite/src/main/codegen/includes/foreignTableParser.ftl b/java/calcite/src/main/codegen/includes/foreignTableParser.ftl index 284ebb4b34..1fac910e21 100644 --- a/java/calcite/src/main/codegen/includes/foreignTableParser.ftl +++ b/java/calcite/src/main/codegen/includes/foreignTableParser.ftl @@ -327,7 +327,7 @@ OmniSqlOptionPair TableOption() : * * DROP FOREIGN TABLE [ IF EXISTS ] */ -SqlDrop SqlDropForeignTable(Span s, boolean replace) : +SqlDrop SqlDropForeignTable(Span s) : { final boolean ifExists; final SqlIdentifier tableName; diff --git a/java/calcite/src/main/codegen/includes/userMappingParser.ftl b/java/calcite/src/main/codegen/includes/userMappingParser.ftl index e2ecb78aed..e0a6de1627 100644 --- a/java/calcite/src/main/codegen/includes/userMappingParser.ftl +++ b/java/calcite/src/main/codegen/includes/userMappingParser.ftl @@ -57,7 +57,7 @@ SqlCreate SqlCreateUserMapping(Span s, boolean replace) : * DROP USER MAPPING [IF EXISTS] FOR { | CURRENT_USER | PUBLIC } * SERVER */ -SqlDrop SqlDropUserMapping(Span s, boolean replace) : +SqlDrop SqlDropUserMapping(Span s) : { final boolean ifExists; final String user; diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlCreateRole.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlCreateRole.java new file mode 100644 index 0000000000..f225f106c3 --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlCreateRole.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.mapd.parser.extension.ddl; + +import org.apache.calcite.sql.SqlCreate; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.SqlWriterConfig; +import org.apache.calcite.sql.dialect.CalciteSqlDialect; +import org.apache.calcite.sql.parser.Span; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.sql.pretty.SqlPrettyWriter; +import org.apache.calcite.util.EscapedStringJsonBuilder; +import org.apache.calcite.util.ImmutableNullableList; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Parse tree for {@code CREATE ROLE} statement. + */ +public class SqlCreateRole extends SqlCreate { + public final SqlIdentifier role; + + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("CREATE_ROLE", SqlKind.OTHER_DDL); + + /** Creates a SqlCreateRole. */ + SqlCreateRole(SqlParserPos pos, SqlIdentifier role) { + super(OPERATOR, pos, false, false); + this.role = Objects.requireNonNull(role); + } + + public List getOperandList() { + return ImmutableNullableList.of(role); + } + + @Override + public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { + writer.keyword("CREATE"); + writer.keyword("ROLE"); + role.unparse(writer, leftPrec, rightPrec); + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + jsonBuilder.put(map, "role", this.role.toString()); + map.put("command", "CREATE_ROLE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +} diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlDropRole.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlDropRole.java new file mode 100644 index 0000000000..294b50b23b --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlDropRole.java @@ -0,0 +1,56 @@ + + +package com.mapd.parser.extension.ddl; + +import static java.util.Objects.requireNonNull; + +import com.google.gson.annotations.Expose; + +import org.apache.calcite.sql.SqlDrop; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.parser.Span; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.EscapedStringJsonBuilder; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Class that encapsulates all information associated with a DROP ROLE DDL command. + */ +public class SqlDropRole extends SqlDrop implements JsonSerializableDdl { + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("DROP_ROLE", SqlKind.OTHER_DDL); + + @Expose + private String role; + @Expose + private String command; + + public SqlDropRole(SqlParserPos pos, String role) { + super(OPERATOR, pos, false); + this.role = role; + this.command = OPERATOR.getName(); + } + + @Override + public List getOperandList() { + return null; + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + jsonBuilder.put(map, "role", this.role.toString()); + map.put("command", "DROP_ROLE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +} diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantPrivilege.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantPrivilege.java new file mode 100644 index 0000000000..61cde984b8 --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantPrivilege.java @@ -0,0 +1,86 @@ +package com.mapd.parser.extension.ddl; + +import static java.util.Objects.requireNonNull; + +import com.google.gson.annotations.Expose; + +import org.apache.calcite.sql.SqlDdl; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.parser.Span; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.EscapedStringJsonBuilder; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SqlGrantPrivilege extends SqlDdl { + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("GRANT_PRIVILEGE", SqlKind.OTHER_DDL); + @Expose + private String command; + @Expose + private SqlNodeList privileges; + @Expose + private String type; + @Expose + private String target; + @Expose + private SqlNodeList grantees; + + public SqlGrantPrivilege(SqlParserPos pos, + SqlNodeList privileges, + String type, + String target, + SqlNodeList grantees) { + super(OPERATOR, pos); + requireNonNull(privileges); + this.command = OPERATOR.getName(); + this.privileges = privileges; + this.type = type; + this.target = target; + this.grantees = grantees; + } + + @Override + public List getOperandList() { + return null; + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + + if (this.privileges != null) { + List privilege_list = jsonBuilder.list(); + for (SqlNode privilege : this.privileges) { + // privilege are string literals, + // so may need to be later striped of the quotes that get added + privilege_list.add(privilege.toString()); + } + map.put("privileges", privilege_list); + } + + map.put("type", this.type); + map.put("target", this.target); + + if (this.grantees != null) { + List grantee_list = jsonBuilder.list(); + for (SqlNode grantee : this.grantees) { + grantee_list.add(grantee.toString()); + } + map.put("grantees", grantee_list); + } + + map.put("command", "GRANT_PRIVILEGE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +} diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantRole.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantRole.java new file mode 100644 index 0000000000..5f97bb859e --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlGrantRole.java @@ -0,0 +1,72 @@ +package com.mapd.parser.extension.ddl; + +import static java.util.Objects.requireNonNull; + +import com.google.gson.annotations.Expose; + +import org.apache.calcite.sql.SqlDdl; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.parser.Span; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.EscapedStringJsonBuilder; +import org.apache.calcite.util.JsonBuilder; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SqlGrantRole extends SqlDdl { + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("GRANT_ROLE", SqlKind.OTHER_DDL); + @Expose + private String command; + @Expose + private SqlNodeList roles; + @Expose + private SqlNodeList grantees; + + public SqlGrantRole(SqlParserPos pos, SqlNodeList roles, SqlNodeList grantees) { + super(OPERATOR, pos); + requireNonNull(roles); + this.command = OPERATOR.getName(); + this.roles = roles; + this.grantees = grantees; + } + + @Override + public List getOperandList() { + return null; + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + + if (this.roles != null) { + List roles_list = jsonBuilder.list(); + for (SqlNode role : this.roles) { + roles_list.add(role.toString()); + } + map.put("roles", roles_list); + } + + if (this.grantees != null) { + List grantee_list = jsonBuilder.list(); + for (SqlNode grantee : this.grantees) { + grantee_list.add(grantee.toString()); + } + map.put("grantees", grantee_list); + } + + map.put("command", "GRANT_ROLE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +} diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokePrivilege.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokePrivilege.java new file mode 100644 index 0000000000..f7d86f7eea --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokePrivilege.java @@ -0,0 +1,87 @@ +package com.mapd.parser.extension.ddl; + +import static java.util.Objects.requireNonNull; + +import com.google.gson.annotations.Expose; + +import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlDdl; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.EscapedStringJsonBuilder; +import org.apache.calcite.util.JsonBuilder; +import org.apache.calcite.util.Pair; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class SqlRevokePrivilege extends SqlDdl { + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("REVOKE_PRIVILEGE", SqlKind.OTHER_DDL); + @Expose + private String command; + @Expose + private SqlNodeList privileges; + @Expose + private String type; + @Expose + private String target; + @Expose + private SqlNodeList grantees; + + public SqlRevokePrivilege(SqlParserPos pos, + SqlNodeList privileges, + String type, + String target, + SqlNodeList grantees) { + super(OPERATOR, pos); + requireNonNull(privileges); + this.command = OPERATOR.getName(); + this.privileges = privileges; + this.type = type; + this.target = target; + this.grantees = grantees; + } + + @Override + public List getOperandList() { + return null; + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + + if (this.privileges != null) { + List privilege_list = jsonBuilder.list(); + for (SqlNode privilege : this.privileges) { + privilege_list.add(privilege.toString()); + } + map.put("privileges", privilege_list); + } + + map.put("type", this.type); + map.put("target", this.target); + + if (this.grantees != null) { + List grantee_list = jsonBuilder.list(); + for (SqlNode grantee : this.grantees) { + grantee_list.add(grantee.toString()); + } + map.put("grantees", grantee_list); + } + + map.put("command", "REVOKE_PRIVILEGE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +} diff --git a/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokeRole.java b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokeRole.java new file mode 100644 index 0000000000..424b31bd43 --- /dev/null +++ b/java/calcite/src/main/java/com/mapd/parser/extension/ddl/SqlRevokeRole.java @@ -0,0 +1,71 @@ +package com.mapd.parser.extension.ddl; + +import static java.util.Objects.requireNonNull; + +import com.google.gson.annotations.Expose; + +import org.apache.calcite.sql.SqlDdl; +import org.apache.calcite.sql.SqlIdentifier; +import org.apache.calcite.sql.SqlKind; +import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNodeList; +import org.apache.calcite.sql.SqlOperator; +import org.apache.calcite.sql.SqlSpecialOperator; +import org.apache.calcite.sql.parser.Span; +import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.util.EscapedStringJsonBuilder; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class SqlRevokeRole extends SqlDdl { + private static final SqlOperator OPERATOR = + new SqlSpecialOperator("REVOKE_ROLE", SqlKind.OTHER_DDL); + @Expose + private String command; + @Expose + private SqlNodeList roles; + @Expose + private SqlNodeList grantees; + + public SqlRevokeRole(SqlParserPos pos, SqlNodeList roles, SqlNodeList grantees) { + super(OPERATOR, pos); + requireNonNull(roles); + this.command = OPERATOR.getName(); + this.roles = roles; + this.grantees = grantees; + } + + @Override + public List getOperandList() { + return null; + } + + @Override + public String toString() { + EscapedStringJsonBuilder jsonBuilder = new EscapedStringJsonBuilder(); + Map map = jsonBuilder.map(); + + if (this.roles != null) { + List roles_list = jsonBuilder.list(); + for (SqlNode role : this.roles) { + roles_list.add(role.toString()); + } + map.put("roles", roles_list); + } + + if (this.grantees != null) { + List grantee_list = jsonBuilder.list(); + for (SqlNode grantee : this.grantees) { + grantee_list.add(grantee.toString()); + } + map.put("grantees", grantee_list); + } + + map.put("command", "REVOKE_ROLE"); + Map payload = jsonBuilder.map(); + payload.put("payload", map); + return jsonBuilder.toJsonString(payload); + } +}