Fra*_*eil 10 postgresql trigger plpgsql composite-types
我正在编写一个验证触发器。触发器必须验证数组的总和是否等于另一个字段。由于我有许多此验证的实例,因此我想编写一个过程并创建多个触发器,每个触发器都有一组不同的要检查的字段。
例如,我有以下架构:
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
);
Run Code Online (Sandbox Code Playgroud)
验证必须确认impressions_count = sum(impressions_graph)。
我被卡住了,因为我不知道如何NEW从 plpgsql 中动态访问一个字段:
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');
Run Code Online (Sandbox Code Playgroud)
我试图执行动态命令这样做EXECUTE 'SELECT $1 FROM NEW' INTO total USING TG_ARGV[0],但是PL / pgSQL的抱怨,新的是一个未知的关系。
我专门针对 PostgreSQL 9.1。
Erw*_*ter 18
Actually, since NEW is a well defined composite type, you can just access any column with plain and simple attribute notation. SQL itself does not allow dynamic identifiers (table or column names etc.). But you can use dynamic SQL with EXECUTE in a PL/pgSQL function.
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;
Run Code Online (Sandbox Code Playgroud)
The cast to text is optional. Using it, because it works universally. If you know the type, you can work without casting ...
Using format() with %s, because the identifier is already escaped at that point.
Else, use format() with %I to safeguard against SQL injection.
Alternatively, in Postgres 9.3 or later, you can convert NEW to JSON with to_json() and access columns as keys:
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;
Run Code Online (Sandbox Code Playgroud)
Since the column name is not concatenated into an SQL string, SQL injection is not possible, and the name does not need to be escaped.
db<>fiddle here (with EXCEPTION instead of NOTICE to make the effect visible).
Related:
| 归档时间: |
|
| 查看次数: |
10326 次 |
| 最近记录: |