Postgres execute function if exists - postgresql

Is it possible to write a select statement that executes function if exists ?
SELECT COALESCE (CASE WHEN EXISTS (SELECT * FROM pg_proc WHERE proname = 'func_name') THEN null ELSE false END, (SELECT func_name() ))
OR
select 1
WHERE EXISTS (
select * from pg_proc where proname = 'func_name'
) AND EXISTS (
select func_name();
)
Above are ofcourse wrong statements but just to give you an idea. Is it possible?? I am using Postgres 10.1 so its not the latest

You can probably get away with using query_to_xml()
select query_to_xml('select func_name()', false, true, '')
where exists (select *
from pg_proc
where proname = 'func_name'
and pronamespace = 'public'::regnamespace)

Related

PostgreSQL + dblink: how to handle multiple fields?

I have this select, that collects data from all local databases that have a table named operation:
select
db_name,
unnest((select *
from dblink(conn_string,
'select array_agg(row(opr.id, opr.descricao))
from Operacao opr')
as (result text array))) as result
from (
select
db_name,
table_name,
conn_string,
(select table_exists
from dblink(db.conn_string,
'(select Count(*) > 0 from information_schema.tables where table_name = ''' || db.table_name || ''')') as (table_exists Boolean))
from (
SELECT
datname as db_name,
'user=postgres password=postgres dbname=' || datname as conn_string,
'operacao'::text as table_name
FROM pg_database
WHERE datistemplate = false
limit 4
) db
) db
where table_exists;
This works somewhat okay, except it is bringing the id and descricao fields as a single column. I tried various variants of the result text array, but couldn't manage to turn the result into a record. Is there any way to split those columns (other than the obvious string manipulation)?
As per Adrian Klaver's suggestion, I rewrote the outermost dblink() call into a join:
select
db_name, t1.*
from (
select
db_name,
table_name,
conn_string,
(select table_exists
from dblink(db.conn_string,
'(select Count(*) > 0 from information_schema.tables where table_name = ''' || db.table_name || ''')') as (table_exists Boolean))
from (
SELECT
datname as db_name,
'user=postgres password=postgres dbname=' || datname as conn_string,
'operacao'::text as table_name
FROM pg_database
WHERE datistemplate = false
limit 4
) db
) db
inner join dblink(conn_string, 'select opr.id, opr.descricao from operacao opr') as t1(id bigint, descricao text) on true
where table_exists;

Get all sequences with current values

I have the following query that gets all sequences and their schemas:
SELECT sequence_schema as schema, sequence_name as sequence
FROM information_schema.sequences
WHERE sequence_schema NOT IN ('topology', 'tiger')
ORDER BY 1, 2
I would like to get the current value of each sequence name with something like select last_value from [sequence];. I have tried the following (and a couple variations), but it doesn't work because the syntax isn't correct:
DO $$
BEGIN
EXECUTE
sequence_schema as schema,
sequence_name as sequence,
last_value
FROM information_schema.sequences
LEFT JOIN (
EXECUTE 'SELECT last_value FROM ' || schema || '.' || sequence
) tmp
ORDER BY 1, 2;
END
$$;
I've found some solutions that create functions to execute text or piece together a query inside a function and return the result, but I would prefer to have a single query that I can run and modify however I like.
In Postgres 12, you can use pg_sequences:
select schemaname as schema,
sequencename as sequence,
last_value
from pg_sequences
You can rely on the function pg_sequence_last_value
SELECT nspname as schema,
relname AS sequence_name,
coalesce(pg_sequence_last_value(s.oid), 0) AS seq_last_value
FROM pg_class AS s
JOIN pg_depend AS d ON d.objid = s.oid
JOIN pg_attribute a ON d.refobjid = a.attrelid
AND d.refobjsubid = a.attnum
JOIN pg_namespace nsp ON s.relnamespace = nsp.oid
WHERE s.relkind = 'S'
AND d.refclassid = 'pg_class'::regclass
AND d.classid = 'pg_class'::regclass
AND nspname NOT IN ('topology', 'tiger')
ORDER BY 1,2 DESC;
Here's a solution that doesn't rely on pg_sequences or pg_sequence_last_value:
CREATE OR REPLACE FUNCTION get_sequences()
RETURNS TABLE (
last_value bigint,
sequence_schema text,
sequence_name text
)
LANGUAGE plpgsql AS
$func$
DECLARE
s RECORD;
BEGIN
FOR s IN SELECT t.sequence_schema, t.sequence_name
FROM information_schema.sequences t
LOOP
RETURN QUERY EXECUTE format(
'SELECT last_value, ''%1$s''::text, ''%2$s''::text FROM %1$I.%2$I',
s.sequence_schema,
s.sequence_name
);
END LOOP;
END;
$func$;
SELECT * FROM get_sequences();
That'll output a table like this:
last_value | sequence_schema | sequence_name
------------+-----------------+-------------------------------------------------------
1 | public | contact_infos_id_seq
1 | media | photos_id_seq
2006 | company | companies_id_seq
2505 | public | houses_id_seq
1 | public | purchase_numbers_id_seq
... etc
The other answers will only work if you are on a modern version of Postgres (I believe 10 or greater).

PostgreSQL how to check table existence?

This is how we can check table existence in MSSQL:
IF OBJECT_ID(N'public."TABLE_NAME"', N'U') IS NOT NULL
select 1 as 'column'
else
select 0 as 'column';
which stores outcome in variable 'column'
How can I do same thing in PostgreSQL ? I want to return 1 or 0 for respective outcome.
Use a SELECT with an EXISTS operator checking e.g. information_schema.tables:
select exists (select *
from information_schema.tables
where table_name = 'table_name'
and table_schema = 'public') as table_exists;
If you can't (or won't) deal with proper boolean values, the simply cast the result to a number (but I have no idea why that should be better):
select exists (select *
from information_schema.tables
where table_name = 'table_name'
and table_schema = 'public')::int as "column";
Note that column is a reserved keyword and thus you need to quote it using double quotes.
Check for column in a table existence use view pg_tables
IF EXISTS ( SELECT attname
FROM pg_attribute
WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'YOURTABLENAME')
AND attname = 'YOURCOLUMNNAME')
THEN
-- do something
END IF;
For my sql use INFORMATION_SCHEMA.COLUMNS
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'tbl_name'
[AND table_schema = 'db_name']
[AND column_name LIKE 'wild']

PostgreSQL structure of function that return a query

Given a PostgreSQL function that returns a query:
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (
txt text -- visible as OUT parameter inside and outside function
, cnt bigint
, ratio bigint) AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt
, count(*) AS cnt -- column alias only visible inside
, (count(*) * 100) / _max_tokens -- I added brackets
FROM (
SELECT t.txt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
LIMIT _max_tokens
) t
GROUP BY t.txt
ORDER BY cnt DESC; -- note the potential ambiguity
END
$func$ LANGUAGE plpgsql;
How can I retrieve the structure of this function? I mean, I know that this function will return the txt, cnt and ratio columns, but how can I make a query that returns these column names? I was trying to find these columns names on information_schema schema, but I couldn't.
The expected result of this hypothetical query would be something like this:
3 results found:
---------------------------------
?column_name? | ?function_name?
---------------------------------
txt word_frequency
cnt word_frequency
ratio word_frequency
This information is stored in pg_proc
SELECT unnest(p.proargnames) as column_name,
p.proname as function_name
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
AND p.proname = 'word_frequency'
Based on the answer of a_horse_with_no_name, I came with this final version:
SELECT
column_name,
function_name
FROM
(
SELECT
unnest(p.proargnames) as column_name,
unnest(p.proargmodes) as column_type,
p.proname as function_name
FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public'
AND p.proname = 'my_function'
) as temp_table
WHERE column_type = 't';
I simply omitted the arguments, returning only the columns that the function returns

Check if a row exists or not in postgresql

I have seen many posts about this in SO. But I could not get an answer.
I want to the query to check if a particular row exists or not in a table. If it exists, it should return me a string true and stop the search there itself and if not return false.
select
case when exists (select true from table_name where table_column=?)
then 'true'
else 'false'
end;
But it would be better to just return boolean instead of string:
select exists (select true from table_name where table_column=?);
Spoiler:
-- EXPLAIN ANALYZE
WITH magic AS (
WITH lousy AS ( SELECT * FROM one WHERE num = -1)
SELECT 'True'::text AS truth
WHERE EXISTS (SELECT * FROM lousy)
UNION ALL
SELECT 'False'::text AS truth
WHERE NOT EXISTS (SELECT * FROM lousy)
)
SELECT *
FROM magic
;