Calling postgres function with multiple params - sql

This is my sql function in postgresql:
FUNCTION test(year integer)
RETURNS SETOF json AS
$BODY$
SELECT ARRAY_TO_JSON(ARRAY_AGG(T))
FROM table t
WHERE year = $1;
$BODY$
This works quite good. But now I want specify more parameters and
I'd like to get a return with the condition if parameters are set. For example following function call:
test(year := 2014, location := 'Belo Horizonte')
How should the function look like and where to set conditions? Here is my (wrong) suggestion:
FUNCTION test(year integer, location text)
RETURNS SETOF json AS
$BODY$
SELECT ARRAY_TO_JSON(ARRAY_AGG(T))
FROM table t
IF $1 IS SET THEN
WHERE year = $1
ELSIF $2 THEN
UNION
WHERE location = $2
END IF;
$BODY$
A further challenge is a return of the function for this statements:
test(year := 1584)
-- should return all entries with year 1584
test(location := 'Cambridge')
-- should return all entries with location Cambridge
test(year := 1584, location := 'Cambridge')
-- should return all entries with year 2014 AND location Belo Horizonte
Thanks in advance!

You may try to do something like that, adding default values, and working with OR clauses
FUNCTION test(year integer DEFAULT -1, location text DEFAULT 'noLocation')
RETURNS SETOF json AS
$BODY$
SELECT ARRAY_TO_JSON(ARRAY_AGG(T))
FROM table t
WHERE ($1 = -1 OR year = $1)
AND ($2 = 'noLocation' OR location = $2);
$BODY$

Related

Postgresql column reference is ambiguous

I want to call my function but I get this error:
ERROR: column reference "list" is ambiguous LINE 3: SET
list = ARRAY_APPEND(list, input_list2),
the error is on the second list inside array_append function.
My function:
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary(id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(list, input_list2),
amount = amount + input_amount,
price = input_price
WHERE NOT list #> input_list;
END
$body$;
You have to use the alias name in the insert query because list has two references, one reference in EXCLUDED.list and another reference to the column for an update statement.
Please check the below query (I append the alias with name os in query):
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary as os (id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(os.list, input_list2),
amount = os.amount + input_amount,
price = input_price
WHERE NOT os.list #> input_list;
END
$body$;
Or you can use table name:
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary (id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(orders_summary.list, input_list2),
amount = orders_summary.amount + input_amount,
price = input_price
WHERE NOT orders_summary.list #> input_list;
END
$body$;

PostgreSQL 11 - using format to assign to variable

I have been awake for well beyond my schedule and I have been stuck with this issue for a long time, I don't even know what I am looking for to solve, but I wish to use format to insert values that I'll be using for column names, and then executing it... but it keeps giving me errors no matter how much I try changing it :c
Heres the part that im trying to do something that doesnt work, but i think you get the idea what im trying to achieve
ratelimit := EXECUTE format('(SELECT %I
FROM users.ratelimits
WHERE user_id = $2)
', $1);
and heres the full code for the brave
CREATE OR REPLACE FUNCTION users.consume_ratelimit(_name text,__user_id integer)
RETURNS boolean
LANGUAGE 'plpgsql'
VOLATILE
PARALLEL UNSAFE
COST 100
AS $BODY$DECLARE
ratelimit INTEGER;
reset_timeout timestamptz;
premium BOOLEAN;
BEGIN
ratelimit := EXECUTE format('(SELECT %I
FROM users.ratelimits
WHERE user_id = $2)
', $1);
reset_timeout := EXECUTE format('(SELECT %I_refresh
FROM users.ratelimits
WHERE user_id = $2)
', $1);
premium := (SELECT users.is_premium($2));
IF premium THEN
RETURN TRUE;
ELSIF reset_timeout <= NOW() THEN
UPDATE users.ratelimits
SET image_refresh = NOW() + '1 hour'::interval,
image = DEFAULT
WHERE user_id = $2;
RAISE NOTICE 'reset';
RETURN TRUE;
ELSE
IF ratelimit > 0 THEN
EXECUTE format('UPDATE users.ratelimits
SET %I = %I - 1
WHERE user_id = $2', $1, $1);
RAISE NOTICE 'decrement';
RETURN TRUE;
ELSIF ratelimit <= 0 THEN
RAISE NOTICE 'out of credits';
RETURN FALSE;
ELSE
EXECUTE format('INSERT INTO users.ratelimits(user_id) VALUES ($2)
ON CONFLICT DO UPDATE SET
%I = excluded.%I,
%I_refresh = excluded.%I_refresh', $1, $1, $1, $1);
RAISE NOTICE 'create';
RETURN TRUE;
END IF;
END IF;
END;$BODY$;
As documented in the manual you need to use into together with EXECUTE to store the result into a variable. This can handle multiple columns/variables as well, so you only need a single EXECUTE to get both values.
For clarity you should reference parameters by name, not by position.
EXECUTE format('SELECT %I, %I_refresh
FROM users.ratelimits WHERE user_id = $1'),
_name, _name)
USING __user_id
INTO ratelimit, reset_timeout;
Note the $1 inside the string for format() is a parameter placeholder used when the SQL statement is executed, and will be replaced with the value of the variable specified in the USING clause.
Variable assignment is also more efficient without a SELECT:
premium := users.is_premium(__user_id);
It seems some_var := EXECUTE ... will not work. Additionally, params inside of an EXECUTE statement are not the same as those in the function - you need to supply them to the execute statement.
I have not tested this, but perhaps
-- q declared above as text
q := format('
SELECT %I
FROM users.ratelimits
WHERE user_id = %s;
', $1, $2);
EXECUTE q INTO ratelimit;
will work? I also removed the parens from the query, those are unnecessary and may the problem themselves.
I have tested the function
CREATE OR REPLACE FUNCTION test_sum (a int, b int)
RETURNS int
AS $$
DECLARE
q text;
result int;
BEGIN
q := FORMAT ('SELECT %s + %s', $1, $2);
EXECUTE q INTO result;
RETURN result;
END
$$ LANGUAGE plpgsql
and that does work. If my suggestion above does not work, perhaps you can use the above as a starting point.

Conditional function not working as intended postgresql

So I got this table on postgresql that people regularly update and the below function.
table
id integer
status integer
date date
on_hold boolean
What this function is supposed to do is to fill out the date column automatically whenever the status becomes 50 and also if it was null
Problem is that i do not want the date column to be filled when the on_hold boolean column is true.
I've tried setting up the function just by typing on_hold = true but then it somehow says it doesn't exist. When i use old. or new. it doesn't pass any error but it still updates the date.
How to get to the intended result which is to not update the date when on_hold is true
CREATE OR REPLACE FUNCTION table_update()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
if new.status = 50
and new.date is null
and (new.on_hold = false or old.on_hold = false)
then new.date = now() + interval '90' day ;
end if;
RETURN NEW;
END;
$function$
;
~~~
Just change the condition to
and old.on_hold != true -- != true include false *AND NULL*
and maybe (if you do not want to change anything if status is 50 and becomes 50 again) add:
(new.status = 50 and old.status != 50)
Full function:
demo:db<>fiddle
CREATE OR REPLACE FUNCTION table_update()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
if (new.status = 50 and old.status != 50)
and new.the_date is null
and old.on_hold != true
then
new.the_date = now() + interval '90' day;
end if;
RETURN NEW;
END;
$$;

Find SUM and MAX in custom aggregate function

I've been working recently with custom aggregate function.
In this custom aggregate, the first function, doesn't compute the sum and the max value correctly.
I'm using a composite type to return the sum and the max value.
I've tried with appending everything with the array, but it's not an efficient way to work
CREATE TYPE sum_max_complex AS (sum real, max_v real);
CREATE OR REPLACE FUNCTION calculateSum(sum_max_complex, real) RETURNS sum_max_complex AS $$
DECLARE
sumValue real := 0;
max_v real := $2;
output sum_max_complex;
BEGIN
RAISE NOTICE '-------------------';
RAISE NOTICE 'IL PRIMO VALORE DI INPUT E: % ... %',$1.sum,$1.max_v;
RAISE NOTICE 'IL SECONDO VALORE DI INPUT E: %',$2;
IF $2 IS NOT NULL THEN
sumValue := calculateSumAggregate(sumValue,$2) + sumValue;
ELSE
sumValue := sumValue;
END IF;
max_v := searchmaximumvalue(max_v,$2);
output.sum := sumValue;
output.max_v := max_v;
RAISE NOTICE '-------------------';
RAISE NOTICE 'IL VALORE DI OUTPUT SONO: % ... %',output.sum,output.max_v;
RETURN output;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION addLaplacianNoiseSum(sum_max_complex) RETURNS real AS $$
DECLARE
epsilon real := 0.005;
sensivity real := $1.max_v;
laplaceDistribution real;
BEGIN
laplaceDistribution := generaterandomvalues(sensivity / (epsilon));
RETURN $1.sum + laplaceDistribution;
END;
$$ LANGUAGE plpgsql;
CREATE AGGREGATE SUM_LAPLACE(real)
(
SFUNC = calculateSum,
STYPE = sum_max_complex,
FINALFUNC = addLaplacianNoiseSum
);
In my table column, I have as values: 19,22,22.5,27.
It takes the correct value in the $2 parameter method, in the 1st function, but doesn't accumulate and sum every value.
It doesn't look like you are ever adding to the values stored in the sum_max_complex type. Here's a simplified example that shows approximately what you should do. I don't know what calculateSumAggregate or generaterandomvalues do, so I wasn't able to reproduce those.
CREATE TYPE sum_max_complex AS (sum real, max_v real);
CREATE OR REPLACE FUNCTION calculateSum(sum_max_complex, real) RETURNS sum_max_complex AS $$
select ROW(
$1.sum + coalesce($2, 0),
greatest($1.max_v, $2)
)::sum_max_complex;
$$ LANGUAGE SQL IMMUTABLE;
CREATE OR REPLACE FUNCTION addLaplacianNoiseSum(sum_max_complex) RETURNS real AS $$
select $1.sum + ($1.max_v/0.005);
$$ LANGUAGE SQL IMMUTABLE;
CREATE AGGREGATE SUM_LAPLACE(real)
(
SFUNC = calculateSum,
STYPE = sum_max_complex,
FINALFUNC = addLaplacianNoiseSum,
INITCOND = '(0, 0)'
);
with a as (select a from (values (19), (22), (22.5), (27)) v(a))
select sum_laplace(a) from a;
sum_laplace
-------------
5490.5

Postgres date-numeric not working in function

Can anyone help me to resolve the issue ??
CREATE OR REPLACE FUNCTION func_maj_get_new_user(in_date numeric)
RETURNS integer AS
$BODY$
declare
count_newusr integer;
begin
SELECT count(a.app_uid)
INTO count_newusr
FROM
(SELECT s_start.app_uid
FROM devdba.s_maj_sdk_bs s_start
WHERE s_start.app_rts::date = current_date - in_date
AND (s_start.app_uid,s_start.app_key) NOT IN(SELECT app_uid,app_key
FROM datemp.maj_usr_mstr)
)a;
return count_newusr;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
The below function throws an error like ,
ERROR: operator does not exist: date - text
LINE 1: ..._start WHERE s_start.app_rts::date = current_date - $1 AND...
in_date must be integer
current_date - in_date::integer
Or just pass it as integer
func_maj_get_new_user(in_date integer)