03月11, 2024
收藏本站

DTS同步引发的存储过程执行报错

场景:今天OP反馈使用DTS将zabbix同步到新实例,原先旧实例执行存储过程call partition_maintenance_all('zabbix')成功,迁移到新实例后,执行报错,提示如下:

ALTER command denied to user 'db_arch_w'@'x.x.x.x' for table 'history'

复现

# 旧实例,执行成功
mysql -h [旧实例地址] -u db_arch_w -p zabbix -e "call partition_maintenance_all('zabbix');"

# 新实例,执行报错,提示`ALTER command denied`
mysql -h [新实例地址] -u db_arch_w -p zabbix -e "call partition_maintenance_all('zabbix');"

排查

1. 权限

提示没权限,首先比对下新旧实例db_arch_w的权限,竟然都一样,如下,且没有zabbix库的ALTER权限!

show grants for 'db_arch_w'@'%';
+---------------------------------------------------------------------------------------------------------------------------------+
| Grants for db_arch_w@%                                                                                                          |
+---------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `db_arch_w`@`%` WITH GRANT OPTION |
| GRANT SHOW_ROUTINE ON *.* TO `db_arch_w`@`%` WITH GRANT OPTION                                                                  |
| GRANT EXECUTE ON `zabbix`.* TO `db_arch_w`@`%`                                                                                  |
+---------------------------------------------------------------------------------------------------------------------------------+

确实没有ALTER权限,但旧实例为什么能执行?

2. 手动执行

看了下报错的位置,手动执行,执行SQL如下

# 新实例,alter报错,提示ALTER command denied
mysql -h [新实例地址] -u db_arch_w -p zabbix -e "alter table zabbix.history add partition (partition p202403260000 values less than(1711468800))"

# 旧实例,一样报错
mysql -h [旧实例地址] -u db_arch_w -p zabbix -e "alter table zabbix.history add partition (partition p202403260000 values less than(1711468800))"

这个时候新旧报错又是一致的,这是为什么呢?

3. 比对存储过程

其实在第2步的时候,就可以确定是存储过程有问题。

# 旧实例存储过程
CREATE DEFINER=`admin`@`%` PROCEDURE `partition_maintenance_all`(
IN `SCHEMA_NAME` varchar(32) charset utf8mb4 collate utf8mb4_0900_ai_ci )
BEGIN
    # xxx 具体内容,忽略
END


# 新实例存储过程
CREATE DEFINER=`admin`@`%` PROCEDURE `partition_maintenance_all`(
IN `SCHEMA_NAME` varchar(32) charset utf8mb4 collate utf8mb4_0900_ai_ci )
    SQL SECURITY INVOKER
BEGIN
    # xxx 具体内容,忽略
END

有个细微区别,新实例存储过程中含有SQL SECURITY INVOKER,其含义为执行权限使用调用者账号所属权限。例如我们一开始用db_arch_w账号连接,则使用db_arch_w的权限去执行,而存储过程中含有ALTER命令,db_arch_w没有权限就报错;而旧实例没有指定SQL SECURITY,则默认使用DEFINER定义者的权限,此处为admin账号权限(超管),因此执行不会报错。

SQL SECURITY:有两个值DEFINER和INVOKER,没有指定则默认DEFINER。
DEFINER:使用定义者权限;INVOKER:使用调用者权限。

结论

DTS同步时,存储过程会自动添加SQL SECURITY INVOKER,导致调用时使用的是调用者的权限,当存储过程中含有调用者没有的权限时就会报错。因此仅需删除所有存储过程中SQL SECURITY INVOKER即可执行。

思考:为什么DTS要指定INVOKER呢?可能是考虑同步的目标实例没有定义的用户,会导致无法执行。


Comments