youngfromnowhere
[MariaDB] definer does not exist, excute command denied to user 에러 본문
[MariaDB] definer does not exist, excute command denied to user 에러
곽일땡 2024. 5. 9. 10:37디버깅 하다가, 실서버에서는 잘 질의되는 쿼리문이 로컬에서는 에러를 뱉는 현상을 봤다.
에러 메세지는
The user specified as a definer ('[user_name]'@'[host_name]') does not exist
user_name은 실DB서버에서 웹서버 클라이언트가 사용하는 유저명이었다.
MySQL error 1449: The user specified as a definer does not exist
When I run the following query I get an error: SELECT `a`.`sl_id` AS `sl_id`, `a`.`quote_id` AS `quote_id`, `a`.`sl_date` AS `sl_date`, ...
stackoverflow.com
해당 링크에 의하면, view를 조회하거나 stored procedure를 호출할 때 definer에 해당하는 user가 DB에 존재하지 않을 때 발생하는 에러라고 한다.

어쨌든 local DB에는 root user밖에 없는 것은 맞다.
문제는 내가 질의한 쿼리문에는 view도 stored procedure도 없었다는 것.
원인을 파악하기 위해 local DB에 실서버에서 사용하는 user명으로 user를 추가해보았다.

이 상태로 다시 문제의 쿼리문을 질의하니 아래와 같이 에러메세지가 바뀌었다.
execute command denied to user '[user_name]'@'%' for routine '[db_name].len'
여기서 routine이란 view, procedure 그리고 사용자 function을 포함하는 용어이다.
즉, len()이라는 이름의 사용자정의 함수가 문제였던 것.
mysql.proc table을 통해 현재 DB에 정의되어 있는 procedure와 사용자정의 함수들을 볼 수 있다.
describe mysql.proc;
select * from mysql.proc;
select db, name, type from mysql.proc;
show create function 구문을 통해 사용자정의 함수의 정의를 볼 수 있다.
show create function [db_name].[function_name];
mysql.proc table을 update하여 len() 함수의 definer를 바꿔주는 것으로 에러를 해결했다. (이후 필요없는 user 계정은 다시 제거했다.)
update mysql.proc
set definer = 'root@%' -- 따옴표 주의!
where db = '[db_name]' and type = '[type]' and name = '[name]';
delete from mysql.user where `User` = '[user_name]';
=============
사실 이 일화에서 얻은 교훈은 따로 있다.
위에서 definer가 맞지 않아 개발환경(로컬환경)에서 에러를 일으킨 사용자 정의 함수 len()의 정의는 다음과 같았다.
CREATE DEFINER=`[user_name]`@`[host]` FUNCTION `len`(p_param varchar(1024)) RETURNS int(11)
BEGIN
RETURN CHAR_LENGTH(TRIM(p_param));
END
즉, 내장함수인 char_length와 trim의 단순한 합성 함수였다.
아마 처음 개발한 사람은 웹서비스 전체에서 char_length(trim()) method가 반복적으로 쓰이는 것을 보고, "중복을 줄이는 것이 좋은 코드이다"라는 생각에 이렇게 사용자 정의 함수를 따로 정의했을 것이다.
하지만 단순히 중복을 줄인다고 좋은 코드인지를 고민했어야 한다고 본다.
아무리 중복이 많다고 하더라도, 단순히 내장함수 두개를 합성하는 패턴이라면 한 눈에 읽고 이해할 수 있으므로, 따로 함수를 정의하여 빼놓을 이유는 없다.
결과론이지만 이 개발자가 정의한 len() function은, 이름도, 맥락에서 파악되는 기능도 마치 내장함수처럼 생겨서, 후에 유지보수하는 입장에서 절대 여기에 에러의 원인이 있으리라고 예상할 수가 없었다. 물론 이 개발자도 function의 definer가 에러의 원인이 될 수 있으리라고는 예상하지 못했을 것이다.
사용자정의 함수명을 이렇게 내장함수처럼 보이도록 하면, 사용자정의 함수의 로직 자체에 논리적 결함이 있어서 전체 프로그램의 output이 의도와 달라지는 경우에도 큰 문제가 될 수 있다. 실행 자체에 문제가 없으면 에러 메세지도 뱉지 않을 것이고, 디버깅 하는 입장에서 이 함수의 로직을 들여다볼 생각을 못하고 지나칠 수 있기 때문이다. 또한 이런식으로 DB object (function, procedure 등)에 로직을 포함시키면 형상관리도 제대로 할 수 없을 것이다.