SQLは呼び出し時にリターンタイプを知る必要があるため、これを解決するのは困難です。 。
また、plpgsql関数には明確に定義されたリターンタイプが必要です。 。
匿名のレコードを返すことを選択した場合 、あなたはあなたが定義したものを手に入れます:匿名の記録。 Postgresは中身を知りません。したがって、列定義リストは必須です。 タイプを分解します。
正確な要件に応じて、さまざまな回避策があります。 呼び出し時にリターンタイプを知る方法がある場合 、多形タイプをお勧めします この回答の最後の章で概説されているように(「さまざまな完全なテーブルタイプ」):
PL / pgSQL関数をリファクタリングして、さまざまなSELECTクエリの出力を返します
ただし、関数内の実行時に戻り型に別の列を追加することはカバーしていません 。それは不可能です。 アプローチ全体を再考する 。
現在のアプローチとして、私が考えることができる最も近いものは、2回目の呼び出しでクエリを実行する一時テーブル(またはカーソル)です。 単一のトランザクション内 。
コードに他の問題がいくつかあります 。以下の注を参照してください。
概念実証
CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
RETURNS void AS -- no direct return type
$func$
DECLARE
-- appending _tmp for temp table
_tmp text := quote_ident(_tbl::text || '_tmp');
BEGIN
-- Create temp table only for duration of transaction
EXECUTE format(
'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);
IF EXISTS (
SELECT 1
FROM pg_attribute a
WHERE a.attrelid = _tbl
AND a.attname = 'infowindow'
AND a.attisdropped = FALSE)
THEN
EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
ELSE
-- This is assuming a NOT NULL column named "id"!
EXECUTE format($x$
ALTER TABLE %1$s ADD COLUMN infowindow text;
INSERT INTO %1$s
SELECT *, 'ID: ' || id::text
FROM %2$s $x$
,_tmp, _tbl);
END IF;
END
$func$ LANGUAGE plpgsql;
呼び出しは単一のトランザクションで行う必要があります。クライアントによっては、明示的なトランザクションを開始する必要がある場合があります。
BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp; -- do something with the returned rows
ROLLBACK; -- or COMMIT, does not matter here
SQLフィドル。
または、セッションの間、一時テーブルを存続させることもできます。ただし、繰り返し呼び出される名前の衝突には注意してください。
メモ
-
古い
ALIAS
の代わりにパラメータ名を使用してください コマンド。 -
現在のスキーマを実際に「デフォルト」にするには、表示するより単純なクエリを使用します。
regclass
の使用 トリックを自動的に行います。詳細:- PostgreSQL関数パラメータとしてのテーブル名
さらに、これにより、構文エラーやSQLインジェクションの可能性も回避されます。 元のコードの非標準(または悪意を持って不正な形式)のテーブル名から。
-
ELSE
のコード 句はまったく機能しません。 -
TABLE tbl;
基本的にはSELECT * FROM tbl;
の略です。 。 -
format()
の詳細 マニュアルで。