【问题标题】:MySQL dynamic SQL table name and resultMySQL 动态 SQL 表名和结果
【发布时间】:2013-11-15 21:47:11
【问题描述】:

我的 sql 动态代码遇到了很多麻烦:

DELIMITER $$

CREATE DEFINER=`remotecontroller`@`%` PROCEDURE `check_pwd`(IN pwd VARCHAR(20), 
IN user VARCHAR(20), IN tab VARCHAR(30), IN pwd_f varchar(30), IN id_f varchar(30), 
OUT result Bit(1))
    READS SQL DATA
BEGIN
 set @u_pwd= null;
 set @txt= concat("select pwd_f from ",tab," where id_f = upper(user) into @u_pwd");
 prepare exec from @txt;
 execute exec;
-- IN previous version the table and column names was preselected: select pwd_f from clinic.np where id_f like upper(user) into u_pwd;

     If Password(pwd) =  @u_pwd then set result= 1;
     else set result= 0;
     end if;
END

代码的目标是将一个密码与表中的另一个密码进行检查并返回布尔值。用于测试项目,我知道我需要更改密码插件,但我会在最终项目中这样做

当我执行代码时抛出一个错误 1064,sql 语法错误。有谁能够帮我?我尝试了很多东西,谢谢大家!

编辑 - 现在,使用相同的代码会引发错误 1054:field_list 中的未知列 pwd_f;

【问题讨论】:

  • 表名,之前我把使用模式排除在程序之外
  • 好吧,出现 1054 错误,我再次检查(再次)我是否在调用者中写错了列字段的名称,但不是,这是正确的,我不明白为什么会发生这种情况。
  • 好的,让我重新措辞。您在变量选项卡中发送什么值?
  • 表“学生”的名称,之前我执行“使用学校”

标签: mysql sql


【解决方案1】:

这不会选择由变量pwd_f 命名的列,它会选择名称​​为字面意思是“pwd_f”的列。

set @txt= concat("select pwd_f from ",tab," where id_f = upper(user) into @u_pwd");

变量id_f命名的列也是如此。

你可能想要这个:

set @txt= concat("select ",pwd_f," from ",tab," where ",id_f," = upper(user) into @u_pwd");

请注意,您的存储过程容易受到 SQL 注入攻击。确保调用此存储过程的代码只能传递有限列表中的列名和表名。您应该使用whitelisting technique 来执行此操作。


你的评论:

user 不是列,是 where 的行值!

好的,那么这也是同样的问题——您将变量放入您的 SQL 字符串中,但它们不会以这种方式被解释为变量。在动态查询中包含 的正确方法是使用参数。

set @txt= concat("select ",pwd_f," from ",tab," where ",id_f," = upper(?) into @u_pwd");
set @user= user;
prepare exec from @txt;
execute exec using @user;

额外的@user 变量是为了解决存储过程中准备/执行的奇怪限制:MySQL 不允许过程变量作为查询参数,您必须使用会话变量(带有@ 前缀的变量) .


我很高兴你让它工作了。对于它的价值,我也在测试它,所以这是我的测试脚本(有效):

use test
drop table if exists np;
create table np (
    id int primary key,
    username varchar(50),
    passwd varchar(50)
);
insert into np values (1, upper('bill'), password('xyzzy'));

DROP PROCEDURE check_pwd;

DELIMITER $$

CREATE DEFINER=`root`@`%` PROCEDURE `check_pwd`(
    IN pwd VARCHAR(20),
    IN user VARCHAR(20),
    IN tab VARCHAR(30),
    IN pwd_f varchar(30),
    IN id_f varchar(30),
    OUT result Bit(1))
    READS SQL DATA
BEGIN
 set @u_pwd= null;
 set @txt= concat("select ",pwd_f," from ",tab," where ",id_f," = upper(?) into @u_pwd");
 set @user= user;
 prepare exec from @txt;
 execute exec using @user;
-- IN previous version the table and column names was preselected: select pwd_f from clinic.np where id_f like upper(user) into u_pwd;

     If Password(pwd) =  @u_pwd then
    set result= 1;
     else
    set result= 0;
     end if;
END $$

DELIMITER ;

CALL check_pwd('xyzzy','bill','np','passwd','username',@did_i_match);

SELECT @did_i_match;

其他提示:

  • PASSWORD() 不是用于应用程序级密码散列的好函数。 They even say so in a note in the documentation.

    另请阅读You're Probably Storing Passwords Incorrectly

  • MySQL 的 BIT 数据类型存在已知错误。经验丰富的 MySQL 专家建议使用 TINYINT 作为事实上的布尔类型。

  • 我不确定您为什么要为此任务使用存储过程。 MySQL 存储过程非常低效,大多数开发人员会使用应用程序代码来做你正在做的事情。它会更容易开发和调试,运行更快,更容易防止 SQL 注入。

【讨论】:

  • 也许他也想要id_f的值?
  • 我希望我能为有关 SQL 注入的 cmets 两次 +1。
  • 现在与 ¿¿¿¿ 列用户抛出 1054 错误?????!!!!!用户不是列,是哪里的行值!看不懂,很奇怪!
  • 现在用 u_pwd 抛出 1054 错误!,我不明白,我要投降(还没有)...
  • 我想接受这两个答案......但我想我会接受你的
【解决方案2】:

也许是这样的:

 set @txt= concat("select pwd_f from ",tab," where id_f = upper(user) into @u_pwd");
 prepare exec from @conc;

应该更像:

 set @txt= concat("select pwd_f from ",tab," where id_f = upper(user) into @u_pwd");
 prepare exec from @txt;

【讨论】:

  • 哦!这个转录错误,在代码中是对的!哈哈哈!对不起
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 2013-08-13
  • 2018-12-29
相关资源
最近更新 更多