From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 13 Apr 2014 04:01:46 +0000 (-0400)
Subject: Provide moving-aggregate support for boolean aggregates.
X-Git-Tag: REL9_4_BETA1~192
X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d95425c8b9d3ea1681bd91b76ce73be95ca5ee21;p=postgresql

Provide moving-aggregate support for boolean aggregates.

David Rowley and Florian Pflug, reviewed by Dean Rasheed
---

diff --git a/src/backend/utils/adt/bool.c b/src/backend/utils/adt/bool.c
index d419b4a7a6..3d2f268961 100644
--- a/src/backend/utils/adt/bool.c
+++ b/src/backend/utils/adt/bool.c
@@ -283,8 +283,11 @@ boolge(PG_FUNCTION_ARGS)
  * boolean-and and boolean-or aggregates.
  */
 
-/* function for standard EVERY aggregate implementation conforming to SQL 2003.
- * must be strict. It is also named bool_and for homogeneity.
+/*
+ * Function for standard EVERY aggregate conforming to SQL 2003.
+ * The aggregate is also named bool_and for consistency.
+ *
+ * Note: this is only used in plain aggregate mode, not moving-aggregate mode.
  */
 Datum
 booland_statefunc(PG_FUNCTION_ARGS)
@@ -292,11 +295,109 @@ booland_statefunc(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(PG_GETARG_BOOL(0) && PG_GETARG_BOOL(1));
 }
 
-/* function for standard ANY/SOME aggregate conforming to SQL 2003.
- * must be strict. The name of the aggregate is bool_or. See the doc.
+/*
+ * Function for standard ANY/SOME aggregate conforming to SQL 2003.
+ * The aggregate is named bool_or, because ANY/SOME have parsing conflicts.
+ *
+ * Note: this is only used in plain aggregate mode, not moving-aggregate mode.
  */
 Datum
 boolor_statefunc(PG_FUNCTION_ARGS)
 {
 	PG_RETURN_BOOL(PG_GETARG_BOOL(0) || PG_GETARG_BOOL(1));
 }
+
+typedef struct BoolAggState
+{
+	int64		aggcount;		/* number of non-null values aggregated */
+	int64		aggtrue;		/* number of values aggregated that are true */
+} BoolAggState;
+
+static BoolAggState *
+makeBoolAggState(FunctionCallInfo fcinfo)
+{
+	BoolAggState *state;
+	MemoryContext agg_context;
+
+	if (!AggCheckCallContext(fcinfo, &agg_context))
+		elog(ERROR, "aggregate function called in non-aggregate context");
+
+	state = (BoolAggState *) MemoryContextAlloc(agg_context,
+												sizeof(BoolAggState));
+	state->aggcount = 0;
+	state->aggtrue = 0;
+
+	return state;
+}
+
+Datum
+bool_accum(PG_FUNCTION_ARGS)
+{
+	BoolAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
+
+	/* Create the state data on first call */
+	if (state == NULL)
+		state = makeBoolAggState(fcinfo);
+
+	if (!PG_ARGISNULL(1))
+	{
+		state->aggcount++;
+		if (PG_GETARG_BOOL(1))
+			state->aggtrue++;
+	}
+
+	PG_RETURN_POINTER(state);
+}
+
+Datum
+bool_accum_inv(PG_FUNCTION_ARGS)
+{
+	BoolAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
+
+	/* bool_accum should have created the state data */
+	if (state == NULL)
+		elog(ERROR, "bool_accum_inv called with NULL state");
+
+	if (!PG_ARGISNULL(1))
+	{
+		state->aggcount--;
+		if (PG_GETARG_BOOL(1))
+			state->aggtrue--;
+	}
+
+	PG_RETURN_POINTER(state);
+}
+
+Datum
+bool_alltrue(PG_FUNCTION_ARGS)
+{
+	BoolAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
+
+	/* if there were no non-null values, return NULL */
+	if (state == NULL || state->aggcount == 0)
+		PG_RETURN_NULL();
+
+	/* true if all non-null values are true */
+	PG_RETURN_BOOL(state->aggtrue == state->aggcount);
+}
+
+Datum
+bool_anytrue(PG_FUNCTION_ARGS)
+{
+	BoolAggState *state;
+
+	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
+
+	/* if there were no non-null values, return NULL */
+	if (state == NULL || state->aggcount == 0)
+		PG_RETURN_NULL();
+
+	/* true if any non-null value is true */
+	PG_RETURN_BOOL(state->aggtrue > 0);
+}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0d301cf1ac..74c6bff86d 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	201404122
+#define CATALOG_VERSION_NO	201404123
 
 #endif
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
index 5116beb62a..89a56d5b8e 100644
--- a/src/include/catalog/pg_aggregate.h
+++ b/src/include/catalog/pg_aggregate.h
@@ -248,9 +248,9 @@ DATA(insert ( 2828	n 0 float8_regr_accum	float8_covar_samp		-				-				-				0	102
 DATA(insert ( 2829	n 0 float8_regr_accum	float8_corr				-				-				-				0	1022	0	0		0	"{0,0,0,0,0,0}" _null_ ));
 
 /* boolean-and and boolean-or */
-DATA(insert ( 2517	n 0 booland_statefunc	-			-				-				-				58	16		0	0		0	_null_ _null_ ));
-DATA(insert ( 2518	n 0 boolor_statefunc	-			-				-				-				59	16		0	0		0	_null_ _null_ ));
-DATA(insert ( 2519	n 0 booland_statefunc	-			-				-				-				58	16		0	0		0	_null_ _null_ ));
+DATA(insert ( 2517	n 0 booland_statefunc	-			bool_accum		bool_accum_inv	bool_alltrue	58	16		0	2281	16	_null_ _null_ ));
+DATA(insert ( 2518	n 0 boolor_statefunc	-			bool_accum		bool_accum_inv	bool_anytrue	59	16		0	2281	16	_null_ _null_ ));
+DATA(insert ( 2519	n 0 booland_statefunc	-			bool_accum		bool_accum_inv	bool_alltrue	58	16		0	2281	16	_null_ _null_ ));
 
 /* bitwise integer */
 DATA(insert ( 2236	n 0 int2and		-					-				-				-				0	21		0	0		0	_null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index a24f4e02f9..643408d250 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3915,6 +3915,14 @@ DATA(insert OID = 2515 ( booland_statefunc			   PGNSP PGUID 12 1 0 0 0 f f f f t
 DESCR("aggregate transition function");
 DATA(insert OID = 2516 ( boolor_statefunc			   PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 16 "16 16" _null_ _null_ _null_ _null_ boolor_statefunc _null_ _null_ _null_ ));
 DESCR("aggregate transition function");
+DATA(insert OID = 3496 ( bool_accum					   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ bool_accum _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 3497 ( bool_accum_inv				   PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 16" _null_ _null_ _null_ _null_ bool_accum_inv _null_ _null_ _null_ ));
+DESCR("aggregate transition function");
+DATA(insert OID = 3498 ( bool_alltrue				   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 16 "2281" _null_ _null_ _null_ _null_ bool_alltrue _null_ _null_ _null_ ));
+DESCR("aggregate final function");
+DATA(insert OID = 3499 ( bool_anytrue				   PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 16 "2281" _null_ _null_ _null_ _null_ bool_anytrue _null_ _null_ _null_ ));
+DESCR("aggregate final function");
 DATA(insert OID = 2517 ( bool_and					   PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 16 "16" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
 DESCR("boolean-and aggregate");
 /* ANY, SOME? These names conflict with subquery operators. See doc. */
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index f7cc1af2ca..11ff4fdef0 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -121,6 +121,10 @@ extern Datum boolle(PG_FUNCTION_ARGS);
 extern Datum boolge(PG_FUNCTION_ARGS);
 extern Datum booland_statefunc(PG_FUNCTION_ARGS);
 extern Datum boolor_statefunc(PG_FUNCTION_ARGS);
+extern Datum bool_accum(PG_FUNCTION_ARGS);
+extern Datum bool_accum_inv(PG_FUNCTION_ARGS);
+extern Datum bool_alltrue(PG_FUNCTION_ARGS);
+extern Datum bool_anytrue(PG_FUNCTION_ARGS);
 extern bool parse_bool(const char *value, bool *result);
 extern bool parse_bool_with_len(const char *value, size_t len, bool *result);
 
diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out
index f08bd9c9ad..c2cc742c90 100644
--- a/src/test/regress/expected/window.out
+++ b/src/test/regress/expected/window.out
@@ -1769,3 +1769,15 @@ SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FO
                       1.0
 (2 rows)
 
+SELECT i, b, bool_and(b) OVER w, bool_or(b) OVER w
+  FROM (VALUES (1,true), (2,true), (3,false), (4,false), (5,true)) v(i,b)
+  WINDOW w AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING);
+ i | b | bool_and | bool_or 
+---+---+----------+---------
+ 1 | t | t        | t
+ 2 | t | f        | t
+ 3 | f | f        | f
+ 4 | f | f        | t
+ 5 | t | t        | t
+(5 rows)
+
diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql
index 11c96aa8bc..31c98eb270 100644
--- a/src/test/regress/sql/window.sql
+++ b/src/test/regress/sql/window.sql
@@ -617,3 +617,7 @@ FROM (VALUES(1,1::numeric),(2,2),(3,'NaN'),(4,3),(5,4)) t(a,b);
 -- hard about it.
 SELECT to_char(SUM(n::float8) OVER (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING),'999999999999999999999D9')
   FROM (VALUES(1,1e20),(2,1)) n(i,n);
+
+SELECT i, b, bool_and(b) OVER w, bool_or(b) OVER w
+  FROM (VALUES (1,true), (2,true), (3,false), (4,false), (5,true)) v(i,b)
+  WINDOW w AS (ORDER BY i ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING);