使用函数的输出作为表名

时间:2019-08-20 05:09:48

标签: postgresql

我有一个多租户数据库,每个租户都有自己的架构。每个模式都有一组用于全文搜索的实例化视图。

以下函数采用模式名称和表名称,并将它们连接为schema.table_name格式:

CREATE OR REPLACE FUNCTION create_table_name(_schema text, _tbl text, OUT result text)
        AS 'select $1 || ''.'' || $2'
        LANGUAGE SQL

它在PGAdmin中可以正常工作:

enter image description here

我试图在准备好的语句中使用此功能,如下所示:

SELECT p.id AS id,
        ts_rank(
          p.document, plainto_tsquery(unaccent(?))
        ) AS rank
        FROM create_table_name(?, 'project_search') AS p
        WHERE p.document @@ plainto_tsquery(unaccent(?))
        OR p.name ILIKE ?

但是,当我运行它时,出现以下错误:

ERROR 42703 (undefined_column) column p.id does not exist

如果我“硬编码”模式和表名,它将起作用。

为什么会出现此错误?

P.S。我应该注意,我知道这种方法的危险,但是架构名称始终来自应用程序内部,因此我不必担心SQL注入。

1 个答案:

答案 0 :(得分:0)

您想在查询中使用函数结果作为表名,但是实际上您正在做的就是使用函数作为表函数。该“表”只有一行和一列,称为result,它解释了错误消息。

为此,您需要动态SQL,例如通过在DO语句中使用PL / pgSQL代码:

DO
$$DECLARE
   ...
BEGIN
   EXECUTE
      format(
         E'SELECT p.id AS id,\n'
         '       ts_rank(\n'
         '          p.document,\n'
         '          plainto_tsquery(unaccent(?))\n'
         '       ) AS rank\n'
         'FROM %I.project_search AS p\n'
         'WHERE p.document @@ plainto_tsquery(unaccent($1))\n'
         'OR p.name ILIKE $2',
         schema_name
      )
   USING fts_query, like_pattern
   INTO var1, ...;
   ...
$$;

要处理多个结果行,您可以使用FOR循环-这只是一个简单的示例来说明原理。

请注意如何将format%I模式一起使用以避免SQL注入。您的功能容易受到攻击。

相关问题