Unable to call procedure inside another procedure with loop using PLPGSQL - plpgsql

Trying to save data into table using procedures and loop, but unable to solve this error.
CREATE OR REPLACE PROCEDURE quality_save(p_1 character varying, p_2 character varying, p_3 character varying DEFAULT NULL::character varying, INOUT response character varying DEFAULT '1'::character varying)
LANGUAGE plpgsql
AS $procedure$
declare
begin
if 1=1 then
raise notice '_Insert start';
insert into table_A
(
brand,
model,
year
)
values(
p_1,
p_2,
p_3
);
raise notice 'insert-end';
else
select 'p_1,p_2 cannot be null' into response;
end if;
exception
when sqlstate '23505' then
select 'Duplicate Record' into response;
--when others then
-- select '-1' into response;
end
$procedure$
********************************************************************************************
CREATE OR REPLACE PROCEDURE auto_save(INOUT response character varying DEFAULT '1'::character varying)
LANGUAGE plpgsql
AS $procedure$
declare
response varchar(100);
f record;
l record;
m record;
begin
for f in select p_1,p_2,p_3 from table_dump
loop
call public.quality_save(
p_1 =>f.p_1,
p_2 =>f.p_2,
p_3 =>f.p_3
)
;
select 1 into response;
end loop;
select 1 into response;
end;$procedure$
;
SQL Error [42601]: ERROR: procedure parameter "response" is an output parameter but corresponding argument is not writable
Where: PL/pgSQL function auto_save(character varying) line 182 at CALL
I tried rewriting second procedure as function but still giving the same error.

You didn't pass an argument for response. Call the procedure like this:
DECLARE
f record;
p_response character varying;
BEGIN
[...]
CALL public.quality_save(
p_1 =>f.p_1,
p_2 =>f.p_2,
p_3 =>f.p_3,
response => p_response
);
[...]

Related

Relation doesn't exists while calling postgres function with parameters

I have a function
CREATE OR REPLACE FUNCTION test_function(
g geometry, _tbl regclass, _tile regclass)
RETURNS SETOF geometry AS
$BODY$
BEGIN
return query EXECUTE format('select geom from %s where tile_id = %s::varchar', _tbl, _tile);
RETURN;
END
$BODY$
LANGUAGE plpgsql
which works if call the function like this
select test_function(st_geometryfromtext('POLYGON((-119.896896986135 39.2639920102033,-119.896896986135 39.2641790174996,-119.896710996922 39.2643649879886,-119.896710996922 39.2645519952849,-119.896896986135 39.2639920102033))'),
'public.test', '123')
but gives error when I call function with different _tile parameter
select test_function(st_geometryfromtext('POLYGON((-119.896896986135 39.2639920102033,-119.896896986135 39.2641790174996,-119.896710996922 39.2643649879886,-119.896710996922 39.2645519952849,-119.896896986135 39.2639920102033))'),
'public.ec_1_eur_1', '123_123_123')
gives error ERROR: relation "123_123_123" does not exist
why does it not throw error for '123' but '123_123_123'. How should I solve this?
When adding varchar for _tile instead of regclass, it doesn't handle quotes within quotes
CREATE OR REPLACE FUNCTION public.test_function(
g geometry,
_tbl regclass,
tile varchar)
RETURNS SETOF geometry AS
$BODY$
BEGIN
return query EXECUTE format('select geom from %s where tile_id = %s::varchar', _tbl, tile);
RETURN;
END
$BODY$
LANGUAGE plpgsql
I get an issue
ERROR: syntax error at or near "_123_123"
LINE 1: select geom from test where tile_id = 123_123_123::var...
You have to use %I and %L to quote correctly and avoid SQL injection:
CREATE OR REPLACE FUNCTION public.test_function(
g geometry,
_tbl regclass,
tile varchar)
RETURNS SETOF geometry AS
$BODY$
BEGIN
return query EXECUTE format('select geom from %I where tile_id = %L::varchar', _tbl, tile);
RETURN;
END
$BODY$
LANGUAGE plpgsql

postgreSQL : oracle sqlerrm equivalent of postgres

I'm new to PostgreSQL. I have experience in oracle. In oracle , to find the exact error, I use code 'dbms_output.put_line(sqlerrm)' . Here I have a postgresql function returning an integer value
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
return -1;
end;
$BODY$;
ALTER FUNCTION public.fn_sqltest(character varying)
OWNER TO postgres;
I call this function as below
DO $$
DECLARE
ae integer;
BEGIN
ae:=fn_sqltest(10);
RAISE NOTICE 'exception: % % ', sqlstate , sqlerrm ;
RAISE NOTICE 'Return value is: % ', ae;
END $$;
and I get the error
ERROR: column "sqlstate" does not exist
How can I show the exact error message like sqlerrm in oracle.
I updated the function and wrote a code in function exception section
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
text_var1 text;
text_var2 text;
text_var3 text;
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
text_var2 = PG_EXCEPTION_DETAIL,
text_var3 = PG_EXCEPTION_HINT;
RAISE NOTICE 'Return value is: % % %',text_var1 , text_var2, text_var3;
return -1;
end;
$BODY$;
ALTER FUNCTION public.fn_sqltest(character varying)
OWNER TO postgres;
Another method is by changing the return type. I changed the return type to text and rewrite the exception section code as below
return sqlerrm;
Unlike PL/SQL, in PL/pgSQL SQLSTATE and SQLERRM are not defined outside an exception handler. See the documentation, section "Obtaining Information About An Error".
This also means that you can't get SQLSTATE and SQLERRM of a successful operation, unlike PL/SQL.
So, if you want to use these special variables outside an exception handler, you have to store them in variables.
I don't know how would you like to return them from the function. I can demonstrate this idea inside your function code:
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
v_sqlerrm text;
v_sqlstate text;
begin
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
v_sqlerrm := sqlerrm;
v_sqlstate := sqlstate;
end;
RAISE NOTICE 'exception: % % ', v_sqlstate , v_sqlerrm ;
return -1;
end;
$BODY$;
I know this is old but just for the record: The easiest way to output the state and message of error is to raise them from within the exception clause. You don't need to declare extra variables.
CREATE OR REPLACE FUNCTION fn_sqltest(p_id integer)
RETURNS integer
AS $$
DECLARE
n integer;
BEGIN
SELECT off_id
INTO n
FROM office
WHERE per_id = p_id;
RETURN n;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'exception: % - %', SQLSTATE, SQLERRM;
RETURN -1;
END;
$$ LANGUAGE plpgsql;

Exception handling postgresql

Hello fellow programmers,
I have a question regarding exception handling.
I have created a function for inserting values into a table with the columns email and username with a max character length of 16 for the email and 32 for the password.
Now I want to give out an error message like 'Password too long,16 Chars max', whenever the inserted email is longer than 16 Chars.
I tried it already with the 'Name_too_long' error code.
How could I do that wiht exceptions in Postgres 10.5 ?
Thanks in advance.
EDIT: CODE that is semi functional
CREATE OR REPLACE FUNCTION users_insert(_email character varying,_passwort character varying) RETURNS void
AS $BODY$
BEGIN
INSERT INTO users(email,passwort,lastlogin)
VALUES(_email,_passwort,CURRENT_TIMESTAMP);
EXCEPTION
WHEN string_data_right_truncation
THEN RAISE NOTICE 'ERROR: INSERT TOO LONG';
END;
$BODY$
LANGUAGE plpgsql ;
EDIT: Working Code, with 2 different error messages, for each error:
CREATE OR REPLACE FUNCTION public.users_insert(
_email character varying,
_passwort character varying)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
BEGIN
IF LENGTH(_passwort) > 16
THEN RAISE EXCEPTION USING errcode = 50001;
END IF;
IF LENGTH(_email) > 32
THEN RAISE EXCEPTION USING errcode = 22001;
END IF;
INSERT INTO users(email,passwort,lastlogin)
VALUES(_email,_passwort,CURRENT_TIMESTAMP);
EXCEPTION
WHEN SQLSTATE '50001' THEN
RAISE NOTICE 'Password too long, 16 Chars max';
WHEN SQLSTATE '22001' THEN
RAISE NOTICE 'Email too long, 32 Chars max';
END;
$BODY$;
ALTER FUNCTION public.users_insert(character varying, character varying)
OWNER TO postgres;
Use an IF condition to check for lengths of specific columns and raise/handle relevant exceptions.
CREATE OR REPLACE FUNCTION users_insert(_email character varying,
_passwort character varying )
RETURNS void
AS $BODY$
BEGIN
IF LENGTH(_passwort) > 16
THEN RAISE EXCEPTION USING errcode = 50001;
END IF;
INSERT INTO users(email,passwort,lastlogin)
VALUES(_email,_passwort,CURRENT_TIMESTAMP);
EXCEPTION
WHEN SQLSTATE '50001' THEN
RAISE NOTICE 'Password too long,16 Chars max';
WHEN OTHERS THEN
raise notice '% %', SQLERRM, SQLSTATE;
END;
$BODY$
LANGUAGE plpgsql ;
Testing
knayak$# PERFORM users_insert('username2251621819101010#mail.com','password123passwod');
knayak$#
knayak$# END $$;
NOTICE: Password too long,16 Chars max
DO
knayak=# DO $$
knayak$# BEGIN
knayak$#
knayak$# PERFORM users_insert('username2251621819101010#mail.com','password');
knayak$#
knayak$# END $$;
NOTICE: value too long for type character varying(30) 22001
DO

Where the NOTICE or error messages?

No NOTICE neither error messages at
CREATE or replace FUNCTION copy_to_csv(
fname text,
query text,
header boolean DEFAULT true,
quotedfields text[] DEFAULT NULL,
usedate boolean DEFAULT true
) RETURNS text AS $f$
DECLARE
aux text :='';
BEGIN
RAISE NOTICE 'HELLO!!!!!';
IF p_quotedfields IS NOT NULL THEN
aux := ', FORCE_QUOTE('|| array_to_string(quote_ident(quotedfields),',') ||')';
END IF;
aux := format(
'COPY (%L) TO (%L) WITH (FORMAT CSV, HEADER %L%s)',
query,
CASE WHEN usedate THEN fname|| now()::date::text ELSE fname END ||'.csv',
header,
aux
);
RAISE NOTICE 'HELLO2';
EXECUTE aux;
RAISE NOTICE 'HELLO3';
RETURN aux;
END;
$f$ LANGUAGE plpgsql STRICT;
... Calling with select copy_to_csv(E'select * from t', '/tmp/t');. Using PostgreSQL v10 at UBUNTU 16 LTS.
But this function is working fine:
CREATE or replace FUNCTION test1() RETURNS void AS $f$
BEGIN
RAISE NOTICE 'HELLO!!!';
END;
$f$ LANGUAGE plpgsql STRICT;
PS: the quote_ident() overload also working fine, was implemented with
CREATE FUNCTION quote_ident(text[]) RETURNS text[] AS $f$
SELECT array_agg(quote_ident(x)) FROM unnest($1) t(x)
$f$ LANGUAGE SQL IMMUTABLE;
When the function is STRICT and one of the arguments is NULL, the function body is not executed and the result is NULL. Remove STRICT from the function definition.
Btw, you've mistaken the order of arguments.

How could I return a varchar message in Postgres as part of function return table that isn't in any table?

I need to return a message (a different message according some evaluations inside de function), but when I call the function it returns this error message: "Returned type unknown does not match expected type character varying in column 2".
CREATE OR REPLACE FUNCTION myfunction()RETURNS TABLE(
cod INTEGER,
answ CHARACTER VARYING
) AS $BODY$
BEGIN
RETURN QUERY
select 0, 'here goes the message';
END;
$BODY$
LANGUAGE plpgsql;
try:
CREATE OR REPLACE FUNCTION myfunction()RETURNS TABLE(
cod INTEGER,
answ CHARACTER VARYING
) AS $BODY$
BEGIN
RETURN QUERY
select 0, 'here goes the message'::CHARACTER VARYING;
END;
$BODY$
LANGUAGE plpgsql;