movavg

Started by David Fetterover 16 years ago1 messages
#1David Fetter
david@fetter.org
1 attachment(s)

Folks,

Here's Harada-san's moving average windowing function, expressed as a
contrib module. It has tests, etc.

Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com

Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate

Attachments:

movavg.difftext/plain; charset=us-asciiDownload
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 +/- <range> 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);