既存のテーブル名にサフィックスを追加して、新しいテーブル名を作成したいと考えています。 Postgres 9.5 PL/pgSQL関数は、regclass
タイプとして渡された既存のテーブル名を取得し、新しい名前を文字列として返します。私はformat()
を使用して新しい名前を作成しており、通常は_%I
_プレースホルダーを使用します。これは、渡されたテーブル名がどのPL/pgSQLキーワードとも一致しない限り機能します。この場合、どのタイプのコンポーネントを選択しても(_%I
_または_%s
_)、引用は正しくありません。
次の関数を考えます。
_CREATE OR REPLACE FUNCTION make_name(old_name regclass)
RETURNS text
LANGUAGE plpgsql AS
$$
BEGIN
RETURN format('%I_new', old_name);
END;
$$;
_
さらに、treenode
とoverlay
の2つのテーブルがあると仮定します。両方に対してこの関数を呼び出すと、次の新しい名前になります。
_SELECT make_name('overlay'::regclass);
make_name
-------------------
"""overlay"""_new
(1 row)
SELECT make_name('treenode'::regclass);
make_name
--------------
treenode_new
(1 row)
_
結局のところ、overlay
はPL/pgSQL関数でもあり(format
と同じですが、treenode
はそうではありません)、引用動作を変更しているようです。 _%s
_をformat()
と共に使用すると、結果は_"overlay"_new
_になります。 _||
_演算子を使用しても同じことが起こります。入力パラメーターの型としてtext
を使用すると、すべてが期待どおりに機能しますが、regclass
を使用することをお勧めします。
キーワードに一致するregclass
テーブル名(例:overlay
)の場合と同様に、キーワードに一致するregclass
テーブル名(例:treenode
)を引用符なしでフォーマットする方法はありますか?
誤解の複数の層を1つずつ確認していきます-シンプルで安全なソリューションに到達します。
overlay
が引用符で囲まれている理由は、ではない関数名だからですが、 予約語 だからです。
また、 overlay()
はではありません「PL/pgSQL関数」(またはPL/pgSQLキーワードではありません)、これはPostgresコアに組み込まれた標準SQL関数です(_LANGUAGE internal
_、Cはカーテンの後ろにあります)。実際、PL/pgSQLはこれとは何の関係もありません。
単純な論理エラー。
format('%I_new', old_name)
これは、 '_ new'が追加される前に二重引用符で囲まれた非標準の識別子をエスケープし、text
を入力タイプとして使用しても失敗します。全体を引用する前に '_ new'を追加する必要があります:
_format('%I', old_name || '_new')
_
しかし、それはregclass
ではまだ機能しません。読み続けます。
_%I
_を使用してregclass
変数に引用符を追加することは意味がありません。そのテキスト表現は、必要に応じてすでに自動的に引用されているためです。 (quote_ident()
に似ていますが、NULLにすることはできません。入力がregclass
であるため、最初はNULLにすることはできません。)これを適用しますtwice。 regclass
入力(常に文字列)には常に_%s
_を使用します。
format()
はこのためにやりすぎです。 quote_ident()
を使用するだけです:
_quote_ident(old_name || '_new')
_
最も重要なこと、前述のように、text
表現のregclass
変数は自動的にエスケープされます。それはキャストに組み込まれています。生のテキストを取得するには、それを システムカタログ_pg_class
_ から直接取得します。 regclass
value は、このテーブルの内部ではoid
にすぎません。
_SELECT relname FROM pg_class WHERE oid = $1;
_
これはあなたが探していることを行います:
_CREATE OR REPLACE FUNCTION make_name(old_name regclass)
RETURNS text AS
$func$
SELECT quote_ident(relname || '_new') FROM pg_class WHERE oid = $1;
$func$ LANGUAGE sql STABLE;
_
_CREATE TEMP TABLE "sUICiDAL' namE"();
CREATE TEMP TABLE overlay();
SELECT make_name('overlay') AS n1
, make_name('"sUICiDAL'' namE"') AS n2;
n1 | n2
-------------+----------------------
overlay_new | "sUICiDAL' namE_new"
_
_overlay_new
_は完全に正当な識別子であるため、引用されていないことに注意してください。
名前をエスケープしない(二重引用符なし)にしたい場合は、次のように使用します。
_SELECT relname || '_new' FROM pg_class WHERE oid = $1;
_
format
関数への呼び出しを次のようにtranslate
でラップすることが、私が考えた唯一の方法です。
RETURN translate(format('%I_new',old_name),'"','');
このポストは、フォーマットの出力を処理し、 "文字を取り除きます。
誰かがよりエレガントな解決策を持っているかどうかはわかりません。私はbtrimやrtrimなどを考えましたが、それぞれを呼び出す必要があります。