Provide moving-aggregate support for boolean aggregates.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Apr 2014 04:01:46 +0000 (00:01 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 13 Apr 2014 04:01:46 +0000 (00:01 -0400)
David Rowley and Florian Pflug, reviewed by Dean Rasheed

src/backend/utils/adt/bool.c
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/window.out
src/test/regress/sql/window.sql

index d419b4a7a66ccd2e1e8c2c6eaf45d2884e6e99ab..3d2f268961f1985f09aa161752edee29eaa46da5 100644 (file)
@@ -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);
+}
index 0d301cf1ace0db124e85277c635a4e6bf10045cd..74c6bff86d7dc4e188641a05d236465d8b74298d 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 201404122
+#define CATALOG_VERSION_NO 201404123
 
 #endif
index 5116beb62a901e2995d4d638013e3768ddeadaa6..89a56d5b8e2bc93620b6134caebe75da94600485 100644 (file)
@@ -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_ ));
index a24f4e02f96198dac7664a532d8e4c9086d0dedd..643408d250c2ab59e7b9c8b2517a4623812686da 100644 (file)
@@ -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. */
index f7cc1af2caf9c8de492ea01419d06a1eb84f51f0..11ff4fdef0caf820ca1a112db6d9cf3698eb8063 100644 (file)
@@ -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);
 
index f08bd9c9adce24e620c4f98e3d4f14361e1510cc..c2cc742c90382419f779449ed9a350b9316856ee 100644 (file)
@@ -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)
+
index 11c96aa8bc057e8ab21cb0e2ace1208ea7398e2f..31c98eb27050b61163d917199a8f10b0f1cdee4d 100644 (file)
@@ -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);