MySql prepare语句-是否可以参数化列名或函数名?

时间:2018-11-09 21:45:54

标签: mysql sql

让我们说我想编写一个过程,使我可以在某些列上调用某些函数,例如:

call foo('min','age') -> SELECT min(age) FROM table;

我希望我的过程从sql注入中是安全的,因此,我愿意使用准备好的语句并对输入进行参数化

SET @var = "SELECT ?(?) FROM table;"
PREPARE x FROM @var;
EXECUTE x USING a, b;

其中a和b分别是输入参数,函数和列。

但是,似乎不可能-每当我想执行此语句时,InnoDB都会抛出错误。

是否可以通过这种方式解决此问题,还是需要将其列入白名单?

编辑: 完整代码:

create procedure test(in func varchar(20), in col varchar(20))
  begin
    set @f = func;
    set @c = col;
    set @sql = "select ?(?) from table;";
    prepare x from @sql;
    execute x using @f, @c;
  end;

致电:

call test('min','age');

完整错误:

  

[42000] [1064]您的SQL语法有错误;查看手册   对应于您的MySQL服务器版本的正确语法,   在第一行的'(?)from table'附近使用

2 个答案:

答案 0 :(得分:1)

无法对列/表/函数名称/别名进行参数设置。同样,PREPARE语句仅允许将SQL查询的“值”部分用作参数。函数/表/列名/别名用于确定SQL语句的有效性;因此无法在运行时执行期间更改。在执行时更改它可能会更改SQL语句是否有效。

您可以将其视为编译代码。因此,编译器必须知道用于创建有效可执行文件的所有函数/类名称等(是的,我们可以执行动态类,但这是 rare )。另一方面,我们可以更改程序的输入“值”,但是通常不能更改要对输入数据执行的操作。

此外,MySQL服务器会将参数视为文字,并在查询执行中使用它们之前对其加引号。

现在,在您的情况下,您仍然可以将函数名称用作存储过程的参数,并使用该名称来生成查询字符串。但是您不能将其用作查询本身的参数。

delimiter $$
create procedure test(in func varchar(20), in col varchar(20))
  begin

    set @c = col;

    -- use concat function to generate the query string using func parameter
    set @sql = concat('select ', func, '(?) from table');

    -- prepare the statement
    prepare stmt from @sql;

    -- execute
    execute x using @c;

    -- don't forget to deallocate the prepared statement
    deallocate prepare stmt;
  end$$
delimiter ;

答案 1 :(得分:0)

可以在关键字中参数化SQL关键字的唯一方法是使用动态查询。需要注意的是,动态查询往往比静态查询要慢,主要是因为它们不容易被缓存。

在没有警告的情况下,应该可以执行以下操作:

const fs = require("fs");
const Discord = require("discord.js");

module.exports.run = async(bot, message, args, con) => {
   fs.readdir("./commands/", (err, files) => {
     if(err) console.error(err);

    let jsfiles = files.filter(f => f.split(".").pop() === "js");
    if(jsfiles.length <= 0) {
        console.log("No commands to load!");
        return;
    }

    var namelist = "";
    var desclist = "";
    var usage = "";

    let result = jsfiles((f, i) => {
        let props = require(`./${f}`);
        namelist = props.help.name;
        desclist = props.help.description;
        usage = props.help.usage;

        // send help text
        let helpembed = new Discord.RichEmbed()
        .setTitle("Commands")
        .setFooter("Please report any bugs to Vati#1662")
        .setColor("RANDOM")
        .addField(`**${namelist}** \n${desclist} \n${usage}`)  
        message.author.sendEmbed(helpembed);
    });

   })
  }
    module.exports.help = {
    name: "help",
    description: "shows all commands",
    usage: "help"
    }