definer 和 invoker 是执行上下文模式而非权限;选错会导致 call 时误报 error 1370 或 error 1044,根源在于权限归属对象错误——definer 模式校验定义者权限,invoker 模式校验调用者权限。

直接说结论:DEFINER 和 INVOKER 不是“权限”,而是执行上下文模式;选错会导致 CALL 时莫名其妙报 ERROR 1370 或 ERROR 1044,根源不在 EXECUTE 授权是否到位,而在权限归属对象错了。
为什么 CALL 存储过程会报 ERROR 1370(execute command denied)
这个错误常被误判为“没给 EXECUTE 权限”,但更大概率是 SQL SECURITY DEFINER 模式下,DEFINER 账号已失效或无权访问内部表:
- 执行
SHOW CREATE PROCEDURE mydb.calc_score,检查DEFINER字段值(如'dba'@'localhost'),再查该账号是否存在:SELECT Host, User FROM mysql.user WHERE User = 'dba' - 若账号已被删、密码过期、或 host 不匹配(
'dba'@'127.0.0.1'≠'dba'@'localhost'),DEFINER 模式就会静默失败,报 ERROR 1370 - 即使账号存在,也要确认它对过程内用到的表(如
mydb.users)有 SELECT/UPDATE 权限——DEFINER 模式下,校验的是 DEFINER 的权限,不是调用者的
SQL SECURITY INVOKER 下必须额外授表级权限
切换成 INVOKER 后,ERROR 1370 消失了,但可能立刻出现 ERROR 1142(update command denied on table)——因为 INVOKER 模式把所有权限校验压到了调用者身上:
- 调用者必须显式拥有过程里每条 SQL 涉及对象的权限,例如过程里有
UPDATE mydb.orders SET status=1,就要提前执行:GRANT UPDATE ON mydb.orders TO 'dev'@'%' - 不能只靠
GRANT EXECUTE ON PROCEDURE mydb.calc_score TO 'dev'@'%'—— 这个权限只管“能不能 CALL”,不管“CALL 之后能不能查/改数据” - 多租户场景推荐 INVOKER,但运维成本高:每个新过程都要人工梳理依赖表,逐个补权限
修改已有存储过程的 DEFINER 或 SQL SECURITY
不能直接改 mysql.proc 表(8.0+ 已废弃该表),必须用 ALTER PROCEDURE 语句重置上下文:
- 改 SQL SECURITY:
ALTER PROCEDURE mydb.calc_score SQL SECURITY INVOKER - 改 DEFINER(需 SUPER 或 SET_ANY_DEFINER 权限):
ALTER DEFINER = 'dev'@'%' PROCEDURE mydb.calc_score - 注意:ALTER 操作本身需要
ALTER ROUTINE权限;若原 DEFINER 是'root'@'localhost'且你没有 SUPER 权限,则无法将其改为其他用户 - 改完务必用实际账号连接并
CALL mydb.calc_score()测试,别信SHOW GRANTS—— routine 级权限它常不显示
DEFINER 账号写 CURRENT_USER() 最安全
创建过程时不硬编码 DEFINER,用 CURRENT_USER() 可避免后期账号变更导致的孤儿对象问题:
- 正确写法:
CREATE DEFINER = CURRENT_USER() PROCEDURE mydb.calc_score(...) SQL SECURITY DEFINER ... - 这样过程永远绑定创建者当前身份,只要创建者账号有效、权限完整,后续调用就不会因 DEFINER 失效崩掉
- 如果用
DEFINER = 'admin'@'%'这种固定值,一旦 admin 账号被删或权限回收,所有依赖它的过程都会变成“能看不能跑”的僵尸逻辑
真正难控的从来不是“谁来 CALL”,而是“CALL 之后那几行 SQL 到底以谁的身份、去碰哪张表”。DEFINER 和 INVOKER 的选择,本质是在封装性与可维护性之间做取舍——前者省事但隐含单点故障,后者透明但权限配置爆炸式增长。











