SET
を使用してセッション/トランザクションでユーザーのIDを記録したいので、_current_setting
_を使用して、トリガー関数で後でアクセスできるようにします。基本的に、私は 以前に投稿された非常に類似したチケット からオプションn2を試していますが、PG 10.1を使用している点が異なります。
私は変数を設定するために3つのアプローチを試みてきました:
SET local myvars.user_id = 4
_、それによってトランザクションでローカルに設定します。SET myvars.user_id = 4
_、それによりセッションでそれを設定します。SELECT set_config('myvars.user_id', '4', false)
は、最後の引数に応じて、前の2つのオプションのショートカットになります。これらはいずれも、_current_setting
_を介して変数を取得するときにNULL
を受け取るトリガーでは使用できません。以下は、トラブルシューティングのために考案したスクリプトです(postgres dockerイメージで簡単に使用できます)。
_database=$POSTGRES_DB
user=$POSTGRES_USER
[ -z "$user" ] && user="postgres"
psql -v ON_ERROR_STOP=1 --username "$user" $database <<-EOSQL
DROP TRIGGER IF EXISTS add_transition1 ON houses;
CREATE TABLE IF NOT EXISTS houses (
id SERIAL NOT NULL,
name VARCHAR(80),
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
PRIMARY KEY(id)
);
CREATE TABLE IF NOT EXISTS transitions1 (
id SERIAL NOT NULL,
house_id INTEGER,
user_id INTEGER,
created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
PRIMARY KEY(id),
FOREIGN KEY(house_id) REFERENCES houses (id) ON DELETE CASCADE
);
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS \$\$
DECLARE
user_id integer;
BEGIN
user_id := current_setting('myvars.user_id')::integer || NULL;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
\$\$ LANGUAGE plpgsql;
CREATE TRIGGER add_transition1 AFTER INSERT OR UPDATE ON houses FOR EACH ROW EXECUTE PROCEDURE add_transition1();
BEGIN;
%1% SELECT current_setting('myvars.user_id');
%2% SELECT set_config('myvars.user_id', '55', false);
%3% SELECT current_setting('myvars.user_id');
INSERT INTO houses (name) VALUES ('HOUSE PARTY') RETURNING houses.id;
SELECT * from houses;
SELECT * from transitions1;
COMMIT;
DROP TRIGGER IF EXISTS add_transition1 ON houses;
DROP FUNCTION IF EXISTS add_transition1;
DROP TABLE transitions1;
DROP TABLE houses;
EOSQL
_
結論は、関数は別のトランザクションと別の(?)セッションでトリガーされるということです。これは、すべて同じコンテキスト内で発生するように構成できるものですか?
customized optionのすべての可能なケースを適切に処理します。
オプションはまだ設定されていません
2番目のパラメーターmissing_ok
で呼び出されない限り、 current_setting()
を含め、すべての参照は exception を発生させます。 マニュアル:
setting_name
という名前の設定がない場合、current_setting
が指定され、true
でない限り、missing_ok
はエラーをスローします。
有効な整数リテラルに設定されたオプション
オプションが無効な整数リテラルに設定されています
オプションのリセット(3.の特殊なケースに焼き付きます)
たとえば、カスタマイズされたオプションをSET LOCAL
またはset_config('myvars.user_id3', '55', true)
で設定した場合、オプションの値はトランザクションの終了時にリセットされます。それはまだ存在します、参照できますが、空の文字列を返します(''
)-integer
にキャストできません。
デモの明らかな間違いは別として、4つのケースすべてに備える必要があります。そう:
CREATE OR REPLACE FUNCTION add_transition1()
RETURNS trigger AS
$func$
DECLARE
_user_id text := current_setting('myvars.user_id', true); -- see 1.
BEGIN
IF _user_id ~ '^\d+$' THEN -- one or more digits?
INSERT INTO transitions1 (user_id, house_id)
VALUES (_user_id::int, NEW.id); -- valid int, cast is safe
ELSE
INSERT INTO transitions1 (user_id, house_id)
VALUES (NULL, NEW.id); -- use NULL instead
RAISE WARNING 'Invalid user_id % for house_id % was reset to NULL!'
, quote_literal(_user_id), NEW.id; -- optional
END IF;
RETURN NULL; -- OK for AFTER trigger
END
$func$ LANGUAGE plpgsql;
db <>フィドル here
ノート:
列名と一致する変数名は避けてください。非常にエラーが発生しやすい。よく使用される命名規則の1つは、変数名の先頭にアンダースコアを付けることです:_user_id
。
宣言時に割り当てて、1つの割り当てを保存します。データ型text
に注意してください。無効な入力を整理した後、後でキャストします。
例外の発生/トラップは避けてください可能であれば。 マニュアル:
EXCEPTION
句を含むブロックは、ブロックを含まないブロックよりも、出入りするのに非常にコストがかかります。したがって、EXCEPTION
を必要なく使用しないでください。
有効な整数文字列をテストします。この単純な正規表現では、数字のみを使用できます(先行符号なし、空白なし):_user_id ~ '^\d+$'
。無効な入力がある場合は、NULLにリセットします。あなたのニーズに適応します。
デバッグの便宜のために、オプションのWARNING
を追加しました。
カスタマイズされたオプションが文字列リテラル(タイプtext
)であるため、3.
および4.
のケースが発生するだけで、有効なデータタイプを自動的に適用できません。
関連:
それはさておき、正確な要件に応じて、カスタマイズされたオプションなしで実行しようとしていることに、より洗練されたソリューションがあるかもしれません。多分これ:
NULL
をuser_id
に連結しようとしている理由は明らかではありませんが、それが明らかに問題の原因です。それを取り除く:
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS $$
DECLARE
user_id integer;
BEGIN
user_id := current_setting('myvars.user_id')::integer;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
ご了承ください
SELECT 55 || NULL
常にNULL
を与えます。
値が存在しないときに例外をキャッチできます。これを機能させるために行った変更は次のとおりです。
CREATE OR REPLACE FUNCTION add_transition1() RETURNS TRIGGER AS $$
DECLARE
user_id integer;
BEGIN
BEGIN
user_id := current_setting('myvars.user_id')::integer;
EXCEPTION WHEN OTHERS THEN
user_id := 0;
END;
INSERT INTO transitions1 (user_id, house_id) VALUES (user_id, NEW.id);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION insert_house() RETURNS void as $$
DECLARE
user_id integer;
BEGIN
PERFORM set_config('myvars.user_id', '55', false);
INSERT INTO houses (name) VALUES ('HOUSE PARTY');
END; $$ LANGUAGE plpgsql;