@a_horse解决了您尝试失败的大部分严重问题。
但是,没有人应该使用这个。以下分步说明应该会导致使用现代 PostgreSQL 进行合理的实施。
阶段 1:消除错误和恶作剧
到目前为止我们有什么:
CREATE OR REPLACE FUNCTION foo(tbl varchar, col varchar)
RETURNS varchar[] LANGUAGE plpgsql AS
$BODY$
DECLARE
_r record;
points varchar[] := '{}';
i int := 0;
BEGIN
FOR _r IN
EXECUTE 'SELECT a.'|| quote_ident(col) || ' AS pt
FROM ' || quote_ident (tbl) ||' AS a'
LOOP
i = i + 1; -- reversed order to make array start with 1
points[i] = _r;
END LOOP;
RETURN points;
END;
$BODY$;
第 2 阶段:去除杂物,使其有用
为简单起见,使用/text
代替。不过,两者都有效。character varying
varchar
您正在选择单个列,但使用类型的变量record
。这样,整个记录被强制转换为text
,其中包括括号。几乎没有任何意义。请改用text
变量。如果您显式转换为( ) ,则适用于任何列。任何类型都可以转换为.text
::text
text
初始化变量没有意义point
。它可以从NULL
这里开始。
在这种情况下,内部的表和列别名EXECUTE
是没有用的。动态执行的 SQL 有自己的作用域!
在 plpgsql 函数的;
final 之后不需要分号 ( )。END
使用 .将每个值附加到数组中更简单||
。
几乎是理智的:
CREATE OR REPLACE FUNCTION foo1(tbl text, col text)
RETURNS text[] LANGUAGE plpgsql AS
$func$
DECLARE
point text;
points text[];
BEGIN
FOR point IN
EXECUTE 'SELECT '|| quote_ident(col) || '::text FROM ' || quote_ident(tbl)
LOOP
points = points || point;
END LOOP;
RETURN points;
END
$func$;
第 3 阶段:让它在现代 PL/pgSQL 中大放异彩
如果将表名作为 传递text
,则会造成模棱两可的情况。您可以使用or很好地阻止SQLi,但是对于您之外的表,这将失败。
然后您需要添加schema-qualification,这会创建一个模棱两可的值。'xy' 可以代表表名“xy”或模式限定的表名“x”.“y”。你不能传递 "x"."y" 因为它会被转义到 """x"".""y"""。您需要为模式名称使用附加参数,或者在强制使用时根据需要自动引用一个类型的参数,这是这里的优雅解决方案。format()
quote_ident()
search_path
regclass
regclass
text
newformat()
比多个(甚至单个)quote_ident()
调用更简单。
您没有指定任何订单。SELECT
以任意顺序返回行,不带ORDER BY
. 这可能看起来很稳定,因为只要基础表不改变,结果通常是可重现的。但这是 100% 不可靠的。您可能想要添加某种ORDER BY
.
最后,您根本不需要循环。SELECT
使用带有Array 构造函数的纯文本。
使用OUT
参数进一步简化代码
适当的解决方案:
CREATE OR REPLACE FUNCTION f_arr(tbl regclass, col text, OUT arr text[])
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT ARRAY(SELECT %I::text FROM %s ORDER BY 1)', col, tbl)
INTO arr;
END
$func$;
称呼:
SELECT f_arr('myschema.mytbl', 'mycol');