1 /* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2 This program is free software; you can redistribute it and/or modify
3 it under the terms of the GNU General Public License, version 2.0,
4 as published by the Free Software Foundation.
5
6 This program is also distributed with certain software (including
7 but not limited to OpenSSL) that is licensed under separate terms,
8 as designated in a particular file or component or in included license
9 documentation. The authors of MySQL hereby grant you an additional
10 permission to link the program and your derivative works with the
11 separately licensed software that they have included with MySQL.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License, version 2.0, for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
21
22 #include "sql/resourcegroups/resource_group_sql_cmd.h"
23
24 #include <stdint.h>
25 #include <string.h>
26 #include <memory>
27 #include <new>
28 #include <string>
29 #include <utility>
30 #include <vector>
31
32 #include "m_ctype.h"
33 #include "m_string.h"
34 #include "my_compiler.h"
35 #include "my_dbug.h"
36 #include "my_psi_config.h"
37 #include "my_sys.h"
38 #include "mysql/components/services/log_shared.h"
39 #include "mysql/components/services/psi_thread_bits.h"
40 #include "mysql/psi/mysql_mutex.h"
41 #include "mysql_com.h"
42 #include "mysqld_error.h"
43 #include "pfs_thread_provider.h"
44 #include "sql/auth/auth_acls.h" // SUPER_ACL
45 #include "sql/auth/auth_common.h" // check_readonly
46 #include "sql/auth/sql_security_ctx.h"
47 #include "sql/current_thd.h" // current_thd
48 #include "sql/dd/cache/dictionary_client.h" // dd::cache::Dictionary_client
49 #include "sql/dd/dd_resource_group.h" // resource_group_exists, etc.
50 #include "sql/dd/string_type.h" // String_type
51 #include "sql/derror.h" // ER_THD
52 #include "sql/lock.h" // acquire_shared_global...
53 #include "sql/mdl.h"
54 #include "sql/mysqld_thd_manager.h" // Find_thd_with_id
55 #include "sql/parse_tree_helpers.h"
56 #include "sql/resourcegroups/resource_group.h" // Resource_group
57 #include "sql/resourcegroups/resource_group_mgr.h" // Resource_group_mgr
58 #include "sql/resourcegroups/thread_resource_control.h" // Thread_resource_control
59 #include "sql/sql_backup_lock.h" // acquire_shared_backup_lock
60 #include "sql/sql_class.h" // THD
61 #include "sql/sql_error.h"
62 #include "sql/sql_lex.h" // is_invalid_string
63 #include "sql/system_variables.h"
64 #include "sql/thd_raii.h"
65
66 namespace dd {
67 class Resource_group;
68 } // namespace dd
69
70 namespace {
71 /**
72 Acquire an exclusive MDL lock on resource group name.
73
74 @param thd Pointer to THD context.
75 @param res_grp_name Resource group name.
76
77 @return true if lock acquisition failed else false.
78 */
79
acquire_exclusive_mdl_for_resource_group(THD * thd,const char * res_grp_name)80 static bool acquire_exclusive_mdl_for_resource_group(THD *thd,
81 const char *res_grp_name) {
82 DBUG_TRACE;
83
84 MDL_key mdl_key;
85 dd::Resource_group::create_mdl_key(res_grp_name, &mdl_key);
86
87 MDL_request mdl_request;
88 MDL_REQUEST_INIT_BY_KEY(&mdl_request, &mdl_key, MDL_EXCLUSIVE,
89 MDL_TRANSACTION);
90 if (thd->mdl_context.acquire_lock(&mdl_request,
91 thd->variables.lock_wait_timeout))
92 return true;
93
94 return false;
95 }
96
97 /**
98 Validate CPU id ranges provided by a user in the statements
99 CREATE RESOURCE GROUP, ALTER RESOURCE GROUP
100
101 @param[out] vcpu_range_vector vector of validated resourcegroups::Range
102 objects.
103 @param cpu_list Array of resourcegroups::Range objects
104 representing CPU identifiers or ranges of
105 CPU identifiers specified in the statements
106 CREATE RESOURCE GROUP, ALTER RESOURCE GROUP.
107 @param num_vcpus Number of VCPUS.
108 */
109
validate_vcpu_range_vector(std::vector<resourcegroups::Range> * vcpu_range_vector,const Mem_root_array<resourcegroups::Range> * cpu_list,uint32_t num_vcpus)110 bool validate_vcpu_range_vector(
111 std::vector<resourcegroups::Range> *vcpu_range_vector,
112 const Mem_root_array<resourcegroups::Range> *cpu_list, uint32_t num_vcpus) {
113 DBUG_TRACE;
114 for (auto vcpu_range : *cpu_list) {
115 if (vcpu_range.m_start > vcpu_range.m_end) {
116 my_error(ER_INVALID_VCPU_RANGE, MYF(0), vcpu_range.m_start,
117 vcpu_range.m_end);
118 return true;
119 }
120
121 if (vcpu_range.m_start >= num_vcpus || vcpu_range.m_end >= num_vcpus) {
122 my_error(ER_INVALID_VCPU_ID, MYF(0),
123 vcpu_range.m_start >= num_vcpus ? vcpu_range.m_start
124 : vcpu_range.m_end);
125 return true;
126 }
127
128 vcpu_range_vector->emplace_back(
129 resourcegroups::Range(vcpu_range.m_start, vcpu_range.m_end));
130 }
131 return false;
132 }
133
134 /**
135 This class represents a functional call to move a thread specified by
136 pfs_thread_id to a resource group specified in class' constructor.
137 */
138
139 class Move_thread_to_default_group {
140 public:
Move_thread_to_default_group(resourcegroups::Resource_group * resource_group)141 explicit Move_thread_to_default_group(
142 resourcegroups::Resource_group *resource_group)
143 : m_resource_group(resource_group) {}
144
operator ()(ulonglong pfs_thread_id)145 void operator()(ulonglong pfs_thread_id) {
146 auto res_grp_mgr = resourcegroups::Resource_group_mgr::instance();
147 auto applied_res_grp =
148 m_resource_group->type() == resourcegroups::Type::SYSTEM_RESOURCE_GROUP
149 ? res_grp_mgr->sys_default_resource_group()
150 : res_grp_mgr->usr_default_resource_group();
151 PSI_thread_attrs pfs_thread_attr;
152
153 memset(&pfs_thread_attr, 0, sizeof(pfs_thread_attr));
154 if (!res_grp_mgr->get_thread_attributes(&pfs_thread_attr, pfs_thread_id)) {
155 applied_res_grp->controller()->apply_control(
156 pfs_thread_attr.m_thread_os_id);
157 res_grp_mgr->set_res_grp_in_pfs(applied_res_grp->name().c_str(),
158 applied_res_grp->name().length(),
159 pfs_thread_id);
160 if (!pfs_thread_attr.m_system_thread) {
161 Find_thd_with_id find_thd_with_id(pfs_thread_attr.m_processlist_id);
162 THD *thd =
163 Global_THD_manager::get_instance()->find_thd(&find_thd_with_id);
164 if (thd != nullptr) {
165 thd->resource_group_ctx()->m_cur_resource_group = nullptr;
166 mysql_mutex_assert_owner(&thd->LOCK_thd_data);
167 mysql_mutex_unlock(&thd->LOCK_thd_data);
168 }
169 }
170 }
171 }
172
173 private:
174 resourcegroups::Resource_group *m_resource_group;
175 };
176
177 /**
178 Check if given resource group name is a default resource group.
179 */
180
is_default_resource_group(const char * res_grp_name)181 inline bool is_default_resource_group(const char *res_grp_name) {
182 return my_strcasecmp(system_charset_info, "USR_default", res_grp_name) == 0 ||
183 my_strcasecmp(system_charset_info, "SYS_default", res_grp_name) == 0;
184 }
185
186 } // Anonymous namespace
187
execute(THD * thd)188 bool resourcegroups::Sql_cmd_create_resource_group::execute(THD *thd) {
189 DBUG_TRACE;
190
191 if (check_readonly(thd, true)) return true;
192
193 Security_context *sctx = thd->security_context();
194 if (!sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_ADMIN")).first) {
195 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "RESOURCE_GROUP_ADMIN");
196 return true;
197 }
198
199 // Resource group name validation.
200 if (is_invalid_string(m_name, system_charset_info)) return true;
201
202 // VCPU IDs list validation.
203 uint32_t num_vcpus =
204 resourcegroups::Resource_group_mgr::instance()->num_vcpus();
205 DBUG_PRINT("info", ("Number of VCPUS: %u", num_vcpus));
206
207 auto vcpu_range_vector = std::unique_ptr<std::vector<Range>>(
208 new (std::nothrow) std::vector<Range>);
209 if (vcpu_range_vector == nullptr) {
210 my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
211 return true;
212 }
213
214 if (validate_vcpu_range_vector(vcpu_range_vector.get(), m_cpu_list,
215 num_vcpus))
216 return true;
217
218 if (acquire_shared_global_read_lock(thd, thd->variables.lock_wait_timeout) ||
219 acquire_shared_backup_lock(thd, thd->variables.lock_wait_timeout))
220 return true;
221
222 // Acquire exclusive lock on the resource group name.
223 if (acquire_exclusive_mdl_for_resource_group(thd, m_name.str)) return true;
224
225 auto res_grp_mgr = Resource_group_mgr::instance();
226 // Check whether resource group exists in-memory.
227 if (res_grp_mgr->get_resource_group(m_name.str) != nullptr) {
228 my_error(ER_RESOURCE_GROUP_EXISTS, MYF(0), m_name.str);
229 return true;
230 }
231
232 bool resource_group_exists;
233 // Check the disk also for existence of resource group.
234 if (dd::resource_group_exists(thd->dd_client(), dd::String_type(m_name.str),
235 &resource_group_exists))
236 return true;
237
238 if (resource_group_exists) {
239 my_error(ER_RESOURCE_GROUP_EXISTS, MYF(0), m_name.str);
240 return true;
241 }
242
243 auto resource_group_ptr = res_grp_mgr->create_and_add_in_resource_group_hash(
244 m_name, m_type, m_enabled, std::move(vcpu_range_vector), m_priority);
245
246 if (resource_group_ptr == nullptr) return true;
247
248 Disable_autocommit_guard autocommit_guard(thd);
249 if (dd::create_resource_group(thd, *resource_group_ptr)) {
250 Resource_group_mgr::instance()->remove_resource_group(
251 std::string(m_name.str));
252 return true;
253 }
254
255 my_ok(thd);
256 return false;
257 }
258
259 /**
260 Check if a resource group specified by a name is present in memory.
261 Load a resource group from the Data Dictionary if it missed in memory.
262
263 @param thd THD context.
264 @param resource_group_name Resource group name.
265
266 @return nullptr in case of error, else return a pointer to an instance of
267 class resourcegroups::Resource_group.
268
269 @note in case nullptr is returned an error is set in Diagnostics_area.
270 */
271
check_and_load_resource_group(THD * thd,const LEX_CSTRING & resource_group_name)272 static inline resourcegroups::Resource_group *check_and_load_resource_group(
273 THD *thd, const LEX_CSTRING &resource_group_name) {
274 resourcegroups::Resource_group *resource_group =
275 resourcegroups::Resource_group_mgr::instance()->get_resource_group(
276 resource_group_name.str);
277
278 if (resource_group == nullptr) {
279 // Check if resource group exists on-disk.
280 bool exists;
281 if (dd::resource_group_exists(thd->dd_client(),
282 dd::String_type(resource_group_name.str),
283 &exists))
284 // Error is reported by the dictionary subsystem.
285 return nullptr;
286
287 if (!exists) {
288 my_error(ER_RESOURCE_GROUP_NOT_EXISTS, MYF(0), resource_group_name.str);
289 return nullptr;
290 }
291
292 const dd::Resource_group *dd_resource_group = nullptr;
293 dd::cache::Dictionary_client::Auto_releaser releaser(thd->dd_client());
294 if (thd->dd_client()->acquire(dd::String_type(resource_group_name.str),
295 &dd_resource_group))
296 // Error is reported by the dictionary subsystem.
297 return nullptr;
298
299 resource_group = resourcegroups::Resource_group_mgr::instance()
300 ->deserialize_resource_group(dd_resource_group);
301 if (resource_group == nullptr) {
302 my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
303 return nullptr;
304 }
305 }
306
307 return resource_group;
308 }
309
execute(THD * thd)310 bool resourcegroups::Sql_cmd_alter_resource_group::execute(THD *thd) {
311 DBUG_TRACE;
312
313 if (check_readonly(thd, true)) return true;
314
315 Security_context *sctx = thd->security_context();
316 if (!sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_ADMIN")).first) {
317 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "RESOURCE_GROUP_ADMIN");
318 return true;
319 }
320
321 // Resource group name validation.
322 if (is_invalid_string(m_name, system_charset_info)) return true;
323
324 // Disallow altering USR_default & SYS_default resource group.
325 if (is_default_resource_group(m_name.str)) {
326 my_error(ER_DISALLOWED_OPERATION, MYF(0), "Alter",
327 "default resource groups.");
328 return true;
329 }
330
331 if (acquire_shared_global_read_lock(thd, thd->variables.lock_wait_timeout) ||
332 acquire_shared_backup_lock(thd, thd->variables.lock_wait_timeout))
333 return true;
334
335 // Acquire exclusive lock on the resource group name.
336 if (acquire_exclusive_mdl_for_resource_group(thd, m_name.str)) return true;
337
338 auto resource_group = check_and_load_resource_group(thd, m_name);
339
340 if (resource_group == nullptr) return true;
341
342 // VCPU IDs list validation.
343 uint32_t num_vcpus = Resource_group_mgr::instance()->num_vcpus();
344 DBUG_PRINT("info", ("Number of VCPUS: %u", num_vcpus));
345
346 auto vcpu_range_vector = std::unique_ptr<std::vector<Range>>(
347 new (std::nothrow) std::vector<Range>);
348 if (vcpu_range_vector == nullptr) {
349 my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR), 0);
350 return true;
351 }
352
353 if (validate_vcpu_range_vector(vcpu_range_vector.get(), m_cpu_list,
354 num_vcpus))
355 return true;
356
357 if (validate_resource_group_priority(thd, &m_priority, m_name,
358 resource_group->type()))
359 return true;
360
361 // FORCE option is not paired with DISABLE option.
362 if (m_force && (!m_use_enable || m_enable)) {
363 my_error(ER_INVALID_USE_OF_FORCE_OPTION, MYF(0));
364 return true;
365 }
366
367 Thread_resource_control *thr_res_ctrl = resource_group->controller();
368 bool thr_res_ctrl_change = false;
369 if (m_priority != thr_res_ctrl->priority()) {
370 thr_res_ctrl->set_priority(m_priority);
371 thr_res_ctrl_change = true;
372 }
373
374 if (!vcpu_range_vector->empty()) {
375 thr_res_ctrl->set_vcpu_vector(*vcpu_range_vector);
376 thr_res_ctrl_change = true;
377 }
378
379 if (m_use_enable && m_enable != resource_group->enabled()) {
380 resource_group->set_enabled(m_enable);
381 thr_res_ctrl_change = m_enable;
382 } else
383 thr_res_ctrl_change = resource_group->enabled();
384
385 // Update on-disk resource group.
386 Disable_autocommit_guard autocommit_guard(thd);
387 dd::String_type name(m_name.str);
388 if (update_resource_group(thd, name, *resource_group)) return true;
389
390 /*
391 Reapply controls on threads if there was some change in
392 the thread resource controls and the resource group is enabled.
393 */
394 if (thr_res_ctrl_change) {
395 resource_group->apply_control_func(
396 [resource_group](ulonglong pfs_thread_id) {
397 auto res_grp_mgr_ptr = Resource_group_mgr::instance();
398 PSI_thread_attrs pfs_thread_attr;
399 memset(&pfs_thread_attr, 0, sizeof(pfs_thread_attr));
400 if (!res_grp_mgr_ptr->get_thread_attributes(&pfs_thread_attr,
401 pfs_thread_id))
402 resource_group->controller()->apply_control(
403 pfs_thread_attr.m_thread_os_id);
404 });
405 }
406
407 /*
408 If some threads are bound with resource group, then
409 (i) If FORCE option is specified, move the threads bound with this
410 resource group to respective default resource groups.
411 (ii) If FORCE option is not specified, the resource group is just
412 disabled.
413 */
414 if (resource_group->is_bound_to_threads()) {
415 if (m_force) {
416 // Move all threads associated with this to default resource groups.
417 resource_group->apply_control_func(
418 Move_thread_to_default_group(resource_group));
419 resource_group->clear();
420 }
421 }
422 my_ok(thd);
423 return false;
424 }
425
execute(THD * thd)426 bool resourcegroups::Sql_cmd_drop_resource_group::execute(THD *thd) {
427 DBUG_TRACE;
428
429 if (check_readonly(thd, true)) return true;
430
431 Security_context *sctx = thd->security_context();
432 if (!sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_ADMIN")).first) {
433 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "RESOURCE_GROUP_ADMIN");
434 return true;
435 }
436
437 // Resource group name validation.
438 if (is_invalid_string(m_name, system_charset_info)) return true;
439
440 // Disallow dropping USR_default & SYS_default resource group.
441 if (is_default_resource_group(m_name.str)) {
442 my_error(ER_DISALLOWED_OPERATION, MYF(0), "Drop operation ",
443 "default resource groups.");
444 return true;
445 }
446
447 if (acquire_shared_global_read_lock(thd, thd->variables.lock_wait_timeout) ||
448 acquire_shared_backup_lock(thd, thd->variables.lock_wait_timeout))
449 return true;
450
451 // Acquire exclusive lock on the resource group name.
452 if (acquire_exclusive_mdl_for_resource_group(thd, m_name.str)) return true;
453
454 auto resource_group = check_and_load_resource_group(thd, m_name);
455
456 if (resource_group == nullptr) return true;
457
458 if (resource_group->is_bound_to_threads()) {
459 if (m_force) // move all threads to the default resource group.
460 {
461 resource_group->apply_control_func(
462 Move_thread_to_default_group(resource_group));
463 resource_group->clear();
464 } else {
465 my_error(ER_RESOURCE_GROUP_BUSY, MYF(0), m_name.str);
466 return true;
467 }
468 }
469
470 // Remove from on-disk resource group.
471 Disable_autocommit_guard autocommit_guard(thd);
472 if (dd::drop_resource_group(thd, m_name.str)) return true;
473
474 // Remove from in-memory hash the resource group.
475 if (resource_group != nullptr)
476 Resource_group_mgr::instance()->remove_resource_group(m_name.str);
477
478 my_ok(thd);
479 return false;
480 }
481
482 /**
483 Check if resource group controls can be applied to thread
484 identified by PFS thread id. Apply the controls to thread
485 if the checks were successful.
486
487 @param thd THD context
488 @param thread_id Thread id of the thread.
489 @param resource_group Pointer to resource group.
490 @param error Log error so that error is returned to client.
491
492 @returns true if the function fails else false.
493 */
494
check_and_apply_resource_grp(THD * thd,ulonglong thread_id,resourcegroups::Resource_group * resource_group,bool error)495 static inline bool check_and_apply_resource_grp(
496 THD *thd, ulonglong thread_id,
497 resourcegroups::Resource_group *resource_group, bool error) {
498 PSI_thread_attrs pfs_thread_attr;
499 memset(&pfs_thread_attr, 0, sizeof(pfs_thread_attr));
500 auto res_grp_mgr = resourcegroups::Resource_group_mgr::instance();
501
502 if (res_grp_mgr->get_thread_attributes(&pfs_thread_attr, thread_id) ||
503 thread_id != pfs_thread_attr.m_thread_internal_id) {
504 if (error)
505 my_error(ER_INVALID_THREAD_ID, MYF(0), thread_id);
506 else
507 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
508 ER_INVALID_THREAD_ID,
509 ER_THD(current_thd, ER_INVALID_THREAD_ID), thread_id);
510 return true;
511 }
512
513 bool res_grp_match = pfs_thread_attr.m_system_thread
514 ? (resource_group->type() ==
515 resourcegroups::Type::SYSTEM_RESOURCE_GROUP)
516 : (resource_group->type() ==
517 resourcegroups::Type::USER_RESOURCE_GROUP);
518
519 if (!res_grp_match) {
520 if (error)
521 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
522 resource_group->name().c_str(), thread_id,
523 "Resource group type and thread type doesn't match.");
524 else
525 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
526 ER_RESOURCE_GROUP_BIND_FAILED,
527 ER_THD(thd, ER_RESOURCE_GROUP_BIND_FAILED),
528 resource_group->name().c_str(), thread_id,
529 "Resource group type & thread type doesn't match");
530 return true;
531 }
532
533 if (!res_grp_mgr->thread_priority_available() &&
534 resource_group->controller()->priority() != 0)
535 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
536 ER_ATTRIBUTE_IGNORED,
537 ER_THD(current_thd, ER_ATTRIBUTE_IGNORED),
538 "thread_priority", "using default value");
539
540 // Check if resource group is already bound to this thread.
541 if (resource_group->is_pfs_thread_id_exists(thread_id)) return false;
542
543 MDL_ticket *ticket = nullptr;
544 if (res_grp_mgr->acquire_shared_mdl_for_resource_group(
545 thd, pfs_thread_attr.m_groupname, MDL_EXPLICIT, &ticket, true)) {
546 if (error)
547 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
548 resource_group->name().c_str(), thread_id,
549 "Unable to acquire MDL lock.");
550 else {
551 thd->clear_error();
552 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
553 ER_RESOURCE_GROUP_BIND_FAILED,
554 ER_THD(current_thd, ER_RESOURCE_GROUP_BIND_FAILED),
555 resource_group->name().c_str(), thread_id,
556 "Unable to acquire MDL lock.");
557 }
558 return true;
559 }
560
561 resourcegroups::Resource_group *prev_cur_res_grp =
562 resourcegroups::Resource_group_mgr::instance()->get_resource_group(
563 std::string(pfs_thread_attr.m_groupname));
564
565 if (resource_group->controller()->apply_control(
566 pfs_thread_attr.m_thread_os_id)) {
567 if (error)
568 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
569 resource_group->name().c_str(), thread_id,
570 "Failed to apply thread controls.");
571 else
572 push_warning_printf(current_thd, Sql_condition::SL_WARNING,
573 ER_RESOURCE_GROUP_BIND_FAILED,
574 ER_THD(current_thd, ER_RESOURCE_GROUP_BIND_FAILED),
575 resource_group->name().c_str(), thread_id,
576 "Failed to apply thread controls.");
577 res_grp_mgr->release_shared_mdl_for_resource_group(thd, ticket);
578 return true;
579 }
580
581 // Set resource group context for non-system threads.
582 if (!pfs_thread_attr.m_system_thread) {
583 Find_thd_with_id find_thd_with_id(pfs_thread_attr.m_processlist_id);
584 THD *cur_thd =
585 Global_THD_manager::get_instance()->find_thd(&find_thd_with_id);
586 if (cur_thd != nullptr) {
587 cur_thd->resource_group_ctx()->m_cur_resource_group = resource_group;
588 mysql_mutex_unlock(&cur_thd->LOCK_thd_data);
589 }
590 }
591
592 if (prev_cur_res_grp != nullptr)
593 prev_cur_res_grp->remove_pfs_thread_id(thread_id);
594 resource_group->add_pfs_thread_id(thread_id);
595
596 res_grp_mgr->set_res_grp_in_pfs(resource_group->name().c_str(),
597 resource_group->name().length(), thread_id);
598 res_grp_mgr->release_shared_mdl_for_resource_group(thd, ticket);
599 return false;
600 }
601
602 /**
603 Check if user has sufficient privilege to exercise SET RESOURCE GROUP.
604
605 @param sctx Pointer to security context.
606
607 @return true if sufficient privilege exists for SET RESOURCE GROUP else false.
608 */
609
check_resource_group_set_privilege(Security_context * sctx)610 static bool check_resource_group_set_privilege(Security_context *sctx) {
611 if (!(sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_ADMIN")).first ||
612 sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_USER")).first)) {
613 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0),
614 "RESOURCE_GROUP_ADMIN OR RESOURCE_GROUP_USER");
615 return true;
616 }
617 return false;
618 }
619
execute(THD * thd)620 bool resourcegroups::Sql_cmd_set_resource_group::execute(THD *thd) {
621 DBUG_TRACE;
622
623 Security_context *sctx = thd->security_context();
624 if (check_resource_group_set_privilege(sctx)) return true;
625
626 // Acquire exclusive lock on the resource group name to synchronize with hint.
627 if (acquire_exclusive_mdl_for_resource_group(thd, m_name.str)) return true;
628
629 auto resource_group = check_and_load_resource_group(thd, m_name);
630 if (resource_group == nullptr) return true;
631
632 if ((resource_group->type() == Type::SYSTEM_RESOURCE_GROUP) &&
633 !sctx->has_global_grant(STRING_WITH_LEN("RESOURCE_GROUP_ADMIN")).first) {
634 my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "RESOURCE_GROUP_ADMIN");
635 return true;
636 }
637
638 if (!resource_group->enabled()) {
639 my_error(ER_RESOURCE_GROUP_DISABLED, MYF(0),
640 resource_group->name().c_str());
641 return true;
642 }
643
644 if (m_thread_id_list == nullptr || m_thread_id_list->empty()) {
645 ulonglong pfs_thread_id = 0;
646 #ifdef HAVE_PSI_THREAD_INTERFACE
647 pfs_thread_id = PSI_THREAD_CALL(get_current_thread_internal_id)();
648 #endif
649
650 if (resource_group->type() != Type::USER_RESOURCE_GROUP) {
651 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
652 resource_group->name().c_str(), pfs_thread_id,
653 "System resource group can't be applied to user thread.");
654 return true;
655 }
656
657 auto res_grp_mgr = resourcegroups::Resource_group_mgr::instance();
658 const char *resource_group_name = nullptr;
659 mysql_mutex_lock(&thd->LOCK_thd_data);
660 auto cur_res_grp = thd->resource_group_ctx()->m_cur_resource_group;
661 if (cur_res_grp != nullptr)
662 resource_group_name = cur_res_grp->name().c_str();
663 mysql_mutex_unlock(&thd->LOCK_thd_data);
664
665 MDL_ticket *ticket = nullptr;
666 if (resource_group_name != nullptr &&
667 res_grp_mgr->acquire_shared_mdl_for_resource_group(
668 thd, resource_group_name, MDL_EXPLICIT, &ticket, true)) {
669 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
670 resource_group->name().c_str(), pfs_thread_id,
671 "Unable to acquire MDL lock.");
672 return true;
673 }
674
675 if (resource_group->controller()->apply_control()) {
676 my_error(ER_RESOURCE_GROUP_BIND_FAILED, MYF(0),
677 resource_group->name().c_str(), pfs_thread_id,
678 "Failed to apply thread resource controls");
679 return true;
680 }
681
682 mysql_mutex_lock(&thd->LOCK_thd_data);
683 cur_res_grp = thd->resource_group_ctx()->m_cur_resource_group;
684 thd->resource_group_ctx()->m_cur_resource_group = resource_group;
685 mysql_mutex_unlock(&thd->LOCK_thd_data);
686
687 if (cur_res_grp != nullptr)
688 cur_res_grp->remove_pfs_thread_id(pfs_thread_id);
689
690 resourcegroups::Resource_group_mgr::instance()->set_res_grp_in_pfs(
691 resource_group->name().c_str(), resource_group->name().length(),
692 pfs_thread_id);
693 resource_group->add_pfs_thread_id(pfs_thread_id);
694 if (ticket != nullptr)
695 res_grp_mgr->release_shared_mdl_for_resource_group(thd, ticket);
696 } else {
697 if (m_thread_id_list->size() == 1 &&
698 check_and_apply_resource_grp(thd, m_thread_id_list->at(0),
699 resource_group, true))
700 return true;
701 else
702 for (const auto &thread_id : *m_thread_id_list)
703 (void)check_and_apply_resource_grp(thd, thread_id, resource_group,
704 false);
705 }
706
707 my_ok(thd);
708 return false;
709 }
710
prepare(THD * thd)711 bool resourcegroups::Sql_cmd_set_resource_group::prepare(THD *thd) {
712 DBUG_TRACE;
713
714 if (Sql_cmd::prepare(thd)) return true;
715
716 bool rc = check_resource_group_set_privilege(thd->security_context());
717
718 return rc;
719 }
720