我想在Postgres函数中传递一个表名作为参数。我试过这个代码:
- CREATE OR REPLACE FUNCTION some_f(param character varying) RETURNS integer
- AS $$
- BEGIN
- IF EXISTS (select * from quote_ident($1) where quote_ident($1).id=1) THEN
- return 1;
- END IF;
- return 0;
- END;
- $$ LANGUAGE plpgsql;
- select some_f('table_name');
我得到这个:
这里是我得到的错误改变为这个select * from quote_ident($ 1)tab where tab.id = 1:
- ERROR: column tab.id does not exist
- LINE 1: ...T EXISTS (select * from quote_ident($1) tab where tab.id...
可能,quote_ident($ 1)工作,因为没有where quote_ident($ 1).id = 1部分,我得到1,这意味着某事被选中。为什么第一个quote_ident($ 1)可以工作,第二个不同时工作?这怎么可能解决?
这可以进一步简化和改进:
- CREATE OR REPLACE FUNCTION some_f(_tbl regclass,OUT result integer) AS
- $func$
- BEGIN
- EXECUTE format('SELECT (EXISTS (SELECT 1 FROM %s WHERE id = 1))::int',_tbl)
- INTO result;
- END
- $func$ LANGUAGE plpgsql;
- SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
要么:
- SELECT some_f('"my very uncommon table name"')
要点
>使用OUT参数简化功能。您可以直接选择动态sql的结果并完成。不需要额外的变量和代码。
> EXISTS确实是你想要的,我把它保存在我的查询。有多种方法可以做到这一点。
>你似乎想要一个整数回,所以我把布尔结果从EXISTS()转换为整数,这产生了你所拥有的。我会返回boolean。
>我使用对象标识符类型regclass
作为_tbl的输入类型。这一切都quote_ident(_tbl)
或format('%I',_tbl)
一切,但更好,因为:
> ..它也阻止sql注入。
> ..如果表名无效/不存在/对当前用户不可见,它立即失败并更优雅。
> ..它与模式限定的表名称,其中纯quote_ident(_tbl)或格式(%I)将失败,因为他们不能解决歧义。
>我仍然使用format()
,因为它简化了语法(并演示如何使用),但使用%s而不是%I。对于简单的例子,我们可以只是连接:
- EXECUTE 'SELECT (EXISTS (SELECT 1 FROM ' || _tbl || ' WHERE id = 1))::int'
>不需要对id列进行表格限定。
用Postgresql 9.1测试。 format()至少需要该版本。
这就是为什么你总是正确转义动态sql的用户输入:
SQL Fiddle demo for SQL injection