i am new for this, background is totally based on Oracle and related products. i have installed XAMPP 3.3.0, i have created a trigger which works fine.
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, next_receipt
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
END$$
DELIMITER ;
but when using NEW.cancel_receipt_no (see below code) it is showing error : Error Code: 1327 Undeclared variable: NEW
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, NEW.cancel_receipt_no
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
END$$
DELIMITER ;
is there any restriction that i cannot assign the value to NEW.field as in my first code here when i am trying to assign the value as: NEW.cancel_receipt_no := next_receipt then it also showing error. please help, how i can do this here?
i used below code and error has gone (used SET):
SET NEW.cancel_receipt_no = next_receipt;
DELIMITER $$
CREATE OR REPLACE
TRIGGER bi_cancel_booking BEFORE INSERT
ON cancelled_booking
FOR EACH ROW BEGIN
--
DECLARE next_receipt VARCHAR(20);
DECLARE rct_no INT(5);
--
SELECT MAX(starting_number)+1, CONCAT(receipt_prefix,'-',MAX(starting_number)+1)
INTO rct_no, next_receipt
FROM com_receipt_types
WHERE receipt_type = 'Cancel Booking';
SET NEW.cancel_receipt_no := next_receipt;
END$$
DELIMITER ;
it was because of my lack of knowledge, gradually i will learn the things with help of all seniors here.
Related
I have function which supposed to create missing objects. I have a variable that I am using to set active schema tschem_name
However feels like proc cannot see this variable if it is trying to create this object, however, if which check if object exists can see this variable feels like it treats it as a column from the table. Any idea how to work around it? I was trying to use declare inside else statement but it doesn't work :(
ERROR: column "tschem_name" does not exist
CREATE OR REPLACE FUNCTION public.fn_upd_schema(
tschem_name character varying
)
RETURNS character varying
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
DECLARE
--tname text := t_shema;
--tschem_name text := tname||'_assets';
tsql_dyn text ;
nschema_check numeric := 0 ;
BEGIN
SELECT 1
INTO nschema_check
FROM PG_NAMESPACE
WHERE NSPNAME = tschem_name;
IF nschema_check = 0 THEN
RETURN 'Schema '||tschem_name ||' Does Not Exist';
ELSE
tsql_dyn := 'SET search_path TO '||tschem_name ||';';
raise notice 'EXECUTE %', tsql_dyn;
EXECUTE tsql_dyn;
IF EXISTS (SELECT FROM information_schema.tables
WHERE table_schema = tschem_name
AND table_type = 'VIEW'
AND table_name = 'vw_aaa')
THEN
null;-- raise notice 'vw_aaa EXISTS';
ELSE
--declare tschemaname text := tschem_name;
CREATE VIEW vw_aaa AS
SELECT
tschem_name::text AS database,
tale1.column1,
tale1.column2
From table1;
endif;
RETURN tschem_name ||' UPDATED TO STANDART';
END IF;
END;
$BODY$;
The problem is your CREATE VIEW statement. You need to use dynamic SQL with EXECUTE for that.
The underlying reason is that CREATE VIEW does not accept parameters.
By the way, your code is vulnerable to SQL injection. Never construct an SQL statement by concatenating strings with ||. Use format with the %I placeholder.
I am new to using postgresql, I am trying to make a trigger that just inserts in the Employee table and also inserts in the Vacations table, but I don't know how to assign the values, I do it like that in sql but here I really don't know how
CREATE FUNCTION SP_InsertaVacacionesEmpleado() RETURNS TRIGGER
AS
$$
DECLARE _NumeroIdentificacion INTEGER;
DECLARE _FechaEntrada DATE;
BEGIN
SET _NumeroIdentificacion = SELECT NEW.NumeroIdentificacion FROM "Empleado"
SET _FechaEntrada = SELECT NEW.FechaEntrada FROM "Empleado"
INSERT INTO Vacaciones VALUES(_NumeroIdentificacion, _FechaEntrada, '', 0);
RETURN NEW;
END
$$
LANGUAGE plpgsql
As documented in the manual assignment is done using the := operator, e.g.:
some_variable := 42;
However to assign one or more variables from the result of a query, use select into, e.g.:
DECLARE
var_1 INTEGER;
var_2 DATE;
BEGIN
select col1, col2
into var_1, var_2
from some_table
...
However neither of that is necessary in a trigger as you can simply use the reference to the NEW record directly in the INSERT statement:
CREATE FUNCTION sp_insertavacacionesempleado()
RETURNS TRIGGER
AS
$$
BEGIN
INSERT INTO Vacaciones (...)
VALUES (NEW.NumeroIdentificacion, NEW.FechaEntrada , '', 0);
RETURN NEW;
END
$$
LANGUAGE plpgsql;
Note that you need to define a row level trigger for this to work:
create trigger ..
before insert on ...
for each row --<< important!
execute procedure sp_insertavacacionesempleado() ;
I have created a procedure like the one below. i know PL SQL a bit but i am new to DB2.
when i execute the below procedure getting an error that the use of reserve keyoword end is invalid.
CREATE PROCEDURE test()
LANGUAGE SQL
DYNAMIC RESULT SETS 1
WLM ENVIRONMENT FOR DEBUG MODE #TTCOM
ASUTIME NO LIMIT
BEGIN
DECLARE SQLSTATE CHAR(5) DEFAULT '00000';
DECLARE SQLCODE INTEGER DEFAULT 0;
DECLARE TABLE_DOES_NOT_EXIST_STATE CONDITION FOR SQLSTATE '42704';
DECLARE tname VARCHAR(5000);
DECLARE CONTINUE HANDLER FOR TABLE_DOES_NOT_EXIST_STATE BEGIN
END;
FOR i AS SELECT tablename FROM EWPS.tablelist
DO
tname = 'create table '+i.tablename+'_2112 like '+i.tablename+';' ;
tname1 = 'insert into '+i.tablename+'_2112 (select * from '+i.tablename+');';
call DBMS_OUTPUT.PUT(tname)
execute immediate tname;
execute immediate tname;
--set tname=i.tablename
END FOR;
--RETURN tname ;
--COMMIT;
END test;
any help is much appreciated
You need two differnt terminators - one for the statements within the procedure and one for the CREATE PPROCEDURE itself.
Check out this command to set a terminator
--#SET TERMINATOR #
And also chekc out this question & answers
I am trying to write a trigger function that will input values into separate child tables, however I am getting an error I have not seen before.
Here is an example set up:
-- create initial table
CREATE TABLE public.testlog(
id serial not null,
col1 integer,
col2 integer,
col3 integer,
name text
);
-- create child table
CREATE TABLE public.testlog_a (primary key(id)) INHERITS(public.testlog);
-- make trigger function for insert
CREATE OR REPLACE FUNCTION public.test_log() RETURNS trigger AS
$$
DECLARE
qry text;
BEGIN
qry := 'INSERT INTO public.testlog_' || NEW.name || ' SELECT ($1).*';
EXECUTE qry USING NEW.*;
RETURN OLD;
END
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
-- add function to table
CREATE TRIGGER test_log_sorter BEFORE INSERT
ON public.testlog FOR EACH ROW
EXECUTE PROCEDURE public.test_log();
and the query:
INSERT INTO public.testlog (col1, col2, col3, name) values (1, 2, 3, 'a');
error message:
[Err] ERROR: query "SELECT NEW.*" returned 5 columns
CONTEXT: PL/pgSQL function test_log() line 7 at EXECUTE statement
5 columns is exactly what I am looking for it to return, so clearly there is something I am not understanding but the error message seems to make no sense.
Can anybody explain why I am getting this?
Your solution fixes the passing of the row-typed NEW variable. However, you have a sneaky SQL-injection hole in your code, that's particularly dangerous in a SECURITY DEFINER function. User input must never be converted to SQL code unescaped.
Sanitize like this:
CREATE OR REPLACE FUNCTION trg_test_log()
RETURNS trigger AS
$$
BEGIN
EXECUTE 'INSERT INTO public.' || quote_ident('testlog_' || NEW.name)
|| ' SELECT ($1).*'
USING NEW;
RETURN NULL;
END
$$
LANGUAGE plpgsql SECURITY DEFINER;
Also:
OLD is not defined in an INSERT trigger.
You don't need a variable. Assignments are comparatively expensive in plpgsql.
The EXECUTE qry USING NEW.* passes in the NEW.* as the arguments to the query. Since NEW.* returns five columns, the query should have $1, $2, $3, $4 and $5 in order to bind the five columns.
You are expecting a single argument ($1) which has five columns in it. I believe that if you change the the line to
EXECUTE qry USING NEW;
it will work as you expect.
With regards to Robert M. Lefkowitz' response, the answer is so simple: NEW as opposed to NEW.*
CREATE OR REPLACE FUNCTION public.test_log() RETURNS trigger AS
$$
DECLARE
qry text;
BEGIN
qry := 'INSERT INTO public.testlog_' || NEW.name || ' SELECT ($1).*';
EXECUTE qry USING NEW;
RETURN OLD;
END
$$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
thanks.
Let's use a test table :
CREATE TABLE labs.date_test
(
pkey int NOT NULL,
val integer,
date timestamp without time zone,
CONSTRAINT date_test_pkey PRIMARY KEY (pkey)
);
I have a trigger function defined as below. It is a function to insert a date into a specified column in the table. Its arguments are the primary key, the name of the date field, and the date to be inserted:
CREATE OR REPLACE FUNCTION tf_set_date()
RETURNS trigger AS
$BODY$
DECLARE
table_name text;
pkey_col text := TG_ARGV[0];
date_col text := TG_ARGV[1];
date_val text := TG_ARGV[2];
BEGIN
table_name := format('%I.%I', TG_TABLE_SCHEMA, TG_TABLE_NAME);
IF TG_NARGS != 3 THEN
RAISE 'Wrong number of args for tf_set_date()'
USING HINT='Check triggers for table ' || table_name;
END IF;
EXECUTE format('UPDATE %s SET %I = %s' ||
' WHERE %I = ($1::text::%s).%I',
table_name, date_col, date_val,
pkey_col, table_name, pkey_col )
USING NEW;
RAISE NOTICE '%', NEW;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
The actual trigger definition is as follows:
CREATE TRIGGER t_set_ready_date
BEFORE UPDATE OF val
ON labs.date_test
FOR EACH ROW
EXECUTE PROCEDURE tf_set_date('pkey', 'date', 'localtimestamp(0)');
Now say I do: INSERT INTO TABLEdate_test(pkey) values(1);`
Then I perform an update as follows:
UPDATE labs.date_test SET val = 1 WHERE pkey = 1;
Now the date gets inserted as expected. But the val field is still NULL. It does not have 1 as one would expect (or rather as I expected).
What am I doing wrong? The RAISE NOTICE in the trigger shows that NEW is still what I expect it to be. Aren't UPDATEs allowed in BEFORE UPDATE triggers? One comment about postgres triggers seems to indicate that original the UPDATE gets overwritten if there is an UPDATE statement in a BEFORE UPDATE trigger. Can someone help me out?
EDIT
I am trying to update the same table that invoked the trigger, and that too the same row which is to be modified by the UPDATE statement that invoked the trigger. I am running Postgresql 9.2
Given all the dynamic table names it isn't entirely clear if this trigger issues an update on the same table that invoked the trigger.
If so: That won't work. You can't UPDATE some_table in a BEFORE trigger on some_table. Or, more strictly, you can, but if you update any row that is affected by the statement that's invoking the trigger results will be unpredictable so it isn't generally a good idea.
Instead, alter the values in NEW directly. You can't do this with dynamic column names, unfortunately; you'll just have to customise the trigger or use an AFTER trigger to do the update after the rows have already been changed.
I am not sure, but your triggers can do recursion calls - it does UPDATE same table from UPDATE trigger. This is usually bad practice, and usually is not good idea to write too generic triggers. But I don't know what you are doing, maybe you need it, but you have to be sure, so you are protected against recursion.
For debugging of triggers is good push to start and to end of function body debug messages. Probably you use GET DIAGNOSTICS statement after EXECUTE statement for information about impact of dynamic SQL
DECLARE
_updated_rows int;
_query text;
BEGIN
RAISE NOTICE 'Start trigger function xxx';
...
_query := format('UPDATE ....);
RAISE NOTICE 'dynamic sql %, %', _query, new;
EXECUTE _query USING new;
GET DIAGNOSICS _updated_rows = ROW_COUNT;
RAISE NOTICE 'Updated rows %', _updated_rows;
...