1

我正在开发一个框架,可以在 PostgreSQL 9.1 上为内容存储动态创建表。API 函数之一允许调用者通过指定给定对象(例如,Web 表单)中的所有字段来保存新的内容条目。为了接收一组字段,框架创建了一个复合类型。

考虑以下代码:

CREATE SEQUENCE seq_contents MINVALUE 10000;
CREATE TABLE contents (
    content_id      int8        not null,
    is_edited       boolean     not null default false,
    is_published    boolean     not null default false,
    "Input1"        varchar(60),
    "CheckBox1"     int2,
    "TheBox"        varchar(60),
    "Slider1"       varchar(60)
);
CREATE TYPE "contentsType" AS (
    "Input1"        varchar(60),
    "CheckBox1"     int2,
    "TheBox"        varchar(60),
    "Slider1"       varchar(60)
);
CREATE OR REPLACE FUNCTION push(in_all anyelement) RETURNS int8 AS $push$
DECLARE
    _c_id   int8;
BEGIN
    SELECT nextval('seq_contents') INTO _c_id;

    EXECUTE $$INSERT INTO contents
    SELECT a.*, b.*
      FROM (SELECT $1, true, false) AS a,
           (SELECT $2.*) AS b$$ USING _c_id, in_all;

    RETURN _c_id;
END;
$push$ LANGUAGE plpgsql;

现在,为了调用这个函数,我必须添加显式转换,如下所示:

SELECT push(('input1',1,'thebox','slider1')::"contentsType");

有没有办法避免显式转换?因为我希望外部调用者不要处理强制转换,即隐藏 PostgreSQL 函数背后的逻辑。目前我有这样的错误:

SELECT push(('input1',1,'thebox','slider1'));
ERROR:  PL/pgSQL functions cannot accept type record
CONTEXT:  compilation of PL/pgSQL function "push" near line 1
4

2 回答 2

1

由于您正在对要插入的表名进行硬编码,并且您需要固定数量和类型的参数,所以我不清楚为什么需要“contentsType”类型。为什么不在函数调用中去掉多余的括号,直接传递四个参数呢?这让一切变得更简单。

CREATE OR REPLACE FUNCTION push(
    "Input1"        varchar(60),
    "CheckBox1"     int2,
    "TheBox"        varchar(60),
    "Slider1"       varchar(60)
) RETURNS int8 AS $push$
DECLARE
    _c_id   int8;
BEGIN
    SELECT nextval('seq_contents') INTO _c_id;

    EXECUTE $$INSERT INTO contents
    VALUES ($1, true, false, $2, %3, %4, $5)
      $$ USING _c_id, "Input1", "CheckBox1", "TheBox", "Slider1");

    RETURN _c_id;
END;
$push$ LANGUAGE plpgsql;

这使得调用函数看起来像这样:

SELECT push('input1',1,'thebox','slider1');

如果您希望推广 push() 函数以使其适用于所有表,那么如果您超过了这个问题,您将遇到其他问题。您将无法忘记函数在执行期间需要知道表名的事实。如果您想重载该函数以便可以为每种记录类型提供单独的 push(),则需要以某种方式提供有关记录类型的信息。所以,如果你想做这样的事情,你的问题的简短回答是“不”。

另一方面,您可能会使这比需要的更难。我希望您知道,会自动为每个表创建一个类型,与该表同名。您可能可以利用它来避免显式声明类型并传递与您的表同名的记录 - 对于函数将填充的值具有虚拟条目。我认为您可以制作一个完全通用的推送功能,尽管可能很难克服 plpgsql 中的强类型问题;如果您熟悉它,用 C 编写函数可能会更容易。

于 2012-04-07T14:33:14.180 回答
1

您是否考虑过将记录变量作为其文本表示形式传递?理论上,每个记录变量都可以使用普通的 CAST 运算符转换为文本。

这是修改后的函数,使其in_all具有 text 类型并"contentsType"在 USING 子句中转换为:

CREATE OR REPLACE FUNCTION push(in_all text) RETURNS int8 AS $push$
DECLARE
    _c_id   int8;
BEGIN
    SELECT nextval('seq_contents') INTO _c_id;

    EXECUTE $$INSERT INTO contents
    SELECT a.*, b.*
      FROM (SELECT $1, true, false) AS a,
           (SELECT $2.*) AS b$$ USING _c_id, in_all::"contentsType";

    RETURN _c_id;
END;
$push$ LANGUAGE plpgsql;

然后可以这样调用(没有显式引用类型)

select push( '(input1,1,thebox,slider1)' );

或类似的(显式记录转换为文本)

SELECT push(('input1',1,'thebox','slider1')::"contentsType"::text);

这不仅适用于“contentsType”,还适用于任何其他记录类型,假设该函数能够将其转换回该类型。

同样在 plpgsql 中,我认为这也应该有效:

   ret := push(r::text);

当 r 是记录变量时。

于 2012-04-08T01:31:43.200 回答