Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 从用户输入打开具有动态列名的游标的安全方法_Sql_Sql Injection_Plpgsql_Dynamic Sql_Postgresql 9.4 - Fatal编程技术网

Sql 从用户输入打开具有动态列名的游标的安全方法

Sql 从用户输入打开具有动态列名的游标的安全方法,sql,sql-injection,plpgsql,dynamic-sql,postgresql-9.4,Sql,Sql Injection,Plpgsql,Dynamic Sql,Postgresql 9.4,我正在尝试编写一个函数,该函数打开带有动态列名的游标。 我很担心SQL注入的可能性。 我很高兴地看到,这可以很容易地做到,但当我在我的例子中尝试它时,它出现了错误 错误:列不存在 我目前的尝试可以概括为。下面,我将介绍此小提琴的格式化代码 tst()函数的目标是能够计算常量查询的任何给定列中值的不同出现次数 我在寻求暗示,我做错了什么,或者可能是以安全的方式实现相同目标的其他方式 CREATE TABLE t1 ( f1 character varying not nul

我正在尝试编写一个函数,该函数打开带有动态列名的游标。 我很担心SQL注入的可能性。 我很高兴地看到,这可以很容易地做到,但当我在我的例子中尝试它时,它出现了错误

错误:列不存在

我目前的尝试可以概括为。下面,我将介绍此小提琴的格式化代码

tst()
函数的目标是能够计算常量查询的任何给定列中值的不同出现次数

我在寻求暗示,我做错了什么,或者可能是以安全的方式实现相同目标的其他方式

    CREATE TABLE t1 (
        f1 character varying not null,
        f2 character varying not null
    );
    CREATE TABLE t2 (
        f1 character varying not null,
        f2 character varying not null
    );
    INSERT INTO t1 (f1,f2) VALUES ('a1','b1'), ('a2','b2');
    INSERT INTO t2 (f1,f2) VALUES ('a1','c1'), ('a2','c2');

    CREATE OR REPLACE FUNCTION tst(p_field character varying)
        RETURNS INTEGER AS
    $BODY$ 
    DECLARE 
        v_r record; 
        v_cur refcursor; 
        v_sql character varying := 'SELECT count(DISTINCT(%I)) as qty 
                                    FROM t1 LEFT JOIN t2 ON (t1.f1=t2.f1)'; 
    BEGIN  
        OPEN v_cur FOR EXECUTE format(v_sql,lower(p_field)); 
        FETCH v_cur INTO v_r; 
        CLOSE v_cur; 
        return v_r.qty; 
    END; 
    $BODY$ 
    LANGUAGE plpgsql;
测试执行:

SELECT tst('t1.f1')
提供错误消息:

这将有助于:

SELECT tst('f1');
您面临的问题是:将与
%I
连接的参数解释为一个标识符。您试图传递一个表限定列名,该列名由两个标识符组成,该标识符被解释为
“t1.f1”
(一个名称,双引号以保留名称中非法的点)

如果要传递表名和列名,请使用两个参数:

CREATE OR REPLACE FUNCTION tst2(_col text, _tbl text = NULL)
  RETURNS int AS
$func$
DECLARE
   v_r record;
   v_cur refcursor;
   v_sql text := 'SELECT count(DISTINCT %s) AS qty
                  FROM t1 LEFT JOIN t2 USING (f1)';
BEGIN
   OPEN v_cur FOR EXECUTE
      format(v_sql, CASE WHEN _tbl <> ''  -- rule out NULL and ''
                         THEN quote_ident(lower(_tbl)) || '.' ||
                              quote_ident(lower(_col))
                         ELSE quote_ident(lower(_col)) END);
   FETCH v_cur INTO v_r;
   CLOSE v_cur;
   RETURN v_r.qty;
END
$func$ LANGUAGE plpgsql;
为了方便起见,我提供了
NULL
作为参数默认值。通过这种方式,您可以仅使用列名或使用列和表名调用函数。但不能不使用列名

电话:

test2()
相同

相关答复:


请添加您在SQL fiddle中获得的准确(且完整)错误消息…但是可以。fiddle很有用,您提供了所需的所有信息(包括现在的错误消息),而问题是一个狡猾的问题-这使这成为一个很好的问题。哈!你已经解决了这个问题。谢谢。我想这是很明显的,如果你关注SQL标识符的语义…我没有。另外,当你使用
时,你应该小心,当你试图像这样调用函数时,你的函数会失败:
tst2('f2')
:)对在t1.f1=t2.f1上使用
可能更好(括号可能是噪声)。
CREATE OR REPLACE FUNCTION tst2(_col text, _tbl text = NULL)
  RETURNS int AS
$func$
DECLARE
   v_r record;
   v_cur refcursor;
   v_sql text := 'SELECT count(DISTINCT %s) AS qty
                  FROM t1 LEFT JOIN t2 USING (f1)';
BEGIN
   OPEN v_cur FOR EXECUTE
      format(v_sql, CASE WHEN _tbl <> ''  -- rule out NULL and ''
                         THEN quote_ident(lower(_tbl)) || '.' ||
                              quote_ident(lower(_col))
                         ELSE quote_ident(lower(_col)) END);
   FETCH v_cur INTO v_r;
   CLOSE v_cur;
   RETURN v_r.qty;
END
$func$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION tst3(_col text, _tbl text = NULL, OUT ct bigint) AS
$func$
BEGIN
   EXECUTE format('SELECT count(DISTINCT %s) AS qty
                   FROM t1 LEFT JOIN t2 USING (f1)'
                 , CASE WHEN _tbl <> ''  -- rule out NULL and ''
                        THEN quote_ident(lower(_tbl)) || '.' ||
                             quote_ident(lower(_col))
                        ELSE quote_ident(lower(_col)) END)
   INTO ct;
   RETURN;
END
$func$ LANGUAGE plpgsql;
SELECT tst3('f1', 't1');
SELECT tst3('f1');
SELECT tst3(_col := 'f1');