2012-04-07 55 views
1

我正在开发一个为PostgreSQL 9.1上的内容存储动态创建表的框架。其中一个API函数允许调用者通过指定给定对象(比如web表单)中的所有字段来保存新的内容条目。为了接收一组字段框架创建一个复合类型。是否可以避免在plpgsql函数中显式转换组合类型?

考虑下面的代码:

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 
+0

根据错误“PL/pgSQL函数不能接受类型记录”,我认为这是PL/pgSQL问题。所以其中一个选项可能是C中的这个功能。 – vyegorov 2012-04-07 14:22:49

回答

1

您是否考虑过将记录变量作为其文本表示? 从理论上讲,每个记录变量可以使用正常的CAST运算符在文本和文本之间进行转换。

下面是函数修改,使得in_all有输入文字,并得到了使用条款中铸造到"contentsType"

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是一个记录变量。

+0

我还没有使用自己的文本表示。感谢您指出这一点,我将采用这种解决方案,并将在'C'函数上工作,以防性能不可接受。 – vyegorov 2012-04-10 04:42:10

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(),你需要以某种方式提供关于记录类型的信息。所以,如果你想要做这样的事情,你的问题的简短答案是“不”。

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

+0

这就是一个例子。实际上,表的名称(在本例中为“内容”),它的结构和组合类型的结构将有所不同:表/类型的名称,列的数量,列的名称,列。 – vyegorov 2012-04-07 15:11:52

+0

还有一条评论。我真的想要一个通用的'push()'函数。我不能使用PostgreSQL为表创建的类型,因为表中有一些额外的字段,我没有公开给外部调用者。我也不想超负荷推。所以我会坚持C函数。感谢您的回答。 – vyegorov 2012-04-07 18:16:06

相关问题