3

问题

我有一个存储过程:

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

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

问题

这次崩溃的原因是什么?

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

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

4

1 回答 1

0

解决方案

连接时使用CLIENT_MULTI_STATEMENTS标志:

mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);

为什么会这样?

调用存储过程意味着执行多个语句。所以,我需要指定我可以一次执行多个语句。因此,我在客户端(在我的 Apache 模块中)使用 MySql C API 函数,我需要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 处理页面中学到了这些。


我是如何调试的?

调试存储过程并不容易。我使用了传统的日志表方法,但在查找错误代码方面表现得有些激进。

首先,定义了两个变量来保存发生错误的代码和消息:

DECLARE E INT  DEFAULT 0;    -- error code
DECLARE M TEXT DEFAULT NULL; -- error message

然后,为客户端服务器错误定义了可能的错误代码和消息(此处为完整列表):

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

于 2012-09-06T13:35:44.257 回答