how to properly implement soft delete in postgresql? - postgresql

in my postgres 9.3 database, I have the following combination of function and a trigger to implement soft delete functionality:
ALTER TABLE "LIBN02"."trigger_test_1"
ADD COLUMN delete_ind integer
CREATE OR REPLACE FUNCTION trigger_test_1_soft_delete()
RETURNS trigger AS $$
DECLARE
command text := ' SET delete_ind = 1 WHERE uuid_col = $1';
BEGIN
EXECUTE 'UPDATE ' || "LIBN02"."trigger_test_1" || command USING OLD.uuid_col;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER test_1_soft_delete_trigger
BEFORE DELETE ON "LIBN02"."trigger_test_1"
FOR EACH ROW EXECUTE PROCEDURE trigger_test_1_soft_delete();
After all of the above is done, I run the following delete statement:
DELETE FROM "LIBN02"."trigger_test_1"
I get the following error:
ERROR: missing FROM-clause entry for table "LIBN02"
LINE 1: SELECT 'UPDATE ' || "LIBN02"."trigger_test_1" || command
^
QUERY: SELECT 'UPDATE ' || "LIBN02"."trigger_test_1" || command
CONTEXT: PL/pgSQL function trigger_test_1_soft_delete() line 5 at EXECUTE
********** Error **********
ERROR: missing FROM-clause entry for table "LIBN02"
SQL state: 42P01
Context: PL/pgSQL function trigger_test_1_soft_delete() line 5 at EXECUTE
What should I change in order for this to work?

The error you are receiving is because "LIBN02"."trigger_test_1" is not a string (these are quoted with single quotes), but an identifier. You should use
EXECUTE 'UPDATE "' || TG_TABLE_SCHEMA || '"."' || TG_TABLE_NAME || '" ' || command
USING OLD.uuid_col;
You could also add AND NOT deleted to the WHERE clause to avoid unnecessary churn.

Related

Unable to pass schema name in pg_get_serial_sequence

Unable to pass schema name in pg_get_serial_sequence. alerts is my schema name.
do $$
begin
execute format('select setval(pg_get_serial_sequence(''' || alerts.nds_email_message || ''', ''' || nds_email_message_id || '''),
coalesce(max(nds_email_message_id), 0) + 1, false)
from ''' || alerts.nds_email_message || '''');
end $$;
SQL Error [42601]: Unterminated dollar quote started at position 244 in SQL begin
execute format('select setval(pg_get_serial_sequence(''' || alerts.nds_email_message || ''', ''' || nds_email_message_id || '''),
coalesce(max(nds_email_message_id), 0) + 1, false)
from ''' || alerts.nds_email_message || '''');
end $$. Expected terminating $$'
how can I add multiple setval statements inside the block.
DO $$
BEGIN
SELECT setval(pg_get_serial_sequence('alerts.incident_log','incident_log_id'),coale sce(max( incident_log_id),0) +1, false) from alerts.incident_log;
SELECT setval(pg_get_serial_sequence('alerts.nds_email_message','nds_email_message_ id'),coa lesce(max(nds_email_message_id),0) +1, false) from alerts.nds_email_message;
SELECT setval(pg_get_serial_sequence('alerts.nds_fax_message','nds_fax_message_id') ,coalesc e(max(nds_fax_message_id),0) +1, false) from alerts.nds_fax_message;
Error occurred during SQL query execution
Reason:
SQL Error [42601]: ERROR: query has no destination for result data
Hint: If you want to discard the results of a SELECT, use PERFORM instead.
Where: PL/pgSQL function inline_code_block line 4 at SQL statement
As you are already using format() use the %L and %I placeholders:
do $$
begin
execute format('select setval(pg_get_serial_sequence(%L, %L), coalesce(max(%I), 0) + 1, false) from %I.%I',
'alerts.nds_email_message', 'nds_email_message_id', 'nds_email_message_id', 'alerts', 'nds_email_message');
end $$;
The parameters to pg_get_serial_sequence() are strings, so you need to use the %L placeholder. The references to the table and columns inside the actual select are identifiers, so you need to use %I

Postgres - Use table name (passed in parameter) in function body

I am a newbie wrt functions and I am struggling with using the name of a table in the function body. I get an error "SQL Error [42703]: ERROR: column "tname" does not exist" when I call the function using
select "JsonToView"('data_import.import_360xero_report');
My code is below
create or replace
function data_import."JsonToView"(tname text) returns numeric
language plpgsql
as $function$
begin
do
$$
declare
l_keys text;
begin
drop view if exists v_json_view cascade;
select
string_agg(distinct format('import_data ->> %L as %I', jkey, jkey), ', ')
into
l_keys
from
import_360xero_report,
json_object_keys(import_data) as t(jkey);
execute 'create view v_json_view as select ' || l_keys || ' from ' || tname;
end;
$$;
return 0;
end $function$ ;
I have modified the code and the second create view query works with the table name but the first one does not.
Below if my modified code
create or replace
function data_import."JsonToView"(tname text) returns numeric
language plpgsql
as $function$
declare
l_keys text;
begin
drop view if exists v_json_view cascade;
execute $a$select
string_agg(distinct format('import_data ->> %L as %I', jkey, jkey), ', ')
into
l_keys
from $a$ ||
tname || $b$,
json_object_keys(import_data) as t(jkey)$b$;
execute 'create view v_json_view as select ' || l_keys || ' from ' || tname;
return 0;
end $function$ ;
The error I am getting is
SQL Error [0A000]: ERROR: EXECUTE of SELECT ... INTO is not implemented
Hint: You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.
Where: PL/pgSQL function "JsonToView"(text) line 10 at EXECUTE
The problem is the superfluous nested DO statement.
The variable tname exists only in the scope of the function, not in the nested DO statement. DO is an SQL statement, not a PL/pgSQL statement, and there are no variables in SQL. Also, DO does not allow parameters.
Get rid of the DO and you will be fine.

POSTGRES: Use result of select in ALTER query

Is it possible to execute a dynamically created query in postgres without using a function?
The following query returns the queries that I want to execute:
SELECT 'ALTER INDEX ' || idx_name_1 || ' RENAME TO ' || idx_name_2 || ';' AS myqueries from myindexes;
This returns the following:
myqueries
------------
ALTER INDEX idxold1 RENAME TO idxnew1;
ALTER INDEX idxold2 RENAME TO idxnew2;
Now ideally I'd like to directly execute the ALTER commands within one psql command without having to use a function.
After the hint by a_horse_with_no_name, I got it to work with the following code:
DO $$DECLARE r record;
BEGIN
FOR r in SELECT idx_name_1, idx_name_2 from myindexes
LOOP
EXECUTE 'ALTER INDEX ' || quote_ident(r.idx_name_1) || ' RENAME TO ' || quote_ident(r.idx_name_2);
END LOOP;
END$$;

PostgreSQL Squence Not Working When Updating Column Details

I have created a function to update a column in a postgresSQL table using Sequence nextval() function.Function body is as follows
BEGIN
EXECUTE 'CREATE SEQUENCE '|| sequence_name || ' START 1';
EXECUTE 'UPDATE ' ||selected_table_name|| ' SET record_id = '||nextval(sequence_name);
RETURN 'SUCCESS';
END;
But when I call function as follows
SELECT staging.update_record_id('staging.test_table','staging.sq_test_table');
Its update my relevant column with 1 for all the records.But when I just use the following command in the console directly it update the all the values with increments.The console code as follows.
update staging.test_table set record_id = nextval('staging.sq_test_table');
Is anyone can give a solution for this, would be much grateful
I found a solution for the question.The function body should change as follows
BEGIN
EXECUTE 'CREATE SEQUENCE '|| sequence_name || ' START 1';
EXECUTE 'UPDATE ' ||selected_table_name|| ' SET record_id = nextval('''||sequence_name||''')';
RETURN 'SUCCESS';
END;

EXECUTE INTO in plpgsql

I want to execute the following:
EXECUTE 'SELECT ' || row_name || ' INTO row_value FROM user_data.data WHERE id = ' || tid || ';';
row_name, row_value and tid are variables of the plpgsql function. My concern is, whether the selected value will be passed to the row_value variable or not.
Try it yourself! Your function raises
ERROR: EXECUTE of SELECT ... INTO is not implemented
HINT: You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.
and all is clear.