我有一个存储过程:
CREATE PROCEDURE `ProblematicProcedure` (IN dbName varchar(50), IN tableId INT)
MODIFIES SQL DATA
BEGIN
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v2;
CALL ExecuteSql(CONCAT("CREATE VIEW v1 AS SELECT * FROM ",dbName,".my_table;"));
CALL ExecuteSql(CONCAT("CREATE VIEW v2 AS SELECT * FROM ",dbName,".table_",tableId,";"));
...
直接从命令行或像Navicat或HeidiSql这样的客户端调用时,效果很好:
CALL ProblematicProcedure("my_schema",1);
但如果从自定义Apache模块调用使用上面完全相同的行,则在第一次ExecuteSql
调用时崩溃。我必须在从Apache模块调用时使其工作,并且找不到崩溃的原因。
ExecuteSql
定义CREATE PROCEDURE ExecuteSql (IN sql_str TEXT)
BEGIN
SET @query = sql_str;
PREPARE stm FROM @query;
EXECUTE stm;
DEALLOCATE PREPARE stm;
END
ExecuteSql
来电。ExecuteSql
来电。ExecuteSql
并使用了带有硬编码dbName
和tableId
值的直接SQL语句。MODIFIES SQL DATA
的程序。CREATE VIEW
权限:GRANT ALL ON *.* TO 'myuser'@'%';
注意:我在行之间添加了简单的插入语句,以查找崩溃的位置。所以,我确信它会在第一次ExecuteSql
来电时崩溃。
这次崩溃的原因是什么?
更新:最后,我找到了错误代码:
错误1312:过程无法在给定的上下文中返回结果集
答案 0 :(得分:0)
连接时使用 CLIENT_MULTI_STATEMENTS
标志:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
调用存储过程意味着执行多个语句。所以,我需要指定我可以一次执行多个语句。因此我在客户端使用MySql C API函数(在我的Apache模块中 ),我需要在连接时指定 CLIENT_MULTI_STATEMENTS
标志:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
或稍后再设置:
mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON);
我从 C API Handling of Multiple Statement Execution 页面了解了这些内容。
调试存储过程并不容易。我使用传统的log-table方法,但是在查找错误代码方面做了一些积极的尝试。
首先,定义了两个变量来保存有关错误的代码和消息:
DECLARE E INT DEFAULT 0; -- error code
DECLARE M TEXT DEFAULT NULL; -- error message
然后,为client和server错误(full list here)定义了可能的错误代码和消息:
DECLARE CONTINUE HANDLER FOR 1000 SET E='1000', M="hashchk";
DECLARE CONTINUE HANDLER FOR 1001 SET E='1001', M="isamchk";
...
...
DECLARE CONTINUE HANDLER FOR 1312 SET E='1312', M="PROCEDURE %s can't return a result set in the given context";
...
...
DECLARE CONTINUE HANDLER FOR 1638 SET E='1638', M="Non-ASCII separator arguments are not fully supported";
DECLARE CONTINUE HANDLER FOR 1639 SET E='1639', M="debug sync point wait timed out";
DECLARE CONTINUE HANDLER FOR 1640 SET E='1640', M="debug sync point hit limit reached";
...
...
DECLARE CONTINUE HANDLER FOR 2057 SET E='2057', M="The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again";
最后,将日志放入关键点:
IF E> 0 THEN
CALL WriteLog(CONCAT("Error ", E, ": ", M));
END IF;
WriteLog
是另一个只插入日志表的过程。这个方法给了我错误代码(1312
),然后一些谷歌搜索工作。