1

PostgreSQL 9.1 でコンテンツ格納用のテーブルを動的に作成するフレームワークを開発しています。API 関数の 1 つを使用すると、呼び出し元は、特定のオブジェクト (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

レコード変数をテキスト表現として渡すことを検討しましたか? 理論的には、すべてのレコード変数は、通常の 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 に答える
1

挿入するテーブル名をハードコーディングしていて、必要なパラメーターの数とタイプが固定されているため、「contentsType」タイプが必要な理由がまったくわかりません。関数呼び出しから余分なレベルの括弧を削除して、4 つのパラメーターを直接渡すだけにしてみませんか? これにより、すべてがよりシンプルになります。

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 の強い型付けの問題を乗り越えるのは難しいかもしれませんが、完全に汎用的なプッシュ関数を 1 つ作成できると思います。C に慣れていれば、関数を C で書く方が簡単かもしれません。

于 2012-04-07T14:33:14.180 に答える