在功能中从动态表中选择而不使用EXECUTE

时间:2013-10-02 10:33:20

标签: function postgresql sql-injection plpgsql dynamic-sql

我们可以在不使用EXECUTE的情况下选择PostgreSQL函数吗? 我正在尝试使用quote_ident()来创建动态SQL,但它不起作用。

CREATE OR REPLACE FUNCTION select_server(p_id text)
RETURNS integer AS $$ 
DECLARE 
   serialnum_value INTEGER; 
   STATEMENT TEXT;
BEGIN
  STATEMENT := 'tbl' || substr($1, 1, 4);  
  SELECT serialnum INTO serialnum_value FROM quote_ident(STATEMENT ) WHERE id = $1;
  RETURN serialnum;
END;

有没有人知道如何在不使用EXECUTE的情况下从PostgreSQL函数中选择动态表?

2 个答案:

答案 0 :(得分:1)

根据igor的评论,你不能避免在这里执行。最好的选择是创建一个创建函数的函数,然后可以根据需要调用该函数。例如:

create function gen_select_server(p_id text)
  returns boolean
as $gen$
begin
  execute $exec$
  create function $exec$ || quote_ident('select_server_' || p_id) || $exec$
    returns integer
  as $body$
  begin
    return serialnum
    from $exec$ || quote_ident('tbl_' || p_id) || $exec$
    where id = $exec$ || quote_literal('id_' || p_id) || $exec$;
  end;
  $body$ language plpgsql;
  $exec$;
  return true;
end;
$gen$ language plpgsql;

-- usage:
select gen_select_server('1234');   -- call this only once; uses execute
select * from select_server_1234(); -- no execute here

如上所述,字符串分隔符可能会变得混乱。另请注意,上述内容不是您的原始功能 - 它主要用于说明如何在大$exec$“字符串”块中正确引用内容。

我建议坚持使用执行,这就是说。或者使用ORM,就此而言。保持这些微观优化可能比表现上的表面上的好处更糟糕。

答案 1 :(得分:1)

EXECUTE中,如果没有PL/pgSQL,则无法执行动态SQL。这就是首先让它成为动态

但是你可以简化你的功能:

CREATE OR REPLACE FUNCTION select_server(p_id text, OUT serial_value integer) AS
$func$ 
BEGIN
   EXECUTE 'SELECT serialnum FROM tbl' || left($1::text, 4)) || ' WHERE id = $1'
   USING $1
   INTO  serial_value;
END
$func$ LANGUAGE plpgsql STRICT;
  • Normally you would have to sanitize any identifier.
    但是,在这种特殊情况下,您不需要 quote_ident(),因为唯一的动态组件是来自integer的4位数字。这种方式既不可能是SQL注入也不是非法标识符。

  • 使用p_id子句将USING作为值传递。

  • 减少作业数量。那些在plpgsql中相对昂贵。您只需要一个SQL语句即可完成所有操作。

  • OUT参数有助于缩短语法。

  • 我还创建了函数STRICTRETURNS NULL ON NULL INPUT),因为NULL作为输入是没有意义的。

  • left()略快于substr()