我有一个多租户数据库,每个租户都有自己的架构。每个模式都有一组用于全文搜索的实例化视图。
以下函数采用模式名称和表名称,并将它们连接为schema.table_name格式:
CREATE OR REPLACE FUNCTION create_table_name(_schema text, _tbl text, OUT result text)
AS 'select $1 || ''.'' || $2'
LANGUAGE SQL
它在PGAdmin中可以正常工作:
我试图在准备好的语句中使用此功能,如下所示:
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注入。
答案 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注入。您的功能容易受到攻击。