update in a function sql in dbeaver/postgres - sql

I have to update a field in a table with concat() and I'm thinking to use a function with an update sql. I also want to have a rollback if update doesnt' work.
I have this function but it just works the select sql and for the first row of the table "clients"
CREATE OR REPLACE FUNCTION value_concat()
RETURNS record
LANGUAGE plpgsql
AS $function$
DECLARE
rows_affected integer := 0;
query constant text not null := 'select * from db.clients';
result record;
BEGIN
EXECUTE query INTO result;
RETURN result;
UPDATE db.clients SET clients.name = concat(clients.name, '-US');
exception when raise_exception
then
begin
rows_affected := 0;
rollback;
end;
RETURN record;
END;
$function$
;
Do I have to make a select sql before the update?
Why the update is not working, should I do a for/loop before the update sql?
The below code returns just one record and not all records form the select sql, why?
EXECUTE query INTO result;
RETURN result;

I hope this help.
CREATE OR REPLACE FUNCTION value_concat()
RETURNS TABLE (
field_name1 VARCHAR,
field_name2 INT [, ...]
)
LANGUAGE plpgsql
AS $function$
BEGIN
UPDATE db.clients SET clients.name = concat(clients.name, '-US');
RETURN QUERY
select * from db.clients;
exception when raise_exception
then
begin
rows_affected := 0;
rollback;
end;
RETURN record;
END;
$function$
;
Do I have to make a select SQL before the update?
There's no need to select, it works well. You should check the query to apply where for unwanted updates for other records.
Why the update is not working, should I do a for/loop before the
update SQL?
Because you're returning before the update and the rest of the code is always skipped.
The below code returns just one record and not all records form the
select sql, why?
It's because you just return a record type. the record type can only store one row from a result.

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$;

Snowflake SQL stored procedure and save value from query with dynamic SQL

I am writing a SQL stored procedure in Snowflake (language SQL NOT Javascript).
I am trying to save a count from a SELECT statement into a variable and the table name for the select statement needs to come from a variable.
Here is what I have so far but this is failing. I don't know where to put the USING or if I can even do this? I feel like I just don't have it syntactically correct yet.
create or replace procedure myprocedure(DBNAME varchar(16777216))
returns int
language sql
as
$$
DECLARE
excludeCount int;
fullyQualifiedProceduresTable varchar(16777216);
BEGIN
fullyQualifiedProceduresTable := CONCAT(DBNAME, '.INFORMATION_SCHEMA.PROCEDURES');
excludeCount := (SELECT count(*) as count from TABLE (?) WHERE PROCEDURE_OWNER = '<ROLE NAME>') USING fullyQualifiedProceduresTable ;
IF (excludeCount > 0) THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
$$;
This is what I got to work using slightly different syntax for using variables.
Syntax taken from here
create or replace procedure myprocedure(DBNAME varchar(16777216))
returns int
language sql
as
$$
DECLARE
excludeCount int;
fullyQualifiedProceduresTable varchar(16777216);
BEGIN
SELECT CONCAT(:DBNAME, '.INFORMATION_SCHEMA.PROCEDURES') into :fullyQualifiedProceduresTable;
SELECT count(*) into :excludeCount from IDENTIFIER(:fullyQualifiedProceduresTable) WHERE PROCEDURE_OWNER = '<role>';
IF (excludeCount > 0) THEN
RETURN 1;
ELSE
RETURN 0;
END IF;
END;
$$;
I am not sure why you need a procedure for this. Could you not do...
set table_name='abc';
set excludeCount=(select case when count(*)>0 then 1 else 0 end from identifier($table_name));
try this
call my_proc('bus_day');
create or replace procedure my_proc(table_name varchar)
returns table(a integer)
language sql
as
$$
declare
res RESULTSET;
query varchar default 'SELECT count(*) FROM ' || :table_name ;
begin
res := (execute immediate :query);
return table (res);
end;
$$;

Postgres function to return number of rows deleted per schema

I'm trying to adapt a Postgres stored procedure into a function in order to provide some feedback to the caller.
The procedure conditionally deletes rows in specific schemas, and I'd want the function to do the same, but also return the amount of rows that were deleted for each schema.
The original stored procedure is:
create or replace procedure clear_tenants()
language plpgsql as $function$
declare
tenant text;
begin
for tenant in
select tenant_schema
from public.tenant_schema_mappings
loop
execute format($ex$
delete from %I.parent
where expiration_date_time < now()
$ex$, tenant);
end loop;
end
$function$;
My current transform into a function is:
CREATE OR REPLACE FUNCTION testfun()
RETURNS TABLE(tsname varchar, amount numeric) AS
$BODY$
declare
tenant text;
trow record;
BEGIN
for tenant in
select tenant_schema
from public.tenant_schema_mappings
loop
execute format($ex$
WITH deleted AS (
delete from %I.parent
where expiration_date_time < now()
IS TRUE RETURNING *
)
tsname := tenant;
amount := (SELECT * FROM deleted;);
return next;
$ex$, tenant);
end loop;
END
$BODY$ language plpgsql;
This is probably wrong in all kinds of ways. I'm definitely confused.
When executing this with SELECT * FROM testfun(), I get the following error:
ERROR: syntax error at or near "tsname"
LINE 7: tsname := tenant;
^
QUERY:
WITH deleted AS (
delete from anhbawys.parent
where expiration_date_time < now()
IS TRUE RETURNING *
)
tsname := tenant;
amount := (SELECT * FROM deleted;);
return next;
CONTEXT: PL/pgSQL function testfun() line 9 at EXECUTE
SQL state: 42601
So clearly I'm not properly assigning the row's columns, but I'm not sure how.
I have found this question which seemed similar, but it's bit complex for my understanding.
You can use GET DIAGNOSTICS after a DELETE statement to get the number of rows deleted:
CREATE OR REPLACE FUNCTION testfun()
RETURNS TABLE(tsname varchar, amount bigint) AS
$BODY$
declare
tenant text;
BEGIN
for tenant in
select tenant_schema
from tenant_schema_mappings
loop
execute format(
'delete from %I.parent
where expiration_date_time < now()', tenant);
tsname := tenant;
GET DIAGNOSTICS amount := ROW_COUNT;
return next;
end loop;
END
$BODY$
language plpgsql;
If the answer is as simple as your question there is a system catalog for this.
select schemaname , count(n_tup_del)
from pg_catalog.pg_stat_all_tables psat
group by schemaname
You can use get diagnostics (noddy function to get my point across)
create or replace function delfunc()
returns void
as $$
declare
affected_rows int ;
begin
delete from atable where a > 998 ;
GET DIAGNOSTICS affected_rows = ROW_COUNT;
insert into logtable values(affected_rows);
end $$
language plpgsql

Postgres - returning row id's into stored procedure variable causes error

I have a stored procedure which I'm trying to use to delete several rows of a table based on an array of id's, from the rows that are deleted I want to return those id's and store them in a variable so that it can be used in another delete statement. Here's a reduced segment of my function.
create or replace function "table_a"."deletes"
(p_ids int[]
)
returns int
as $$
declare
v_directories int[];
begin
delete from "table_a"."foo" where "hoo_id" = any(unnest(p_ids)) returning id into v_dirs;
delete from "table_a"."bar" where "foo_id" = any(unnest(v_dirs));
return 1;
exception
when others
then raise exception '% %', sqlstate, sqlerrm;
end;
$$ LANGUAGE plpgsql;
This gives me an error of -
'set-returning functions are not allowed in WHERE'
What am I missing?
Use a CTE instead:
with ids as (
delete from "table_a"."foo"
where "hoo_id" = any(unnest(p_ids))
returning id
)
delete from "table_a"."bar"
where "foo_id" in (select id from ids);

PostgreSQL trigger after update only on a updated row

I have a small table for news. I want to make a trigger which sets the update date and update time in the row (only for the rows that were updated)
I tried making the following:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text) THEN
UPDATE news SET news_update_date = current_date, news_update_time = current_time;
END IF;
RETURN new;
END
$$;
CREATE TRIGGER update_news_dates
AFTER UPDATE ON news
FOR EACH ROW
EXECUTE PROCEDURE add_update_dates();
But the trigger updates each row in my table (even those that are not updated), when I want only the updated ones. What am I doing wrong?
Your update statement is updating all the rows in the table! It has no where clause.
Just use assignment:
CREATE FUNCTION add_update_dates()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
IF (OLD.news_name IS DISTINCT FROM NEW.news_name OR
OLD.news_description IS DISTINCT FROM NEW.news_description OR
OLD.news_text IS DISTINCT FROM NEW.news_text
) THEN
NEW.news_update_date := current_date;
NEW.news_update_time := current_time;
END IF;
RETURN new;
END;
$$;
As an aside, storing date/time in separate columns makes no sense to me.