patternMinor
Pass Parameters to trigger function to execute dynamic SQL
Viewed 0 times
triggerpasssqlfunctiondynamicparametersexecute
Problem
I have this code which was solved by Erwin:
Execute dynamic INSERT ON CONFLICT DO UPDATE in plpgsql function
Now I want the trigger to call the trigger function
There are different models, that's why I'm trying to simplify things by using dynamic SQL.
Function:
Trigger:
The trigger passes the model and process date parameters to the trigger function
ERROR: relation "model_cd201707" does not exist
LINE 9: FROM model_cd201707
How to properly use the "$x$" on the
Execute dynamic INSERT ON CONFLICT DO UPDATE in plpgsql function
Now I want the trigger to call the trigger function
insert_data_func with parameters (model_cd, processdate).There are different models, that's why I'm trying to simplify things by using dynamic SQL.
Function:
CREATE OR REPLACE FUNCTION insert_data_func()
RETURNS TRIGGER AS
$func$
DECLARE
model_cd text;
processdate text;
BEGIN
model_cd := TG_ARGV[0];
processdate := TG_ARGV[1];
EXECUTE
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine
, $1.model
, split_part(lot, '_', 1)
, right(lot, position('_' IN lot) * -1)
, COUNT(lot)
, $1.datetimestamp
FROM model$x$ || to_char(now(), 'YYYYMM') || '
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique
DO UPDATE
SET machine = $1.machine
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp'
USING NEW;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;Trigger:
DO$
BEGIN
EXECUTE $x$CREATE TRIGGER insert_data_trigger
AFTER INSERT ON modelsample$x$ || to_char(now(), 'YYYYMM') ||
' FOR EACH ROW EXECUTE PROCEDURE
insert_data_func' ||
(''modelsample''' || ',' || to_char(now(), 'YYYYMM') || ')';
END
$;The trigger passes the model and process date parameters to the trigger function
insert_data_func(). But I get this eror msg.:ERROR: relation "model_cd201707" does not exist
LINE 9: FROM model_cd201707
How to properly use the "$x$" on the
insert_data_func() function because I think that's the reason why the model name does not get the value from model_cdvariable.Solution
The error msg says
That aside, after assigning passed parameter values to the variables
Assuming your 2 passed parameters are supposed to be used to build the table name, it might work like this:
Trigger (passing a single concatenated parameter as table name):
Note how I use
You may want to schema-qualify function and table names to be unambiguous. Like
Be aware that the date component built into the trigger definition is immutable once created. Only the creating statement itself (and the trigger function) are "dynamic".
Related:
But that seems like needless complication. No need to pass the name of the table for which the trigger is called. Some special variables are available automatically in plpgsql trigger functions. Among others:
And no parameters in the trigger itself.
relation "model_cd201707" does not exist. I suppose there is no table of that name in the search_path then? There is a confusion of table names in your question. modelsample..., model_cd..., model... tb_moldsummary. Something is not right here ...That aside, after assigning passed parameter values to the variables
model_cd and processdate you are not using them at all (yet).Assuming your 2 passed parameters are supposed to be used to build the table name, it might work like this:
CREATE OR REPLACE FUNCTION trg_insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN
EXECUTE format(
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine
, $1.model
, split_part($1.lot, '_', 1)
, right($1.lot, position('_' IN lot) * -1)
, COUNT(*) -- never null in this query
, $1.datetimestamp
FROM %I
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique -- constraint has same name?
DO UPDATE
SET machine = $1.machine
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp
$x$, TG_ARGV[0]
)
USING NEW;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;Trigger (passing a single concatenated parameter as table name):
DO
$
BEGIN
EXECUTE format(
'CREATE TRIGGER insert_data_trigger
AFTER INSERT ON %1$s
FOR EACH ROW EXECUTE PROCEDURE trg_insert_data_func(%1$L)'
, 'modelsample' || to_char(now(), 'YYYYMM')
);
END
$;Note how I use
%1$s for the (unquoted) table name in the SQL statement and %1$L for the parameter (quoted as string literal). %I is not needed in this case for a known legal identifier. See the manual on format() for details.You may want to schema-qualify function and table names to be unambiguous. Like
public.modelsample. More:- Special syntax sth.name() in the CREATE FUNCTION statement, what does it mean?
Be aware that the date component built into the trigger definition is immutable once created. Only the creating statement itself (and the trigger function) are "dynamic".
Related:
- Trigger function taking column names as parameters to modify the row
But that seems like needless complication. No need to pass the name of the table for which the trigger is called. Some special variables are available automatically in plpgsql trigger functions. Among others:
TG_TABLE_SCHEMA and TG_TABLE_NAME. So use dynamic SQL in the trigger function, but the trigger itself can be simple and static:CREATE OR REPLACE FUNCTION trg_insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN
EXECUTE format(
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine
, $1.model
, split_part($1.lot, '_', 1)
, right($1.lot, position('_' IN lot) * -1)
, COUNT(*) -- never null in this query
, $1.datetimestamp
FROM %I.%I -- !!
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique -- constraint has same name?
DO UPDATE
SET machine = $1.machine
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp
$x$, TG_TABLE_SCHEMA, TG_TABLE_NAME -- !!
)
USING NEW;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;And no parameters in the trigger itself.
Code Snippets
CREATE OR REPLACE FUNCTION trg_insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN
EXECUTE format(
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine
, $1.model
, split_part($1.lot, '_', 1)
, right($1.lot, position('_' IN lot) * -1)
, COUNT(*) -- never null in this query
, $1.datetimestamp
FROM %I
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique -- constraint has same name?
DO UPDATE
SET machine = $1.machine
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp
$x$, TG_ARGV[0]
)
USING NEW;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;DO
$$
BEGIN
EXECUTE format(
'CREATE TRIGGER insert_data_trigger
AFTER INSERT ON %1$s
FOR EACH ROW EXECUTE PROCEDURE trg_insert_data_func(%1$L)'
, 'modelsample' || to_char(now(), 'YYYYMM')
);
END
$$;CREATE OR REPLACE FUNCTION trg_insert_data_func()
RETURNS TRIGGER AS
$func$
BEGIN
EXECUTE format(
$x$INSERT INTO tb_moldsummary AS t
SELECT $1.machine
, $1.model
, split_part($1.lot, '_', 1)
, right($1.lot, position('_' IN lot) * -1)
, COUNT(*) -- never null in this query
, $1.datetimestamp
FROM %I.%I -- !!
WHERE lot = $1.lot
AND machine = $1.machine
GROUP BY machine, model, lot
ON CONFLICT ON CONSTRAINT tb_summary_unique -- constraint has same name?
DO UPDATE
SET machine = $1.machine
, totalshots = t.totalshots + 1
, datetimestamp = $1.datetimestamp
$x$, TG_TABLE_SCHEMA, TG_TABLE_NAME -- !!
)
USING NEW;
RETURN NEW;
END
$func$
LANGUAGE plpgsql;Context
StackExchange Database Administrators Q#179900, answer score: 4
Revisions (0)
No revisions yet.