psql , creating stored procedure - converting string to schema type - postgresql

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

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;

Save execute results into a table

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;

Postgres Stored procedure in select statement cannot get the datas

I am trying to get the data from the Database use function of select prison();.but i got error .Please advise me.
CREATE OR REPLACE FUNCTION prison() RETURNS refcursor AS $$
DECLARE
ref refcursor;
BEGIN
OPEN ref FOR SELECT round,ben_sc,ben_st FROM prison_issue;
RETURN ref;
END;
$$ LANGUAGE plpgsql;
and calling like this
select prison();
also i tried.but cannot executed the rows.
BEGIN;
SELECT prison();
-- Returns: <unnamed portal 2>
FETCH ALL IN "<unnamed portal 24>";
COMMIT;
There is no need for a PL/pgSQL function for this:
CREATE OR REPLACE FUNCTION prison()
RETURNS setof prison_issue
AS $$
SELECT * FROM prison_issue;
$$ LANGUAGE sql;
You also need to use:
select * from prison();
to retrieve the data, do not use select prison() (which only returns a single record, not multiple rows)
You didn't show us your definition of the table prison_issue if you don't want to return all columns you need something like this:
CREATE OR REPLACE FUNCTION prison()
RETURNS table (round integer, ben_sc text, ben_st text)
AS $$
SELECT SELECT round,ben_sc,ben_st FROM prison_issue;
$$ LANGUAGE sql;
You will need to adjust the part table (round integer, ben_sc text, ben_st text) to match the data type of the columns you select.
Below is an example code.I have assumed the data types.Replace with the real ones.
CREATE OR REPLACE FUNCTION prison() RETURNS TABLE(round numeric,ben_sc character varying,ben_st character varying) AS $$
BEGIN
RETURN QUERY SELECT p.round,p.ben_sc,p.ben_st FROM prison_issue p;
END;
$$ LANGUAGE plpgsql;

Passing table names in an array

I need to do the same deletion or purge operation (based on several conditions) on a set of tables. For that I am trying to pass the table names in an array to a function. I am not sure if I am doing it right. Or is there a better way?
I am pasting just a sample example this is not the real function I have written but the basic is same as below:
CREATE OR REPLACE FUNCTION test (tablename text[]) RETURNS int AS
$func$
BEGIN
execute 'delete * from '||tablename;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
But when I call the function I get an error:
select test( {'rajeev1'} );
ERROR: syntax error at or near "{"
LINE 10: select test( {'rajeev1'} );
^
********** Error **********
ERROR: syntax error at or near "{"
SQL state: 42601
Character: 179
Array syntax
'{rajeev1, rajeev2}' or ARRAY['rajeev1', 'rajeev2']. Read the manual.
TRUNCATE
Since you are deleting all rows from the tables, consider TRUNCATE instead. Per documentation:
Tip: TRUNCATE is a PostgreSQL extension that provides a faster
mechanism to remove all rows from a table.
Be sure to study the details. If TRUNCATE works for you, the whole operation becomes very simple, since the command accepts multiple tables:
TRUNCATE rajeev1, rajeev2, rajeev3, ..
Dynamic DELETE
Else you need dynamic SQL like you already tried. The scary missing detail: you are completely open to SQL injection and catastrophic syntax errors. Use format() with %I (not %s to sanitize identifiers like table names. Or, better yet in this particular case, use an array of regclass as parameter instead:
CREATE OR REPLACE FUNCTION f_del_all(_tbls regclass)
RETURNS void AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOREACH _tbl IN ARRAY _tbls LOOP
EXECUTE format('DELETE * FROM %s', _tbl);
END LOOP;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT f_del_all('{rajeev1,rajeev2,rajeev3}');
Explanation here:
Table name as a PostgreSQL function parameter
You used wrong syntax for text array constant in the function call. But even if it was right, your function is not correct.
If your function has text array as argument you should loop over the array to execute query for each element.
CREATE OR REPLACE FUNCTION test (tablenames text[]) RETURNS int AS
$func$
DECLARE
tablename text;
BEGIN
FOREACH tablename IN ARRAY tablenames LOOP
EXECUTE FORMAT('delete * from %s', tablename);
END LOOP;
RETURN 1;
END
$func$ LANGUAGE plpgsql;
You can then call the function for several tables at once, not only for one.
SELECT test( '{rajeev1, rajeev2}' );
If you do not need this feature, simply change the argument type to text.
CREATE OR REPLACE FUNCTION test (tablename text) RETURNS int AS
$func$
BEGIN
EXECUTE format('delete * from %s', tablename);
RETURN 1;
END
$func$ LANGUAGE plpgsql;
SELECT test('rajeev1');
I recommend using the format function.
If you want to execute a function (say purge_this_one_table(tablename)) on a group of tables identified by similar names you can use this construction:
create or replace function purge_all_these_tables(mask text)
returns void language plpgsql
as $$
declare
tabname text;
begin
for tabname in
select relname
from pg_class
where relkind = 'r' and relname like mask
loop
execute format(
'purge_this_one_table(%s)',
tabname);
end loop;
end $$;
select purge_all_these_tables('agg_weekly_%');
It should be:
select test('{rajeev1}');

how to circumvent missing record type on insert

I'd like to make a copy of a row in one table addressed by a field in another table, like this:
CREATE OR REPLACE FUNCTION f_ins_up_vorb()
RETURNS TRIGGER AS $$
DECLARE
dienst dienst%ROWTYPE;
account record;
BEGIN
-- ...
EXECUTE format('SELECT * FROM %s WHERE id=$1',dienst.tabelle)
USING NEW.id INTO account;
EXECUTE 'INSERT INTO ' || dienst.tabelle || 'shadow SELECT ($1).*, now(), $2' USING account, jobid;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
But this yields:
ERROR: record type has not been registered
CONTEXT: SQL statement "INSERT INTO accountadshadow SELECT ($1).*, now(), $2"
PL/pgSQL function f_ins_up_vorb() line 30 at EXECUTE statement
The tables addressed by dienst.tabelle have no common type but the target table (dienst.tabelle || 'shadow') is always a superset of the source table. So this should always work (and does work in a trigger function, where I use NEW, which seems to have a record type).
Is there any way around this?
Try something like:
CREATE OR REPLACE FUNCTION f_ins_up_vorb()
RETURNS TRIGGER AS $$
DECLARE
dienst dienst%ROWTYPE;
BEGIN
-- ...
EXECUTE 'INSERT INTO '||dienst.tabelle||'shadow
SELECT *, now(), $2
FROM '||dienst.tabelle||'
WHERE id=$1'
USING NEW.id, jobid;
RETURN NEW;
END
$$ LANGUAGE plpgsql;
If you are trying to create some kind of log trigger - read this page first.