OUT parameter of MySQL stored procedure is null after calling stored procedure

我的梦境 提交于 2021-02-08 10:14:49

问题


I've created a stored procedure to return a value for row count of any table I pass in as an "IN" parameter, and output that rowcount to an OUT parameter

PROCEDURE `GetCount`(in tblname varchar(255), out rowcount int)
BEGIN
    SET @sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
    SET @sql_text2 = concat(@sql_text1,' INTO ');
    SET @sql_final = concat(@sql_text2,  rowcount);

    PREPARE stmt1 FROM @sql_text1;
    EXECUTE stmt1;
    DEALLOCATE PREPARE stmt1;

END

when I open a query window in MySQL workbench and do the following:

set @tablename = 'my_table_name;
set @cnt = -9998;
call GetCount(@tablename,@cnt);
SELECT @cnt;

the value of @cnt is NULL.

Is my method of testing the stored proc incorrect, or is the stored proc to blame?


回答1:


Your test methodology is valid, but you have made three errors in the procedure.

Error #1 you are using the wrong variable for the prepared statement.

PREPARE stmt1 FROM @sql_text1;

This should have been...

PREPARE stmt1 FROM @sql_final;

Error #2 This doesn't do what you intend:

SET @sql_final = concat(@sql_text2,  rowcount);

This concatenates the value of @sql_text2 with the value of rowcount. Since rowcount is an out parameter, it is null at this point. If any argument to CONCAT() is null, then the result is also null, so you are actually setting @sql_final to null. If not for error #1, either the PREPARE or subsequent EXECUTE would have thrown an error about ...the right syntax to use near NULL at line 1.

Here's what you actually intended, the literal string 'rowcount':

SET @sql_final = concat(@sql_text2,  'rowcount');

...but that would also fail, because rowcount is a program variable. Prepared statements run with session scope, not program scope, so program variables are out of scope in a prepared statement.

The fix requires you to use a user-defined variable, which has session scope, and then copy it into the program variable, as @nbk illustrated in their answer.

SET @sql_final = concat(@sql_text2,  '@rowcount');
PREPARE stmt1 FROM @sql_text1;
EXECUTE stmt1;
SET rowcount = @rowcount;

Note that program variables like rowcount and user-defined variables like @rowcount are from completely different namespaces, so there's no need for the names to be the same and no need for the names to be different.

Error #3 is not strictly an error, in the sense that it isn't stopping your code from working, but here's a note about a potentially dangerous practice.

You are accepting a table name as input, which opens up a security vulnerability called SQL Injection, where malicious input can cause unexpected/unauthorized results. Even if the argument can be made that this input comes from a trusted source, that argument is disregarded as a matter of best practice, because future changes could invalidate that assumption. It is worth your time to learn to do this, and do it consistently so that it becomes second-nature to you.

You can safely escape a table name, column name, or other object identifier in MySQL by replacing any embedded backtick with a double backtick, then prepending and appending a single backtick on each end.

You can do this at the top of the procedure...

SET tblname = CONCAT('`',REPLACE(tblname,'`','``'),'`');

...or inline...

SET @sql_text1 = concat('SELECT COUNT(*) FROM ',CONCAT('`',REPLACE(tblname,'`','``'),'`'));

...but of course not both. In the second example, the nested CONCAT() isn't strictly necessary, so this would also work, but the intention is less obvious:

SET @sql_text1 = concat('SELECT COUNT(*) FROM ','`',REPLACE(tblname,'`','``'),'`');



回答2:


Use this stored procdure:

DELIMITER //
DROP PROCEDURE IF EXISTS GetCount //
CREATE DEFINER=`root`@`localhost` PROCEDURE `GetCount`(IN tblname varchar(255), OUT rowcount int)
BEGIN

  SET @sql_text1 = concat('SELECT COUNT(*) FROM ',tblname);
  SET @sql_text1 = concat(@sql_text1,' INTO ');
  SET @sql_text1 = concat(@sql_text1, ' @rowcount;' );

  PREPARE stmt1 FROM @sql_text1;
  EXECUTE stmt1;
  Set rowcount = @rowcount;
  DEALLOCATE PREPARE stmt1;

END
//
DELIMITER ;

The idea is that mysql stores the count into the sessionvariable @rowcount which will be created automatically. The rest is simple getting the result to the proper variable.



来源:https://stackoverflow.com/questions/57828895/out-parameter-of-mysql-stored-procedure-is-null-after-calling-stored-procedure

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!