如何将记录传递给PL/pgSQL函数? [英] How to pass a record to a PL/pgSQL function?

查看:122
本文介绍了如何将记录传递给PL/pgSQL函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have 8 similar PL/pgSQL functions; they are used as INSTEAD OF INSERT/UPDATE/DELETE triggers on views to make them writable. The views each combine columns of one generic table (called "things" in the example below) and one special table ("shaped_things" and "flavored_things" below). PostgreSQL's inheritance feature can't be used in our case, by the way.

触发器必须在通用表中插入/更新行;这些部分在所有8个功能中都是相同的.由于通用表具有约30列,因此我试图在其中使用辅助函数,但是在将视图的NEW记录传递给需要things记录作为输入的函数时遇到了麻烦.

The triggers have to insert/update rows in the generic table; these parts are identical across all 8 functions. Since the generic table has ~30 columns, I'm trying to use a helper function there, but I'm having trouble passing the view's NEW record to a function that needs a things record as input.

(在此处此处,但我没有认为我可以在我的情况下应用建议的解决方案.)

(Similar questions have been asked here and here, but I don't think I can apply the suggested solutions in my case.)

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

favoured_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)"的语句.

The statements marked "(A)" and "(B)" above are what I would like to replace with a call to a helper function.

我最初的尝试是将语句(A)"替换为

My initial attempt was to replace statement "(A)" with

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

This fails with the error message "PL/pgSQL functions cannot accept type record".

当调用函数insert_thing(NEW)时,给参数类型things无效:函数insert_thing(flavored_view)不存在"..

Giving the parameter the type things doesn't work when the function is called as insert_thing(NEW): "function insert_thing(flavored_view) does not exist".

这里似乎不提供简单的转换; insert_thing(NEW::things)产生无法将类型的flavored_view转换为事物" .为每个视图编写CAST函数将删除我们通过使用辅助函数获得的成果.

Simple casting doesn't seem to be available here; insert_thing(NEW::things) produces "cannot cast type flavored_view to things". Writing a CAST function for each view would remove what we gained by using a helper function.

有什么想法吗?

推荐答案

有多种选择,具体取决于完整图片.
基本上,您的插入函数可以像这样工作:

There are various options, depending on the complete picture.
Basically, your insert function could work like this:

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函数,该函数可以内联并且可能会更好.

Using the row type of the view, because NEW in your trigger is of this type.
Use a simple SQL function, which can be inlined and might perform better.

演示呼叫:

SELECT insert_thing('(1, foo, 1, bar)');

在触发器内flavored_trig ():

inserted_id := insert_thing(NEW);

或者,基本上是重写的:

Or, basically rewritten:

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中未知行类型的通用占位符),因此您不能将其用于函数声明.

record is not a valid type outside PL/pgSQL, it's just a generic placeholder for a yet unknown row type in PL/pgSQL) so you cannot use it for an input parameter in a function declaration.

对于接受各种行类型的更具动态性的功能,您可以使用

For a more dynamic function accepting various row types you could use a polymorphic type. Examples:

  • 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?

这篇关于如何将记录传递给PL/pgSQL函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆