I'm trying to figure out how to resolve this ERROR: null values cannot be formatted as an SQL identifier when trying to select my function:
select * from store_keys();
ERROR: null values cannot be formatted as an SQL identifier
CONTEXT: SQL statement "SELECT string_agg(
format('SELECT %1$I, count(email_store_key), email_store_key, form_created_datetime
FROM %1$I where email_store_key=0 GROUP BY 3, 4', tbl_name),
' UNION ') FROM information_schema.tables
WHERE table_schema = 'abc_dev_sch_1234'
AND table_name LIKE 'fact_%'"
The base query it uses, does not produce null values. So where is this coming from??
select count(email_store_key), email_store_key, form_created_datetime
FROM <table_name> where email_store_key=0 GROUP BY email_store_key, form_created_datetime;
Here's my create statement:
DROP FUNCTION store_keys();
CREATE OR REPLACE FUNCTION store_keys()
RETURNS TABLE (tbl_name varchar, count_keys bigint, email_store_key integer, form_created_datetime timestamp)
AS $$
DECLARE
qry text;
BEGIN
SELECT string_agg(
format('SELECT %1$I, count(email_store_key), email_store_key, form_created_datetime
FROM %1$I where email_store_key=0 GROUP BY 3, 4', tbl_name),
' UNION ') INTO qry
FROM information_schema.tables
WHERE table_schema = 'abc_dev_sch_1234'
AND table_name LIKE 'fact_%';
RETURN QUERY EXECUTE qry;
END
$$ LANGUAGE plpgsql;
Related
On PostgreSQL, I need to see the table's columns in alphabetical order, so I'm using the query:
SELECT column_name, data_type FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'organizations' ORDER BY column_name ASC;
I use it a lot every day, so I want to create a function:
CREATE OR REPLACE FUNCTION seecols(table_name text)
RETURNS TABLE (column_name varchar, data_type varchar)
AS $func$
DECLARE
_query varchar;
BEGIN
-- Displays columns by alphabetic order
_query := 'SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '''||table_name||''' ';
RETURN QUERY EXECUTE _query;
END;
$func$ LANGUAGE plpgsql;
But when I try:
SELECT seecols('organizations');
I'm getting:
**structure of query does not match function result type**
I guess the line "RETURNS TABLE (column_name varchar, data_type varchar)" is wrongly defined. But since this is my first time using plpgsql, I don't know how to make it more dynamic.
You don't need neither dynamic sql nor plpgsql here. Just embed your sql query into a sql function :
CREATE OR REPLACE FUNCTION seecols (IN t_name text, OUT column_name varchar, OUT data_type varchar)
RETURNS setof record LANGUAGE sql AS $$
SELECT column_name, data_type
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = t_name
ORDER BY column_name ASC ;
$$ ;
see dbfiddle
how to resolve this ERROR: null values cannot be formatted as an SQL identifier when trying to select my function:
select ws_sls_core.ars_pricing_test()
ERROR: null values cannot be formatted as an SQL identifier
CONTEXT: SQL statement "select string_agg(distinct format('(props ->> %L) as %I', w_order_line_d.matl_grp2_desc, w_order_line_d.matl_grp2_desc), ', ')
from ws_sls_core.w_support_pricing_d
left join ws_sls_core.w_order_line_d
on w_support_pricing_d.svc_pricing_type = w_order_line_d.matl_grp2_cd"
PL/pgSQL function ars_pricing_test() line 6 at SQL statement
I have checked and the query in use doesn't produce any NULL
select * from ws_sls_core.w_support_pricing_d where svc_pricing_type is
null
I have tried the below-mentioned code without JOIN and it works fine, I need an additional column and have to use join, and I am only seeing this error.
Here is my complete code
CREATE OR REPLACE FUNCTION ws_sls_core.ars_pricing_test(
)
RETURNS boolean
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
declare
l_sql text;
l_columns text;
begin
select string_agg(distinct format('(props ->> %L) as %I', w_order_line_d.matl_grp2_desc, w_order_line_d.matl_grp2_desc), ', ')
into l_columns
from ws_sls_core.w_support_pricing_d
left join ws_sls_core.w_order_line_d
on w_support_pricing_d.svc_pricing_type = w_order_line_d.matl_grp2_cd;
-- and A.svc_pricing_type is not null;
l_sql :=
'create or replace view ars_pricing_test as
select w_support_pricing_d.item_num, '||l_columns||'
from (
select w_support_pricing_d.item_num, json_object_agg(w_order_line_d.matl_grp2_desc,w_support_pricing_d.mnth_maint_price) as props
from ws_sls_core.w_support_pricing_d
left join ws_sls_core.w_order_line_d
on w_support_pricing_d.svc_pricing_type = w_order_line_d.matl_grp2_cd
group by w_support_pricing_d.item_num
) t';
execute l_sql;
return true;
end;
$BODY$;
ALTER FUNCTION ws_sls_core.ars_pricing_test()
I have written a function to read certain columns from a table below using a dynamic query:
CREATE OR REPLACE FUNCTION select_cols ()
RETURNS SETOF mytable_name
LANGUAGE plpgsql
AS $$
DECLARE
list_of_columns text;
BEGIN
SELECT
string_agg(trim(cols::text, '()'), ', ') INTO list_of_columns
FROM (
SELECT
'mytable_name.' || column_name
FROM
information_schema.columns
WHERE
table_name = 'mytable_name'
AND column_name LIKE 'rm%_b'
OR column_name LIKE 'rm%_s') AS cols;
RETURN query EXECUTE concat(format('select %s from mytable_name', list_of_columns), ' RETURNING *');
END
$$;
Though when I run
select * from select_cols();
it gives me an error : "cannot open EXECUTE query as cursor".
I appreciate if someone can help with this issue
You are not returning a set, but you aggreagte the result set for only one table. So, for only one table you can use:
CREATE OR REPLACE FUNCTION select_colsx ()
RETURNS text
LANGUAGE plpgsql
AS $$
DECLARE
list_of_columns text;
BEGIN
SELECT
'select '||string_agg(trim(cols::text, '()'), ', ') || ' from pg_class RETURNING *'
INTO list_of_columns
FROM (
SELECT
'pg_class.' || column_name
FROM
information_schema.columns
WHERE
table_name = 'pg_class'
AND column_name LIKE 'oid'
OR column_name LIKE 'relacl') AS cols;
RETURN list_of_columns ;
END
$$;
select select_colsx();
DB Fiddle Example
RETURN QUERY EXECUTE was introduced in PostgreSQL 8.4. Upgrade to a less ancient version.
I have been trying the below code for a wildcard search of a record in the entire database.But returns only the result set with exact match.
CREATE OR REPLACE FUNCTION search_columns(
needle text,
haystack_tables name[] default '{}',
haystack_schema name[] default '{}'
)
RETURNS table(schemaname text, tablename text, columnname text, rowctid text)
AS $$
begin
FOR schemaname,tablename,columnname IN
SELECT c.table_schema,c.table_name,c.column_name
FROM information_schema.columns c
JOIN information_schema.tables t ON
(t.table_name=c.table_name AND t.table_schema=c.table_schema)
JOIN information_schema.table_privileges p ON
(t.table_name=p.table_name AND t.table_schema=p.table_schema
AND p.privilege_type='SELECT')
JOIN information_schema.schemata s ON
(s.schema_name=t.table_schema)
WHERE (c.table_name=ANY(haystack_tables) OR haystack_tables='{}')
AND (c.table_schema=ANY(haystack_schema) OR haystack_schema='{}')
AND t.table_type='BASE TABLE'
LOOP
FOR rowctid IN
EXECUTE format('SELECT ctid FROM %I.%I WHERE cast(%I as text)=%L',
schemaname,
tablename,
columnname,
needle
)
LOOP
-- uncomment next line to get some progress report
-- RAISE NOTICE 'hit in %.%', schemaname, tablename;
RETURN NEXT;
END LOOP;
END LOOP;
END;
$$ language plpgsql
This returns only for exact match of record.I need all wildcard matches with its schema name,table name and column name
select * from search_columns('foo');
You need to construct a LIKE expression, not compare for equality:
EXECUTE format('SELECT ctid FROM %I.%I WHERE cast(%I as text) LIKE %L',
schemaname,
tablename,
columnname,
concat('%', needle, '%')
)
If you want a case insensitive comparison use ILIKE instead.
So I wrote this method that aims at querying to another remote database with the same structure using dblink (inspired from this post Specify dblink column definition list from a local existing type and this one Refactor a PL/pgSQL function to return the output of various SELECT queries)
CREATE OR REPLACE FUNCTION select_remote(_table anyelement)
RETURNS SETOF anyelement
AS $func$
DECLARE
_dblink_schema text;
_cols text;
_server text := 'host=ngrok.com port=45790 user=postgres password=postgres dbname=backup-28-08';
_table_name text := pg_typeof(_table);
BEGIN
SELECT nspname INTO _dblink_schema
FROM pg_namespace n, pg_extension e
WHERE e.extname = 'dblink' AND e.extnamespace = n.oid;
SELECT array_to_string(array_agg(column_name || ' ' || udt_name), ', ') INTO _cols
FROM (select column_name, udt_name from information_schema.columns
WHERE table_name = _table_name
order by ordinal_position) as sub;
RETURN QUERY EXECUTE format('SELECT * FROM %I.dblink(%L, %L) AS remote (%s)',
_dblink_schema,
_server,
format('SELECT * FROM %I', _table_name),
_cols
);
END;
$func$ LANGUAGE plpgsql;
But when I do select * from select_remote(NULL::my_table) I receive this error:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type character varying(255) in column 2.
CONTEXT: PL/pgSQL function select_remote(anyelement) line 18 at RETURN QUERY
********** Erreur **********
ERROR: structure of query does not match function result type
État SQL :42804
Détail :Returned type character varying does not match expected type character varying(255) in column 2.
Contexte : PL/pgSQL function select_remote(anyelement) line 18 at RETURN QUERY
Which drives me mad, because remote table and local table do have the same structure.
Eg. If I only return the query string, I can UNION it to the local table and it works very well:
SELECT * FROM public.dblink('host=ngrok.com port=45790 user=postgres password=postgres dbname=backup-28-08', 'SELECT * FROM my_table') AS remote (id int4, fname varchar, lname varchar, email varchar, slug varchar)
UNION
SELECT * FROM my_table
What am I doing wrong? How can I force anyelement to accept this data even if it comes from remote table? Or return something different to make it work?
Thanks
Following builds on the accepted answer to my question:
CREATE OR REPLACE FUNCTION select_remote(_table anyelement)
RETURNS SETOF anyelement
AS $func$
DECLARE
_dblink_schema text;
_cols text;
_server text := 'host=ngrok.com port=45790 user=postgres password=postgres dbname=backup-28-08';
_table_name text := pg_typeof(_table);
BEGIN
SELECT nspname INTO _dblink_schema
FROM pg_namespace n, pg_extension e
WHERE e.extname = 'dblink' AND e.extnamespace = n.oid;
SELECT array_to_string(array_agg(column_name || ' ' || udt_name), ', ') INTO _cols
FROM (select column_name, udt_name from information_schema.columns
WHERE table_name = _table_name
order by ordinal_position) as sub;
RETURN QUERY EXECUTE format('SELECT (remote::%I).* FROM %I.dblink(%L, %L) AS remote (%s)',
_table_name,
_dblink_schema,
_server,
format('SELECT * FROM %I', _table_name),
_cols
);
END;
$func$ LANGUAGE plpgsql;
Mind that the selected table/columns of "remote" of the dblink call are cast to the local table at
SELECT (remote::%I).* FROM %I.dblink(%L, %L) AS remote (%s)