diff --git a/contrib/Makefile b/contrib/Makefile index 738a28a..e9fc2f9 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -23,6 +23,7 @@ WANTED_DIRS = \ isn \ lo \ ltree \ + movavg \ oid2name \ pageinspect \ pg_buffercache \ diff --git a/contrib/movavg/Makefile b/contrib/movavg/Makefile new file mode 100644 index 0000000..b295575 --- /dev/null +++ b/contrib/movavg/Makefile @@ -0,0 +1,17 @@ +# $PostgreSQL$ + +MODULES = movavg +DATA_built = movavg.sql +DATA = uninstall_movavg.sql +REGRESS = install_movavg one_sliding + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/movavg +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/movavg/data/one_sliding.data b/contrib/movavg/data/one_sliding.data new file mode 100644 index 0000000..9811557 --- /dev/null +++ b/contrib/movavg/data/one_sliding.data @@ -0,0 +1,11 @@ +develop 10 5200 2007/08/01 +sales 1 5000 2006/10/01 +personnel 5 3500 2007/12/10 +sales 4 4800 2007/08/08 +sales 6 5500 2007/01/02 +personnel 2 3900 2006/12/23 +develop 7 4200 2008/01/01 +develop 9 4500 2008/01/01 +sales 3 4800 2007/08/01 +develop 8 6000 2006/10/01 +develop 11 5200 2007/08/15 diff --git a/contrib/movavg/expected/install_movavg.out b/contrib/movavg/expected/install_movavg.out new file mode 100644 index 0000000..43f11fa --- /dev/null +++ b/contrib/movavg/expected/install_movavg.out @@ -0,0 +1,3 @@ +SET client_min_messages = warning; +\set ECHO none +RESET client_min_messages; diff --git a/contrib/movavg/expected/one_sliding.out b/contrib/movavg/expected/one_sliding.out new file mode 100644 index 0000000..e398e4f --- /dev/null +++ b/contrib/movavg/expected/one_sliding.out @@ -0,0 +1,32 @@ +CREATE TABLE empsalary( + depname varchar, + empno bigint, + salary int, + enroll_date date +); +\COPY empsalary FROM 'data/one_sliding.data' +SELECT + depname, + salary, + movavg(salary::float8, 1) OVER w +FROM empsalary +WINDOW w AS ( + PARTITION BY depname + ORDER BY salary + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING +); + depname | salary | movavg +-----------+--------+------------------ + develop | 4200 | 4350 + develop | 4500 | 4633.33333333333 + develop | 5200 | 4966.66666666667 + develop | 5200 | 5466.66666666667 + develop | 6000 | 5600 + personnel | 3500 | 3700 + personnel | 3900 | 3700 + sales | 4800 | 4800 + sales | 4800 | 4866.66666666667 + sales | 5000 | 5100 + sales | 5500 | 5250 +(11 rows) + diff --git a/contrib/movavg/movavg.c b/contrib/movavg/movavg.c new file mode 100644 index 0000000..b5e28ca --- /dev/null +++ b/contrib/movavg/movavg.c @@ -0,0 +1,125 @@ +#include "postgres.h" +#include "fmgr.h" +#include "windowapi.h" + +PG_MODULE_MAGIC; + +PG_FUNCTION_INFO_V1(movavg); + +Datum movavg(PG_FUNCTION_ARGS); + +typedef struct { + int64 exiting; /* absolute position of exiting row */ + int64 entering; /* absolute position of entering row */ + float8 svalue; /* trans value */ + int count; /* the number of accumlated values */ +} movavg_data; + +Datum +movavg(PG_FUNCTION_ARGS) +{ + WindowObject winobj = PG_WINDOW_OBJECT(); + bool isnull, isout; + int64 cur = WinGetCurrentPosition(winobj); + int32 range; + Datum value; + int i; + bool const_range; + + /* current row +/- rows are the targets */ + range = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); + if (isnull || range < 0) + { + elog(ERROR, "invalid range"); + } + /* if it's stable Const value or not */ + const_range = get_fn_expr_arg_stable(fcinfo->flinfo, 1); + + /* + * For variable range arguments, we only calculate exact + * average for all the target rows. Otherwise we can + * optimze it by subtract/add method. + */ + if (!const_range) + { + float8 svalue = 0.0; + int count = 0; + + for(i = cur - range; i <= cur + range; i++) + { + value = WinGetFuncArgInPartition(winobj, 0, i, + WINDOW_SEEK_HEAD, false, &isnull, &isout); + if (!isnull && !isout) + { + svalue += DatumGetFloat8(value); + count++; + } + } + + WinSetMarkPosition(winobj, cur - range); + + if (count > 0) + PG_RETURN_FLOAT8(svalue / (float8) count); + PG_RETURN_NULL(); + } + else + { + movavg_data *sdata = (movavg_data *) + WinGetPartitionLocalMemory(winobj, sizeof(movavg_data)); + + /* optimize for the single row case */ + if (range == 0) + { + value = WinGetFuncArgCurrent(winobj, 0, &isnull); + if (isnull) + PG_RETURN_NULL(); + PG_RETURN_DATUM(value); + } + + /* + * The first row is the only special case. + * Calculate avarage as usual. Otherwise, + * exiting row value is subtracted and entering + * row is added then returns result. + */ + if (sdata->entering == 0) + { + for(i = 0; i <= range; i++) + { + value = WinGetFuncArgInPartition(winobj, 0, i, + WINDOW_SEEK_HEAD, false, &isnull, &isout); + if (!isnull && !isout) + { + sdata->svalue += DatumGetFloat8(value); + sdata->count++; + } + } + sdata->exiting = -range; + sdata->entering = range; + } + else + { + value = WinGetFuncArgInPartition(winobj, 0, sdata->exiting, + WINDOW_SEEK_HEAD, true, &isnull, &isout); + if (!isnull && !isout) + { + sdata->svalue -= DatumGetFloat8(value); + sdata->count--; + } + sdata->exiting++; + + sdata->entering++; + value = WinGetFuncArgInPartition(winobj, 0, sdata->entering, + WINDOW_SEEK_HEAD, false, &isnull, &isout); + if (!isnull && !isout) + { + sdata->svalue += DatumGetFloat8(value); + sdata->count++; + } + } + + if (sdata->count > 0) + PG_RETURN_FLOAT8(sdata->svalue / (float8) sdata->count); + PG_RETURN_NULL(); + } +} diff --git a/contrib/movavg/movavg.sql.in b/contrib/movavg/movavg.sql.in new file mode 100644 index 0000000..2ffe3f9 --- /dev/null +++ b/contrib/movavg/movavg.sql.in @@ -0,0 +1,6 @@ +SET search_path = public; + +CREATE OR REPLACE FUNCTION movavg(float8, int4) +RETURNS float8 +AS 'MODULE_PATHNAME', 'movavg' +LANGUAGE C WINDOW; diff --git a/contrib/movavg/sql/install_movavg.sql b/contrib/movavg/sql/install_movavg.sql new file mode 100644 index 0000000..cb58f23 --- /dev/null +++ b/contrib/movavg/sql/install_movavg.sql @@ -0,0 +1,5 @@ +SET client_min_messages = warning; +\set ECHO none +\i movavg.sql +\set ECHO all +RESET client_min_messages; diff --git a/contrib/movavg/sql/one_sliding.sql b/contrib/movavg/sql/one_sliding.sql new file mode 100644 index 0000000..ec85d73 --- /dev/null +++ b/contrib/movavg/sql/one_sliding.sql @@ -0,0 +1,19 @@ +CREATE TABLE empsalary( + depname varchar, + empno bigint, + salary int, + enroll_date date +); + +\COPY empsalary FROM 'data/one_sliding.data' + +SELECT + depname, + salary, + movavg(salary::float8, 1) OVER w +FROM empsalary +WINDOW w AS ( + PARTITION BY depname + ORDER BY salary + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING +); diff --git a/contrib/movavg/uninstall_movavg.sql b/contrib/movavg/uninstall_movavg.sql new file mode 100644 index 0000000..2aab014 --- /dev/null +++ b/contrib/movavg/uninstall_movavg.sql @@ -0,0 +1,3 @@ +SET search_path = public; + +DROP FUNCTION IF EXISTS movavg(float8, int4);