如何将记录传递给PL / pgSQL函数?

时间:2015-01-23 12:50:21

标签: postgresql function parameters triggers plpgsql

我有8个类似的PL / pgSQL函数;它们在视图上用作INSTEAD OF INSERT/UPDATE/DELETE触发器,使它们可写。每个视图组合了一个通用表的列(在下面的示例中称为"事物")和一个特殊的表(" shaped_things"和" flavored_things"在下面)。顺便说一句,PostgreSQL的继承功能不能在我们的案例中使用。

触发器必须在通用表中插入/更新行;这些部分在所有8个功能中是相同的。由于通用表有大约30列,我试图在那里使用辅助函数,但是我无法将视图的NEW记录传递给需要{的things记录的函数{1}}记录为输入。

(类似问题已被问及herehere,但我认为我不能在我的案例中应用建议的解决方案。)

简化架构

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...

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函数无法接受类型记录"

当函数被调用为things时,赋予insert_thing(NEW)类型的参数不起作用:&#34;函数insert_thing(flavored_view)不存在&#34; < / em>的

这里似乎没有简单的铸造; insert_thing(NEW::things)生成&#34;无法将类型flavoured_view转换为&#34; 。为每个视图编写CAST函数将删除我们通过使用辅助函数获得的功能。

有什么想法吗?

3 个答案:

答案 0 :(得分:9)

根据完整图片,有多种选择 基本上,您的插入功能可以这样工作:

CREATE FUNCTION insert_thing (_thing flavored_view)
   RETURNS int AS
$func$
   INSERT INTO things (name) VALUES ($1.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。例子:

答案 1 :(得分:1)

基本上,您可以将记录转换为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));

希望有所帮助

答案 2 :(得分:0)

复合类型。 PostgresSQL有关于此的文档,你基本上需要使用像

这样的东西

'()'或ROW()构造要传递给函数的行的复合类型。