Break update operation when function returns NULL value in PostgreSQL - sql

Let' assume I have a table named mytable:
I have one function which returns text and sometime it can return NULL also. ( this is just demo function in real use case function is complex )
CREATE OR REPLACE FUNCTION parag_test (id text)
RETURNS text
LANGUAGE plpgsql
AS
$$
DECLARE
--- variables
BEGIN
IF(id= 'Ram') THEN
RETURN 'shyam';
ELSE
RETURN NULL;
END IF;
END
$$
I want to update mytable till the time when my function returns non NULL values. if it returns NULL value I want to break update operation that time.
if we use below update query it will not stops updating when function returns NULL
update mytable SET id = parag_test (id) ;
Table after triggering above query looks like :
But what my expectation of output is :
because when we try to update second row function parag_test will return NULL and I want to stop update operation there.
So is there any way in PostgreSQL for achieving that ?

If you do have a primary key (say row number) to your table, this approach could work. - https://dbfiddle.uk/?rdbms=postgres_14&fiddle=0350562961be16333f54ebbe0eb5d5cb
CREATE OR REPLACE FUNCTION parag_test()
RETURNS void
LANGUAGE plpgsql
AS
$$
DECLARE
a int;
i varchar;
BEGIN
FOR a, i IN SELECT row_num, id FROM yourtable order by row_num asc
LOOP
IF(i = 'Ram') THEN
UPDATE yourtable set id = 'shyam' where row_num = a;
END IF;
IF (i is null) then
EXIT;
END IF;
END LOOP;
END;
$$

Related

Update columns for the first user inserted

I'm trying to create a trigger and a function to update some columns (roles and is_verified) for the first user created. Here is my function :
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
begin
if(select count(*) from public.user = 1) then
update new set new.is_verified = true and new.roles = ["ROLE_USER", "ROLE_ADMIN"]
end if;
return new;
end;
$function$
;
and my trigger :
create trigger first_user
before insert
on
public.user for each row execute function first_user()
I'm working on Dbeaver and Dbeaver won't persist my function because of a syntax error near the "=". Any idea ?
Quite a few things are wrong in your trigger function. Here it is revised w/o changing your business logic.
However this will affect the second user, not the first. Probably you shall compare the count to 0. Then the condition shall be if not exists (select from public.user) then
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger LANGUAGE plpgsql AS
$function$
begin
if ((select count(*) from public.user) = 1) then
-- probably if not exists (select from public.user) then
new.is_verified := true;
new.roles := array['ROLE_USER', 'ROLE_ADMIN'];
end if;
return new;
end;
$function$;

PostgreSQL - Function with conditional local variable

I want to create a PostgreSQL function that will filter out table based on selected parameters.
However, I also need to perform some more complex logic based on the argument supplied. So I wanted to declare a local variable which would be based on some conditional logic.
What I have:
CREATE OR REPLACE FUNCTION get_something(parameter1 INT, parameter2 VARCHAR[])
DECLARE
conditional_variable := (
IF $1 = 50 THEN
'result-1'
ELSIF $1 = 100 THEN
ARRAY['result-2', 'result-3']
END IF;
)
RETURNS TABLE (
"time" BIGINT,
some_column NUMERIC
) AS $$
SELECT
time,
some_column
FROM "SomeNiceTable"
WHERE time = $1
AND some_dimension = ANY($2::VARCHAR[])
AND some_other_dimension = ANY(conditional_variable::VARCHAR[]);
$$ LANGUAGE SQL;
But it does not work this way. Is there a way how to achieve such thing?
You can not have DECLARE block and variables in a language sql function.
So you need to switch to language plpgsql and adjust the structure to be valid PL/pgSQL
CREATE OR REPLACE FUNCTION get_something(parameter1 INT, parameter2 VARCHAR[])
RETURNS TABLE ("time" BIGINT, some_column NUMERIC)
AS
$$
declare
conditional_variable text[];
begin
IF parameter1 = 50 THEN
conditional_variable := array['result-1'];
ELSIF parameter1 = 100 THEN
conditional_variable := ARRAY['result-2', 'result-3']
ELSE
????
END IF;
return query
SELECT
time,
some_column
FROM "SomeNiceTable"
WHERE time = $1
AND some_dimension = ANY($2::VARCHAR[])
AND some_other_dimension = ANY(conditional_variable);
END;
$$
LANGUAGE plpgsql;

How can we use a constraint to ensure that no values of an array are NULL?

Let's say I have the following table:
CREATE TABLE test (
arr_column VARCHAR[] NOT NULL
)
This does not prevent values of the array being set to NULL when a row is inserted. So, I would like a constraint to enforce this rule. My attempt is as follows:
CREATE TABLE test (
arr_column VARCHAR[] NOT NULL
CHECK (NOT (ARRAY[NULL]::VARCHAR[] <# arr_column))
)
But unfortunately, this does not fail if I insert:
INSERT INTO test (ARRAY['some_string', NULL]::VARCHAR[])
One easy and straightforward way to do such a check is using triggers, but you can also simply create a function and use it at the CHECK clause as you've been doing so far:
CREATE OR REPLACE FUNCTION check_null_element(arr TEXT[])
RETURNS BOOLEAN AS $BODY$
DECLARE j INT;
BEGIN
FOR j IN 1 .. ARRAY_UPPER(arr, 1) LOOP
IF arr[j] IS NULL THEN
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END;
$BODY$
LANGUAGE plpgsql;
So when creating your table you just need:
CREATE temp TABLE test (
arr_column VARCHAR[] NOT NULL
CHECK (check_null_element(arr_column))
);
Trying to insert an array with NULL values:
db=# INSERT INTO test VALUES (ARRAY['some_string', NULL]::VARCHAR[]);
FEHLER: neue Zeile für Relation »test« verletzt Check-Constraint »test_arr_column_check«
DETAIL: Fehlgeschlagene Zeile enthält ({some_string,NULL}).
And with a valid one ..
db=# INSERT INTO test VALUES (ARRAY['some_string', 'NOT NULL :-)']::VARCHAR[]);
INSERT 0 1
EDIT: Nice to have:
To avoid unwanted exceptions, you can additionally check if the parameter itself is NULL - redundant for this question, since it's been already checked with a NOT NULL constraint at the CREATE TABLE statement. This can be done by adding the following condition to the function: IF arr IS NULL THEN RETURN FALSE; END IF;
CREATE OR REPLACE FUNCTION check_null_element(arr TEXT[])
RETURNS BOOLEAN AS $BODY$
DECLARE j INT;
BEGIN
IF arr IS NULL THEN RETURN FALSE; END IF;
FOR j IN 1 .. ARRAY_UPPER(arr, 1) LOOP
IF arr[j] IS NULL THEN
RETURN FALSE;
END IF;
END LOOP;
RETURN TRUE;
END;
$BODY$
LANGUAGE plpgsql;
The following seems to do the trick, with very little code:
CHECK (array_position(arr_column, NULL) is NULL)

Create stored procedure (int[] as param) which deletes existing records in table when there is no match in int array

ex: if i have sent 1,2,3 params to stored procedure with idxyz, then table has 1,2,3,4,5 ids then 4,5 should be deleted from table.
CREATE OR REPLACE FUNCTION example_array_input(INT[]) RETURNS SETOF ids AS
$BODY$
DECLARE
in_clause ALIAS FOR $1;
clause TEXT;
rec RECORD;
BEGIN
FOR rec IN SELECT id FROM ids WHERE id = ANY(in_clause)
LOOP
RETURN NEXT rec;
END LOOP;
-- final return
RETURN;
END
$BODY$ language plpgsql;
ex: SELECT * FROM example_array_input('{1,2,4,5,6}'::INT[]);
if existing table has 1,2,3,4,5,6,7,8,9. then it should delete 7,8,9 from that table since these are not there in the input array
You can use a DELETE statement like this for your purpose.
DELETE FROM ids
where id NOT IN ( select UNNEST('{1,2,4,5,6}'::INT[]) ) ;
DEMO
You can use a sql function that returns the deleted ids:
CREATE OR REPLACE FUNCTION example_array_input(in_clause INT[]) RETURNS SETOF ids
language sql
AS
$SQL$
DELETE
FROM ids
WHERE id NOT IN ( SELECT unnest(in_clause) )
RETURNING id;
$SQL$;
You can see a running example in http://rextester.com/PFG55537
In a 1 to 10 table running
SELECT * FROM example_array_input('{1,2,4,5,6}'::INT[]);
you obtain:

Write a PL/pgSQL function so that FOUND is not set when "nothing" is found?

I am just starting out on functions in PostgreSQL, and this is probably pretty basic, but how is this done?
I would like to be able to use the following in a function:
PERFORM id_exists();
IF FOUND THEN
-- Do something
END IF;
where the id_exists() function (to be used with SELECT and PERFORM) is:
CREATE OR REPLACE FUNCTION id_exists() RETURNS int AS $$
DECLARE
my_id int;
BEGIN
SELECT id INTO my_id
FROM tablename LIMIT 1;
RETURN my_id;
END;
$$ LANGUAGE plpgsql;
Currently, even when my_id does not exist in the table, FOUND is true, presumably because a row is still being returned (a null integer)? How can this be re-written so that an integer is returned if found, otherwise nothing at all is?
Your assumption is correct, FOUND is set to TRUE if the last statement returned a row, regardless of the value (may be NULL in your case). Details in the manual here.
Rewrite to, for instance:
IF id_exists() IS NOT NULL THEN
-- Do something
END IF;
Or rewrite the return value of your function with SETOF so it can return multiple rows - or no row! Use RETURN QUERY like I demonstrate. You can use this function in your original setting.
CREATE OR REPLACE FUNCTION id_exists()
RETURNS SETOF int LANGUAGE plpgsql AS
$BODY$
BEGIN
RETURN QUERY
SELECT id
FROM tablename
LIMIT 1;
END;
$BODY$;
Or, even simpler with a language SQL function:
CREATE OR REPLACE FUNCTION id_exists()
RETURNS SETOF int LANGUAGE sql AS
$BODY$
SELECT id
FROM tablename
LIMIT 1;
$BODY$;