Testing error within function in PostgreSQL - postgresql

How to check the error within the function of PostgreSQL?
My try for the same as shown below in the example.
Example:
create or replace function fun_testing(sn int, na text, gen text, ad text, rn int, flag int)
returns int as
$$
begin
if flag = 1 then
insert into testing(ssn,name,gender,address,rno) values(sn,na,gen,ad,rn);
elsif flag = 2 then
update testing
set ssn=sn,
name=na,
gender=gen,
address=ad,
rno = rn
where ssn=sn;
elsif flag =3 then
delete from testing
where ssn =sn;
end if;
if error <> 0 then /* Error Testing */
return(1);
else
return(0);
end if;
end;
$$
language plpgsql;
ERROR: column "error" does not exist
Note: The same if condition(using ##error) works fine with SQL Server but not getting executed in PostgreSQL.

You need a BEGIN ... EXCEPTION ... END block.
create or replace function fun_testing(sn int, na text, gen text, ad text, rn int,
flag int)
returns int as
$$
begin
if flag = 1 then
insert into testing(ssn,name,gender,address,rno) values(sn,na,gen,ad,rn);
elsif flag = 2 then
update testing
set ssn=sn,
name=na,
gender=gen,
address=ad,
rno = rn
where ssn=sn;
elsif flag =3 then
delete from testing
where ssn =sn;
end if;
exception when others then
raise notice 'The transaction is in an uncommittable state. '
'Transaction was rolled back';
raise notice '% %', SQLERRM, SQLSTATE;
end;
$$ language 'plpgsql';

Related

Cursor not found

i have created procedure, inside used cursor to update the some data, while calling the procedure it's getting the error.
create or REPLACE PROCEDURE bal_upd(p_id int) as
$$
DECLARE rc record;
----- cursor
bal_upd1 CURSOR (p_id int)
for
select * from tbal where custid = p_id;
begin
open bal_upd1 (p_id);
loop
FETCH bal_upd1 into rc;
exit when not found;
update t_trans set balance = balance + rc.trans;
COMMIT;
end loop;
close bal_upd1;
end;
$$ LANGUAGE plpgsql;
call bal_upd(1)
ERROR: cursor "bal_upd1" does not exist
CONTEXT: PL/pgSQL function bal_upd(integer) line 12 at FETCH
SQL state: 34000
create or REPLACE PROCEDURE bal_upd(p_id int) as
$$
DECLARE rc record;
----- cursor
bal_upd1 CURSOR (p_id int)
for
select * from tbal where custid = p_id;
begin
open bal_upd1 (p_id);
loop
FETCH bal_upd1 into rc;
exit when not found;
update t_trans set balance = balance + rc.trans;
COMMIT;
end loop;
close bal_upd1;
end;
$$ LANGUAGE plpgsql;
call bal_upd(1)
ERROR: cursor "bal_upd1" does not exist
CONTEXT: PL/pgSQL function bal_upd(integer) line 12 at FETCH
SQL state: 34000
You don't need a function or a loop for that:
UPDATE t_trans
SET balance = t_trans.balance + t.trans
FROM (SELECT sum(trans) AS trans
FROM tbal
GROUP BY custid) AS t
WHERE t_trans.custid = t.custid;
I tried, failed. I just found just use for loop (implicit cursor) is far more simple.
BEGIN;
CREATE temp TABLE tbal (
custid bigint
, trans numeric
);
INSERT INTO tbal VALUES (1 , 1);
INSERT INTO tbal VALUES (1 , 2);
CREATE temp TABLE t_trans (
custid bigint
, balance numeric
);
INSERT INTO t_trans VALUES (1 , 10);
COMMIT;
CREATE OR REPLACE PROCEDURE bal_upd (bigint)
AS $func$
DECLARE
rc record;
BEGIN
FOR rc IN
SELECT
*
FROM
tbal
WHERE
custid = $1 LOOP
RAISE NOTICE 'custid: %, trans: % ' , rc.custid , rc.trans;
UPDATE
t_trans ta
SET
balance = balance + (rc.trans)
WHERE
ta.custid = (rc.custid);
END LOOP;
END;
$func$
LANGUAGE plpgsql;
Then call it. CALL bal_upd(1);

Refcursors in Postgresql

Good Morning,
I have a quick question on how to see the data that is fetched in an Output refcursor
I'm executing the below block, its successfully executed but I want to see the data fetched in Output RefCursor, by the way I'm using PGADMIN 4 tool to connect to Postgres Servers,
BEGIN;
SELECT prod_package_list ('G6028|G6026|G6025|G6024|G6022|G6021|G6020', NULL);
FETCH ALL IN vref_cur;
COMMIT;
I found online and according to them I should follow the below format,
BEGIN;
SELECT test_prod_package_list ('G6028|G6026|G6025|G6024|G6022|G6021|G6020', NULL, 'vref_cur');
FETCH ALL IN "vref_cur";
COMMIT;
But the above execution block throws an error
function prod_package_list(unknown, unknown, unknown) does not exist,
The function is as follows,
CREATE OR REPLACE FUNCTION ssp2_pcat.prod_package_list(
prodlist text,
billcodelist text,
OUT vref_cur refcursor)
RETURNS refcursor
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
vref_cur := 'vref_cur';
IF prodlist IS NOT NULL THEN
OPEN vref_cur FOR
SELECT DISTINCT
b.package_id, a.product_id, a.pdct_ctlg_id, a.product_name, a.billing_system, a.billing_code, a.fulfill_system, a.fulfill_code, a.speed_code, a.product_type, a.product_type_value, a.return_auth_applies, a.return_auth_term, a.nrc_waiver, a.nrc_waiver_product_id, a.linked_nrc_product_id, a.national_east_west, a.lata_list, a.state_list, a.product_info_url, a.isp_prov_flags, a.product_coefficient, a.last_updated, a.product_keywords, a.combo_product_list, a.additional_info, a.usoc_info, a.min_billing_days, a.data_nrf_product_id, a.video_nrf_product_id, a.account_line_level, a.web_desc, a.orderguicd, a.prod_startdate, a.prod_enddate, b.pdct_ctlg_id, b.package_name, b.billing_code, b.category_id, b.standalone_cpe, b.standalone_truckroll, b.btn_billed, b.emp_discount, b.package_type, b.last_updated, b.pkg_detail_descr, b.grandfathered AS pkg_grandfathered,
CASE CONCAT_WS('', a.grandfathered, c.pkg_prod_grandfathered)
WHEN 'YY' THEN 'Y'
WHEN 'YN' THEN 'Y'
WHEN 'NY' THEN 'Y'
ELSE 'N'
END AS grandfathered
FROM ssp2_pcat.products AS a, ssp2_pcat.packages AS b, ssp2_pcat.package_products AS c
WHERE strpos(prodlist, CONCAT_WS('', '|', a.product_id, '|')) > 0
AND a.product_id = c.product_id AND c.package_id = b.package_id
AND c.start_date <= LOCALTIMESTAMP AND c.end_date >= LOCALTIMESTAMP
ORDER BY 1;
ELSIF billcodelist IS NOT NULL THEN
OPEN vref_cur FOR
SELECT DISTINCT
b.package_id, a.product_id, a.pdct_ctlg_id, a.product_name, a.billing_system, a.billing_code, a.fulfill_system, a.fulfill_code, a.speed_code, a.product_type, a.product_type_value, a.return_auth_applies, a.return_auth_term, a.nrc_waiver, a.nrc_waiver_product_id, a.linked_nrc_product_id, a.national_east_west, a.lata_list, a.state_list, a.product_info_url, a.isp_prov_flags, a.product_coefficient, a.last_updated, a.product_keywords, a.combo_product_list, a.additional_info, a.usoc_info, a.min_billing_days, a.data_nrf_product_id, a.video_nrf_product_id, a.account_line_level, a.web_desc, a.orderguicd, a.prod_startdate, a.prod_enddate, b.pdct_ctlg_id, b.package_name, b.billing_code, b.category_id, b.standalone_cpe, b.standalone_truckroll, b.btn_billed, b.emp_discount, b.package_type, b.last_updated, b.pkg_detail_descr, b.grandfathered AS pkg_grandfathered,
CASE CONCAT_WS('', a.grandfathered, c.pkg_prod_grandfathered)
WHEN 'YY' THEN 'Y'
WHEN 'YN' THEN 'Y'
WHEN 'NY' THEN 'Y'
ELSE 'N'
END AS grandfathered
FROM ssp2_pcat.products AS a, ssp2_pcat.packages AS b, ssp2_pcat.package_products AS c
WHERE strpos(billcodelist, CONCAT_WS('', '|', a.billing_code, '|')) > 0
AND a.product_id = c.product_id AND c.package_id = b.package_id
AND c.start_date <= LOCALTIMESTAMP AND c.end_date >= LOCALTIMESTAMP
ORDER BY 1;
ELSE
RAISE USING hint = -20001, message = 'Product List and Billing Code Lists are empty.', detail = 'User-defined exception';
END IF;
END;
$BODY$;
ALTER FUNCTION ssp2_pcat.prod_package_list(text, text)
OWNER TO ssp2_pcat;
Please advise,
Use IN parameter to pass a cursor name. Do not forget to RETURN vref_cursor. Working example:
CREATE FUNCTION my_func(arg1 text, arg2 text, vref_cursor refcursor)
RETURNS refcursor AS $$
BEGIN
OPEN vref_cursor FOR SELECT generate_series(1,3);
RETURN vref_cursor;
END;
$$ LANGUAGE plpgsql;
BEGIN;
SELECT my_func('first arg', 'second arg', 'vref_cursor');
FETCH ALL IN vref_cursor;
COMMIT;

how to create triggers function before insert show test message

i have a table
CREATE TABLE test.emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);
and function
CREATE FUNCTION test.emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Check that empname and salary are given
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- Remember who changed the payroll when
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
how to show message no need to update the values how can i modify my function
trigger is
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON test.emp
FOR EACH ROW EXECUTE PROCEDURE test.emp_stamp();
I think all you need to do is 1) make sure it's an update and not an insert (otherwise the reference to old won't make any sense and 2) compare to the existing record. Since Employee name seems to be the PK and the last two fields are only to capture changes, my guess is you only want to test the salary:
-- Who works for us when she must pay for it?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
elsif TG_OP = 'UPDATE' and new.salary = old.salary then
RAISE EXCEPTION 'salary is already %', NEW.salary;
END IF;
But of course you could also use this for any field. If you wanted to make sure at least one changed, it would be something like this:
elsif TG_OP = 'UPDATE' and
new.salary = old.salary and
new.last_date = old.last_date and
new.last_user = old.last_user then
RAISE EXCEPTION 'Update does not alter any data';
END IF;

Postgres trigger function: substitute value before insert or update

I've looked up pretty much everything I could find regarding this issue, but I still don't understand what is wrong with this trigger:
CREATE OR REPLACE FUNCTION func_SubstitutePostLatLng_Upt()
RETURNS trigger AS
$BODY$
BEGIN
IF OLD.post_latlng IS NULL AND NEW.post_latlng IS NULL AND NEW.place_guid IS NOT NULL THEN
raise notice 'SELECT';
SELECT place.geom_center, place.city_guid
INTO NEW.post_latlng, NEW.city_guid
FROM public.place
WHERE (place.origin_id, place.place_guid) IN (VALUES (NEW.origin_id,NEW.place_guid));
raise notice 'Value db_geom: %', NEW.post_latlng;
raise notice 'Value db_city_guid: %', NEW.city_guid;
IF NEW.post_latlng IS NOT NULL THEN
NEW.post_geoaccuracy = 'place';
IF NEW.city_guid IS NOT NULL THEN
SELECT country_guid INTO NEW.country_guid
FROM public.city WHERE (origin_id, city_guid) IN (VALUES (NEW.origin_id,NEW.city_guid));
END IF;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
DROP TRIGGER IF EXISTS trig_SubstitutePostLatLng_Upd on public.post;
CREATE TRIGGER trig_SubstitutePostLatLng_Upd
BEFORE UPDATE
ON public.post
FOR EACH ROW
WHEN (pg_trigger_depth() < 1)
EXECUTE PROCEDURE func_SubstitutePostLatLng_Upt()
(I have a second similar trigger for insert)
The code is supposed to do the following:
On Update on table "post", check if no post_latlng is submitted (=NULL), and if yes, substitute post_latlng from table place (geom_center), if available.
However, no matter what I do, I get the following when updating an entry in table "post" (=triggering the above trigger):
NOTICE: SELECT
NOTICE: Value db_geom: <NULL>
NOTICE: Value db_city_guid: <NULL>
INSERT 0 1
Query returned successfully in 47 msec.
The test-data for place_guid, geom_center etc. is definitely available and both
raise notice 'Value db_geom: %', NEW.post_latlng;
raise notice 'Value db_city_guid: %', NEW.city_guid;
should not output NULL.
There were several smaller issues, it now works. Here is a more cleaner code that uses variables in between:
CREATE OR REPLACE FUNCTION func_SubstitutePostLatLng_Upt()
RETURNS trigger AS
$BODY$
DECLARE
db_geom_center text;
db_city_guid text;
db_country_guid text;
BEGIN
IF OLD.post_latlng IS NULL AND NEW.post_latlng IS NULL AND NEW.place_guid IS NOT NULL THEN
SELECT place.geom_center, place.city_guid
INTO db_geom_center, db_city_guid
FROM public.place
WHERE (place.origin_id, place.place_guid) IN (VALUES (NEW.origin_id,NEW.place_guid));
IF db_geom_center IS NOT NULL THEN
NEW.post_latlng = db_geom_center;
NEW.post_geoaccuracy = 'place';
END IF;
IF db_city_guid IS NOT NULL THEN
NEW.city_guid = db_city_guid;
SELECT city.country_guid
INTO db_country_guid
FROM public.city
WHERE (city.origin_id, city.city_guid) IN (VALUES (NEW.origin_id,db_city_guid));
NEW.country_guid = db_country_guid;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;

PostgreSQL triggers and exceptions

I'm trying to get my first ever trigger and function to work, but how I throw exceptions and return data right way?
PostgreSQL 8.4.1
CREATE TABLE "SHIFTS" (
id integer NOT NULL, -- SERIAL
added timestamp without time zone DEFAULT now() NOT NULL,
starts timestamp without time zone NOT NULL,
ends timestamp without time zone NOT NULL,
employee_id integer,
modified timestamp without time zone,
status integer DEFAULT 1 NOT NULL,
billid integer,
CONSTRAINT "SHIFTS_check" CHECK ((starts < ends))
);
-- Check if given shift time overlaps with existing data
CREATE OR REPLACE FUNCTION
shift_overlaps (integer, timestamp, timestamp)
RETURNS
boolean AS $$
DECLARE
_employeeid ALIAS FOR $1;
_start ALIAS FOR $2;
_end ALIAS FOR $3;
BEGIN
SELECT
COUNT(id) AS c
FROM
"SHIFTS"
WHERE
employee_id = _employeeid AND
status = 1 AND
(
(starts BETWEEN _start AND _end)
OR
(ends BETWEEN _start AND _end)
)
;
-- Return boolean
RETURN (c > 0);
END;
$$
LANGUAGE
plpgsql
;
CREATE OR REPLACE FUNCTION
check_shift()
RETURNS trigger AS '
BEGIN
-- Bill ID is set, do not allow update
IF tg_op = "UPDATE" THEN
IF old.billid IS NOT NULL THEN
RAISE EXCEPTION "Shift is locked"
END IF;
END IF;
-- Check for overlap
IF tg_op = "INSERT" THEN
IF new.employee_id IS NOT NULL THEN
IF shift_overlaps(new.employee_id, new.starts, new.ends) THEN
RAISE EXCEPTION "Given time overlaps with shifts"
END IF;
END IF;
END IF;
-- Check for overlap
IF tg_op = "UPDATE" THEN
IF (new.employee_id IS NOT NULL) AND (new.status = 1) THEN
IF shift_overlaps(new.employee_id, new.starts, new.ends) THEN
RAISE EXCEPTION "Given time overlaps with shifts"
END IF;
END IF;
END IF;
RETURN new;
END
'
LANGUAGE
plpgsql
;
-- Shift checker trigger
CREATE TRIGGER
check_shifts
BEFORE
INSERT OR UPDATE
ON
"SHIFTS"
FOR EACH ROW EXECUTE PROCEDURE
check_shift()
;
shift_overlaps():
SQL error: ERROR: query has no destination for result data
check_shift():
SQL error: ERROR: unrecognized exception condition "Shift is locked"
You've got an error here:
SELECT
COUNT(id) AS c
FROM
"SHIFTS"
WHERE
employee_id = _employeeid AND
status = 1 AND
(
(starts BETWEEN _start AND _end)
OR
(ends BETWEEN _start AND _end)
)
;
Such a select in a plpgsql procedure has to be SELECT INTO... like this:
DECLARE
c INTEGER;
BEGIN
SELECT
COUNT(id)
INTO c
FROM
"SHIFTS"
WHERE
employee_id = _employeeid AND
status = 1 AND
(
(starts BETWEEN _start AND _end)
OR
(ends BETWEEN _start AND _end)
)
;
RETURN (c > 0);
END;
And here you've got to have the semicolon at the end of the line:
enter code here`RAISE EXCEPTION "Shift is locked";
Not sure what you're trying to find out. You're managing to raise your own exceptions, so that's good. I would expect that any error handling would be in the code that evokes this method.
If you want to do something inside the procedure, you need an EXCEPTION section:
[ <> ]
[ DECLARE
declarations ]
BEGIN
statements
EXCEPTION
WHEN condition [ OR condition ... ] THEN
handler_statements
[ WHEN condition [ OR condition ... ] THEN
handler_statements
... ]
END;
But generally I would expect you'd handle it in the calling code.
You have to use SELECT INTO to get a value returned by a query
DECLARE
[...]
c boolean;
SELECT
COUNT(id) INTO c
FROM
"SHIFTS"
WHERE
[...]