为何此PL/pgSQL函数返回<NULL>?求技术排查方案
问题原因与修复方案
你的函数返回NULL的核心原因是**query_string变量未初始化**,PL/pgSQL中未赋值的变量默认是NULL,而NULL和任何字符串拼接结果依然是NULL,所以循环里的赋值操作根本不会改变它的值。
另外,当前代码还存在严重的SQL注入风险——直接拼接用户传入的表名、列名和值,恶意输入会直接执行危险SQL。下面是修复后的完整函数:
DROP FUNCTION IF EXISTS api.func; CREATE OR REPLACE FUNCTION api.func ( _tables json, conditions json ) RETURNS text AS $$ DECLARE _key text ; _value text ; _table text ; where_clause text := ''; -- 初始化为空字符串 query_parts text[]; -- 用数组存储每个子查询,避免拼接时的UNION冗余 BEGIN -- 构建WHERE子句(处理空条件的情况) IF conditions <> '{}'::json THEN where_clause := 'WHERE '; FOR _key, _value IN SELECT * FROM json_each_text(conditions) LOOP -- 使用quote_ident处理列名,quote_literal处理值,防止SQL注入 where_clause := where_clause || quote_ident(_key) || ' = ' || quote_literal(_value) || ' AND '; END LOOP ; -- 移除末尾多余的' AND ' where_clause := rtrim(where_clause, ' AND '); END IF; -- 构建每个子查询并加入数组 FOR _table IN SELECT * FROM json_array_elements_text(_tables -> 'tabnames') LOOP -- 用quote_ident处理表名,避免标识符注入 query_parts := query_parts || format('(SELECT * FROM %s %s)', quote_ident(_table), where_clause); END LOOP ; -- 用UNION拼接所有子查询 RETURN string_agg(unnest(query_parts), ' UNION '); END ; $$ LANGUAGE plpgsql ;
关键修改点说明
- 初始化变量:把
query_string换成数组query_parts并自动初始化为空数组,若坚持用字符串则需先赋值query_string := '';。 - 防SQL注入:用
quote_ident()处理表名、列名(避免标识符注入),quote_literal()处理值(避免值注入),或用format()函数简化拼接逻辑。 - 边界处理:增加空条件判断,避免生成仅含
WHERE关键字的无效SQL。 - 更简洁的拼接方式:用数组存储子查询,最后用
string_agg完成拼接,无需手动处理末尾冗余的UNION。
测试调用
执行你的测试语句:
SELECT api.func('{"tabnames" : ["tabname1", "tabname2"]}'::json, '{"col" : "val"}'::json);
会返回正确的SQL字符串:
(SELECT * FROM tabname1 WHERE "col" = 'val') UNION (SELECT * FROM tabname2 WHERE "col" = 'val')
内容的提问来源于stack exchange,提问作者eslukas
相关产品推荐
相关产品推荐

