执行预准备语句时,过程以静默方式终止

时间:2012-09-06 09:20:13

标签: mysql stored-procedures prepared-statement

问题

我有一个存储过程:

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并使用了带有硬编码dbNametableId值的直接SQL语句。
  • 创建没有MODIFIES SQL DATA的程序。
  • 授予CREATE VIEW权限:GRANT ALL ON *.* TO 'myuser'@'%';

注意:我在行之间添加了简单的插入语句,以查找崩溃的位置。所以,我确信它会在第一次ExecuteSql来电时崩溃。

问题

这次崩溃的原因是什么?

更新:最后,我找到了错误代码:

  

错误1312:过程无法在给定的上下文中返回结果集

1 个答案:

答案 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

然后,为clientserver错误(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),然后一些谷歌搜索工作。