我正在开发一个框架,为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
答案 0 :(得分: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编写函数可能会更容易。
答案 1 :(得分:1)
您是否考虑过将记录变量作为文字表示? 理论上,每个记录变量都可以使用普通的CAST运算符转换为文本。
以下是修改过的函数,以便in_all
具有类型文本并在USING子句中转换为"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是记录变量时。