pax_global_header00006660000000000000000000000064133266132700014515gustar00rootroot0000000000000052 comment=d63ea3b0c8568e0547d76350e374f1ecd854f595 first-last-agg/000077500000000000000000000000001332661327000137055ustar00rootroot00000000000000first-last-agg/.gitignore000066400000000000000000000003101332661327000156670ustar00rootroot00000000000000.deps/ sql/first_last_agg--0.1.0.sql sql/first_last_agg--0.1.1.sql sql/first_last_agg--0.1.2.sql sql/first_last_agg--0.1.3.sql sql/first_last_agg--0.1.4.sql src/first_last_agg.o src/first_last_agg.so first-last-agg/META.json000066400000000000000000000020571332661327000153320ustar00rootroot00000000000000{ "name": "first_last_agg", "abstract": "Provides first() and last() aggregate functions.", "version": "0.1.4", "maintainer": [ "Jan Urbański " ], "release_status": "stable", "license": "postgresql", "prereqs": { "build": { "requires": { "PostgreSQL": "9.0.0" } } }, "provides": { "first_last_agg": { "file": "sql/first_last_agg.sql", "docfile": "doc/first_last_agg.md", "version": "0.1.4" } }, "resources": { "bugtracker": { "web": "http://github.com/wulczer/first_last_agg/issues/" }, "repository": { "url": "git://github.com/wulczer/first_last_agg.git", "web": "http://github.com/wulczer/first_last_agg/", "type": "git" } }, "generated_by": "Jan Urbański", "meta-spec": { "version": "1.0.0", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ "first aggregate", "last aggregate", "aggregate function" ] } first-last-agg/Makefile000066400000000000000000000016361332661327000153530ustar00rootroot00000000000000EXTENSION = first_last_agg EXTVERSION = $(shell grep default_version $(EXTENSION).control | \ sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") DATA = $(filter-out $(wildcard sql/*--*.sql),$(wildcard sql/*.sql)) DOCS = $(wildcard doc/*.md) TESTS = $(wildcard test/sql/*.sql) REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) REGRESS_OPTS = --inputdir=test MODULES = $(patsubst %.c,%,$(wildcard src/*.c)) PG_CONFIG = pg_config PG91 = $(shell $(PG_CONFIG) --version | grep -qE " 8\.| 9\.0" && echo no || echo yes) ifeq ($(PG91),yes) all: sql/$(EXTENSION)--$(EXTVERSION).sql sql/$(EXTENSION)--$(EXTVERSION).sql: sql/$(EXTENSION).sql cp $< $@ DATA_built = sql/$(EXTENSION)--$(EXTVERSION).sql DATA = $(filter-out sql/$(EXTENSION)--$(EXTVERSION).sql, $(wildcard sql/*--*.sql)) endif PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) first-last-agg/README.md000066400000000000000000000013261332661327000151660ustar00rootroot00000000000000first_last_agg -------------- A simple extension providing two aggregate functions, `last` and `first` aggregate functions, operating on any element type and returning the last or the first value of the group. Since by default the ordering inside groups created by a `GROUP BY` expression is not defined, it is advisable to use an `ORDER BY` clause inside the aggregate expression (see the [aggregate function syntax](http://www.postgresql.org/docs/current/static/sql-expressions.html#SYNTAX-AGGREGATES)). This feature has been added in PostgreSQL 9.0. This extension is intended to replace the corresponding [first and last](http://wiki.postgresql.org/wiki/First/last_%28aggregate%29) aggregates from the PostgreSQL wiki. first-last-agg/doc/000077500000000000000000000000001332661327000144525ustar00rootroot00000000000000first-last-agg/doc/first_last_agg.md000066400000000000000000000020001332661327000177540ustar00rootroot00000000000000first-last-agg 0.1.4 ==================== Synopsis -------- % CREATE EXTENSION first_last_agg; CREATE EXTENSION % SELECT last(x order by y) FROM (VALUES (1, 3), (2, 1), (3, 2)) AS v(x, y); last ------ 1 % SELECT first(x order by y) FROM (VALUES (1, 3), (2, 1), (3, 2)) AS v(x, y); first ------- 2 Description ----------- This library contains two simple aggregate functions, `first` and `last`, operating on any element type. They return the first or the last value of the aggregated group, respectively. It is useful with aggregates with `order by`, for easily taking one element from an aggregate group when you don't really care which do you get and for emulating a similar functionality from Oracle. Author ------ [Jan Urbański](http://wulczer.org/), based on code from the [PostgreSQL wiki](http://wiki.postgresql.org/), specifically on the SQL definitions of [first and last](http://wiki.postgresql.org/wiki/First/last_%28aggregate%29) taken from there. first-last-agg/first_last_agg.control000066400000000000000000000002461332661327000203010ustar00rootroot00000000000000# first_last_agg extension comment = 'first() and last() aggregate functions' default_version = '0.1.4' module_pathname = '$libdir/first_last_agg' relocatable = true first-last-agg/sql/000077500000000000000000000000001332661327000145045ustar00rootroot00000000000000first-last-agg/sql/first_last_agg--0.1.0--0.1.1.sql000066400000000000000000000004631332661327000214370ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION last_sfunc(anyelement, anyelement) RETURNS anyelement AS '$libdir/first_last_agg', 'last_sfunc' LANGUAGE C IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION first_sfunc(anyelement, anyelement) RETURNS anyelement AS '$libdir/first_last_agg', 'first_sfunc' LANGUAGE C IMMUTABLE STRICT; first-last-agg/sql/first_last_agg--0.1.1--0.1.2.sql000066400000000000000000000000001332661327000214240ustar00rootroot00000000000000first-last-agg/sql/first_last_agg--0.1.2--0.1.3.sql000066400000000000000000000000001332661327000214260ustar00rootroot00000000000000first-last-agg/sql/first_last_agg--0.1.3--0.1.4.sql000066400000000000000000000000001332661327000214300ustar00rootroot00000000000000first-last-agg/sql/first_last_agg--unpackaged--0.1.0.sql000066400000000000000000000004401332661327000230770ustar00rootroot00000000000000ALTER EXTENSION first_last_agg ADD FUNCTION first_sfunc(anyelement, anyelement); ALTER EXTENSION first_last_agg ADD FUNCTION last_sfunc(anyelement, anyelement); ALTER EXTENSION first_last_agg ADD AGGREGATE last(anyelement); ALTER EXTENSION first_last_agg ADD AGGREGATE first(anyelement); first-last-agg/sql/first_last_agg.sql000066400000000000000000000025651332661327000202250ustar00rootroot00000000000000CREATE OR REPLACE FUNCTION last_sfunc(anyelement, anyelement) RETURNS anyelement AS '$libdir/first_last_agg', 'last_sfunc' LANGUAGE C IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION first_sfunc(anyelement, anyelement) RETURNS anyelement AS '$libdir/first_last_agg', 'first_sfunc' LANGUAGE C IMMUTABLE STRICT; DROP AGGREGATE IF EXISTS first(anyelement); CREATE AGGREGATE first(anyelement) ( SFUNC = first_sfunc, STYPE = anyelement ); DROP AGGREGATE IF EXISTS last(anyelement); CREATE AGGREGATE last(anyelement) ( SFUNC = last_sfunc, STYPE = anyelement ); DO $$ DECLARE version_num integer; BEGIN SELECT current_setting('server_version_num') INTO STRICT version_num; IF version_num > 90600 THEN EXECUTE $E$ ALTER FUNCTION last_sfunc(anyelement, anyelement) PARALLEL SAFE $E$; EXECUTE $E$ ALTER FUNCTION first_sfunc(anyelement, anyelement) PARALLEL SAFE $E$; EXECUTE $E$ DROP AGGREGATE IF EXISTS first(anyelement) $E$; EXECUTE $E$ CREATE AGGREGATE first(anyelement) ( SFUNC = first_sfunc, STYPE = anyelement, COMBINEFUNC = first_sfunc, parallel = SAFE ); $E$; EXECUTE $E$ DROP AGGREGATE IF EXISTS last(anyelement) $E$; EXECUTE $E$ CREATE AGGREGATE last(anyelement) ( SFUNC = last_sfunc, STYPE = anyelement, COMBINEFUNC = last_sfunc, parallel = SAFE ); $E$; END IF; END; $$;first-last-agg/src/000077500000000000000000000000001332661327000144745ustar00rootroot00000000000000first-last-agg/src/first_last_agg.c000066400000000000000000000015111332661327000176260ustar00rootroot00000000000000/*------------------------------------------------------------------------ * * first-last-agg.c * first() and last() aggregate functions working on anyelement * * Copyright (c) 2011, PostgreSQL Global Development Group * *------------------------------------------------------------------------- */ #include "postgres.h" #include "fmgr.h" PG_MODULE_MAGIC; extern Datum first_sfunc(PG_FUNCTION_ARGS); extern Datum last_sfunc(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(first_sfunc); Datum first_sfunc(PG_FUNCTION_ARGS) { Datum element; /* simply return the first argument */ element = PG_GETARG_DATUM(0); PG_RETURN_DATUM(element); } PG_FUNCTION_INFO_V1(last_sfunc); Datum last_sfunc(PG_FUNCTION_ARGS) { Datum element; /* simply return the second argument */ element = PG_GETARG_DATUM(1); PG_RETURN_DATUM(element); } first-last-agg/test/000077500000000000000000000000001332661327000146645ustar00rootroot00000000000000first-last-agg/test/expected/000077500000000000000000000000001332661327000164655ustar00rootroot00000000000000first-last-agg/test/expected/agg.out000066400000000000000000000037071332661327000177630ustar00rootroot00000000000000BEGIN; SET client_min_messages TO 'WARNING'; \set ECHO none RESET client_min_messages; CREATE TEMPORARY TABLE agg_test ( akey integer, val1 integer, val2 integer ); INSERT INTO agg_test (akey, val1, val2) VALUES (1, 2, 1), (1, 4, 2), (1, 3, 3), (2, 1, 4), (2, 5, 3), (2, NULL, 2), (2, 2, 1), (3, NULL, NULL), (4, 3, 1), (4, 5, NULL), (4, 7, 2), (5, 5, 1), (5, 5, 2), (5, 5, 3); SELECT akey, first(val1 ORDER BY val2) AS first, last(val1 ORDER BY val2) AS last FROM agg_test GROUP BY akey ORDER BY akey; akey | first | last ------+-------+------ 1 | 2 | 3 2 | 2 | 1 3 | | 4 | 3 | 5 5 | 5 | 5 (5 rows) SELECT akey, first(val1 ORDER BY val2 NULLS LAST) AS first, last(val1 ORDER BY val2 NULLS LAST) AS last FROM agg_test GROUP BY akey ORDER BY akey; akey | first | last ------+-------+------ 1 | 2 | 3 2 | 2 | 1 3 | | 4 | 3 | 5 5 | 5 | 5 (5 rows) SELECT akey, first(val1 ORDER BY val2 ASC) AS first, last(val1 ORDER BY val2 DESC) AS last FROM agg_test GROUP BY akey ORDER BY akey; akey | first | last ------+-------+------ 1 | 2 | 2 2 | 2 | 2 3 | | 4 | 3 | 3 5 | 5 | 5 (5 rows) SELECT akey, first(val1 ORDER BY val2 ASC NULLS FIRST) AS first, last(val1 ORDER BY val2 NULLS FIRST) AS last FROM agg_test GROUP BY akey ORDER BY akey; akey | first | last ------+-------+------ 1 | 2 | 3 2 | 2 | 1 3 | | 4 | 5 | 7 5 | 5 | 5 (5 rows) SELECT akey, first(val1) AS first, last(val1) AS last FROM agg_test WHERE akey IN (3, 5) GROUP BY akey ORDER BY akey; akey | first | last ------+-------+------ 3 | | 5 | 5 | 5 (2 rows) SELECT akey, first(val1) AS first, last(val1) AS last FROM agg_test WHERE akey = 100 GROUP BY akey; akey | first | last ------+-------+------ (0 rows) ROLLBACK; first-last-agg/test/sql/000077500000000000000000000000001332661327000154635ustar00rootroot00000000000000first-last-agg/test/sql/agg.sql000066400000000000000000000023411332661327000167420ustar00rootroot00000000000000BEGIN; SET client_min_messages TO 'WARNING'; \set ECHO none \i sql/first_last_agg.sql \set ECHO all RESET client_min_messages; CREATE TEMPORARY TABLE agg_test ( akey integer, val1 integer, val2 integer ); INSERT INTO agg_test (akey, val1, val2) VALUES (1, 2, 1), (1, 4, 2), (1, 3, 3), (2, 1, 4), (2, 5, 3), (2, NULL, 2), (2, 2, 1), (3, NULL, NULL), (4, 3, 1), (4, 5, NULL), (4, 7, 2), (5, 5, 1), (5, 5, 2), (5, 5, 3); SELECT akey, first(val1 ORDER BY val2) AS first, last(val1 ORDER BY val2) AS last FROM agg_test GROUP BY akey ORDER BY akey; SELECT akey, first(val1 ORDER BY val2 NULLS LAST) AS first, last(val1 ORDER BY val2 NULLS LAST) AS last FROM agg_test GROUP BY akey ORDER BY akey; SELECT akey, first(val1 ORDER BY val2 ASC) AS first, last(val1 ORDER BY val2 DESC) AS last FROM agg_test GROUP BY akey ORDER BY akey; SELECT akey, first(val1 ORDER BY val2 ASC NULLS FIRST) AS first, last(val1 ORDER BY val2 NULLS FIRST) AS last FROM agg_test GROUP BY akey ORDER BY akey; SELECT akey, first(val1) AS first, last(val1) AS last FROM agg_test WHERE akey IN (3, 5) GROUP BY akey ORDER BY akey; SELECT akey, first(val1) AS first, last(val1) AS last FROM agg_test WHERE akey = 100 GROUP BY akey; ROLLBACK;