How to use string with apostrophe in function variable plpgsql - sql

Hello I am having trouble querying when I have apostrophe in my where clause in postgresql using pgpsql function, I know that manually I could do something like:
select 'author''s'
however my word is stored in a variable, here is my function:
CREATE OR REPLACE FUNCTION public.fn_inserir_doc(caminho_arqv text, conteudo text)
RETURNS void
LANGUAGE plpgsql
AS $function$
declare
conteudo_array text array;
palavra text;
begin
execute 'insert into documento(caminho)
select ''' || caminho_arqv || '''
where not exists(select id
from documento
where caminho='''||caminho_arqv||''')';
conteudo_array := regexp_split_to_array(conteudo, E'\\s+');
FOREACH palavra in array conteudo_array
loop
if length(palavra) >=3 then
raise notice 'palavra: %', palavra;
execute 'insert into termo(descricao)
select ''' || palavra || '''
where not exists(
select id from termo
where descricao='''||palavra||''')';
execute 'insert into documento_termo(id_termo, id_documento, frequencia)
select t.id, d.id, 1
from termo t
cross join documento d
where t.descricao = '''|| palavra ||'''
and d.caminho = '''|| caminho_arqv ||'''
on conflict (id_termo, id_documento) do update set frequencia = documento_termo.frequencia + 1;';
end if;
end loop;
end;
$function$
The following sample is the one that has the problem:
select id from termo
where descricao='''||palavra||'''
because palavra contains single quote

Use dollar quoting and the function format(). Example:
create or replace function test(str text)
returns setof text language plpgsql as $$
begin
-- instead of this:
-- return query execute 'select '''||str||'''::text';
-- use:
return query execute format(
$fmt$
select %L::text
$fmt$, str);
end $$;
select * from test('O''Brian');
test
---------
O'Brian
(1 row)

Related

Include parameter in function to UNION multiple PostgreSQL tables

I have developed a function to UNION ALL tables from a list of table names (a table called tablelist below) inspired by this SO post.
The initial function just returns a selection, but now I'd like to write a new table with a name taken from a parameter new_table_name.
I'm struggling with the syntax to insert the parameter into the DROP TABLE AND CREATE TABLE statements. Here's one of the attempts which returns ERROR: mismatched parentheses at or near ";"
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS Table (my_id int, metric double precision, geom geometry)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
(
DROP TABLE IF EXISTS working.'' || new_tab_name || '';
CREATE TABLE working.'' || new_tab_name || '' AS (
SELECT string_agg(format('SELECT * FROM %s', tbl), ' UNION ALL ')
FROM (SELECT tbl FROM working.tablelist) sub
)
);
END
$func$;
Something like this?
DROP FUNCTION IF EXISTS f_multi_union(text);
CREATE OR REPLACE FUNCTION f_multi_union(new_tab_name text)
RETURNS void -- nothing to return
LANGUAGE plpgsql AS
$func$
DECLARE
_sql TEXT;
BEGIN
_sql := format('DROP TABLE IF EXISTS working.%I;', new_tab_name); -- avoid SQL injection
EXECUTE _sql;
_sql := 'SELECT string_agg(format(''SELECT * FROM %I'', tbl), '' UNION ALL '')
FROM (SELECT tbl FROM working.tablelist) sub;';
EXECUTE _sql
INTO _sql; -- overwrite current _sql content
_sql := format('CREATE TABLE working.%I AS %s;', new_tab_name, _sql);
EXECUTE _sql;
END
$func$;
I would replace the * in the SELECT statement with the columns that you need.

Execute text string returned from function as a query

I have a function with the following "signature":
CREATE OR REPLACE FUNCTION iterate_test()
RETURNS text
LANGUAGE plpgsql
...
That returns a query in text form:
SELECT DISTINCT(t1.date) AS date, AMD.adjusted_close AS AMD, GME.adjusted_close AS GME FROM clean_daily AS t1 INNER JOIN clean_daily AS AMD ON t1.date=AMD.date AND AMD.ticker='AMD' INNER JOIN clean_daily AS GME ON AMD.date=GME.date AND GME.ticker='GME' ORDER BY t1.date DESC;
How can I execute this string as a query? I have tried EXECUTE like this:
EXECUTE iterate_test();
EXECUTE QUERY iterate_test();
But I can't get it to work. How do I execute a data query in the form of text?
Full function code:
CREATE OR REPLACE FUNCTION iterate_test()
RETURNS text
LANGUAGE plpgsql
AS
$$
DECLARE
temprow record;
str_query text := 'SELECT DISTINCT(t1.date) AS date';
prev_table text := 't1';
BEGIN
FOR temprow IN SELECT * FROM portfolios WHERE user_name='snigelnmjau'
LOOP
str_query := str_query || format(', %s.adjusted_close AS %s', temprow.ticker, temprow.ticker);
END LOOP;
str_query := str_query || format(' FROM clean_daily AS t1 ');
FOR temprow IN SELECT * FROM portfolios WHERE user_name='snigelnmjau'
LOOP
str_query := str_query || format(E'INNER JOIN clean_daily AS %s ON %s.date=%s.date AND %s.ticker=''%s'' ', temprow.ticker, prev_table, temprow.ticker, temprow.ticker, temprow.ticker);
prev_table := temprow.ticker;
END LOOP;
str_query := str_query || 'ORDER BY t1.date DESC;';
--EXECUTE str_query;
RETURN str_query;
END;
$$;
To answer your question: use RETURN QUERY EXECUTE. See:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Function to loop through and select data from multiple tables
Refactor a PL/pgSQL function to return the output of various SELECT queries
Your function with a couple of fixes, including one for the core question:
CREATE OR REPLACE FUNCTION pg_temp.iterate_test()
RETURNS SETOF text -- !!
LANGUAGE plpgsql AS
$func$
DECLARE
temprow record;
str_query text := 'SELECT DISTINCT t1.date'; -- !!
prev_table text := 't1';
BEGIN
FOR temprow IN
SELECT * FROM portfolios WHERE user_name = 'snigelnmjau'
LOOP
str_query := str_query || format(', %1$I.adjusted_close AS %1$I', temprow.ticker); -- !!
END LOOP;
str_query := str_query || ' FROM clean_daily AS t1 '; -- !!
FOR temprow IN
SELECT * FROM portfolios WHERE user_name = 'snigelnmjau'
LOOP
str_query := str_query || format('JOIN clean_daily AS %1$I ON %2$I.date=%1$I.date AND %1$I.ticker=%1$L ', temprow.ticker, prev_table); -- !!
prev_table := temprow.ticker;
END LOOP;
str_query := str_query || 'ORDER BY t1.date DESC';
RETURN QUERY EXECUTE str_query; -- !!!!
END
$func$;
But the whole statement may possibly be generated in a single SELECT statement without looping (twice). And why DISTINCT?
Moreover, I am not convinced we need dynamic SQL to begin with. A plain query may be possible.

sqlconcat two variables postgresql

Im writing a psql function. I want to do a concat between two variables inside psql.
and im getting syntax_error.
Let's notice that date_contract is of type date.
Thank you Who can help me
declare
result_table regclass := $$public.contract$$||text;
time_now time
BEGIN
execute $$SELECT MIN(date_contract) FROM $$||result_table INTO date_;
execute $$SELECT CURRENT_TIMESTAMP::time FROM $$||result_table INTO time_now;
execute $$
INSERT INTO $$||result_table||$$
(id, dat_beg_contract, dat_end_contract,date_contract, long_c)
SELECT id, dat_beg_contract, dat_end_contract, sum(extract(epoch from (least(s.dat_beg_contract, gs.date_contract||time_ + interval '1 day')::timestamp -
greatest(s.dat_beg_contract, gs.date_contract)
)
) / 60) as long_c
$$;
END;
This code block might give you an idea of how to concatenate variables
DO $$
DECLARE
rec1 text;rec2 text;
BEGIN
EXECUTE 'SELECT ''foo'' ' INTO rec1;
EXECUTE 'SELECT ''bar'' 'INTO rec2;
RAISE NOTICE 'Option 1 %, Opiton 2: %', rec1||rec2, rec1||' '||rec2 ;
END; $$ LANGUAGE plpgsql;
NOTICE: Option 1 foobar, Opiton 2: foo bar
EDIT Returning a query with a function
CREATE TABLE tab (id int);
INSERT INTO tab VALUES (100),(42);
CREATE OR REPLACE FUNCTION myfunc (table_name TEXT)
RETURNS TABLE (res int) AS $$
DECLARE v1 int; v2 int;
BEGIN
EXECUTE 'SELECT max(id) FROM '||table_name INTO v1;
EXECUTE 'SELECT min(id) FROM '||table_name INTO v2;
RETURN QUERY EXECUTE 'SELECT ' || v1 || v2 ;
END;
$$ LANGUAGE plpgsql;
SELECT myfunc('tab');
myfunc
--------
10042
EDIT 2 Example concatenating timestamp and integer
CREATE TABLE tab (id int);
INSERT INTO tab VALUES (100),(42);
CREATE OR REPLACE FUNCTION myfunc (table_name TEXT)
RETURNS TABLE (res text) AS $$
DECLARE v1 int; v2 timestamp;
BEGIN
EXECUTE 'SELECT min(id) FROM '||table_name INTO v1;
EXECUTE 'SELECT CURRENT_TIMESTAMP' INTO v2;
RETURN QUERY EXECUTE 'SELECT ' || quote_literal(v1 || ' - ' ||v2) ;
END;
$$ LANGUAGE plpgsql;
SELECT myfunc('tab');
EXECUTE 'SELECT min(id) FROM '||table_name INTO v1;
EXECUTE 'SELECT CURRENT_TIMESTAMP' INTO v2;
RETURN QUERY EXECUTE 'SELECT ' || quote_literal(v1::text || ' - ' ||v2) ;
END;
$$ LANGUAGE plpgsql;
SELECT myfunc('tab');
myfunc
--------------------------------
42 - 2021-02-02 15:54:24.24179
(1 Zeile)
EDIT 3 let me know if this works, so that I can clean the answer
CREATE TABLE tab (date_contract date);
INSERT INTO tab VALUES (current_date+7),(current_date);
CREATE OR REPLACE FUNCTION myfunc (table_name TEXT)
RETURNS TABLE (date_contract date, col_new text) AS $$
DECLARE date_ date;
BEGIN
EXECUTE 'SELECT min(date_contract) FROM '||table_name INTO date_;
RETURN QUERY EXECUTE 'SELECT date_contract,'|| quote_literal(date_|| ' ' ||current_time) ||' FROM ' || table_name;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM myfunc('tab');
date_contract | col_new
---------------+-------------------------------
2021-02-09 | 2021-02-02 16:29:49.013344+01
2021-02-02 | 2021-02-02 16:29:49.013344+01
(2 Zeilen)

Select from all tables inside the schema containing column with name

How can I get select (table_name, table_name.age)?
I need to get values from column 'age' from all tables having this column/
I have this function
CREATE OR REPLACE FUNCTION union_all_tables()
RETURNS TABLE
(
age bigint
) AS
$$
DECLARE
dynamic_query text = '';
r_row record;
BEGIN
FOR r_row IN SELECT table_schema || '.' || table_name qualified_table_name
FROM information_schema.COLUMNS
WHERE column_name = 'age'
LOOP
dynamic_query := dynamic_query || format('UNION SELECT ' ||
'age ' ||
'FROM %s ',r_row.qualified_table_name) || E'\n'; -- adding new line for pretty print, it is not necessary
END LOOP;
dynamic_query := SUBSTRING(dynamic_query, 7) || ';';
RAISE NOTICE 'Union all tables in staging, executing statement: %', dynamic_query;
RETURN QUERY EXECUTE dynamic_query;
END;
$$
LANGUAGE plpgsql;
You don't need to generate a single huge UNION statement. If you use RETURN QUERY the result of that query is appended to the overall result of the function every time you use it.
When dealing with dynamic SQL you should also use format() to properly deal with identifiers.
Your function can be simplified to:
CREATE OR REPLACE FUNCTION union_all_tables()
RETURNS TABLE (table_schema text, table_name text, age bigint)
AS
$$
DECLARE
dynamic_query text = '';
r_row record;
BEGIN
FOR r_row IN SELECT c.table_schema, c.table_name
FROM information_schema.columns c
WHERE c.column_name = 'age'
LOOP
dynamic_query := format(
'select %L as table_schema, %L as table_name, age from %I.%I',
r_row.table_schema, r_row.table_name,
r_row.table_schema, r_row.table_name);
RETURN QUERY EXECUTE dynamic_query;
END LOOP;
END;
$$
LANGUAGE plpgsql;
Note that the whole function will fail if there is (at least) one table where the age column is not a bigint.

insert into not work in function postgresql

I have the following procedure, when I run it displays all the insertions in console, the field ind_id is increased but does not save any record.
create or replace function renameDescripcionTramite() returns varchar as $$
declare
aguja record;
pajar record;
last_id integer;
begin
for aguja in
select trim(t.tra_descripcion) tra_descripcion from tab_tramite t
join tab_serietramite st on st.tra_id = t.tra_id
group by t.tra_descripcion
order by t.tra_descripcion loop
for pajar in
select u.uni_id,s.ser_id,st.sts_id,t.tra_id,trim(t.tra_descripcion) tra_descripcion
from tab_unidad u join tab_series s on u.uni_id = s.uni_id
join tab_serietramite st on st.ser_id = s.ser_id
join tab_tramite t on t.tra_id = st.tra_id
order by u.uni_id,s.ser_id,t.tra_id loop
if aguja.tra_descripcion = pajar.tra_descripcion then
insert into tab_indexpediente (uni_id,ser_id,tra_id) values (pajar.uni_id,pajar.ser_id,pajar.tra_id);-- returning ind_id into last_id;
--update tab_serietramite set wid = last_id where sts_id = pajar.sts_id;
RAISE NOTICE 'insert into tab_indexpediente (uni_id,ser_id,tra_id) values (%',cast(pajar.uni_id as text)
|| ',' || cast(pajar.ser_id as text) || ',' || cast(pajar.tra_id as text) || ')';
end if;
end loop;
--RAISE NOTICE ' ANTERIOR ES %',aguja;
end loop;
return tra_descripcion;
end;
$$ LANGUAGE plpgsql;
Can someone help me please?
Yes, I did not know that this would affect the procedure.
Thank's laurenz Albe