検証トリガーを書いています。トリガーは、配列の合計が別のフィールドと等しいことを検証する必要があります。この検証には多くのインスタンスがあるため、単一のプロシージャを記述して、チェックするフィールドのセットがそれぞれ異なる複数のトリガーを作成します。
たとえば、次のスキーマがあります。
_CREATE TABLE daily_reports(
start_on date
, show_id uuid
, primary key(start_on, show_id)
-- _graph are hourly values, while _count is total for the report
, impressions_count bigint not null
, impressions_graph bigint[] not null
-- interactions_count, interactions_graph
-- Twitter_interactions_count, Twitter_interactions_graph
);
_
検証では、impressions_count = sum(impressions_graph)
であることを確認する必要があります。
Plpgsql内からNEW
からフィールドに動的にアクセスする方法がわからないので、行き詰まっています。
_CREATE FUNCTION validate_sum_of_array_equals_other() RETURNS TRIGGER AS $$
DECLARE
total bigint;
array_sum bigint;
BEGIN
-- TG_NARGS = 2
-- TG_ARGV[0] = 'impressions_count'
-- TG_ARGV[1] = 'impressions_graph'
-- How to access impressions_count and impressions_graph from NEW?
RETURN NEW;
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER validate_daily_reports_impressions
ON daily_reports BEFORE INSERT OR UPDATE
FOR EACH ROW EXECUTE
validate_sum_of_array_equals_other('impressions_count', 'impressions_graph');
_
_EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0]
_を実行して 動的コマンドの実行 を試しましたが、PL/PGsqlはNEWが不明な関係であると文句を言っています。
特にPostgreSQL 9.1をターゲットにしています。
実際、NEW
は明確に定義された複合型であるため、単純で単純な属性表記で任意の列にアクセスできます。 SQL自体は動的識別子(テーブル名や列名など)を許可しません。ただし、PL/pgSQL関数で 動的SQLをEXECUTE
とともに使用できます。
_CREATE OR REPLACE FUNCTION trg_demo1()
RETURNS TRIGGER AS
$func$
DECLARE
_col_value text;
_col_name text := quote_ident(TG_ARGV[0]); -- escape identifier
BEGIN
EXECUTE format('SELECT ($1).%s::text', _col_name)
USING NEW
INTO _col_value;
-- do something with _col_value ...
RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', _col_name, _col_value;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
_
text
へのキャストはオプションです。普遍的に機能するため、それを使用します。タイプを知っている場合、キャストせずに作業できます...
_%s
_でformat()
を使用します。これは、識別子がその時点ですでにエスケープされているためです。
または、SQLインジェクションから保護するために、format()
を_%I
_とともに使用します。
または、Postgres 9.3以降では、 to_json()
を使用してNEW
をJSONに変換できます および列をキーとしてアクセス:
_CREATE OR REPLACE FUNCTION trg_demo2()
RETURNS TRIGGER AS
$func$
DECLARE
_col_value text := to_json(NEW) ->> TG_ARGV[0]; -- no need to escape identifier
BEGIN
RAISE NOTICE 'It works. The value of NEW.% is >>%<<.', TG_ARGV[0], _col_value;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
_
列名はSQL文字列に連結されないため、SQLインジェクションは不可能であり、名前をエスケープする必要はありません。
db <> fiddle here(EXCEPTION
の代わりにNOTICE
を使用して、効果を表示します) 。
関連: