PostgreSQL, drop custom functions - postgresql

In order to restore my data from pgdump I need to delete custom functions from database.
This function identify them well:
SELECT pp.proname
FROM pg_proc pp
INNER JOIN pg_namespace pn on (pp.pronamespace = pn.oid)
INNER JOIN pg_language pl on (pp.prolang = pl.oid)
WHERE pl.lanname NOT IN ('c','internal')
AND pn.nspname NOT LIKE 'pg_%'
AND pn.nspname <> 'information_schema';
So, immediately after I hear for dynamic SQL and ensure that I can use it from .NET I try to apply it for that purpose.
do
$$
declare
func_rec record;
begin
for func_rec in (SELECT pp.proname as funcname
FROM pg_proc pp
INNER JOIN pg_namespace pn on (pp.pronamespace = pn.oid)
INNER JOIN pg_language pl on (pp.prolang = pl.oid)
WHERE pl.lanname NOT IN ('c','internal')
AND pn.nspname NOT LIKE 'pg_%'
AND pn.nspname <> 'information_schema')
loop
execute 'drop function '||func_rec.funcname||' cascade';
end loop;
end;
$$
In meantime I found that for drop a function I have to supply it's parameters to DROP command.
Here is one example subquery on how those parameters can be retrieved:
(SELECT typname FROM pg_type WHERE oid = funcrow.proargtypes[i])
Now remain a problem that I don't know how to put those in functional code which will add needed parameters to func_rec.funcname in order to delete such functions.
So, please help to get query for deleting custom functions from all functions.

Because functions can be overloaded in Postgres, you need to include the function's signature in the drop.
Suppose you have these functions:
get_answer(p1 integer);
get_answer(p1 integer, p2 integer);
then Postgres wouldn't know which one to drop when using drop function get_answer;.
Luckily Postgres has a function to format the arguments so that they can be used for that purpose: pg_get_function_identity_arguments.
So you need to change your select to:
SELECT pp.proname||'('||pg_get_function_identity_arguments(pp.oid)||')' as funcname
FROM pg_proc pp
INNER JOIN pg_namespace pn on (pp.pronamespace = pn.oid)
INNER JOIN pg_language pl on (pp.prolang = pl.oid)
WHERE pl.lanname NOT IN ('c','internal')
AND pn.nspname NOT LIKE 'pg_%'
AND pn.nspname <> 'information_schema';

Related

PL/pgsql Correct way to cast regclass dynamically in query

I have a function that is going to loop through a table, and then loop through all the tables that inherit from each table from the outerloop. I found a great function to find tables that inherit from a parent table. My question is, how do I dynamically change the table name to find all parent? so 'shopmaster.pb'::regclass would be something like 'shopmaster.'||i.tablename::regClass.
CREATE OR REPLACE FUNCTION shopmaster.cascade_filters()
RETURNS jsonb AS $$
DECLARE
i record;
k shopmaster."catalog_filters";
BEGIN
DELETE FROM shopmaster.catalog_filters WHERE isparent=false;
FOR i IN SELECT shopmaster.catalog.catalogid,columnname,columntype,columnnvalues,
tablename FROM shopmaster.catalog_filters INNER JOIN catalog ON
(catalog_filters.catalogid=catalog.catalogid) WHERE isparent=true LOOP
FOR k IN
WITH RECURSIVE inh AS (
SELECT i.inhrelid FROM pg_catalog.pg_inherits i WHERE inhparent=
'shopmaster.pb'::regclass
UNION
SELECT i.inhrelid FROM inh INNER JOIN pg_catalog.pg_inherits i ON
(inh.inhrelid = i.inhparent)
)
SELECT pg_namespace.nspname, pg_class.relname
FROM inh
INNER JOIN pg_catalog.pg_class ON (inh.inhrelid=pg_class.oid)
INNER JOIN pg_catalog.pg_namespace ON
(pg_class.relnamespace=pg_namespace.oid) LOOP
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
Turns out there's an easy way. Perform a sub-expression to concat or whatever to get a table identifier, then cast it to a ::regclass. E.g. ('shopmaster.'||i.tablename)::regclass
CREATE OR REPLACE FUNCTION shopmaster.cascade_filters()
RETURNS jsonb AS $$
DECLARE
i record;
k record;
BEGIN
DELETE FROM shopmaster.catalog_filters WHERE isparent=false;
FOR i IN SELECT shopmaster.catalog.catalogid,columnname,columntype,
columnnvalues,tablename
FROM shopmaster.catalog_filters INNER JOIN shopmaster.catalog ON
(catalog_filters.catalogid=catalog.catalogid) WHERE isparent=true LOOP
FOR k IN
WITH RECURSIVE inh AS (
SELECT ih.inhrelid FROM pg_catalog.pg_inherits ih WHERE inhparent=
('shopmaster.'||i.tablename)::regclass
UNION
SELECT ih.inhrelid FROM inh INNER JOIN pg_catalog.pg_inherits ih ON
(inh.inhrelid = ih.inhparent)
)
SELECT pg_namespace.nspname, pg_class.relname,
shopmaster.catalog.catalogid
FROM inh
INNER JOIN pg_catalog.pg_class ON (inh.inhrelid=pg_class.oid)
INNER JOIN pg_catalog.pg_namespace ON
(pg_class.relnamespace=pg_namespace.oid)
INNER JOIN shopmaster.catalog ON
(pg_class.relname=shopmaster.catalog.tablename) LOOP
EXECUTE 'INSERT INTO shopmaster.catalog_filters (catalogid,'
||'columnname,columntype,columnnvalues,isparent,owner) '
||'VALUES($1,$2,$3,$4,false,$5)' USING k.catalogid,i.columnname,
i.columntype,i.columnnvalues,i.catalogid;
END LOOP;
END LOOP;
RETURN jsonb_build_object('ok',true);
END;
$$ LANGUAGE plpgsql;

Alter table add column default and execute the default for each row

I am making a function that adds a id column to a given table, creates a sequence and fills the new columns value. The thing is that the column is created but now I need to fill it with nextval() of the created sequence (1,2,3,4,5...). I don't know how to specify that in the add column sentence.
CREATE OR REPLACE FUNCTION create_id(tabla character varying)
RETURNS void AS
$BODY$
DECLARE
BEGIN
IF NOT EXISTS (SELECT information_schema.columns.column_name FROM information_schema.columns WHERE information_schema.columns.table_name=tabla AND information_schema.columns.column_name='id')
THEN
EXECUTE 'ALTER TABLE '|| tabla ||' ADD COLUMN id numeric(8,0)';
IF NOT EXISTS (SELECT relname FROM pg_class WHERE relname='seq_id_'||tabla)
THEN
EXECUTE 'CREATE SEQUENCE seq_id_'||tabla||' INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 START 1 CACHE 1';
EXECUTE 'GRANT ALL ON TABLE seq_id_'||tabla||' TO postgres';
EXECUTE 'ALTER TABLE ONLY '||tabla||' ALTER COLUMN id SET DEFAULT nextval(''seq_id_'||tabla||'''::regclass)';
END IF;
END IF;
RETURN;
END;
$BODY$
LANGUAGE plpgsql;
Your function suffers from a number of series problems. Use this instead:
CREATE OR REPLACE FUNCTION f_create_id(_tbl text)
RETURNS void AS
$func$
DECLARE
_seq text := _tbl || '_id_seq';
BEGIN
IF EXISTS (
SELECT 1 FROM pg_namespace n
JOIN pg_class c ON c.relnamespace = n.oid
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE n.nspname = current_schema() -- default to current schema
AND c.relname = _tbl
AND a.attname = 'id'
AND NOT a.attisdropped)
THEN
RAISE EXCEPTION 'Column already exists!'; RETURN;
END IF;
IF EXISTS (
SELECT 1 FROM pg_namespace n
JOIN pg_class c ON c.relnamespace = n.oid
WHERE n.nspname = current_schema() -- default to current schema
AND c.relname = _seq)
THEN
RAISE EXCEPTION 'Sequence already exists!'; RETURN;
END IF;
EXECUTE format('CREATE SEQUENCE %I.%I', current_schema(), _seq;
EXECUTE format($$ALTER TABLE %I.%I ADD COLUMN id numeric(8,0)
DEFAULT nextval('%I'::regclass)$$ -- one statement!
, current_schema(), _tbl, _seq);
END
$func$ LANGUAGE plpgsql;
Major points
If you set the column default in the same ALTER TABLE statement, values are inserted automatically. Be aware that this makes a big difference in performance for big tables, since every row has to be updated, while adding a NULL column only needs a tiny change to the system catalog.
You must define the schema to create objects in. If you want to default to the current schema, you still have to consider this in your queries to catalog (or information schema) tables. Table names are only unique in combination with the schema name.
I use the session information functions current_schema() to find out the current schema.
You must safeguard against SQL injection when using dynamic SQL with user input. Details:
Table name as a PostgreSQL function parameter
If the sequence already exists, do not use it! You might interfere wit existing objects.
Normally, you do not need EXECUTE GRANT ALL ON TABLE ... TO postgres. If postgres is a superuser (default) the role has all rights anyway. You might want to make postgres the owner. That would make a difference.
I am using the system catalog in both queries, while you use the information schema in one of them. I am generally not a fan of the information schema.Its bloated views are slow. The presented information adheres to a cross-database standard, but what's that good for when writing plpgsql functions, which are 100% not portable anyway?
Superior alternative
I would suggest not to use the column name id, which is an SQL anti-pattern. Use a proper descriptive name instead, like tablename || '_id'.
What's the point of using numeric(8,0)? If you don't want fractional digits, why not use integer? Simpler, smaller, faster.
Given that, you are much better off with a serial type, making everything much simpler:
CREATE OR REPLACE FUNCTION f_create_id(_tbl text)
RETURNS void AS
$func$
BEGIN
IF EXISTS (
SELECT 1 FROM pg_namespace n
JOIN pg_class c ON c.relnamespace = n.oid
JOIN pg_attribute a ON a.attrelid = c.oid
WHERE n.nspname = current_schema() -- default to current schema
AND c.relname = _tbl
AND a.attname = _tbl || '_id' -- proper column name
AND NOT a.attisdropped)
THEN
RAISE EXCEPTION 'Column already exists!';
ELSE
EXECUTE format('ALTER TABLE %I.%I ADD COLUMN %I serial'
, current_schema(), _tbl, _tbl || '_id');
END IF;
END
$func$ LANGUAGE plpgsql;

Query on subquery that gets tables' names

I have few tables in my database. They all have the same columns (id, name) but differ in the table name. Those tables have names that start with letter 'h'.
Not a very interesting schema design but I have to follow it.
I need to search for id in all those tables.
I tried something similar to:
select id from (select table_name
FROM information_schema.tables
where table_name like 'h%') as t;
I got error:
ERROR: column "id" does not exist.
I understand the error now but I still do not know how to do the query?
You need dynamic SQL to do that since you cannot use values as identifiers in plain SQL. Write a PL/pgSQL function with EXECUTE:
CREATE FUNCTION f_all_tables()
RETURNS TABLE (id int) AS
$func$
DECLARE
_tbl regclass;
BEGIN
FOR _tbl IN
SELECT c.oid::regclass
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND c.relname LIKE 'h%'
AND n.nspname = 'public' -- your schema name here
LOOP
RETURN QUERY EXECUTE '
SELECT id FROM ' || _tbl;
END LOOP;
END
$func$ LANGUAGE plpgsql;
I am using a variable of the object identifier type regclass to prevent SQL injection effectively. More about that in this related answer:
Table name as a PostgreSQL function parameter

PlPgsql function which returns result set of database

I have a problem in PL/PGSQL with functions.
Basically, I have a query:
SELECT nspname AS schemaname,relname,reltuples
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE
nspname NOT IN ('pg_catalog', 'information_schema') AND
relkind='r' and nspname='1'
ORDER BY relname;
This query derives database statistics of opened rows in tables. But our database has a huge amount of schemas. I have to write functions that iterate over all schemes with that query, getting the number of rows opened in tables in each scheme. I have tried to Google the problem but I couldn't find any related examples.
You can define return value in your function, just remember to have correct datatypes or prepare to do some typecasting.create or replace function get_my_database_status()
returns TABLE(schemaname name, relname name, reltuples real)AS
$$
BEGIN
return Query (SELECT N.nspname AS schemaname, C.relname , C.reltuples
FROM pg_class C
LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE N.nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind='r' -- here maybe some filtering
ORDER BY relname);
END
$$
language 'plpgsql';
Use the force, read the ... err ... manual !
SQL Functions Returning TABLE (applies to PL/pgSQL functions too)
SQL Functions Returning Sets (applies to PL/pgSQL functions too)
PL/pgSQL Control Structures: Returning From a Function
However in this case there is no need for a fancy logic but only a single SQL query is wrapped inside a function so a plain SQL function will do.
The only extra feature used here is Copying Types.
create or replace function get_my_database_status2()
returns table(schemaname pg_namespace.nspname%type,
relname pg_class.relname%type,
reltuples pg_class.reltuples%type)
as $$
SELECT N.nspname, C.relname, C.reltuples
FROM pg_class C
LEFT JOIN pg_namespace N ON N.oid = C.relnamespace
WHERE N.nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind = 'r' -- here maybe some filtering
ORDER BY relname;
$$ language sql;

How to display the function, procedure, triggers source code in postgresql?

How to print functions and triggers sourcecode in postgresql? please let me know if any one know the query to display the function, triggers source code.
\df+ in psql gives you the sourcecode.
For function:
you can query the pg_proc view , just as the following
select proname,prosrc from pg_proc where proname= your_function_name;
Another way is that just execute the commont \df and \ef which can list the functions.
skytf=> \df
List of functions
Schema | Name | Result data type | Argument data types | Type
--------+----------------------+------------------+------------------------------------------------+--------
public | pg_buffercache_pages | SETOF record | | normal
skytf=> \ef pg_buffercache_pages
It will show the source code of the function.
For triggers:
I dont't know if there is a direct way to get the source code. Just know the following way, may be it will help you!
step 1 : Get the table oid of the trigger:
skytf=> select tgrelid from pg_trigger where tgname='insert_tbl_tmp_trigger';
tgrelid
---------
26599
(1 row)
step 2: Get the table name of the above oid !
skytf=> select oid,relname from pg_class where oid=26599;
oid | relname
-------+-----------------------------
26599 | tbl_tmp
(1 row)
step 3: list the table information
skytf=> \d tbl_tmp
It will show you the details of the trigger of the table . Usually a trigger uses a function. So you can get the source code of the trigger function just as the above that I pointed out !
Here are few examples from PostgreSQL-9.5
Display list:
Functions: \df+
Triggers : \dy+
Display Definition:
postgres=# \sf
function name is required
postgres=# \sf pg_reload_conf()
CREATE OR REPLACE FUNCTION pg_catalog.pg_reload_conf()
RETURNS boolean
LANGUAGE internal
STRICT
AS $function$pg_reload_conf$function$
postgres=# \sf pg_encoding_to_char
CREATE OR REPLACE FUNCTION pg_catalog.pg_encoding_to_char(integer)
RETURNS name
LANGUAGE internal
STABLE STRICT
AS $function$PG_encoding_to_char$function$
There are many possibilities. Simplest way is to just use pgAdmin and get this from SQL window. However if you want to get this programmatically then examinate pg_proc and pg_trigger system catalogs or routines and triggers views from information schema (that's SQL standard way, but it might not cover all features especially PostgreSQL-specific). For example:
SELECT
routine_definition
FROM
information_schema.routines
WHERE
specific_schema LIKE 'public'
AND routine_name LIKE 'functionName';
Slightly more than just displaying the function, how about getting the edit in-place facility as well.
\ef <function_name> is very handy. It will open the source code of the function in editable format.
You will not only be able to view it, you can edit and execute it as well.
Just \ef without function_name will open editable CREATE FUNCTION template.
For further reference -> https://www.postgresql.org/docs/9.6/static/app-psql.html
\sf function_name in psql yields editable source code of a single function.
From https://www.postgresql.org/docs/9.6/static/app-psql.html:
\sf[+] function_description
This command fetches and shows the definition of the named function, in the form of a CREATE OR REPLACE FUNCTION command.
If + is appended to the command name, then the output lines are numbered, with the first line of the function body being line 1.
additionally to #franc's answer you can use this from sql interface:
select
prosrc
from pg_trigger, pg_proc
where
pg_proc.oid=pg_trigger.tgfoid
and pg_trigger.tgname like '<name>'
(taken from here: http://www.postgresql.org/message-id/Pine.BSF.4.10.10009140858080.28013-100000#megazone23.bigpanda.com)
Since Version: psql (9.6.17, server 11.6)
I have tried all of above answer but For me
postgres=> \sf jsonb_extract_path_text
CREATE OR REPLACE FUNCTION pg_catalog.jsonb_extract_path_text(from_json jsonb, VARIADIC path_elems text[])
RETURNS text
LANGUAGE internal
IMMUTABLE PARALLEL SAFE STRICT
AS $function$jsonb_extract_path_text$function$
postgres=> \df+
ERROR: column p.proisagg does not exist
LINE 6: WHEN p.proisagg THEN 'agg'
^
HINT: Perhaps you meant to reference the column "p.prolang".
df seems not working for me.
To list all functions:
select n.nspname as function_schema,
p.proname as function_name,
l.lanname as function_language,
case when l.lanname = 'internal' then p.prosrc
else pg_get_functiondef(p.oid)
end as definition,
pg_get_function_arguments(p.oid) as function_arguments,
t.typname as return_type
from pg_proc p
left join pg_namespace n on p.pronamespace = n.oid
left join pg_language l on p.prolang = l.oid
left join pg_type t on t.oid = p.prorettype
where n.nspname not in ('pg_catalog', 'information_schema')
order by function_schema,
function_name;
Now if you want to search a specific word or text in all function is something like that, make sure you replace your text in the function:
with tbl as (
select n.nspname as function_schema,
p.proname as function_name,
l.lanname as function_language,
case when l.lanname = 'internal' then p.prosrc
else pg_get_functiondef(p.oid)
end as definition,
pg_get_function_arguments(p.oid) as function_arguments,
t.typname as return_type
from pg_proc p
left join pg_namespace n on p.pronamespace = n.oid
left join pg_language l on p.prolang = l.oid
left join pg_type t on t.oid = p.prorettype
where n.nspname not in ('pg_catalog', 'information_schema')
)
select *
from tbl
where definition ilike '%word or text you want to search%'
order by function_schema,
function_name;