How to use unnest argument in "EXECUTE format()" in plpgsql? - postgresql

Im trying to send an array inside a "unnest" but using it in an a "EXECUTE format()" , how can i do that?
Table
CREATE TABLE IF NOT EXISTS table_xvx(
row_id SERIAL NOT NULL PRIMARY KEY,
open FLOAT(36),
high FLOAT(36),
low FLOAT(36),
close FLOAT(36),
volume FLOAT(36),
capital FLOAT(36),
transactions FLOAT(36)
)
Type
CREATE TYPE type_ohlcvct AS(
type_open float,
type_high float,
type_low float,
type_close float,
type_volume float,
type_capital float,
type_transactions float
);
Function
CREATE OR REPLACE FUNCTION insert_data_ohlcvct(table_name_ varchar(70) , data_list type_ohlcvct[])
RETURNS VOID AS
$$
DECLARE
error_message varchar;
BEGIN
EXECUTE format(
'INSERT INTO %I(open,high,low,close,volume,capital,transactions)
SELECT * FROM unnest(%s)',table_name_ , data_list);
END;
$$ LANGUAGE plpgsql;
Ways to call
SELECT insert_data_ohlcvct('table_xvx' ,'{"(10,10,10,10,10,10,10)","(10,10,10,10,10,10,10)"}'::type_ohlcvct[]);
SELECT insert_data_ohlcvct('table_xvx' , (ARRAY['(10,10,10,10,10,10,10)','(10,10,10,10,10,10,10)'])::type_ohlcvct[]);
SELECT insert_data_ohlcvct('table_xvx' , ARRAY['(10,10,10,10,10,10,10)'::type_ohlcvct,'(10,10,10,10,10,10,10)']);
The error is always the same
ERROR: syntax error at or near "{"
LINE 2: SELECT * FROM unnest({"(10,10,10,10,10,10,10)","(10,10,10,...
^
QUERY: INSERT INTO table_xvx(open,high,low,close,volume,capital,transactions)
SELECT * FROM unnest({"(10,10,10,10,10,10,10)","(10,10,10,10,10,10,10)"})
CONTEXT: PL/pgSQL function insert_data_ohlcvct(character varying,type_ohlcvct[]) line 5 at EXECUTE
SQL state: 42601

Don't pass parameters to dynamic SQL as strings - pass them as parameters:
EXECUTE format(
'INSERT INTO %I(open,high,low,close,volume,capital,transactions)
SELECT * FROM unnest($1)',table_name_)
USING data_list; --<< passes the value to the $1 placeholder

Related

How to execute a query with a column name passed as parameter to a plpgsql function?

I have a table with multiple columns in PostgreSQL. I try to make a function returning a table with a few default columns and a variable column. The column name should be passed as function parameter. Example:
SELECT * FROM get_gas('temperature');
This is my code right now:
CREATE OR REPLACE FUNCTION get_gas(gas text)
RETURNS TABLE (id INTEGER, node_id INTEGER,
gas_name DOUBLE PRECISION,
measurement_timestamp timestamp without time zone )
AS
$$
BEGIN
SELECT measurements_lora.id, measurements_lora.node_id, gas, measurements_lora.measurement_timestamp
AS measure
FROM public.measurements_lora;
END
$$ LANGUAGE plpgsql;
When passing, for example, 'temperature' as column name (gas), I want to get a table with these columns from the function call.
id - node_id - temperature - measurement_timestamp
How would I achieve this?
You can use EXECUTE statement.
CREATE OR REPLACE FUNCTION get_gas(gas text) RETURNS TABLE (f1 INTEGER, f2 INTEGER, f3 DOUBLE PRECISION, f4 timestamp without time zone ) AS
$$
DECLARE
sql_to_execute TEXT;
BEGIN
SELECT 'SELECT measurements_lora.id,
measurements_lora.node_id, '
|| gas ||',
measurements_lora.measurement_timestamp AS measure
FROM public.measurements_lora '
INTO sql_to_execute;
RETURN QUERY EXECUTE sql_to_execute;
END
$$ LANGUAGE plpgsql;
This will create a variable sql_to_execute with your field and. QUERY EXECUTE will execute your interpreted query.
EDIT 1: Look at the another answer the concernings about security issues.
If you really need dynamic SQL in a PL/pgSQL function (which you don't), be sure to defend against SQL injection! Like:
CREATE OR REPLACE FUNCTION get_gas(gas text)
RETURNS TABLE (id integer
, node_id integer
, gas_name double precision
, measurement_timestamp timestamp)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'SELECT m.id, m.node_id, m.%I, m.measurement_timestamp
FROM public.measurements_lora m'
, gas
);
END
$func$;
The format specifier %I in format() double-quotes identifiers where needed,
See:
SQL injection in Postgres functions vs prepared queries
Insert text with single quotes in PostgreSQL

column reference "col_1" is ambiguous plpgsql

I'm writing postgresql function which inserts data and returns successfully inserted data rows.
The code is below.
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql
AS $function$
declare
--
begin
with my_table as (
with my_table1(my_json) as (
values(
json_data
)
)
insert into "test"(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t, jsonb_array_elements(my_json->'_data') elem
on conflict(col_1) do nothing
returning *
)
select * from my_table;
end
$function$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');
It occurs error below.
SQL Error [42702]: ERROR: column reference "col_1" is ambiguous
Detail: It could refer to either a PL/pgSQL variable or a table column.
Where: PL/pgSQL function fn_insert_test(jsonb) line 5 at SQL statement
[edit]
This is not the real problem here. There is a lot of problems in your code :
-> If you want to return a table, you have to use 'return query' : http://www.postgresqltutorial.com/plpgsql-function-returns-a-table/
-> I do not think you can use the WITH like you do: https://www.postgresql.org/docs/current/queries-with.html
Here is a functoinnal version of your code. But I am not quite sure of what you want :
CREATE OR REPLACE FUNCTION public.fn_insert_test(json_data jsonb)
returns table(col_1 varchar(255),
col_2 varchar(255),
col_3 timestamp)
LANGUAGE plpgsql AS
$$
declare
--json_data jsonb := '{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}';
begin
create temp table res (col_1 varchar (255), col_2 varchar (255), col_3 timestamp) on commit drop;
with my_table1(my_json) as
(
values(
json_data
)
)
, inserted as
(
insert into test(col_1, col_2, col_3)
select
elem->>'val1', elem->>'val2', now()
from
my_table1 t
, jsonb_array_elements(json_data->'_data') elem
on conflict do nothing
returning *
)
insert into res
select *
from inserted;
--raise notice '%', res_v;
return query select * from res;
end
$$
;
select fn_insert_test('{"_data": [{"val1":"1", "val2":"1"}, {"val1":"2", "val2":"2"}]}');

Getting Error - No function matches the given name and argument types. in Postgres

I create a function and inside the function, I used predefined function array_to_string. When I try to execute my function I am getting the following error.
ERROR: function array_to_string(integer, unknown) does not exist
LINE 1: SELECT array_to_string(id, ',') FROM ame.stops where custome...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
QUERY: SELECT array_to_string(id, ',') FROM ame.stops where customer_id=customer_id and driver_id=driver_id
CONTEXT: PL/pgSQL function ame.fn_get_stopids(integer,integer) line 4 at SQL statement
SQL state: 42883
When I use array_to_string in select statement it's working fine.
select array_to_string(array(SELECT id FROM ame.stops where customer_id='31' and driver_id='770'), ', ')
Function Code
CREATE OR REPLACE FUNCTION ame.fn_Get_StopIds(customer_id integer, driver_id integer)
RETURNS text AS $$
DECLARE StopIds text;
BEGIN
SELECT array_to_string(id, ',') FROM ame.stops where customer_id=customer_id and driver_id=driver_id into StopIds;
RETURN StopIds;
END;
$$ LANGUAGE plpgsql;
the function is expecting an array, but you are passing an integer (id).
In your working attempt, you are first converting the id to an array.
You may want to build the array first using array_agg
SELECT array_to_string(array_agg(id), ',')
FROM ame.stops
where customer_id=customer_id and driver_id=driver_id
into StopIds;
But then you don't need to build and concatenate the array, you can simply do
SELECT string_agg(id::text, ',')
FROM ame.stops
where customer_id=customer_id and driver_id=driver_id
into StopIds;
You may want to order by IDs
CREATE OR REPLACE FUNCTION ame.fn_get_stopids
(_customer_id integer,
_driver_id integer)
RETURNS text AS
$$
DECLARE
stopids text;
BEGIN
SELECT string_agg(id::text, ',') INTO stopids;
FROM ame.stops
WHERE customer_id = _customer_id
AND driver_id = _driver_id
RETURN stopids;
END;
$$
LANGUAGE plpgsql;

SQL state: 42883 in PostgreSQL 9.3

I have the following table called as test_type which contains two columns namely cola and colb.
Table: test_type
create table test_type
(
cola int,
colb varchar(50)
);
Now I want to create a type with same columns.
Type: type1
create type type1 as
(
cola int,
colb varchar(50)
);
Here I have created function in which I am passing type name type1 to insert the data to the
table test_type.
--Creating Function
create or replace function fun_test ( p_Type type1 )
returns void
as
$$
begin
insert into test_type(cola,colb)
select cola,colb from p_type
EXCEPT
select cola,colb from test_type;
end
$$
language plpgsql;
---Calling Function
SELECT fun_test(1,'Xyz');
Error Details:
ERROR: function fun_test(integer, unknown) does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Character: 8
You need to "pack" the arguments together: (1,'xs'), so that postgres recognise them as single argument of type type1:
SELECT fun_test((1,'xs'));
For a better readability you can cast the argument to type1 (not really necessary):
SELECT fun_test((1,'xs')::type1);
If the purpose of the function is to to insert the values only if they are not already contained in the table, you could change your code so:
create or replace function fun_test ( p_Type type1 )
returns void AS $$
BEGIN
INSERT INTO test_type(cola,colb)
SELECT p_Type.cola,p_Type.colb
EXCEPT
SELECT cola,colb FROM test_type;
END;
$$ language plpgsql;
But this syntax is my opinion not good readable. This statement looks better:
...
BEGIN
PERFORM 0 FROM test_type WHERE (cola, colb) = p_Type;
IF NOT FOUND THEN
INSERT INTO test_type(cola,colb) VALUES (p_Type.cola,p_Type.colb);
END IF;
END;
...

PostgreSQL 9.3: Check only time from timestamp

I have the following table with one field of type timestamp.
Create table Test_Timestamp
(
ColumnA timestamp
);
Now inserting some records for demonstration:
INSERT INTO Test_Timestamp VALUES('1900-01-01 01:21:15'),
('1900-01-01 02:11:25'),
('1900-01-01 12:52:10'),
('1900-01-01 03:20:05');
Now I have created function Function_Test with two parameters namely St_time and En_Time which
are of type varchar, In which I only pass the time like 00:00:01. And after that Function has
to return the table with that condition of two time's parameters.
CREATE OR REPLACE FUNCTION Function_Test
(
St_Time varchar,
En_Time varchar
)
RETURNS TABLE
(
columX timestamp
)
AS
$BODY$
Declare
sql varchar;
wher varchar;
BEGIN
wher := 'Where columna BETWEEN '|| to_char(cast(St_Time as time),'''HH24:MI:SS''') ||' AND '|| to_char(cast(En_Time as time),'''HH24:MI:SS''') ||'';
RAISE INFO '%',wher;
sql := 'SELECT * FROM Test_Timestamp ' || wher ;
RAISE INFO '%',sql;
RETURN QUERY EXECUTE sql;
END;
$BODY$
LANGUAGE PLPGSQL;
---Calling function
SELECT * FROM Function_Test('00:00:00','23:59:59');
But getting an error:
ERROR: invalid input syntax for type timestamp: "00:00:01"
LINE 1: ...ELECT * FROM Test_Timestamp where ColumnA BETWEEN '00:00:01'...
You can cast the column to a time: ColumnA::time
You should also not pass a time (or a date, or a timestamp) as a varchar. And you don't need dynamic SQL or a PL/pgSQL function for this:
CREATE OR REPLACE FUNCTION Function_Test(St_Time time, en_Time time)
RETURNS TABLE (columX timestamp)
AS
$BODY$
SELECT *
FROM Test_Timestamp
where columna::time between st_time and en_time;
$BODY$
LANGUAGE sql;
Call it like this:
select *
from Function_Test(time '03:00:00', time '21:10:42');
You can use extract to the hour, minute and second
http://www.postgresql.org/docs/9.3/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT