Postgres User-defined Aggregation - postgresql

i'd like to create a user defined aggregation function.
My own type:
CREATE TYPE state AS(
reservoir integer[5],
skipcnt int,
reservoir_size int
);
SFUNC:
create function res_trans (currentstate state,newsample int)
returns state
as $$
DECLARE
pos integer := 0;
BEGIN
IF currentstate.skipcnt = -1 THEN
currentstate.skipcnt := 5;
ELSIF currentstate.skipcnt = 0 THEN
pos := floor(random()*currentstate.reservoir_size+1);
currentstate.reservoir := res_array_replace(currentstate.reservoir,pos,newsample);
currentstate.skipcnt := 5;
ELSE
currentstate.skipcnt := currentstate.skipcnt - 1;
END IF;
RETURN currentstate;
END;
$$LANGUAGE plpgsql;
finalfunc:
CREATE FUNCTION finalize_trans(finalstate state) RETURNS integer[]
AS $$
BEGIN
RETURN finalstate.reservoir;
END;
$$LANGUAGE plpgsql;
All the functions above can be created successfully. However, when i create the aggregation. It shows some error.
CREATE AGGREGATE reservoir_sampling(int)
(
INITCOND = ROW('{1,2,3,4,5}',-1,5),
STYPE = state,
SFUNC = res_trans,
FINALFUNC = finalize_trans
);
enter image description here
Is there a problem of the initcond?
Please help me, Thanks!
I tried to let the initcond = null and deal with the null case in SFUNC, but i got enter image description here

The manual about initial_condition says
This must be a string constant in the form accepted for the data type state_data_type
So you need to provide a string constant, not a "row" expression:
CREATE AGGREGATE reservoir_sampling(int)
(
INITCOND = '("{1,2,3,4,5}",-1,5)',
STYPE = state,
SFUNC = res_trans,
FINALFUNC = finalize_trans
);

Related

Postgresql Simple IF ELSE Statement

In MS SQL I can execute a statement like:
Declare #foo int = 1, #foo1 int = 42
IF #foo <> 0
BEGIN
SELECT #foo
END
ELSE
BEGIN
SELECT #foo, #foo1
END
Does anyone have any idea how to run this statement on postgresql?
EDIT: MS SQL Example like :
CREATE PROCEDURE dbo.spIFtest
#p1 int = 1,
#p2 int = 10,
#isFilter bit = 0
AS
BEGIN
IF #isFilter = 1
BEGIN
SELECT idx FROM rw.octest where idx between #p1 and #p2
END
ELSE
BEGIN
SELECT idx FROM rw.octest
END
END
GO
Using DO With caveats:
DO $$
DECLARE
foo integer := 1;
foo1 integer := 42;
BEGIN
IF foo <> 0 THEN
PERFORM foo;
ELSE
PERFORM foo, foo1;
END IF;
END;
$$
;
DO cannot return anything.
You can fake a return:
DO $$
DECLARE
foo integer := 0;
foo1 integer := 42;
BEGIN
IF foo <> 0 THEN
SELECT INTO foo 1;
RAISE NOTICE 'foo is %', foo;
ELSE
SELECT INTO foo, foo1 1, 42 ;
RAISE NOTICE 'foo is %, foo1 is %', foo, foo1;
END IF;
END;
$$
;
NOTICE: foo is 1, foo1 is 42
DO
In PostgreSQL DO Block can execute the queries but they can not return any value.
So the first part of your question is not possible directly in postgresql.
For second part of your question: In PostgreSQL you can use Function (which is very powerful and effective) like below:
create or replace function spiftest()
returns table(idx_ int)
as $$
declare
p1 int := 1;
p2 int := 10;
isfilter boolean := 0;
begin
if isfilter then
return query
SELECT idx FROM octest where idx between p1 and p2;
else
return query
SELECT idx FROM octest ;
end if;
end;
$$
language plpgsql
calling above function for result:
select * from spiftest()
You can write it with parameters also like below:
create or replace function spiftest(p1 int, p2 int, isfilter boolean)
returns table(idx_ int)
as $$
begin
if isfilter then
return query
SELECT idx FROM octest where idx between p1 and p2;
else
return query
SELECT idx FROM octest ;
end if;
end;
$$
language plpgsql
to call above function
select * from spiftest(1,10,'t')

Aggregates in PostgreSQL

Write an aggregate to count the number of times the number 40 is seen in a column.
Use your aggregate to count the number of 40 year olds in the directory table.
This is what I was doing:
Create function aggstep(curr int) returns int as $$
begin
return curr.count where age = 40;
end;
$$ language plpgsql;
Create aggregate aggs(integer) (
stype = int,
initcond = '',
sfunc = aggstep);
Select cas(age) from directory;
You could do it for example like this:
First, create a transition function:
CREATE FUNCTION count40func(bigint, integer) RETURNS bigint
LANGUAGE sql IMMUTABLE CALLED ON NULL INPUT AS
'SELECT $1 + ($2 IS NOT DISTINCT FROM 40)::integer::bigint';
That works because FALSE::integer is 0 and TRUE::integer is 1.
I use IS NOT DISTINCT FROM rather than = so that it does the correct thing for NULLs.
The aggregate can then be defined as
CREATE AGGREGATE count40(integer) (
SFUNC = count40func,
STYPE = bigint,
INITCOND = 0
);
You can then query like
SELECT count40(age) FROM directory;

How to do a simple array search in PL/pgSQL

I need to know if an integer exists in an array of integers.
Here's what I have:
CREATE OR REPLACE FUNCTION mytest1 () RETURNS integer
LANGUAGE plpgsql
AS $fun$
DECLARE
testid INTEGER := 22;
testary INTEGER [] := '{1,2,3}';
BEGIN
PERFORM testid = ANY (testary);
IF FOUND THEN
RAISE NOTICE '############### found:';
END IF;
RETURN 1;
END;
$fun$
It always returns true. This should be so simple I feel ashamed asking for help. What am I doing wrong?
This is how you can resolve the issue:
CREATE OR REPLACE FUNCTION mytest1 () RETURNS integer
LANGUAGE plpgsql
AS $fun$
DECLARE
testid INTEGER := 1;
testary INTEGER [] := '{1,2,3}';
BEGIN
IF testid = ANY (testary::INTEGER[]) THEN
RETURN 1;
END IF;
RETURN 0;
END;
$fun$;
SELECT mytest1();
Can be simpler, yet:
CREATE OR REPLACE FUNCTION mytest1()
RETURNS boolean AS
$func$
DECLARE
testid int := 1;
testary int[] := '{1,2,3}';
BEGIN
RETURN (testid = ANY(testary));
END
$func$ LANGUAGE plpgsql IMMUTABLE;
The expression returns a boolean value which can be returned directly.

"Function does not exist" error when creating the function with more parameters

I have dropped a PL/pgSQL function and I try to recreate it with one more parameter (with default value). But I get a strange error:
ERROR: function vytvor_kod_sj(text, integer, integer) does not exist
SQL state: 42883
I would expect such an error while dropping or calling the function, not while creating it. I made sure that the error occurs exactly while trying to create it, not in any of the other functions or triggers I created from the same sql file.
I made a dummy function without the last parameter and it works now, but it is definitely not what I want - not only I don't need the function without the last parameter anymore, I usually call the function only with the first two or three parameter, so I need to drop it just after creating the new version of my function to avoid mismatches. Fortunately, there are no errors while doing this, but it is hardly optimal solution.
So does anyone know how to solve this mysterious problem?
I have PostgreSQL 9.3.4 on Windows 32; I use pgAdmin 1.18.1
My code:
CREATE OR REPLACE FUNCTION vytvor_kod_sj( _akce text, typ_sj integer,
podtyp integer DEFAULT 0, styl integer DEFAULT NULL )
RETURNS text AS $$
DECLARE
--styl integer;
_nazev_seq text;
_min integer;
_max integer;
_nazev text;
_soucasna integer;
BEGIN
IF styl IS NULL THEN
SELECT nomenklatura INTO styl FROM akce WHERE kod_akce = _akce;
END IF;
IF NOT EXISTS( SELECT id_nom FROM pro_nom_pravidlo_sj
WHERE id_nom = styl AND sj = typ_sj AND typ = podtyp ) THEN
IF podtyp = 0 OR NOT EXISTS( SELECT id_nom FROM pro_nom_pravidlo_sj
WHERE id_nom = styl AND sj = typ_sj AND typ = 0 ) THEN
RAISE NOTICE 'Pro daný typ stratigrafické jednotky není vytvořeno žádné pravidlo!';
RETURN NULL;
ELSE
podtyp := 0;
END IF;
END IF;
_nazev_seq := _akce || '_' || typ_sj || '_' || podtyp || '_seq';
IF NOT EXISTS (SELECT 0 FROM pg_class where relname = _nazev_seq ) THEN
SELECT min, max INTO _min, _max FROM pro_nom_pravidlo_sj
WHERE id_nom = styl AND sj = typ_sj AND typ = podtyp;
IF _max IS NOT NULL THEN
EXECUTE format('CREATE SEQUENCE %I MINVALUE %s MAXVALUE %s ', _nazev_seq, _min, _max);
ELSE
EXECUTE format('CREATE SEQUENCE %I MINVALUE %s ', _nazev_seq, _min);
END IF;
END IF;
--IF strict IS TRUE THEN
RETURN (
SELECT predpona FROM pro_nom_pravidlo_sj
WHERE id_nom = styl AND sj = typ_sj AND typ = podtyp
) || CAST(nextval(_nazev_seq) AS TEXT);
--END IF;
END;
$$ LANGUAGE plpgsql
SECURITY DEFINER;
ALTER FUNCTION vytvor_kod_sj( text, integer, integer ) OWNER TO ins_daemon;
The old version had styl variable declared, not as a parameter, and there was no test whether it is null. Otherwise I didn't change anything.
I noticed the cause of the problem just after posting the question:
ALTER FUNCTION vytvor_kod_sj( text, integer, integer ) OWNER TO ins_daemon;
It tries to alter the old function.

How to get the result parameter of a NativeQuery

I want to make this Query:
Select
srid,
substring(srtext
from (position('DATUM["' in srtext)+7)
for (position('ID["' in srtext)+2)
- (position('DATUM["' in srtext)+7))
from spatial_ref_sys
order by substring
So, I create a
Query query = em().createNativeQuery(QUERY)
Also create a Srid object that has a Long srid and a String sridText.
I need to get those values and put into a List.
The substring on sridText, and obviously the srid into Long srid.
Please help!!!
Found the answer. First of all, I did a plpgsql function called getSrid
CREATE OR REPLACE FUNCTION getSRID() returns text[] as $$
declare
resultado text[];
consulta cursor for
Select srid,substring(srtext from (position('DATUM["' in srtext)+7)
for(position('ID["' in srtext)+2)
- (position('DATUM["' in srtext)+7)) as sub
from spatial_ref_sys;
cont int;
i int;
sridd int;
srTextt text;
begin
open consulta;
i=1;
EXECUTE 'SELECT COUNT(*) FROM SPATIAL_REF_SYS' into cont;
resultado := ARRAY[cont];
while(i<cont) loop
fetch consulta into sridd,srTextt;
resultado[i] := sridd;
resultado[i+1] := srTextt;
i:=i+2;
end loop;
return resultado;
end
$$ language plpgsql;
As you see I got a Text[] on return so in my java call I cast the object to String[]:
public List<SridDTO> listaa(){
int i = 0;
List<SridDTO> list = new ArrayList<SridDTO>();
Query query = em().createNativeQuery("Select getSrid()");
String[] vector = (String[]) query.getSingleResult();
while(i<vector.length){
SridDTO sridDTO = new SridDTO(new Long(vector[i]),vector[i+1]);
i = i + 2;
list.add(sridDTO);
}
return list;
}