Save execute results into a table - postgresql

Below is a simplified postgres stored procedure I am trying to run:
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute(' select *
into my_schema.MyTable_'||suffix||'
From my_schema.MyTable
');
end;
$$
language plpgsql;
When I attempt to run using something like:
call my_schema.tst('test');
I get this error Invalid operation: EXECUTE of SELECT ... INTO is not supported;
Is it possible to execute a dynamic query that creates a new table? I have seen examples that look like:
Execute('... some query ...') into Table;
but for my use case I need the resulting tablename to be passed as a variable.

In PostgreSQL you can use INSERT INTO tname SELECT...
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' INSERT INTO my_schema.MyTable_'||suffix||' SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;
or Use CREATE TABLE tname AS SELECT..., :
create or replace procedure my_schema.tst(suffix varchar)
as $$
begin
execute ' CREATE TABLE my_schema.MyTable_'||suffix||' as SELECT *
FROM my_schema.MyTable
';
end;
$$
language plpgsql;

Related

Argument not taking the value from Postgres function

I have a simple Postgres function where I want to take table_name as a parameter and pass it into an argument and delete the data from table by condition.
CREATE OR REPLACE FUNCTION cdc.audit_refresh(tablename text)
RETURNS integer AS
$$
BEGIN
delete from tablename where id<4;
RETURN(select 1);
END;
$$ LANGUAGE plpgsql;
select cdc.audit_refresh('cdc.adf_test');
But it throws out an error that tablename
ERROR: relation "tablename" does not exist in the delete statement.(refer snapshot)
What you want to achieve is to execute Dynamic SQL statements. You can do this with EXECUTE. See more here
CREATE OR REPLACE FUNCTION audit_refresh(tablename text)
RETURNS integer AS
$$
DECLARE
stmt TEXT;
BEGIN
stmt = 'delete from '||tablename||' where id<4;';
EXECUTE stmt;
RETURN 1;
END
$$ LANGUAGE plpgsql;

Select Statement using Stored Procedure

May I ask on how to call a method when the content of the stored procedure is about select statement? (Using postgreSQL)
CREATE OR REPLACE PROCEDURE select_table(table_name VARCHAR(255))
language plpgsql
as $$
BEGIN
EXECUTE('SELECT * FROM' || ' ' || quote_ident(table_name));
END $$;
CALL select_table('employee_table');
EDITED(USING FUNCTION)
CREATE OR REPLACE FUNCTION select_table(table_name VARCHAR(255))
language plpgsql
as $$
BEGIN
SELECT * FROM table_name
RETURN table_name;
END $$;
In PostgreSQL procedures doesn't execute any select statements and doesn't have return.
For returning data you can use functions. But functions also cannot return different structural data, examples:
CREATE OR REPLACE FUNCTION fr_test()
RETURNS TABLE(id integer, bookname character varying)
LANGUAGE plpgsql
AS $function$
begin
return QUERY
SELECT tb.id, tb.bookname from rbac.books tb;
end;
$function$
;
or
CREATE OR REPLACE FUNCTION fr_test()
RETURNS setof public.books
LANGUAGE plpgsql
AS $function$
begin
return QUERY
SELECT * from public.books;
end;
$function$
;
But for returning difference tables you can do it using procedures and using out refcursor, like as in Oracle. For example:
create or replace procedure pr_test(OUT r1 refcursor)
as $$
begin
open r1 for
select * from public.books;
end;
$$ language plpgsql;

psql , creating stored procedure - converting string to schema type

I have a stored producedure that does this, my_schema is the name of the current schema I'm using in my db.
CREATE OR REPLACE myprocedure(data TEXT) LANGUAGE plpgsql AS $$
DECLARE
my_variable my_schema.my_table%ROWTYPE;
BEGIN
SELECT * FROM my_schema.my_table WHERE oid=3;
END;
$$;
But, I need this to be a bit more generic, and I'd like to use this for any schema I create in my db, so, I'd like to pass in the schema name instead like this, so that I won't have to create a procedure for every schema created in the future:
note I've used angle brackets for syntax I'm not sure of:
CREATE OR REPLACE myprocedure(data TEXT, my_schema_name <SOME_TYPE>) LANGUAGE plpgsql AS $$
DECLARE
my_variable <my_schema_name>.my_table%ROWTYPE;
BEGIN
SELECT * FROM <my_schema_name>.my_table WHERE oid=3;
END;
$$;
and then call it like this:
CALL myprocedure('SOMETHIGN', <MY_SCHEMA_NAME>);
what is the correct syntax to do this?
You can use the record type for a generic record, and you use dynamic SQL for the query. Here is an example:
CREATE PROCEDURE x(schema_name text) LANGUAGE plpgsql AS
$$DECLARE
q record;
BEGIN
EXECUTE format(
'SELECT * FROM %I.data WHERE id = 1',
schema_name
) INTO q;
RAISE NOTICE '%', q.id;
END;$$;

Using to_jsonb(NEW) and execute

CREATE OR REPLACE FUNCTION change_trigger() RETURNS trigger AS $$
BEGIN
INSERT INTO static_table_name (content) VALUES (to_jsonb(NEW));
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
↑ is working, but I'd like to have opportunity to send target table name for inserting.
So,
by the code above, I was able to use dymanic_table_name,
CREATE OR REPLACE FUNCTION change_trigger() RETURNS trigger AS $$
DECLARE
dymanic_table_name TEXT;
BEGIN
dymanic_table_name := TG_ARGV[0];
EXECUTE 'INSERT INTO ' || dymanic_table_name || ' (content) VALUES (' || to_json(NEW) || ');';
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
but it fails, when try to insert result of 'to_json' function...
ERROR:
ERROR: "{"またはその近辺で構文エラー
LINE 1: ..._table (content) VALUES ({"id":43,"...
^
※ Sorry for Japanese syntax ( *´艸`)
You'll have to use dynamic SQL like this:
EXECUTE
format(
'INSERT INTO %I (content) VALUES (to_json($1))',
dymanic_table_name
)
USING NEW;

How to log delete queries on Postgresql?

I created a function which writes information about table deletions.
And another function which simply adds a trigger call after delete.
But I would like to store the whole row as string into my table.
According to Postgresql Documentation it should work by adding "OLD.*" into a text based column. But it fails telling me that I try to put too many columns into this table.
OLD is from type RECORD. And i want to have it in my text field like "value1,value2,value3" or it could be "colname:value,colname2:value". I dont care, I just want to see the row which has been deleted.
Another approach can be to log all delete queries from pg_stat_activity. But I don't know how to do that. Simply accessing pg_stat_activity every second would cause too much traffic I guess.
My table is simple:
create table delete_history (date timestamp, tablename varchar(100), data text);
This is my function:
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, OLD.*);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
This is my trigger:
CREATE OR REPLACE FUNCTION history_create_triggers() RETURNS void
AS $$
DECLARE
r RECORD;
BEGIN
FOR r IN SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type='BASE TABLE' LOOP
EXECUTE 'CREATE TRIGGER log_history AFTER DELETE ON public.' || r.table_name || ' FOR EACH ROW EXECUTE PROCEDURE ondelete();';
END LOOP;
END;
$$ LANGUAGE plpgsql;
You can convert type record into text:
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, OLD::text);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo
another approach could be converting your row into JSON with row_to_json function (if you have version 9.2):
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, row_to_json(OLD));
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
sql fiddle demo
Another approach can be convert your data to hstore
CREATE or REPLACE FUNCTION ondelete() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO delete_history VALUES (CURRENT_TIMESTAMP, TG_TABLE_NAME, hstore(OLD));
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
I can't test it now - sqlfiddle is not allowing to use hstore.