使用动态 SQL 的 PL/pgSQL 函数中存在语法错误

问题描述 投票:0回答:1

我在 PostgreSQL 10 中使用 PL/pgSQL 来创建复杂的查询。我正在使用几个

JOIN
AND
测试查询。这是我到目前为止所拥有的:

DROP FUNCTION IF EXISTS search_person(name text);
CREATE  FUNCTION search_person(name text) RETURNS TABLE(address_id integer, address_geom text, event_name text) AS $$
--DECLARE 

BEGIN
    RETURN QUERY EXECUTE 
    'SELECT address.id, event.name, address.geom 
    FROM  event JOIN person JOIN address JOIN person_address JOIN event_person
    WHERE 
    person_address.event_id = event.id AND
    event_person.event_id = event.id AND
    person.id = event_person.person_id AND
    person.name like
    $1'        
    
    USING name;
END;
$$
LANGUAGE plpgsql;

创建此函数时没有出现任何错误。我这样称呼它

select search_person('nick');
,我得到:

ERROR:  syntax error at or near "WHERE"
LINE 3:     WHERE 
            ^
QUERY:  SELECT address.id, event.name, address.geom 
    FROM  event JOIN person JOIN address JOIN person_address JOIN event_person
    WHERE 
    person_address.event_id = event.id AND
    event_person.event_id = event.id AND
    person.id = event_person.person_id AND
    person.name like
    $1
CONTEXT:  PL/pgSQL function search_creator(text) line 5 at RETURN QUERY
SQL state: 42601

我尝试在

AND
子句中将
||
替换为
WHERE
,但没有任何改变。

这是我现在拥有的代码,尽管根据我检查的数据库数据我应该得到结果,但我得到一个空表。

CREATE  FUNCTION search_person(name character(600)) RETURNS TABLE(address_id bigint, address_geom geometry, event_name character(200)) AS $$

BEGIN
    RETURN QUERY EXECUTE 
    'SELECT address.id, address.geom, event.name        

    FROM
    person 
    JOIN event_creator ON event_person.person_id = person.id
    JOIN event ON event.id = event_person.event_id 
    JOIN person_address ON person_address.event_id = event.id 
    JOIN address ON address.id = cep.address_id
    
    WHERE person.name LIKE $1'
    USING name;
    
END;
$$
LANGUAGE plpgsql;

我该怎么办?

sql postgresql where-clause plpgsql dynamic-sql
1个回答
2
投票

PL/pgSQL 函数的函数体按原样保存为字符串文字。在创建时仅运行表面语法检查。包含的语句并未实际执行或进行更深层次的测试。

在实际的 SQL 语句中仍然会检测到查询字符串中的基本语法错误。但您正在使用

动态 SQL 和 EXECUTE

。该语句以嵌套字符串文字形式传递,您独自承担责任。

这似乎一开始就被误导了。动态 SQL 没有明显的原因。

(除非您的数据分布非常不均匀并且想要强制 Postgres 为每个输入值生成自定义计划。)

普通的 SQL 语句会立即生成错误消息:

CREATE OR REPLACE FUNCTION search_person(name text) -- still incorrect! RETURNS TABLE(address_id integer, address_geom text, event_name text) LANGUAGE plpgsql AS $func$ BEGIN RETURN QUERY SELECT address.id, event.name, address.geom FROM event JOIN person JOIN address JOIN person_address JOIN event_person WHERE person_address.event_id = event.id AND event_person.event_id = event.id AND person.id = event_person.person_id AND person.name like $1; -- this $1 refers to function parameter! END $func$;
SQL语句仍然无效。 

[INNER] JOIN

 需要一个连接条件 - 就像 Nick 评论的那样。而且我根本不认为有必要使用 PL/pgSQL。一个简单的 SQL 函数 应该可以很好地发挥作用:

CREATE FUNCTION search_person(name text) RETURNS TABLE(address_id integer, address_geom text, event_name text) LANGUAGE sql AS $func$ SELECT a.id, a.geom, e.name -- column order matching return type! FROM person AS p JOIN event_person AS ep ON ep.person_id = p.id JOIN event AS e ON e.id = ep.event_id JOIN person_address AS pa ON pa.event_id = e.id JOIN address AS a ON a.id = pa.address_id -- missing join condition! WHERE p.name LIKE $1; $func$;
我重写了查询以修复语法错误,使用表别名以获得更好的可读性。还根据有根据的猜测添加了一个缺失的条件:

a.id = pa.address_id

现在应该可以了。

相关:

  • PL/pgSQL 函数未按预期插入数据
  • PostgreSQL函数中sql语言和plpgsql语言的区别
或者根本没有函数,只需使用

prepared statements 代替。示例:

  • 拆分给定字符串并准备 case 语句
如果您毕竟需要动态 SQL,请像以前一样使用

USING 子句传递 values

,并确保在 
concatenating 查询时防止 SQL 注入。 Postgres 提供了各种工具:

  • Postgres 函数中的 SQL 注入与准备好的查询
  • 将表和列名称定义为 plpgsql 函数中的参数?
  • 表名作为 PostgreSQL 函数参数
© www.soinside.com 2019 - 2024. All rights reserved.