如何将记录传递给 PL/pgSQL 函数?
How to pass a record to a PL/pgSQL function?
我有8个类似的PL/pgSQL函数;它们用作视图的 INSTEAD OF INSERT/UPDATE/DELETE
触发器,使它们成为 writable。每个视图都组合了一个通用 table(在下面的示例中称为 "things")和一个特殊 table(下面的 "shaped_things" 和 "flavored_things")的列。顺便说一句,PostgreSQL 的继承特性不能用在我们的例子中。
触发器必须在通用 table 中 insert/update 行;这些部分在所有 8 个功能中都是相同的。由于通用 table 有 ~30 列,我试图在那里使用辅助函数,但我无法将视图的 NEW
记录传递给需要 things
的函数记录为输入。
(有人问过类似的问题 here and here,但我认为我不能在我的案例中应用建议的解决方案。)
简化架构
CREATE TABLE things (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
-- (plus 30 more columns)
);
CREATE TABLE flavored_things (
thing_id INT PRIMARY KEY REFERENCES things (id) ON DELETE CASCADE,
flavor TEXT NOT NULL
);
CREATE TABLE shaped_things (
thing_id INT PRIMARY KEY REFERENCES things (id) ON DELETE CASCADE,
shape TEXT NOT NULL
);
-- etc...
Writable 查看 flavored_things
的实现
CREATE VIEW flavored_view AS
SELECT t.*,
f.*
FROM things t
JOIN flavored_things f ON f.thing_id = t.id;
CREATE FUNCTION flavored_trig () RETURNS TRIGGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO things VALUES ( -- (A)
DEFAULT,
NEW.name
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
INSERT INTO flavored_things VALUES (
inserted_id,
NEW.flavor
);
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
UPDATE things SET -- (B)
name = NEW.name
-- (plus 30 more columns)
WHERE id = OLD.id;
UPDATE flavored_things SET
flavor = NEW.flavor
WHERE thing_id = OLD.id;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
DELETE FROM flavored_things WHERE thing_id = OLD.id;
DELETE FROM things WHERE id = OLD.id;
RETURN OLD;
END IF;
END;
$fun$ LANGUAGE plpgsql;
CREATE TRIGGER write_flavored
INSTEAD OF INSERT OR UPDATE OR DELETE ON flavored_view
FOR EACH ROW EXECUTE PROCEDURE flavored_trig();
上面标记为“(A)”和“(B)”的语句是我想用辅助函数调用替换的内容。
INSERT 的辅助函数
我最初的尝试是将语句“(A)”替换为
inserted_id = insert_thing(NEW);
使用这个函数
CREATE FUNCTION insert_thing (new_thing RECORD) RETURNS INTEGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
INSERT INTO things (name) VALUES (
new_thing.name
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
RETURN inserted_id;
END;
$fun$ LANGUAGE plpgsql;
失败并显示错误消息 "PL/pgSQL functions cannot accept type record".
当函数被调用为 insert_thing(NEW)
时,给参数类型 things
不起作用:"function insert_thing(flavored_view) does not exist".
此处似乎无法进行简单的转换; insert_thing(NEW::things)
产生 "cannot cast type flavored_view to things"。为每个视图编写一个 CAST 函数会删除我们通过使用辅助函数获得的内容。
有什么想法吗?
基本上您可以将记录转换为 hstore 变量并将 hstore 变量而不是记录变量传递给函数。您将记录转换为 hstore 即:
DECLARE r record; h hstore;
h = hstore(r);
您的辅助函数也应更改为:
CREATE FUNCTION insert_thing (new_thing hstore) RETURNS INTEGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
INSERT INTO things (name) VALUES (
new_thing -> 'name'
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
RETURN inserted_id;
END;
$fun$ LANGUAGE plpgsql;
然后调用:
inserted_id = insert_thing(hstore(NEW));
希望对您有所帮助
有多种选择,要看完整图。
基本上,您的插入函数可以像这样工作:
CREATE FUNCTION insert_thing (_thing <b>flavored_view</b>)
RETURNS int AS
$func$
INSERT INTO things (name) VALUES (.name) -- plus 30 more columns
RETURNING id;
$func$ LANGUAGE sql;
使用视图的行类型,因为你的触发器中的NEW
就是这种类型。
使用简单的 SQL 函数,它可以内联并且性能可能更好。
演示电话:
SELECT insert_thing('(1, foo, 1, bar)');
在你的触发器中 flavored_trig ()
:
inserted_id := insert_thing(NEW);
或者,基本上重写:
IF TG_OP = 'INSERT' THEN
INSERT INTO flavored_things(thing_id, flavor)
VALUES (insert_thing(NEW), NEW.flavor);
RETURN NEW;
ELSIF ...
record
不是 PL/pgSQL 之外的有效类型,它只是 PL/pgSQL 中未知行类型的通用占位符)因此您不能将它用作函数声明中的输入参数。
对于接受各种行类型 的更动态的函数,您可以使用 polymorphic type。示例:
- How to return a table by rowtype in PL/pgSQL
- Refactor a PL/pgSQL function to return the output of various SELECT queries
- How to write a function that returns text or integer values?
复合类型。 PostgresSQL 有这方面的文档,你基本上需要使用像
这样的东西
'()' 或 ROW() 构造要传递给函数的行的复合类型。
我有8个类似的PL/pgSQL函数;它们用作视图的 INSTEAD OF INSERT/UPDATE/DELETE
触发器,使它们成为 writable。每个视图都组合了一个通用 table(在下面的示例中称为 "things")和一个特殊 table(下面的 "shaped_things" 和 "flavored_things")的列。顺便说一句,PostgreSQL 的继承特性不能用在我们的例子中。
触发器必须在通用 table 中 insert/update 行;这些部分在所有 8 个功能中都是相同的。由于通用 table 有 ~30 列,我试图在那里使用辅助函数,但我无法将视图的 NEW
记录传递给需要 things
的函数记录为输入。
(有人问过类似的问题 here and here,但我认为我不能在我的案例中应用建议的解决方案。)
简化架构
CREATE TABLE things (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
-- (plus 30 more columns)
);
CREATE TABLE flavored_things (
thing_id INT PRIMARY KEY REFERENCES things (id) ON DELETE CASCADE,
flavor TEXT NOT NULL
);
CREATE TABLE shaped_things (
thing_id INT PRIMARY KEY REFERENCES things (id) ON DELETE CASCADE,
shape TEXT NOT NULL
);
-- etc...
Writable 查看 flavored_things
的实现CREATE VIEW flavored_view AS
SELECT t.*,
f.*
FROM things t
JOIN flavored_things f ON f.thing_id = t.id;
CREATE FUNCTION flavored_trig () RETURNS TRIGGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO things VALUES ( -- (A)
DEFAULT,
NEW.name
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
INSERT INTO flavored_things VALUES (
inserted_id,
NEW.flavor
);
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
UPDATE things SET -- (B)
name = NEW.name
-- (plus 30 more columns)
WHERE id = OLD.id;
UPDATE flavored_things SET
flavor = NEW.flavor
WHERE thing_id = OLD.id;
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
DELETE FROM flavored_things WHERE thing_id = OLD.id;
DELETE FROM things WHERE id = OLD.id;
RETURN OLD;
END IF;
END;
$fun$ LANGUAGE plpgsql;
CREATE TRIGGER write_flavored
INSTEAD OF INSERT OR UPDATE OR DELETE ON flavored_view
FOR EACH ROW EXECUTE PROCEDURE flavored_trig();
上面标记为“(A)”和“(B)”的语句是我想用辅助函数调用替换的内容。
INSERT 的辅助函数
我最初的尝试是将语句“(A)”替换为
inserted_id = insert_thing(NEW);
使用这个函数
CREATE FUNCTION insert_thing (new_thing RECORD) RETURNS INTEGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
INSERT INTO things (name) VALUES (
new_thing.name
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
RETURN inserted_id;
END;
$fun$ LANGUAGE plpgsql;
失败并显示错误消息 "PL/pgSQL functions cannot accept type record".
当函数被调用为 insert_thing(NEW)
时,给参数类型 things
不起作用:"function insert_thing(flavored_view) does not exist".
此处似乎无法进行简单的转换; insert_thing(NEW::things)
产生 "cannot cast type flavored_view to things"。为每个视图编写一个 CAST 函数会删除我们通过使用辅助函数获得的内容。
有什么想法吗?
基本上您可以将记录转换为 hstore 变量并将 hstore 变量而不是记录变量传递给函数。您将记录转换为 hstore 即:
DECLARE r record; h hstore;
h = hstore(r);
您的辅助函数也应更改为:
CREATE FUNCTION insert_thing (new_thing hstore) RETURNS INTEGER AS $fun$
DECLARE
inserted_id INT;
BEGIN
INSERT INTO things (name) VALUES (
new_thing -> 'name'
-- (plus 30 more columns)
) RETURNING id INTO inserted_id;
RETURN inserted_id;
END;
$fun$ LANGUAGE plpgsql;
然后调用:
inserted_id = insert_thing(hstore(NEW));
希望对您有所帮助
有多种选择,要看完整图。
基本上,您的插入函数可以像这样工作:
CREATE FUNCTION insert_thing (_thing <b>flavored_view</b>)
RETURNS int AS
$func$
INSERT INTO things (name) VALUES (.name) -- plus 30 more columns
RETURNING id;
$func$ LANGUAGE sql;
使用视图的行类型,因为你的触发器中的NEW
就是这种类型。
使用简单的 SQL 函数,它可以内联并且性能可能更好。
演示电话:
SELECT insert_thing('(1, foo, 1, bar)');
在你的触发器中 flavored_trig ()
:
inserted_id := insert_thing(NEW);
或者,基本上重写:
IF TG_OP = 'INSERT' THEN
INSERT INTO flavored_things(thing_id, flavor)
VALUES (insert_thing(NEW), NEW.flavor);
RETURN NEW;
ELSIF ...
不是 PL/pgSQL 之外的有效类型,它只是 PL/pgSQL 中未知行类型的通用占位符)因此您不能将它用作函数声明中的输入参数。record
对于接受各种行类型 的更动态的函数,您可以使用 polymorphic type。示例:
- How to return a table by rowtype in PL/pgSQL
- Refactor a PL/pgSQL function to return the output of various SELECT queries
- How to write a function that returns text or integer values?
复合类型。 PostgresSQL 有这方面的文档,你基本上需要使用像
这样的东西'()' 或 ROW() 构造要传递给函数的行的复合类型。