diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 487c7ff750..d6fd518862 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -14729,8 +14729,8 @@ SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
partition through the last peer of the current row. This is
likely to give unhelpful results for last_value and
sometimes also nth_value. You can redefine the frame by
- adding a suitable frame specification (RANGE or
- ROWS) to the OVER clause.
+ adding a suitable frame specification (RANGE,
+ ROWS or GROUPS) to the OVER clause.
See for more information
about frame specifications.
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 8a3e86b6db..3381dca3f2 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -859,8 +859,8 @@ WINDOW window_name AS ( frame_clause can be one of
-{ RANGE | ROWS } frame_start
-{ RANGE | ROWS } BETWEEN frame_start AND frame_end
+{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion_clause ]
+{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion_clause ]
where frame_start and frame_end can be
@@ -874,6 +874,16 @@ CURRENT ROW
UNBOUNDED FOLLOWING
+ and the optional frame_exclusion_clause can be
+ one of
+
+
+EXCLUDE CURRENT ROW
+EXCLUDE GROUP
+EXCLUDE TIES
+EXCLUDE NO OTHERS
+
+
If frame_end is omitted it defaults to CURRENT
ROW. Restrictions are that
frame_start cannot be UNBOUNDED FOLLOWING,
@@ -894,25 +904,37 @@ UNBOUNDED FOLLOWING
In general, UNBOUNDED PRECEDING means that the frame
starts with the first row of the partition, and similarly
UNBOUNDED FOLLOWING means that the frame ends with the last
- row of the partition (regardless of RANGE or ROWS
- mode). In ROWS mode, CURRENT ROW
+ row of the partition (regardless of RANGE, ROWS
+ or GROUPS mode). In ROWS mode, CURRENT ROW
means that the frame starts or ends with the current row; but in
- RANGE mode it means that the frame starts or ends with
+ RANGE or GROUPS mode it means that the frame starts or ends with
the current row's first or last peer in the ORDER BY ordering.
The value PRECEDING and
- value FOLLOWING cases are currently only
- allowed in ROWS mode. They indicate that the frame starts
- or ends with the row that many rows before or after the current row.
- value must be an integer expression not
- containing any variables, aggregate functions, or window functions.
- The value must not be null or negative; but it can be zero, which
- selects the current row itself.
+ value FOLLOWING cases differ depending on
+ whether the frame clause is in ROWS, RANGEor GROUPS mode. In
+ ROWS mode, they indicate that the frame starts or ends with the row that
+ many rows before or after the current row. In RANGE mode, they indicate that
+ the frame starts or ends when the ORDER BY column's value for each row is within the bounds
+ specified by value for both the start and the end of the frame. In GROUPS mode,
+ they indicate the number of changes to the value of the ORDER BY columns (i.e., groups of peers).
+ In ROWS or GROUPS mode, value must be an integer expression not
+ containing any variables, aggregate functions, or window functions.In RANGE mode,
+ there must be exactly one ORDER BY column of a supported type. In all three modes, the value must not be null or
+ negative; but it can be zero, which just selects the current row itself.
+
+
+
+ For the frame_exclusion_clause, EXCLUDE CURRENT ROW
+ excludes the current row from the frame. EXCLUDE TIES excludes any peers of the current row from the
+ frame. EXCLUDE GROUP excludes both the current row and any peers of the current row from the frame.
+ EXCLUDE NO OTHERS does nothing, but is provided in order to optionally document the intention
+ not to exclude any other rows.
Beware that the ROWS options can produce unpredictable
results if the ORDER BY ordering does not order the rows
- uniquely. The RANGE options are designed to ensure that
+ uniquely. The RANGE and GROUPS options are designed to ensure that
rows that are peers in the ORDER BY ordering are treated
alike; all peer rows will be in the same frame.
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index a938a21334..162ee04228 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -1805,8 +1805,8 @@ FROM generate_series(1,10) AS s(i);
and the optional frame_clause
can be one of
-{ RANGE | ROWS } frame_start
-{ RANGE | ROWS } BETWEEN frame_start AND frame_end
+{ RANGE | ROWS | GROUPS } frame_start [ frame_exclusion_clause ]
+{ RANGE | ROWS | GROUPS } BETWEEN frame_start AND frame_end [ frame_exclusion_clause ]
where frame_start and frame_end can be
one of
@@ -1817,6 +1817,13 @@ CURRENT ROW
value FOLLOWING
UNBOUNDED FOLLOWING
+ where the optional frame_exclusion_clause can be one of
+
+EXCLUDE CURRENT ROW
+EXCLUDE GROUP
+EXCLUDE TIES
+EXCLUDE NO OTHERS
+
@@ -1857,8 +1864,8 @@ UNBOUNDED FOLLOWING
the set of rows constituting the window frame, which is a
subset of the current partition, for those window functions that act on
the frame instead of the whole partition. The frame can be specified in
- either RANGE or ROWS mode; in either case, it
- runs from the frame_start to the
+ either RANGE, ROWS or GROUPS mode;
+ in each case, it runs from the frame_start to the
frame_end. If frame_end is omitted,
it defaults to CURRENT ROW.
@@ -1871,7 +1878,7 @@ UNBOUNDED FOLLOWING
- In RANGE mode, a frame_start of
+ In RANGE or GROUPS mode, a frame_start of
CURRENT ROW means the frame starts with the current row's
first peer row (a row that ORDER BY considers
equivalent to the current row), while a frame_end of
@@ -1882,13 +1889,30 @@ UNBOUNDED FOLLOWING
The value PRECEDING and
- value FOLLOWING cases are currently only
- allowed in ROWS mode. They indicate that the frame starts
- or ends the specified number of rows before or after the current row.
- value must be an integer expression not
- containing any variables, aggregate functions, or window functions.
- The value must not be null or negative; but it can be zero, which
- just selects the current row.
+ value FOLLOWING cases, when used
+ in ROWS mode, indicate that the frame starts or ends the specified
+ number of rows before or after the current row. In ROWS mode,
+ value must be an integer expression not containing any variables,
+ aggregate functions, or window functions.
+ When used in RANGE mode, they indicate that the frame starts or ends when the value of
+ each row's ORDER BY column is within the start value and end value bounds. In both modes,
+ the value must not be null or negative; but it can be zero, which just selects the current row.
+
+
+
+ In GROUPS mode, value PRECEDING and
+ value FOLLOWING cases indicate that the frame starts or ends the specified
+ number of window framing groups before or after the current window framing group.
+ Two rows are in the same window framing group if they are peers, (i.e., their ORDER BY column values
+ match). This mode allows the selection of a frame by the number of changes to the ORDER BY columns.
+
+
+
+ For the frame_exclusion_clause, EXCLUDE CURRENT ROW
+ excludes the current row from the frame. EXCLUDE TIES excludes any peers of the current row from the
+ frame. EXCLUDE GROUP excludes both the current row and any peers of the current row from the frame.
+ EXCLUDE NO OTHERS does nothing, but is provided in order to optionally document the intention not to
+ exclude any other rows.
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 8e746f36d4..20d61f3780 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -498,7 +498,7 @@ T616 Null treatment option for LEAD and LAG functions NO
T617 FIRST_VALUE and LAST_VALUE function YES
T618 NTH_VALUE function NO function exists, but some options missing
T619 Nested window functions NO
-T620 WINDOW clause: GROUPS option NO
+T620 WINDOW clause: GROUPS option YES
T621 Enhanced numeric functions YES
T631 IN predicate with one list element YES
T641 Multiple column assignment NO only some syntax variants supported
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 0afb1c83d3..a2c9e0d8f1 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -37,6 +37,7 @@
#include "catalog/objectaccess.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "executor/nodeWindowAgg.h"
#include "miscadmin.h"
@@ -180,7 +181,9 @@ static void begin_partition(WindowAggState *winstate);
static void spool_tuples(WindowAggState *winstate, int64 pos);
static void release_partition(WindowAggState *winstate);
-static bool row_is_in_frame(WindowAggState *winstate, int64 pos,
+static bool row_is_in_group(WindowAggState *winstate, int64 currpos, int64 slotpos,
+ int64 offset, bool preceding, bool end);
+static int row_is_in_frame(WindowAggState *winstate, int64 pos,
TupleTableSlot *slot);
static void update_frameheadpos(WindowObject winobj, TupleTableSlot *slot);
static void update_frametailpos(WindowObject winobj, TupleTableSlot *slot);
@@ -683,9 +686,6 @@ eval_windowaggregates(WindowAggState *winstate)
temp_slot = winstate->temp_slot_1;
/*
- * Currently, we support only a subset of the SQL-standard window framing
- * rules.
- *
* If the frame start is UNBOUNDED_PRECEDING, the window frame consists of
* a contiguous group of rows extending forward from the start of the
* partition, and rows only enter the frame, never exit it, as the current
@@ -737,15 +737,17 @@ eval_windowaggregates(WindowAggState *winstate)
* the result values that were previously saved at the bottom of this
* function. Since we don't know the current frame's end yet, this is not
* possible to check for fully. But if the frame end mode is UNBOUNDED
- * FOLLOWING or CURRENT ROW, and the current row lies within the previous
- * row's frame, then the two frames' ends must coincide. Note that on the
- * first row aggregatedbase == aggregatedupto, meaning this test must
- * fail, so we don't need to check the "there was no previous row" case
- * explicitly here.
+ * FOLLOWING or CURRENT ROW, no exclusion clause is specified, we are not
+ * in GROUPS BETWEEN with values mode, and the current row lies within the
+ * previous row's frame, then the two frames' ends must coincide. Note that on
+ * the first row aggregatedbase == aggregatedupto, meaning this test must fail, so we
+ * don't need to check the "there was no previous row" case explicitly here.
*/
if (winstate->aggregatedbase == winstate->frameheadpos &&
(winstate->frameOptions & (FRAMEOPTION_END_UNBOUNDED_FOLLOWING |
FRAMEOPTION_END_CURRENT_ROW)) &&
+ !(winstate->frameOptions & FRAMEOPTION_EXCLUSION) &&
+ !(winstate->frameOptions & FRAMEOPTION_GROUPS_BETWEEN) &&
winstate->aggregatedbase <= winstate->currentpos &&
winstate->aggregatedupto > winstate->currentpos)
{
@@ -766,6 +768,9 @@ eval_windowaggregates(WindowAggState *winstate)
* - if we're processing the first row in the partition, or
* - if the frame's head moved and we cannot use an inverse
* transition function, or
+ * - we are in RANGE BETWEEN with values mode, or
+ * - we are in GROUPS BETWEEN with values mode, or
+ * - we are in EXCLUDE CURRENT ROW/EXCLUDE TIES mode, or
* - if the new frame doesn't overlap the old one
*
* Note that we don't strictly need to restart in the last case, but if
@@ -780,6 +785,9 @@ eval_windowaggregates(WindowAggState *winstate)
if (winstate->currentpos == 0 ||
(winstate->aggregatedbase != winstate->frameheadpos &&
!OidIsValid(peraggstate->invtransfn_oid)) ||
+ winstate->frameOptions & FRAMEOPTION_RANGE_BETWEEN ||
+ winstate->frameOptions & FRAMEOPTION_GROUPS_BETWEEN ||
+ winstate->frameOptions & FRAMEOPTION_EXCLUSION ||
winstate->aggregatedupto <= winstate->frameheadpos)
{
peraggstate->restart = true;
@@ -920,6 +928,8 @@ eval_windowaggregates(WindowAggState *winstate)
*/
for (;;)
{
+ int ret;
+
/* Fetch next row if we didn't already */
if (TupIsNull(agg_row_slot))
{
@@ -928,9 +938,15 @@ eval_windowaggregates(WindowAggState *winstate)
break; /* must be end of partition */
}
- /* Exit loop (for now) if not in frame */
- if (!row_is_in_frame(winstate, winstate->aggregatedupto, agg_row_slot))
+ /*
+ * Exit loop if no more rows can be in frame. Skip aggregation if current
+ * row is not in frame.
+ */
+ ret = row_is_in_frame(winstate, winstate->aggregatedupto, agg_row_slot);
+ if (ret == -1)
break;
+ else if (ret == 0)
+ goto next_tuple;
/* Set tuple context for evaluation of aggregate arguments */
winstate->tmpcontext->ecxt_outertuple = agg_row_slot;
@@ -951,6 +967,7 @@ eval_windowaggregates(WindowAggState *winstate)
peraggstate);
}
+ next_tuple:
/* Reset per-input-tuple context after each tuple */
ResetExprContext(winstate->tmpcontext);
@@ -1113,7 +1130,10 @@ begin_partition(WindowAggState *winstate)
int readptr_flags = 0;
/* If the frame head is potentially movable ... */
- if (!(winstate->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING))
+ if (!(winstate->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) ||
+ winstate->frameOptions & (FRAMEOPTION_RANGE_BETWEEN) ||
+ winstate->frameOptions & (FRAMEOPTION_GROUPS_BETWEEN) ||
+ winstate->frameOptions & (FRAMEOPTION_EXCLUSION))
{
/* ... create a mark pointer to track the frame head */
agg_winobj->markptr = tuplestore_alloc_read_pointer(winstate->buffer, 0);
@@ -1155,6 +1175,94 @@ begin_partition(WindowAggState *winstate)
*/
tuplestore_puttupleslot(winstate->buffer, winstate->first_part_slot);
winstate->spooled_rows++;
+
+ /*
+ * In RANGE BETWEEN and GROUPS BETWEEN with values mode, pre-compute the lengths
+ * of each window group by loading the partition and checking if each row is a peer
+ * of the succeeding row. The number of groups is stored in winobj->winGroupsCount
+ * and the number of rows in each group is stored in winobj->winGroupLen.
+ *
+ * In GROUPS BETWEEN mode, we also store copies of these pointers and move them forward
+ * using the offset in order to keep track of the frame head/tail.
+ */
+ if (winstate->frameOptions & (FRAMEOPTION_GROUPS_BETWEEN | FRAMEOPTION_RANGE_BETWEEN))
+ {
+ int64 i,
+ partitionSize;
+ int64 *lenptr;
+ int64 groupLenSize = 16;
+ bool peers;
+ WindowObject currWinobj;
+
+ if (winstate->numaggs > 0)
+ currWinobj = winstate->agg_winobj;
+ else
+ currWinobj = (&(winstate->perfunc[0]))->winobj;
+ winstate->winGroupsCount = 1;
+ winstate->winGroupLen = palloc0(sizeof(int64) * groupLenSize);
+ lenptr = winstate->winGroupLen;
+ *lenptr = 1;
+ partitionSize = WinGetPartitionRowCount(currWinobj);
+
+ for (i = 0; i < partitionSize - 1; i++)
+ {
+ peers = WinRowsArePeers(currWinobj, i, i+1);
+ if (peers)
+ (*lenptr)++;
+ else
+ {
+ winstate->winGroupsCount++;
+ if (winstate->winGroupsCount > groupLenSize)
+ {
+ int64 prevSize = groupLenSize;
+
+ groupLenSize *= 2;
+ winstate->winGroupLen = repalloc(winstate->winGroupLen,
+ sizeof(int64) * groupLenSize);
+ lenptr = winstate->winGroupLen;
+ lenptr += prevSize;
+ }
+ else
+ lenptr++;
+
+ *lenptr = 1;
+ }
+ }
+
+ /* Initialize frame head/tail pointers and lengths */
+ winstate->frameheadWinGroup = winstate->winGroupLen;
+ winstate->currWinGroup = winstate->winGroupLen;
+ winstate->frametailWinGroup = winstate->winGroupLen;
+ winstate->frametailpos = 0;
+ winstate->prevRowTotal = 0;
+ winstate->frameheadGroupsCount = 0;
+ winstate->frametailGroupsCount = 0;
+
+ /*
+ * In value following mode, move frame head/tail to offset position
+ */
+ if (winstate->frameOptions & FRAMEOPTION_GROUPS_BETWEEN &&
+ (winstate->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) == 0)
+ {
+ int64 offset = DatumGetInt64(winstate->startOffsetValue);
+ for (i = offset; i > 0; i--)
+ {
+ winstate->frameheadpos += *(winstate->frameheadWinGroup);
+ winstate->frameheadWinGroup++;
+ }
+ }
+ if (winstate->frameOptions & FRAMEOPTION_GROUPS_BETWEEN &&
+ (winstate->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) == 0)
+ {
+ int64 offset = DatumGetInt64(winstate->endOffsetValue);
+ for (i = 0; i < offset; i++)
+ {
+ winstate->frametailpos += *(winstate->frametailWinGroup);
+ winstate->frametailWinGroup++;
+ winstate->frametailGroupsCount++;
+ }
+ }
+ }
}
/*
@@ -1265,6 +1373,71 @@ release_partition(WindowAggState *winstate)
tuplestore_end(winstate->buffer);
winstate->buffer = NULL;
winstate->partition_spooled = false;
+
+ if (winstate->frameOptions & (FRAMEOPTION_GROUPS_BETWEEN | FRAMEOPTION_RANGE_BETWEEN) &&
+ winstate->winGroupsCount > 0)
+ {
+ pfree(winstate->winGroupLen);
+ winstate->winGroupsCount = 0;
+ }
+}
+
+/*
+ * row_is_in_group
+ * Determine whether a row is in range when in GROUPS BETWEEN with values
+ * mode.
+ *
+ * Compares the current position to the slot position and checks if they are
+ * within the specified window group offset.
+ */
+static bool row_is_in_group(WindowAggState *winstate, int64 currpos, int64 slotpos,
+ int64 offset, bool preceding, bool end)
+{
+ int64 i,
+ len = 0,
+ currGroup = 0,
+ slotGroup = 0;
+ int64 *lenptr = winstate->winGroupLen;
+
+ if (preceding)
+ offset = -offset;
+ /*
+ * Calculate the currpos window group, then the slotpos window group. If
+ * the slotpos group is outside of the offset bounds, return false.
+ */
+ for (i = 0; i < winstate->winGroupsCount; i++, lenptr++)
+ {
+ len += *lenptr;
+ if (len > currpos)
+ {
+ currGroup = i;
+ break;
+ }
+ }
+ lenptr = winstate->winGroupLen;
+ len = 0;
+ for (i = 0; i < winstate->winGroupsCount; i++, lenptr++)
+ {
+ len += *lenptr;
+ if (len > slotpos)
+ {
+ slotGroup = i;
+ break;
+ }
+ }
+
+ currGroup += offset;
+ if (end)
+ {
+ if (slotGroup > currGroup)
+ return false;
+ }
+ else
+ {
+ if (slotGroup < currGroup)
+ return false;
+ }
+ return true;
}
/*
@@ -1275,12 +1448,16 @@ release_partition(WindowAggState *winstate)
* The caller must have already determined that the row is in the partition
* and fetched it into a slot. This function just encapsulates the framing
* rules.
+ *
+ * Returns:
+ * -1, if the row is out of frame and no succeeding rows can be in frame
+ * 0, if the row is out of frame but succeeding rows might be in frame
+ * 1, if the row is in frame
*/
-static bool
+static int
row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
{
int frameOptions = winstate->frameOptions;
-
Assert(pos >= 0); /* else caller error */
/* First, check frame starting conditions */
@@ -1290,35 +1467,61 @@ row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
{
/* rows before current row are out of frame */
if (pos < winstate->currentpos)
- return false;
+ return -1;
}
- else if (frameOptions & FRAMEOPTION_RANGE)
+ else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
{
/* preceding row that is not peer is out of frame */
if (pos < winstate->currentpos &&
!are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
- return false;
+ return -1;
}
else
Assert(false);
}
else if (frameOptions & FRAMEOPTION_START_VALUE)
{
+ int64 offset = DatumGetInt64(winstate->startOffsetValue);
+
if (frameOptions & FRAMEOPTION_ROWS)
{
- int64 offset = DatumGetInt64(winstate->startOffsetValue);
-
/* rows before current row + offset are out of frame */
if (frameOptions & FRAMEOPTION_START_VALUE_PRECEDING)
offset = -offset;
-
if (pos < winstate->currentpos + offset)
- return false;
+ return 0;
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
- /* parser should have rejected this */
- elog(ERROR, "window frame with value offset is not implemented");
+ bool isnull,
+ preceding,
+ inrange;
+ Datum slotval,
+ currval;
+
+ slotval = slot_getattr(slot, 1, &isnull);
+ if (isnull)
+ return 0;
+ currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, 1, &isnull);
+ if (isnull)
+ return 0;
+ preceding = (frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) != 0 ? true : false;
+ inrange = DatumGetBool(DirectFunctionCall5(winstate->startRangeFunc,
+ currval,
+ slotval,
+ winstate->startOffsetValue,
+ BoolGetDatum(preceding),
+ BoolGetDatum(false)));
+ if (!inrange)
+ return 0;
+ }
+ else if (frameOptions & FRAMEOPTION_GROUPS)
+ {
+ bool preceding;
+
+ preceding = (frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) != 0 ? true : false;
+ if (!row_is_in_group(winstate, winstate->currentpos, pos, offset, preceding, false))
+ return 0;
}
else
Assert(false);
@@ -1331,42 +1534,84 @@ row_is_in_frame(WindowAggState *winstate, int64 pos, TupleTableSlot *slot)
{
/* rows after current row are out of frame */
if (pos > winstate->currentpos)
- return false;
+ return -1;
}
- else if (frameOptions & FRAMEOPTION_RANGE)
+ else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
{
/* following row that is not peer is out of frame */
if (pos > winstate->currentpos &&
!are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
- return false;
+ return -1;
}
else
Assert(false);
}
else if (frameOptions & FRAMEOPTION_END_VALUE)
{
+ int64 offset = DatumGetInt64(winstate->endOffsetValue);
if (frameOptions & FRAMEOPTION_ROWS)
{
- int64 offset = DatumGetInt64(winstate->endOffsetValue);
-
/* rows after current row + offset are out of frame */
if (frameOptions & FRAMEOPTION_END_VALUE_PRECEDING)
offset = -offset;
-
if (pos > winstate->currentpos + offset)
- return false;
+ return -1;
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
- /* parser should have rejected this */
- elog(ERROR, "window frame with value offset is not implemented");
+ bool isnull,
+ preceding,
+ inrange;
+ Datum slotval,
+ currval;
+
+ slotval = slot_getattr(slot, 1, &isnull);
+ if (isnull)
+ return -1;
+ currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, 1, &isnull);
+ if (isnull)
+ return -1;
+ preceding = (frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) != 0 ? true : false;
+ inrange = DatumGetBool(DirectFunctionCall5(winstate->endRangeFunc,
+ currval,
+ slotval,
+ winstate->endOffsetValue,
+ BoolGetDatum(preceding),
+ BoolGetDatum(true)));
+ if (!inrange)
+ return -1;
+ }
+ else if (frameOptions & FRAMEOPTION_GROUPS)
+ {
+ bool preceding;
+
+ preceding = (frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) != 0 ? true : false;
+ if (!row_is_in_group(winstate, winstate->currentpos, pos, offset, preceding, true))
+ return -1;
}
else
Assert(false);
}
+ /* Check exclusion clause */
+ if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT)
+ {
+ if (pos == winstate->currentpos)
+ return 0;
+ } else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
+ {
+ if (are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
+ return 0;
+ }
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
+ {
+ if ((pos != winstate->currentpos) &&
+ are_peers(winstate, slot, winstate->ss.ss_ScanTupleSlot))
+ return 0;
+ }
+
/* If we get here, it's in frame */
- return true;
+ return 1;
}
/*
@@ -1402,7 +1647,7 @@ update_frameheadpos(WindowObject winobj, TupleTableSlot *slot)
winstate->frameheadpos = winstate->currentpos;
winstate->framehead_valid = true;
}
- else if (frameOptions & FRAMEOPTION_RANGE)
+ else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
{
int64 fhprev;
@@ -1441,11 +1686,11 @@ update_frameheadpos(WindowObject winobj, TupleTableSlot *slot)
}
else if (frameOptions & FRAMEOPTION_START_VALUE)
{
+ int64 offset = DatumGetInt64(winstate->startOffsetValue);
+
if (frameOptions & FRAMEOPTION_ROWS)
{
/* In ROWS mode, bound is physically n before/after current */
- int64 offset = DatumGetInt64(winstate->startOffsetValue);
-
if (frameOptions & FRAMEOPTION_START_VALUE_PRECEDING)
offset = -offset;
@@ -1464,8 +1709,43 @@ update_frameheadpos(WindowObject winobj, TupleTableSlot *slot)
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
- /* parser should have rejected this */
- elog(ERROR, "window frame with value offset is not implemented");
+ bool isnull,
+ inrange;
+ Datum slotval,
+ currval;
+ window_gettupleslot(winobj, winstate->frameheadpos, slot);
+ while (winstate->frameheadGroupsCount < winstate->winGroupsCount - 1)
+ {
+ slotval = slot_getattr(slot, 1, &isnull);
+ if (isnull)
+ break;
+ currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, 1, &isnull);
+ if (isnull)
+ break;
+ inrange = DatumGetBool(DirectFunctionCall5(winstate->startRangeFunc,
+ currval,
+ slotval,
+ winstate->startOffsetValue,
+ BoolGetDatum((frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) ? true : false),
+ BoolGetDatum(false)));
+ if (inrange)
+ break;
+ winstate->frameheadpos += *(winstate->frameheadWinGroup);
+ winstate->frameheadWinGroup++;
+ winstate->frameheadGroupsCount++;
+ window_gettupleslot(winobj, winstate->frameheadpos, slot);
+ }
+ winstate->framehead_valid = true;
+ }
+ else if (frameOptions & FRAMEOPTION_GROUPS)
+ {
+ if (!row_is_in_group(winstate, winstate->currentpos, winstate->frameheadpos, offset,
+ (frameOptions & FRAMEOPTION_START_VALUE_PRECEDING) ? true : false, false))
+ {
+ winstate->frameheadpos += *(winstate->frameheadWinGroup);
+ winstate->frameheadWinGroup++;
+ }
+ winstate->framehead_valid = true;
}
else
Assert(false);
@@ -1508,7 +1788,7 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot)
winstate->frametailpos = winstate->currentpos;
winstate->frametail_valid = true;
}
- else if (frameOptions & FRAMEOPTION_RANGE)
+ else if (frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS))
{
int64 ftnext;
@@ -1545,11 +1825,11 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot)
}
else if (frameOptions & FRAMEOPTION_END_VALUE)
{
+ int64 offset = DatumGetInt64(winstate->endOffsetValue);
+
if (frameOptions & FRAMEOPTION_ROWS)
{
/* In ROWS mode, bound is physically n before/after current */
- int64 offset = DatumGetInt64(winstate->endOffsetValue);
-
if (frameOptions & FRAMEOPTION_END_VALUE_PRECEDING)
offset = -offset;
@@ -1568,8 +1848,60 @@ update_frametailpos(WindowObject winobj, TupleTableSlot *slot)
}
else if (frameOptions & FRAMEOPTION_RANGE)
{
- /* parser should have rejected this */
- elog(ERROR, "window frame with value offset is not implemented");
+ bool isnull,
+ inrange;
+ Datum slotval,
+ currval;
+
+ if (winstate->frametailpos < 0)
+ winstate->frametailpos = 0;
+ if (winstate->frametailpos > 0)
+ winstate->frametailpos -= *(winstate->frametailWinGroup) - 1;
+ while (winstate->frametailGroupsCount < winstate->winGroupsCount - 1)
+ {
+ window_gettupleslot(winobj, winstate->frametailpos + *(winstate->frametailWinGroup), slot);
+ slotval = slot_getattr(slot, 1, &isnull);
+ if (isnull)
+ break;
+ currval = slot_getattr(winstate->ss.ss_ScanTupleSlot, 1, &isnull);
+ if (isnull)
+ break;
+ inrange = DatumGetBool(DirectFunctionCall5(winstate->endRangeFunc,
+ currval,
+ slotval,
+ winstate->endOffsetValue,
+ BoolGetDatum((frameOptions & FRAMEOPTION_END_VALUE_PRECEDING) ? true : false),
+ BoolGetDatum(true)));
+ if (!inrange)
+ break;
+ winstate->frametailpos += *(winstate->frametailWinGroup);
+ winstate->frametailWinGroup++;
+ winstate->frametailGroupsCount++;
+ window_gettupleslot(winobj, winstate->frametailpos, slot);
+ }
+
+ winstate->frametailpos += *(winstate->frametailWinGroup) - 1;
+ winstate->frametail_valid = true;
+ }
+ else if (frameOptions & FRAMEOPTION_GROUPS)
+ {
+ if (winstate->frametailpos < 0)
+ winstate->frametailpos = 0;
+
+ if (winstate->frametailpos > 0)
+ winstate->frametailpos -= *(winstate->frametailWinGroup) - 1;
+ if (winstate->prevRowTotal + *(winstate->currWinGroup) <= winstate->currentpos &&
+ winstate->frametailGroupsCount < winstate->winGroupsCount - 1)
+ {
+ winstate->frametailpos += *(winstate->frametailWinGroup);
+ winstate->frametailWinGroup++;
+ winstate->frametailGroupsCount++;
+ winstate->prevRowTotal += *(winstate->currWinGroup);
+ winstate->currWinGroup++;
+ }
+
+ winstate->frametailpos += *(winstate->frametailWinGroup) - 1;
+ winstate->frametail_valid = true;
}
else
Assert(false);
@@ -1627,7 +1959,7 @@ ExecWindowAgg(PlanState *pstate)
get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
&len, &byval);
winstate->startOffsetValue = datumCopy(value, byval, len);
- if (frameOptions & FRAMEOPTION_ROWS)
+ if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
@@ -1652,7 +1984,7 @@ ExecWindowAgg(PlanState *pstate)
get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
&len, &byval);
winstate->endOffsetValue = datumCopy(value, byval, len);
- if (frameOptions & FRAMEOPTION_ROWS)
+ if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
@@ -1670,6 +2002,7 @@ ExecWindowAgg(PlanState *pstate)
{
/* Initialize for first partition and set current row = 0 */
begin_partition(winstate);
+
/* If there are no input rows, we'll detect that and exit below */
}
else
@@ -1782,6 +2115,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
numaggs,
aggno;
ListCell *l;
+ FmgrInfo finfo;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
@@ -1990,6 +2324,17 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
winstate->agg_winobj = agg_winobj;
}
+ if (node->startRangeOid != 0)
+ {
+ fmgr_info(node->startRangeOid, &finfo);
+ winstate->startRangeFunc = finfo.fn_addr;
+ }
+ if (node->endRangeOid != 0)
+ {
+ fmgr_info(node->endRangeOid, &finfo);
+ winstate->endRangeFunc = finfo.fn_addr;
+ }
+
/* copy frame options to state node for easy access */
winstate->frameOptions = node->frameOptions;
@@ -2736,7 +3081,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
WindowAggState *winstate;
ExprContext *econtext;
TupleTableSlot *slot;
- bool gottuple;
+ int gottuple;
int64 abs_pos;
Assert(WindowObjectIsValid(winobj));
@@ -2767,7 +3112,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
if (gottuple)
gottuple = row_is_in_frame(winstate, abs_pos, slot);
- if (!gottuple)
+ if (gottuple != 1)
{
if (isout)
*isout = true;
@@ -2794,7 +3139,7 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
* was its mark. Perhaps use a separate mark for frame head
* probes?
*/
- if ((frameOptions & FRAMEOPTION_RANGE) &&
+ if ((frameOptions & (FRAMEOPTION_RANGE | FRAMEOPTION_GROUPS)) &&
!(frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING))
{
update_frameheadpos(winobj, winstate->temp_slot_2);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fd3001c493..977a39a54e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1009,6 +1009,8 @@ _copyWindowAgg(const WindowAgg *from)
COPY_POINTER_FIELD(ordColIdx, from->ordNumCols * sizeof(AttrNumber));
COPY_POINTER_FIELD(ordOperators, from->ordNumCols * sizeof(Oid));
}
+ COPY_SCALAR_FIELD(startRangeOid);
+ COPY_SCALAR_FIELD(endRangeOid);
COPY_SCALAR_FIELD(frameOptions);
COPY_NODE_FIELD(startOffset);
COPY_NODE_FIELD(endOffset);
@@ -2409,6 +2411,8 @@ _copyWindowClause(const WindowClause *from)
COPY_STRING_FIELD(refname);
COPY_NODE_FIELD(partitionClause);
COPY_NODE_FIELD(orderClause);
+ COPY_SCALAR_FIELD(startRangeOid);
+ COPY_SCALAR_FIELD(endRangeOid);
COPY_SCALAR_FIELD(frameOptions);
COPY_NODE_FIELD(startOffset);
COPY_NODE_FIELD(endOffset);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 7d2aa1a2d3..ef24634320 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2731,6 +2731,8 @@ _equalWindowClause(const WindowClause *a, const WindowClause *b)
COMPARE_STRING_FIELD(refname);
COMPARE_NODE_FIELD(partitionClause);
COMPARE_NODE_FIELD(orderClause);
+ COMPARE_SCALAR_FIELD(startRangeOid);
+ COMPARE_SCALAR_FIELD(endRangeOid);
COMPARE_SCALAR_FIELD(frameOptions);
COMPARE_NODE_FIELD(startOffset);
COMPARE_NODE_FIELD(endOffset);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e0f4befd9f..b1d03a1c9c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -837,6 +837,8 @@ _outWindowAgg(StringInfo str, const WindowAgg *node)
for (i = 0; i < node->ordNumCols; i++)
appendStringInfo(str, " %u", node->ordOperators[i]);
+ WRITE_OID_FIELD(startRangeOid);
+ WRITE_OID_FIELD(endRangeOid);
WRITE_INT_FIELD(frameOptions);
WRITE_NODE_FIELD(startOffset);
WRITE_NODE_FIELD(endOffset);
@@ -2981,6 +2983,8 @@ _outWindowClause(StringInfo str, const WindowClause *node)
WRITE_STRING_FIELD(refname);
WRITE_NODE_FIELD(partitionClause);
WRITE_NODE_FIELD(orderClause);
+ WRITE_OID_FIELD(startRangeOid);
+ WRITE_OID_FIELD(endRangeOid);
WRITE_INT_FIELD(frameOptions);
WRITE_NODE_FIELD(startOffset);
WRITE_NODE_FIELD(endOffset);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 22d8b9d0d5..3487765376 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -366,6 +366,8 @@ _readWindowClause(void)
READ_STRING_FIELD(refname);
READ_NODE_FIELD(partitionClause);
READ_NODE_FIELD(orderClause);
+ READ_OID_FIELD(startRangeOid);
+ READ_OID_FIELD(endRangeOid);
READ_INT_FIELD(frameOptions);
READ_NODE_FIELD(startOffset);
READ_NODE_FIELD(endOffset);
@@ -2136,6 +2138,8 @@ _readWindowAgg(void)
READ_INT_FIELD(ordNumCols);
READ_ATTRNUMBER_ARRAY(ordColIdx, local_node->ordNumCols);
READ_OID_ARRAY(ordOperators, local_node->ordNumCols);
+ READ_OID_FIELD(startRangeOid);
+ READ_OID_FIELD(endRangeOid);
READ_INT_FIELD(frameOptions);
READ_NODE_FIELD(startOffset);
READ_NODE_FIELD(endOffset);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 86e7e74793..025ecb71e8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -260,8 +260,8 @@ static Material *make_material(Plan *lefttree);
static WindowAgg *make_windowagg(List *tlist, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
- int frameOptions, Node *startOffset, Node *endOffset,
- Plan *lefttree);
+ Oid startRangeOid, Oid endRangeOid, int frameOptions,
+ Node *startOffset, Node *endOffset, Plan *lefttree);
static Group *make_group(List *tlist, List *qual, int numGroupCols,
AttrNumber *grpColIdx, Oid *grpOperators,
Plan *lefttree);
@@ -2117,6 +2117,8 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
ordNumCols,
ordColIdx,
ordOperators,
+ wc->startRangeOid,
+ wc->endRangeOid,
wc->frameOptions,
wc->startOffset,
wc->endOffset,
@@ -6076,8 +6078,8 @@ static WindowAgg *
make_windowagg(List *tlist, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
- int frameOptions, Node *startOffset, Node *endOffset,
- Plan *lefttree)
+ Oid startRangeOid, Oid endRangeOid, int frameOptions,
+ Node *startOffset, Node *endOffset, Plan *lefttree)
{
WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan;
@@ -6089,6 +6091,8 @@ make_windowagg(List *tlist, Index winref,
node->ordNumCols = ordNumCols;
node->ordColIdx = ordColIdx;
node->ordOperators = ordOperators;
+ node->startRangeOid = startRangeOid;
+ node->endRangeOid = endRangeOid;
node->frameOptions = frameOptions;
node->startOffset = startOffset;
node->endOffset = endOffset;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5329432f25..7b405cb05c 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -569,7 +569,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type filter_clause
%type window_clause window_definition_list opt_partition_clause
%type window_definition over_clause window_specification
- opt_frame_clause frame_extent frame_bound
+ opt_frame_clause frame_extent frame_bound opt_window_exclusion_clause
%type opt_existing_window_name
%type opt_if_not_exists
%type generated_when override_kind
@@ -632,7 +632,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
- GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
+ GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING GROUPS
HANDLER HAVING HEADER_P HOLD HOUR_P
@@ -656,7 +656,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
- ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
+ ORDER ORDINALITY OTHERS OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
@@ -676,7 +676,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
SUBSCRIPTION SUBSTRING SYMMETRIC SYSID SYSTEM_P
TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
- TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P
+ TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P
TRUNCATE TRUSTED TYPE_P TYPES_P
UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED
@@ -725,8 +725,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* various unreserved keywords as needed to resolve ambiguities (this can't
* have any bad effects since obviously the keywords will still behave the
* same as if they weren't keywords). We need to do this for PARTITION,
- * RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS
- * so that they can follow a_expr without creating postfix-operator problems;
+ * RANGE, ROWS, or GROUPS to support opt_existing_window_name; and for RANGE, ROWS
+ * or GROUPS so that they can follow a_expr without creating postfix-operator problems;
* for GENERATED so that it can follow b_expr;
* and for NULL so that it can follow b_expr in ColQualList without creating
* postfix-operator problems.
@@ -746,7 +746,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* blame any funny behavior of UNBOUNDED on the SQL standard, though.
*/
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
-%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
+%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS GROUPS PRECEDING FOLLOWING CUBE ROLLUP
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
@@ -14003,7 +14003,7 @@ window_specification: '(' opt_existing_window_name opt_partition_clause
;
/*
- * If we see PARTITION, RANGE, or ROWS as the first token after the '('
+ * If we see PARTITION, RANGE, ROWS or GROUPS as the first token after the '('
* of a window_specification, we want the assumption to be that there is
* no existing_window_name; but those keywords are unreserved and so could
* be ColIds. We fix this by making them have the same precedence as IDENT
@@ -14023,33 +14023,43 @@ opt_partition_clause: PARTITION BY expr_list { $$ = $3; }
/*
* For frame clauses, we return a WindowDef, but only some fields are used:
* frameOptions, startOffset, and endOffset.
- *
- * This is only a subset of the full SQL:2008 frame_clause grammar.
- * We don't support yet.
*/
opt_frame_clause:
- RANGE frame_extent
+ RANGE frame_extent opt_window_exclusion_clause
{
WindowDef *n = $2;
+ WindowDef *n2 = $3;
n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_RANGE;
- if (n->frameOptions & (FRAMEOPTION_START_VALUE_PRECEDING |
- FRAMEOPTION_END_VALUE_PRECEDING))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("RANGE PRECEDING is only supported with UNBOUNDED"),
- parser_errposition(@1)));
- if (n->frameOptions & (FRAMEOPTION_START_VALUE_FOLLOWING |
- FRAMEOPTION_END_VALUE_FOLLOWING))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("RANGE FOLLOWING is only supported with UNBOUNDED"),
- parser_errposition(@1)));
+ if ((n->frameOptions & (FRAMEOPTION_START_VALUE_PRECEDING |
+ FRAMEOPTION_END_VALUE_PRECEDING)) ||
+ (n->frameOptions & (FRAMEOPTION_START_VALUE_FOLLOWING |
+ FRAMEOPTION_END_VALUE_FOLLOWING)))
+ n->frameOptions |= FRAMEOPTION_RANGE_BETWEEN;
+ if (n2 != NULL)
+ n->frameOptions |= n2->frameOptions;
$$ = n;
}
- | ROWS frame_extent
+ | ROWS frame_extent opt_window_exclusion_clause
{
WindowDef *n = $2;
+ WindowDef *n2 = $3;
n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_ROWS;
+ if (n2 != NULL)
+ n->frameOptions |= n2->frameOptions;
+ $$ = n;
+ }
+ | GROUPS frame_extent opt_window_exclusion_clause
+ {
+ WindowDef *n = $2;
+ WindowDef *n2 = $3;
+ n->frameOptions |= FRAMEOPTION_NONDEFAULT | FRAMEOPTION_GROUPS;
+ if ((n->frameOptions & (FRAMEOPTION_START_VALUE_PRECEDING |
+ FRAMEOPTION_END_VALUE_PRECEDING)) ||
+ (n->frameOptions & (FRAMEOPTION_START_VALUE_FOLLOWING |
+ FRAMEOPTION_END_VALUE_FOLLOWING)))
+ n->frameOptions |= FRAMEOPTION_GROUPS_BETWEEN;
+ if (n2 != NULL)
+ n->frameOptions |= n2->frameOptions;
$$ = n;
}
| /*EMPTY*/
@@ -14166,6 +14176,34 @@ frame_bound:
}
;
+opt_window_exclusion_clause:
+ EXCLUDE CURRENT_P ROW
+ {
+ WindowDef *n = makeNode(WindowDef);
+ n->frameOptions = FRAMEOPTION_EXCLUDE_CURRENT;
+ $$ = n;
+ }
+ | EXCLUDE GROUP_P
+ {
+ WindowDef *n = makeNode(WindowDef);
+ n->frameOptions = FRAMEOPTION_EXCLUDE_GROUP;
+ $$ = n;
+ }
+ | EXCLUDE TIES
+ {
+ WindowDef *n = makeNode(WindowDef);
+ n->frameOptions = FRAMEOPTION_EXCLUDE_TIES;
+ $$ = n;
+ }
+ | EXCLUDE NO OTHERS
+ {
+ WindowDef *n = makeNode(WindowDef);
+ n->frameOptions = FRAMEOPTION_EXCLUDE_NO_OTHERS;
+ $$ = n;
+ }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
/*
* Supporting nonterminals for expressions.
@@ -15027,6 +15065,7 @@ unreserved_keyword:
| GENERATED
| GLOBAL
| GRANTED
+ | GROUPS
| HANDLER
| HEADER_P
| HOLD
@@ -15092,6 +15131,7 @@ unreserved_keyword:
| OPTION
| OPTIONS
| ORDINALITY
+ | OTHERS
| OVER
| OVERRIDING
| OWNED
@@ -15182,6 +15222,7 @@ unreserved_keyword:
| TEMPLATE
| TEMPORARY
| TEXT_P
+ | TIES
| TRANSACTION
| TRANSFORM
| TRIGGER
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 6a9f1b0217..747139489a 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -420,6 +420,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
err = _("grouping operations are not allowed in window ROWS");
break;
+ case EXPR_KIND_WINDOW_FRAME_GROUPS:
+ if (isAgg)
+ err = _("aggregate functions are not allowed in window GROUPS");
+ else
+ err = _("grouping operations are not allowed in window GROUPS");
+
+ break;
case EXPR_KIND_SELECT_TARGET:
/* okay */
break;
@@ -835,6 +842,7 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_WINDOW_ORDER:
case EXPR_KIND_WINDOW_FRAME_RANGE:
case EXPR_KIND_WINDOW_FRAME_ROWS:
+ case EXPR_KIND_WINDOW_FRAME_GROUPS:
err = _("window functions are not allowed in window definitions");
break;
case EXPR_KIND_SELECT_TARGET:
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 9fbcfd4fa6..d1f6ec4f2a 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -17,13 +17,17 @@
#include "miscadmin.h"
+#include "access/amvalidate.h"
#include "access/heapam.h"
+#include "access/htup_details.h"
#include "access/tsmapi.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/pg_am.h"
+#include "catalog/pg_amproc.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint_fn.h"
+#include "catalog/pg_opfamily.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
@@ -45,6 +49,7 @@
#include "rewrite/rewriteManip.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/rel.h"
@@ -96,6 +101,8 @@ static List *addTargetToGroupList(ParseState *pstate, TargetEntry *tle,
static WindowClause *findWindowClause(List *wclist, const char *name);
static Node *transformFrameOffset(ParseState *pstate, int frameOptions,
Node *clause);
+static void getRangeBetweenFuncOid(ParseState *pstate, WindowDef *windef,
+ SortBy *sortby, Node *offset, Oid sortOid, Oid *rangeOid);
/*
@@ -2608,6 +2615,29 @@ transformSortClause(ParseState *pstate,
return sortlist;
}
+static void
+getRangeBetweenFuncOid(ParseState *pstate, WindowDef *windef, SortBy *sortby,
+ Node *offset, Oid sortOid, Oid *rangeOid)
+{
+ CatCList *proclist;
+ HeapTuple proctup;
+ Form_pg_amproc procform;
+ Oid offsetOid = exprType(offset);
+
+ proclist = SearchSysCacheList4(AMPROCNUM, ObjectIdGetDatum(IN_RANGE_BTREE_FAM_OID),
+ sortOid, offsetOid,
+ sortby->sortby_dir == SORTBY_DESC ? 2 : 1);
+ if (proclist->n_members != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("no in_range function for given types"),
+ parser_errposition(pstate, windef->location)));
+ proctup = &proclist->members[0]->tuple;
+ procform = (Form_pg_amproc) GETSTRUCT(proctup);
+ *rangeOid = procform->amproc;
+ ReleaseSysCacheList(proclist);
+}
+
/*
* transformWindowDefinitions -
* transform window definitions (WindowDef to WindowClause)
@@ -2753,6 +2783,7 @@ transformWindowDefinitions(ParseState *pstate,
parser_errposition(pstate, windef->location)));
}
wc->frameOptions = windef->frameOptions;
+
/* Process frame offset expressions */
wc->startOffset = transformFrameOffset(pstate, wc->frameOptions,
windef->startOffset);
@@ -2760,9 +2791,35 @@ transformWindowDefinitions(ParseState *pstate,
windef->endOffset);
wc->winref = winref;
+ /* The RANGE BETWEEN clause requires exactly one ORDER BY column,
+ * of either an int or date/time type and start/end values to match by type.
+ */
+ if (wc->frameOptions & FRAMEOPTION_RANGE_BETWEEN)
+ {
+ SortBy *sortby;
+ TargetEntry *tle;
+ Oid sortOid;
+
+ if (list_length(orderClause) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE clause requires exactly one ORDER BY column"),
+ parser_errposition(pstate, windef->location)));
+ sortby = (SortBy *) lfirst(list_head(windef->orderClause));
+ tle = findTargetlistEntrySQL99(pstate, sortby->node,
+ targetlist, EXPR_KIND_WINDOW_ORDER);
+
+ /* Check that the sortOid and start/end offset Oids match */
+ sortOid = exprType((Node *) tle->expr);
+ if (wc->frameOptions & FRAMEOPTION_START_VALUE)
+ getRangeBetweenFuncOid(pstate, windef, sortby, wc->startOffset, sortOid, &wc->startRangeOid);
+
+ if (wc->frameOptions & FRAMEOPTION_END_VALUE)
+ getRangeBetweenFuncOid(pstate, windef, sortby, wc->endOffset, sortOid, &wc->endRangeOid);
+ }
+
result = lappend(result, wc);
}
-
return result;
}
@@ -3515,14 +3572,18 @@ transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause)
{
/* Transform the raw expression tree */
node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_RANGE);
+ constructName = "RANGE";
+ }
+ else if (frameOptions & FRAMEOPTION_GROUPS)
+ {
+ /* Transform the raw expression tree */
+ node = transformExpr(pstate, clause, EXPR_KIND_WINDOW_FRAME_GROUPS);
/*
- * this needs a lot of thought to decide how to support in the context
- * of Postgres' extensible datatype framework
+ * Like LIMIT clause, simply coerce to int8
*/
- constructName = "RANGE";
- /* error was already thrown by gram.y, this is just a backstop */
- elog(ERROR, "window frame with value offset is not implemented");
+ constructName = "GROUPS";
+ node = coerce_to_specific_type(pstate, node, INT8OID, constructName);
}
else
{
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index b2f5e46e3b..d45926f27f 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1805,6 +1805,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_WINDOW_ORDER:
case EXPR_KIND_WINDOW_FRAME_RANGE:
case EXPR_KIND_WINDOW_FRAME_ROWS:
+ case EXPR_KIND_WINDOW_FRAME_GROUPS:
case EXPR_KIND_SELECT_TARGET:
case EXPR_KIND_INSERT_TARGET:
case EXPR_KIND_UPDATE_SOURCE:
@@ -3428,6 +3429,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "window RANGE";
case EXPR_KIND_WINDOW_FRAME_ROWS:
return "window ROWS";
+ case EXPR_KIND_WINDOW_FRAME_GROUPS:
+ return "window GROUPS";
case EXPR_KIND_SELECT_TARGET:
return "SELECT";
case EXPR_KIND_INSERT_TARGET:
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index ffae0f3cf3..4a7bc77c0f 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2227,6 +2227,7 @@ check_srf_call_placement(ParseState *pstate, Node *last_srf, int location)
break;
case EXPR_KIND_WINDOW_FRAME_RANGE:
case EXPR_KIND_WINDOW_FRAME_ROWS:
+ case EXPR_KIND_WINDOW_FRAME_GROUPS:
err = _("set-returning functions are not allowed in window definitions");
break;
case EXPR_KIND_SELECT_TARGET:
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 747ef49789..3958ae5cd8 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -44,6 +44,9 @@
static int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result);
static int tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result);
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
+static bool in_range_internal(Datum curr, Datum slot, Datum offset, bool preceding, bool end,
+ PGFunction miIntervalFunc, PGFunction plIntervalFunc,
+ PGFunction gtComparatorFunc, PGFunction ltComparatorFunc, bool asc);
/* common code for timetypmodin and timetztypmodin */
@@ -2754,3 +2757,89 @@ timetz_izone(PG_FUNCTION_ARGS)
PG_RETURN_TIMETZADT_P(result);
}
+
+
+/*----------------------------------------------------------
+ * In RANGE procedures on dates.
+ *---------------------------------------------------------*/
+
+static bool in_range_internal(Datum curr, Datum slot, Datum offset, bool preceding, bool end,
+ PGFunction miIntervalFunc, PGFunction plIntervalFunc,
+ PGFunction gtComparatorFunc, PGFunction ltComparatorFunc, bool asc)
+{
+ Datum result;
+
+ if (preceding)
+ result = DirectFunctionCall2(asc ? miIntervalFunc : plIntervalFunc, curr, offset);
+ else
+ result = DirectFunctionCall2(asc ? plIntervalFunc : miIntervalFunc, curr, offset);
+
+ if (end)
+ {
+ if (DatumGetBool(DirectFunctionCall2(asc ? gtComparatorFunc : ltComparatorFunc, slot, result)))
+ return false;
+ }
+ else
+ {
+ if (DatumGetBool(DirectFunctionCall2(asc ? ltComparatorFunc : gtComparatorFunc, slot, result)))
+ return false;
+ }
+
+ return true;
+}
+
+Datum
+in_range_timetz_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timetz_mi_interval, timetz_pl_interval,
+ timetz_gt, timetz_lt, true));
+}
+
+Datum
+in_range_timetz_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timetz_mi_interval, timetz_pl_interval,
+ timetz_gt, timetz_lt, false));
+}
+
+Datum
+in_range_date_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0),
+ DirectFunctionCall1(date_timestamp, PG_GETARG_DATUM(1)),
+ PG_GETARG_DATUM(2), PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ date_mi_interval, date_pl_interval,
+ timestamp_gt, timestamp_lt, true));
+}
+
+Datum
+in_range_date_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0),
+ DirectFunctionCall1(date_timestamp, PG_GETARG_DATUM(1)),
+ PG_GETARG_DATUM(2), PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ date_mi_interval, date_pl_interval,
+ timestamp_gt, timestamp_lt, false));
+}
+
+Datum
+in_range_time_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(DirectFunctionCall1(time_interval, PG_GETARG_DATUM(0)),
+ DirectFunctionCall1(time_interval, PG_GETARG_DATUM(1)),
+ PG_GETARG_DATUM(2), PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ interval_mi, interval_pl, interval_gt, interval_lt, true));
+}
+
+Datum
+in_range_time_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(DirectFunctionCall1(time_interval, PG_GETARG_DATUM(0)),
+ DirectFunctionCall1(time_interval, PG_GETARG_DATUM(1)),
+ PG_GETARG_DATUM(2), PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ interval_mi, interval_pl, interval_gt, interval_lt, false));
+}
diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c
index 7352908365..a9d20dd752 100644
--- a/src/backend/utils/adt/int.c
+++ b/src/backend/utils/adt/int.c
@@ -307,8 +307,261 @@ int4send(PG_FUNCTION_ARGS)
}
+/*----------------------------------------------------------
+ * In RANGE procedures on 16-bit and 32-bit integers.
+ *---------------------------------------------------------*/
+
+Datum
+in_range_int4_int4_asc(PG_FUNCTION_ARGS)
+{
+ int32 curr = PG_GETARG_INT32(0);
+ int32 slot = PG_GETARG_INT32(1);
+ int32 offset = PG_GETARG_INT32(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int32 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %d", offset)));
+ if (preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s32_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int4_int4_desc(PG_FUNCTION_ARGS)
+{
+ int32 curr = PG_GETARG_INT32(0);
+ int32 slot = PG_GETARG_INT32(1);
+ int32 offset = PG_GETARG_INT32(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int32 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %d", offset)));
+ if (!preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s32_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int4_int8_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset32;
+ int64 offset64 = PG_GETARG_INT64(2);
+
+ offset32 = DirectFunctionCall1(int84, offset64);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int4_int4_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset32,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int4_int8_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset32;
+ int32 offset64 = PG_GETARG_INT64(2);
+
+ offset32 = DirectFunctionCall1(int84, offset64);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int4_int4_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset32,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int4_int2_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset32;
+ int16 offset16 = PG_GETARG_INT16(2);
+
+ offset32 = DirectFunctionCall1(i2toi4, offset16);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int4_int4_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset32,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int4_int2_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset32;
+ int16 offset16 = PG_GETARG_INT16(2);
+
+ offset32 = DirectFunctionCall1(i2toi4, offset16);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int4_int4_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset32,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int2_int2_asc(PG_FUNCTION_ARGS)
+{
+ int16 curr = PG_GETARG_INT16(0);
+ int16 slot = PG_GETARG_INT16(1);
+ int16 offset = PG_GETARG_INT16(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int16 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %d", offset)));
+ if (preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s16_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int2_int2_desc(PG_FUNCTION_ARGS)
+{
+ int16 curr = PG_GETARG_INT16(0);
+ int16 slot = PG_GETARG_INT16(1);
+ int16 offset = PG_GETARG_INT16(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int16 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %d", offset)));
+ if (!preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s16_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int2_int8_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset16;
+ int64 offset64 = PG_GETARG_INT64(2);
+
+ offset16 = DirectFunctionCall1(int82, offset64);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int2_int2_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset16,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int2_int8_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset16;
+ int32 offset64 = PG_GETARG_INT64(2);
+
+ offset16 = DirectFunctionCall1(int82, offset64);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int2_int2_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset16,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int2_int4_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset16;
+ int32 offset32 = PG_GETARG_INT32(2);
+
+ offset16 = DirectFunctionCall1(i4toi2, offset32);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int2_int2_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset16,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int2_int4_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset16;
+ int32 offset32 = PG_GETARG_INT32(2);
+
+ offset16 = DirectFunctionCall1(i4toi2, offset32);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int2_int2_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset16,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+
/*
- * ===================
+ * ===================in_range_
* CONVERSION ROUTINES
* ===================
*/
diff --git a/src/backend/utils/adt/int8.c b/src/backend/utils/adt/int8.c
index ae6a4683d4..a72047edb9 100644
--- a/src/backend/utils/adt/int8.c
+++ b/src/backend/utils/adt/int8.c
@@ -1101,6 +1101,136 @@ int8shr(PG_FUNCTION_ARGS)
PG_RETURN_INT64(arg1 >> arg2);
}
+
+/*----------------------------------------------------------
+ * In RANGE procedures on 64-bit integers.
+ *---------------------------------------------------------*/
+
+Datum
+in_range_int8_int8_asc(PG_FUNCTION_ARGS)
+{
+ int64 curr = PG_GETARG_INT64(0);
+ int64 slot = PG_GETARG_INT64(1);
+ int64 offset = PG_GETARG_INT64(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int64 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %zd", offset)));
+ if (preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s64_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int8_int8_desc(PG_FUNCTION_ARGS)
+{
+ int64 curr = PG_GETARG_INT64(0);
+ int64 slot = PG_GETARG_INT64(1);
+ int64 offset = PG_GETARG_INT64(2);
+ bool preceding = PG_GETARG_BOOL(3);
+ bool end = PG_GETARG_BOOL(4);
+ int64 result;
+
+ if (offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("RANGE offsets cannot be negative. invalid value %zd", offset)));
+ if (!preceding)
+ offset = -offset;
+
+ if (unlikely(pg_add_s64_overflow(curr, offset, &result)))
+ PG_RETURN_BOOL(true);
+
+ if (end)
+ {
+ if (slot < result)
+ PG_RETURN_BOOL(false);
+ }
+ else
+ {
+ if (slot > result)
+ PG_RETURN_BOOL(false);
+ }
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+in_range_int8_int4_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset64;
+ int32 offset32 = PG_GETARG_INT32(2);
+
+ offset64 = DirectFunctionCall1(int48, offset32);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int8_int8_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset64,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int8_int4_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset64;
+ int32 offset32 = PG_GETARG_INT32(2);
+
+ offset64 = DirectFunctionCall1(int48, offset32);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int8_int8_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset64,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int8_int2_asc(PG_FUNCTION_ARGS)
+{
+ Datum offset64;
+ int16 offset16 = PG_GETARG_INT16(2);
+
+ offset64 = DirectFunctionCall1(int28, offset16);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int8_int8_asc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset64,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+Datum
+in_range_int8_int2_desc(PG_FUNCTION_ARGS)
+{
+ Datum offset64;
+ int16 offset16 = PG_GETARG_INT16(2);
+
+ offset64 = DirectFunctionCall1(int28, offset16);
+ PG_RETURN_BOOL(DirectFunctionCall5(in_range_int8_int8_desc, PG_GETARG_DATUM(0),
+ PG_GETARG_DATUM(1),
+ offset64,
+ PG_GETARG_DATUM(3),
+ PG_GETARG_DATUM(4)));
+}
+
+
/*----------------------------------------------------------
* Conversion operators.
*---------------------------------------------------------*/
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c5f5a1ca3f..3d07a648b4 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5877,6 +5877,8 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
appendStringInfoString(buf, "RANGE ");
else if (wc->frameOptions & FRAMEOPTION_ROWS)
appendStringInfoString(buf, "ROWS ");
+ else if (wc->frameOptions & FRAMEOPTION_GROUPS)
+ appendStringInfoString(buf, "GROUPS ");
else
Assert(false);
if (wc->frameOptions & FRAMEOPTION_BETWEEN)
@@ -5917,6 +5919,14 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
else
Assert(false);
}
+ if (wc->frameOptions & FRAMEOPTION_EXCLUDE_CURRENT)
+ appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
+ else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
+ appendStringInfoString(buf, "EXCLUDE GROUP ");
+ else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_TIES)
+ appendStringInfoString(buf, "EXCLUDE TIES ");
+ else if (wc->frameOptions & FRAMEOPTION_EXCLUDE_NO_OTHERS)
+ appendStringInfoString(buf, "EXCLUDE NO OTHERS ");
/* we will now have a trailing space; remove it */
buf->len--;
}
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index e6a1eed191..effb305e71 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -74,6 +74,9 @@ static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
static TimestampTz timestamp2timestamptz(Timestamp timestamp);
static Timestamp timestamptz2timestamp(TimestampTz timestamp);
+static bool in_range_internal(Datum curr, Datum slot, Datum offset, bool preceding, bool end,
+ PGFunction miIntervalFunc, PGFunction plIntervalFunc,
+ PGFunction gtComparatorFunc, PGFunction ltComparatorFunc, bool asc);
/* common code for timestamptypmodin and timestamptztypmodin */
@@ -5148,6 +5151,90 @@ timestamptz_izone(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(result);
}
+/*----------------------------------------------------------
+ * In RANGE procedures on timestamps.
+ *---------------------------------------------------------*/
+
+static bool in_range_internal(Datum curr, Datum slot, Datum offset, bool preceding, bool end,
+ PGFunction miIntervalFunc, PGFunction plIntervalFunc,
+ PGFunction gtComparatorFunc, PGFunction ltComparatorFunc, bool asc)
+{
+ Datum result;
+
+ if (preceding)
+ result = DirectFunctionCall2(asc ? miIntervalFunc : plIntervalFunc, curr, offset);
+ else
+ result = DirectFunctionCall2(asc ? plIntervalFunc : miIntervalFunc, curr, offset);
+
+ if (end)
+ {
+ if (DatumGetBool(DirectFunctionCall2(asc ? gtComparatorFunc : ltComparatorFunc, slot, result)))
+ return false;
+ }
+ else
+ {
+ if (DatumGetBool(DirectFunctionCall2(asc ? ltComparatorFunc : gtComparatorFunc, slot, result)))
+ return false;
+ }
+
+ return true;
+}
+
+Datum
+in_range_timestamp_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timestamp_mi_interval, timestamp_pl_interval,
+ timestamp_gt, timestamp_lt, true));
+}
+
+Datum
+in_range_timestamp_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timestamp_mi_interval, timestamp_pl_interval,
+ timestamp_gt, timestamp_lt, false));
+}
+
+Datum
+in_range_timestamptz_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timestamptz_mi_interval, timestamptz_pl_interval,
+ timestamp_gt, timestamp_lt, true));
+}
+
+Datum
+in_range_timestamptz_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ timestamptz_mi_interval, timestamptz_pl_interval,
+ timestamp_gt, timestamp_lt, false));
+}
+
+Datum
+in_range_interval_interval_asc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ interval_mi, interval_pl,
+ interval_gt, interval_lt, true));
+}
+
+Datum
+in_range_interval_interval_desc(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(in_range_internal(PG_GETARG_DATUM(0), PG_GETARG_DATUM(1), PG_GETARG_DATUM(2),
+ PG_GETARG_BOOL(3), PG_GETARG_BOOL(4),
+ interval_mi, interval_pl,
+ interval_gt, interval_lt, false));
+}
+
+
/* generate_series_timestamp()
* Generate the set of timestamps from start to finish by step
*/
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 8a0a42ce71..3f61ed8497 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1542,7 +1542,7 @@ SearchCatCacheList(CatCache *cache,
if (cache->cc_tupdesc == NULL)
CatalogCacheInitializeCache(cache);
- Assert(nkeys > 0 && nkeys < cache->cc_nkeys);
+ Assert(nkeys > 0 && nkeys <= cache->cc_nkeys);
#ifdef CATCACHE_STATS
cache->cc_lsearches++;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f1765af4ba..9b98c04124 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201712251
+#define CATALOG_VERSION_NO 201801311
#endif
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index f545a0580d..751973de03 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -566,5 +566,36 @@ DATA(insert ( 4104 603 603 3 4107 ));
DATA(insert ( 4104 603 603 4 4108 ));
DATA(insert ( 4104 603 603 11 4067 ));
DATA(insert ( 4104 603 603 13 187 ));
+/* in RANGE functions */
+DATA(insert ( 5009 20 20 1 6122 ));
+DATA(insert ( 5009 20 20 2 6123 ));
+DATA(insert ( 5009 20 23 1 6124 ));
+DATA(insert ( 5009 20 23 2 6125 ));
+DATA(insert ( 5009 20 21 1 6126 ));
+DATA(insert ( 5009 20 21 2 6127 ));
+DATA(insert ( 5009 23 20 1 6128 ));
+DATA(insert ( 5009 23 20 2 6129 ));
+DATA(insert ( 5009 23 23 1 6130 ));
+DATA(insert ( 5009 23 23 2 6131 ));
+DATA(insert ( 5009 23 21 1 6132 ));
+DATA(insert ( 5009 23 21 2 6133 ));
+DATA(insert ( 5009 21 20 1 6134 ));
+DATA(insert ( 5009 21 20 2 6135 ));
+DATA(insert ( 5009 21 23 1 6136 ));
+DATA(insert ( 5009 21 23 2 6137 ));
+DATA(insert ( 5009 21 21 1 6138 ));
+DATA(insert ( 5009 21 21 2 6139 ));
+DATA(insert ( 5009 1114 1186 1 6140 ));
+DATA(insert ( 5009 1114 1186 2 6141 ));
+DATA(insert ( 5009 1184 1186 1 6142 ));
+DATA(insert ( 5009 1184 1186 2 6143 ));
+DATA(insert ( 5009 1186 1186 1 6144 ));
+DATA(insert ( 5009 1186 1186 2 6145 ));
+DATA(insert ( 5009 1266 1186 1 6146 ));
+DATA(insert ( 5009 1266 1186 2 6147 ));
+DATA(insert ( 5009 1082 1186 1 6148 ));
+DATA(insert ( 5009 1082 1186 2 6149 ));
+DATA(insert ( 5009 1083 1186 1 6150 ));
+DATA(insert ( 5009 1083 1186 2 6151 ));
#endif /* PG_AMPROC_H */
diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h
index b544474254..9086fea15f 100644
--- a/src/include/catalog/pg_opfamily.h
+++ b/src/include/catalog/pg_opfamily.h
@@ -187,5 +187,7 @@ DATA(insert OID = 4082 ( 3580 pg_lsn_minmax_ops PGNSP PGUID ));
DATA(insert OID = 4104 ( 3580 box_inclusion_ops PGNSP PGUID ));
DATA(insert OID = 5000 ( 4000 box_ops PGNSP PGUID ));
DATA(insert OID = 5008 ( 4000 poly_ops PGNSP PGUID ));
+DATA(insert OID = 5009 ( 403 in_range_ops PGNSP PGUID ));
+#define IN_RANGE_BTREE_FAM_OID 5009
#endif /* PG_OPFAMILY_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f01648c961..b2a172b8f1 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -2701,6 +2701,68 @@ DESCR("aggregate final function");
DATA(insert OID = 3545 ( string_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i s 2 0 17 "17 17" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into a bytea");
+/* In RANGE functions */
+DATA(insert OID = 6122 ( in_range_int8_int8_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int8_asc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int8 offset, ascending");
+DATA(insert OID = 6123 ( in_range_int8_int8_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int8_desc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int8 offset, descending");
+DATA(insert OID = 6124 ( in_range_int8_int4_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int4_asc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int4 offset, ascending");
+DATA(insert OID = 6125 ( in_range_int8_int4_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int4_desc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int4 offset, descending");
+DATA(insert OID = 6126 ( in_range_int8_int2_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int2_asc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int2 offset, ascending");
+DATA(insert OID = 6127 ( in_range_int8_int2_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "20 20 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int8_int2_desc _null_ _null_ _null_ ));
+DESCR("in range for int8 column, int2 offset, descending");
+DATA(insert OID = 6128 ( in_range_int4_int8_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int8_asc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int8 offset, ascending");
+DATA(insert OID = 6129 ( in_range_int4_int8_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int8_desc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int8 offset, descending");
+DATA(insert OID = 6130 ( in_range_int4_int4_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int4_asc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int4 offset, ascending");
+DATA(insert OID = 6131 ( in_range_int4_int4_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int4_desc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int4 offset, descending");
+DATA(insert OID = 6132 ( in_range_int4_int2_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int2_asc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int2 offset, ascending");
+DATA(insert OID = 6133 ( in_range_int4_int2_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "23 23 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int4_int2_desc _null_ _null_ _null_ ));
+DESCR("in range for int4 column, int2 offset, descending");
+DATA(insert OID = 6134 ( in_range_int2_int8_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int8_asc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int8 offset, ascending");
+DATA(insert OID = 6135 ( in_range_int2_int8_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 20 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int8_desc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int8 offset, descending");
+DATA(insert OID = 6136 ( in_range_int2_int4_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int4_asc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int4 offset, ascending");
+DATA(insert OID = 6137 ( in_range_int2_int4_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 23 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int4_desc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int4 offset, descending");
+DATA(insert OID = 6138 ( in_range_int2_int2_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int2_asc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int2 offset, ascending");
+DATA(insert OID = 6139 ( in_range_int2_int2_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "21 21 21 16 16" _null_ _null_ _null_ _null_ _null_ in_range_int2_int2_desc _null_ _null_ _null_ ));
+DESCR("in range for int2 column, int2 offset, descending");
+DATA(insert OID = 6140 ( in_range_timestamp_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1114 1114 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timestamp_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for timestamp column, interval offset, ascending");
+DATA(insert OID = 6141 ( in_range_timestamp_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1114 1114 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timestamp_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for timestamp column, interval offset, descending");
+DATA(insert OID = 6142 ( in_range_timestamptz_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1184 1184 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timestamptz_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for timestamptz column, interval offset, ascending");
+DATA(insert OID = 6143 ( in_range_timestamptz_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1184 1184 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timestamptz_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for timestamptz column, interval offset, descending");
+DATA(insert OID = 6144 ( in_range_interval_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1186 1186 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_interval_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for interval column, interval offset, ascending");
+DATA(insert OID = 6145 ( in_range_interval_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1186 1186 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_interval_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for interval column, interval offset, descending");
+DATA(insert OID = 6146 ( in_range_timetz_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1266 1266 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timetz_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for timetz column, interval offset, ascending");
+DATA(insert OID = 6147 ( in_range_timetz_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1266 1266 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_timetz_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for timetz column, interval offset, descending");
+DATA(insert OID = 6148 ( in_range_date_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1082 1082 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_date_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for date column, interval offset, ascending");
+DATA(insert OID = 6149 ( in_range_date_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1082 1082 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_date_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for date column, interval offset, descending");
+DATA(insert OID = 6150 ( in_range_time_interval_asc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1083 1083 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_time_interval_asc _null_ _null_ _null_ ));
+DESCR("in range for time column, interval offset, ascending");
+DATA(insert OID = 6151 ( in_range_time_interval_desc PGNSP PGUID 12 1 0 0 0 f f f t t f i s 5 0 16 "1083 1083 1186 16 16" _null_ _null_ _null_ _null_ _null_ in_range_time_interval_desc _null_ _null_ _null_ ));
+DESCR("in range for time column, interval offset, descending");
+
/* To ASCII conversion */
DATA(insert OID = 1845 ( to_ascii PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "25" _null_ _null_ _null_ _null_ _null_ to_ascii_default _null_ _null_ _null_ ));
DESCR("encode text from DB encoding to ASCII text");
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1bf67455e0..1f96de0542 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1893,6 +1893,8 @@ typedef struct WindowAggState
int64 aggregatedbase; /* start row for current aggregates */
int64 aggregatedupto; /* rows before this one are aggregated */
+ PGFunction startRangeFunc; /* Start comparator func, used for RANGE with values */
+ PGFunction endRangeFunc; /* End comparator func, used for RANGE with values */
int frameOptions; /* frame_clause options, see WindowDef */
ExprState *startOffset; /* expression for starting bound offset */
ExprState *endOffset; /* expression for ending bound offset */
@@ -1922,6 +1924,16 @@ typedef struct WindowAggState
TupleTableSlot *agg_row_slot;
TupleTableSlot *temp_slot_1;
TupleTableSlot *temp_slot_2;
+
+ /* used for RANGE BETWEEN and GROUPS BETWEEN with values */
+ int64 winGroupsCount; /* number of window groups */
+ int64 *winGroupLen; /* length of each window group */
+ int64 *currWinGroup; /* current window group */
+ int64 prevRowTotal; /* previous row total */
+ int64 *frameheadWinGroup; /* window group for the frame head */
+ int64 *frametailWinGroup; /* window group for the frame tail */
+ int64 frameheadGroupsCount; /* count of frame head groups */
+ int64 frametailGroupsCount; /* count of frame tail groups */
} WindowAggState;
/* ----------------
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 76a73b2a37..ed743e01cc 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -499,22 +499,28 @@ typedef struct WindowDef
* which were defaulted; the correct behavioral bits must be set either way.
* The START_foo and END_foo options must come in pairs of adjacent bits for
* the convenience of gram.y, even though some of them are useless/invalid.
- * We will need more bits (and fields) to cover the full SQL:2008 option set.
*/
#define FRAMEOPTION_NONDEFAULT 0x00001 /* any specified? */
#define FRAMEOPTION_RANGE 0x00002 /* RANGE behavior */
#define FRAMEOPTION_ROWS 0x00004 /* ROWS behavior */
-#define FRAMEOPTION_BETWEEN 0x00008 /* BETWEEN given? */
-#define FRAMEOPTION_START_UNBOUNDED_PRECEDING 0x00010 /* start is U. P. */
-#define FRAMEOPTION_END_UNBOUNDED_PRECEDING 0x00020 /* (disallowed) */
-#define FRAMEOPTION_START_UNBOUNDED_FOLLOWING 0x00040 /* (disallowed) */
-#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING 0x00080 /* end is U. F. */
-#define FRAMEOPTION_START_CURRENT_ROW 0x00100 /* start is C. R. */
-#define FRAMEOPTION_END_CURRENT_ROW 0x00200 /* end is C. R. */
-#define FRAMEOPTION_START_VALUE_PRECEDING 0x00400 /* start is V. P. */
-#define FRAMEOPTION_END_VALUE_PRECEDING 0x00800 /* end is V. P. */
-#define FRAMEOPTION_START_VALUE_FOLLOWING 0x01000 /* start is V. F. */
-#define FRAMEOPTION_END_VALUE_FOLLOWING 0x02000 /* end is V. F. */
+#define FRAMEOPTION_GROUPS 0x00008 /* GROUPS behavior */
+#define FRAMEOPTION_BETWEEN 0x00010 /* BETWEEN given? */
+#define FRAMEOPTION_START_UNBOUNDED_PRECEDING 0x00020 /* start is U. P. */
+#define FRAMEOPTION_END_UNBOUNDED_PRECEDING 0x00040 /* (disallowed) */
+#define FRAMEOPTION_START_UNBOUNDED_FOLLOWING 0x00080 /* (disallowed) */
+#define FRAMEOPTION_END_UNBOUNDED_FOLLOWING 0x00100 /* end is U. F. */
+#define FRAMEOPTION_START_CURRENT_ROW 0x00200 /* start is C. R. */
+#define FRAMEOPTION_END_CURRENT_ROW 0x00400 /* end is C. R. */
+#define FRAMEOPTION_START_VALUE_PRECEDING 0x00800 /* start is V. P. */
+#define FRAMEOPTION_END_VALUE_PRECEDING 0x01000 /* end is V. P. */
+#define FRAMEOPTION_START_VALUE_FOLLOWING 0x02000 /* start is V. F. */
+#define FRAMEOPTION_END_VALUE_FOLLOWING 0x04000 /* end is V. F. */
+#define FRAMEOPTION_RANGE_BETWEEN 0x08000 /* RANGE BETWEEN with values */
+#define FRAMEOPTION_GROUPS_BETWEEN 0x010000 /* GROUPS BETWEEN with values */
+#define FRAMEOPTION_EXCLUDE_CURRENT 0x020000 /* exclude current row */
+#define FRAMEOPTION_EXCLUDE_TIES 0x040000 /* exclude ties */
+#define FRAMEOPTION_EXCLUDE_NO_OTHERS 0x080000 /* exclude no others */
+#define FRAMEOPTION_EXCLUDE_GROUP 0x100000 /* exclude group */
#define FRAMEOPTION_START_VALUE \
(FRAMEOPTION_START_VALUE_PRECEDING | FRAMEOPTION_START_VALUE_FOLLOWING)
@@ -525,6 +531,10 @@ typedef struct WindowDef
(FRAMEOPTION_RANGE | FRAMEOPTION_START_UNBOUNDED_PRECEDING | \
FRAMEOPTION_END_CURRENT_ROW)
+#define FRAMEOPTION_EXCLUSION \
+ (FRAMEOPTION_EXCLUDE_CURRENT | FRAMEOPTION_EXCLUDE_TIES | \
+ FRAMEOPTION_EXCLUDE_GROUP)
+
/*
* RangeSubselect - subquery appearing in a FROM clause
*/
@@ -1275,8 +1285,9 @@ typedef struct GroupingSet
* if the clause originally came from WINDOW, and is NULL if it originally
* was an OVER clause (but note that we collapse out duplicate OVERs).
* partitionClause and orderClause are lists of SortGroupClause structs.
- * winref is an ID number referenced by WindowFunc nodes; it must be unique
- * among the members of a Query's windowClause list.
+ * startRangeFunc and/or endRangeFunc are set if the clause is a RANGE BETWEEN
+ * with values. winref is an ID number referenced by WindowFunc nodes;it must
+ * be unique among the members of a Query's windowClause list.
* When refname isn't null, the partitionClause is always copied from there;
* the orderClause might or might not be copied (see copiedOrder); the framing
* options are never copied, per spec.
@@ -1288,6 +1299,8 @@ typedef struct WindowClause
char *refname; /* referenced window name, if any */
List *partitionClause; /* PARTITION BY list */
List *orderClause; /* ORDER BY list */
+ Oid startRangeOid; /* Start comparator Oid, used for RANGE with values */
+ Oid endRangeOid; /* End comparator Oid, used for RANGE with values */
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index baf3c07417..ce2cbf1ef7 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -808,6 +808,8 @@ typedef struct WindowAgg
int ordNumCols; /* number of columns in ordering clause */
AttrNumber *ordColIdx; /* their indexes in the target list */
Oid *ordOperators; /* equality operators for ordering columns */
+ Oid startRangeOid; /* Start comparator Oid, used for RANGE with values */
+ Oid endRangeOid; /* End comparator Oid, used for RANGE with values */
int frameOptions; /* frame_clause options, see WindowDef */
Node *startOffset; /* expression for starting bound, if any */
Node *endOffset; /* expression for ending bound, if any */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 26af944e03..cf32197bc3 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -182,6 +182,7 @@ PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD)
PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD)
PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD)
PG_KEYWORD("grouping", GROUPING, COL_NAME_KEYWORD)
+PG_KEYWORD("groups", GROUPS, UNRESERVED_KEYWORD)
PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD)
PG_KEYWORD("having", HAVING, RESERVED_KEYWORD)
PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD)
@@ -283,6 +284,7 @@ PG_KEYWORD("options", OPTIONS, UNRESERVED_KEYWORD)
PG_KEYWORD("or", OR, RESERVED_KEYWORD)
PG_KEYWORD("order", ORDER, RESERVED_KEYWORD)
PG_KEYWORD("ordinality", ORDINALITY, UNRESERVED_KEYWORD)
+PG_KEYWORD("others", OTHERS, UNRESERVED_KEYWORD)
PG_KEYWORD("out", OUT_P, COL_NAME_KEYWORD)
PG_KEYWORD("outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("over", OVER, UNRESERVED_KEYWORD)
@@ -397,6 +399,7 @@ PG_KEYWORD("template", TEMPLATE, UNRESERVED_KEYWORD)
PG_KEYWORD("temporary", TEMPORARY, UNRESERVED_KEYWORD)
PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
+PG_KEYWORD("ties", TIES, UNRESERVED_KEYWORD)
PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
PG_KEYWORD("to", TO, RESERVED_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 4e96fa7907..1abfe2400f 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -45,6 +45,7 @@ typedef enum ParseExprKind
EXPR_KIND_WINDOW_ORDER, /* window definition ORDER BY */
EXPR_KIND_WINDOW_FRAME_RANGE, /* window frame clause with RANGE */
EXPR_KIND_WINDOW_FRAME_ROWS, /* window frame clause with ROWS */
+ EXPR_KIND_WINDOW_FRAME_GROUPS, /* window frame clause with GROUPS */
EXPR_KIND_SELECT_TARGET, /* SELECT target list item */
EXPR_KIND_INSERT_TARGET, /* INSERT target list item */
EXPR_KIND_UPDATE_SOURCE, /* UPDATE assignment source item */
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 684f7f20a8..d1928ca860 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -717,6 +717,36 @@ macaddr8_le(macaddr8,macaddr8)
macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
+in_range_int8_int8_asc(bigint,bigint,bigint,boolean,boolean)
+in_range_int8_int8_desc(bigint,bigint,bigint,boolean,boolean)
+in_range_int8_int4_asc(bigint,bigint,integer,boolean,boolean)
+in_range_int8_int4_desc(bigint,bigint,integer,boolean,boolean)
+in_range_int8_int2_asc(bigint,bigint,smallint,boolean,boolean)
+in_range_int8_int2_desc(bigint,bigint,smallint,boolean,boolean)
+in_range_int4_int8_asc(integer,integer,bigint,boolean,boolean)
+in_range_int4_int8_desc(integer,integer,bigint,boolean,boolean)
+in_range_int4_int4_asc(integer,integer,integer,boolean,boolean)
+in_range_int4_int4_desc(integer,integer,integer,boolean,boolean)
+in_range_int4_int2_asc(integer,integer,smallint,boolean,boolean)
+in_range_int4_int2_desc(integer,integer,smallint,boolean,boolean)
+in_range_int2_int8_asc(smallint,smallint,bigint,boolean,boolean)
+in_range_int2_int8_desc(smallint,smallint,bigint,boolean,boolean)
+in_range_int2_int4_asc(smallint,smallint,integer,boolean,boolean)
+in_range_int2_int4_desc(smallint,smallint,integer,boolean,boolean)
+in_range_int2_int2_asc(smallint,smallint,smallint,boolean,boolean)
+in_range_int2_int2_desc(smallint,smallint,smallint,boolean,boolean)
+in_range_timestamp_interval_asc(timestamp without time zone,timestamp without time zone,interval,boolean,boolean)
+in_range_timestamp_interval_desc(timestamp without time zone,timestamp without time zone,interval,boolean,boolean)
+in_range_timestamptz_interval_asc(timestamp with time zone,timestamp with time zone,interval,boolean,boolean)
+in_range_timestamptz_interval_desc(timestamp with time zone,timestamp with time zone,interval,boolean,boolean)
+in_range_interval_interval_asc(interval,interval,interval,boolean,boolean)
+in_range_interval_interval_desc(interval,interval,interval,boolean,boolean)
+in_range_timetz_interval_asc(time with time zone,time with time zone,interval,boolean,boolean)
+in_range_timetz_interval_desc(time with time zone,time with time zone,interval,boolean,boolean)
+in_range_date_interval_asc(date,date,interval,boolean,boolean)
+in_range_date_interval_desc(date,date,interval,boolean,boolean)
+in_range_time_interval_asc(time without time zone,time without time zone,interval,boolean,boolean)
+in_range_time_interval_desc(time without time zone,time without time zone,interval,boolean,boolean)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
diff --git a/src/test/regress/expected/window.out b/src/test/regress/expected/window.out
index 19f909f3d1..e9b76908bc 100644
--- a/src/test/regress/expected/window.out
+++ b/src/test/regress/expected/window.out
@@ -5,19 +5,24 @@ CREATE TEMPORARY TABLE empsalary (
depname varchar,
empno bigint,
salary int,
- enroll_date date
+ enroll_date date,
+ enroll_time time,
+ enroll_timetz timetz,
+ enroll_interval interval,
+ enroll_timestamptz timestamptz,
+ enroll_timestamp timestamp
);
INSERT INTO empsalary VALUES
-('develop', 10, 5200, '2007-08-01'),
-('sales', 1, 5000, '2006-10-01'),
-('personnel', 5, 3500, '2007-12-10'),
-('sales', 4, 4800, '2007-08-08'),
-('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');
+('develop', 10, 5200, '2007-08-01', '11:00', '11:00 BST', '1 year'::interval, TIMESTAMP '2000-10-19 10:23:54+01', TIMESTAMP '2000-10-19 10:23:54'),
+('sales', 1, 5000, '2006-10-01', '12:00', '12:00 BST', '2 years'::interval, TIMESTAMP '2001-10-19 10:23:54+01', TIMESTAMP '2001-10-19 10:23:54'),
+('personnel', 5, 3500, '2007-12-10', '13:00', '13:00 BST', '3 years'::interval, TIMESTAMP '2001-10-19 10:23:54+01', TIMESTAMP '2001-10-19 10:23:54'),
+('sales', 4, 4800, '2007-08-08', '14:00', '14:00 BST', '4 years'::interval, TIMESTAMP '2002-10-19 10:23:54+01', TIMESTAMP '2002-10-19 10:23:54'),
+('personnel', 2, 3900, '2006-12-23', '15:00', '15:00 BST', '5 years'::interval, TIMESTAMP '2003-10-19 10:23:54+01', TIMESTAMP '2003-10-19 10:23:54'),
+('develop', 7, 4200, '2008-01-01', '15:00', '15:00 BST', '5 years'::interval, TIMESTAMP '2004-10-19 10:23:54+01', TIMESTAMP '2004-10-19 10:23:54'),
+('develop', 9, 4500, '2008-01-01', '17:00', '17:00 BST', '7 years'::interval, TIMESTAMP '2005-10-19 10:23:54+01', TIMESTAMP '2005-10-19 10:23:54'),
+('sales', 3, 4800, '2007-08-01', '18:00', '18:00 BST', '8 years'::interval, TIMESTAMP '2006-10-19 10:23:54+01', TIMESTAMP '2006-10-19 10:23:54'),
+('develop', 8, 6000, '2006-10-01', '19:00', '19:00 BST', '9 years'::interval, TIMESTAMP '2007-10-19 10:23:54+01', TIMESTAMP '2007-10-19 10:23:54'),
+('develop', 11, 5200, '2007-08-15', '20:00', '20:00 BST', '10 years'::interval, TIMESTAMP '2008-10-19 10:23:54+01', TIMESTAMP '2008-10-19 10:23:54');
SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname) FROM empsalary ORDER BY depname, salary;
depname | empno | salary | sum
-----------+-------+--------+-------
@@ -819,6 +824,74 @@ FROM tenk1 WHERE unique1 < 10;
10 | 0 | 0
(10 rows)
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude no others),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 7 | 4 | 0
+ 13 | 2 | 2
+ 22 | 1 | 1
+ 26 | 6 | 2
+ 29 | 9 | 1
+ 31 | 8 | 0
+ 32 | 5 | 1
+ 23 | 3 | 3
+ 15 | 7 | 3
+ 10 | 0 | 0
+(10 rows)
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 3 | 4 | 0
+ 11 | 2 | 2
+ 21 | 1 | 1
+ 20 | 6 | 2
+ 20 | 9 | 1
+ 23 | 8 | 0
+ 27 | 5 | 1
+ 20 | 3 | 3
+ 8 | 7 | 3
+ 10 | 0 | 0
+(10 rows)
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 4 | 0
+ | 2 | 2
+ | 1 | 1
+ | 6 | 2
+ | 9 | 1
+ | 8 | 0
+ | 5 | 1
+ | 3 | 3
+ | 7 | 3
+ | 0 | 0
+(10 rows)
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 4 | 4 | 0
+ 2 | 2 | 2
+ 1 | 1 | 1
+ 6 | 6 | 2
+ 9 | 9 | 1
+ 8 | 8 | 0
+ 5 | 5 | 1
+ 3 | 3 | 3
+ 7 | 7 | 3
+ 0 | 0 | 0
+(10 rows)
+
SELECT sum(unique1) over (rows between 2 preceding and 1 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
@@ -887,13 +960,57 @@ FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
10 | 7 | 3
(10 rows)
--- fail: not implemented yet
-SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude current row),
unique1, four
-FROM tenk1 WHERE unique1 < 10;
-ERROR: RANGE PRECEDING is only supported with UNBOUNDED
-LINE 1: SELECT sum(unique1) over (order by four range between 2::int...
- ^
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
+ sum | unique1 | four
+-----+---------+------
+ 12 | 0 | 0
+ 4 | 8 | 0
+ 8 | 4 | 0
+ 22 | 5 | 1
+ 18 | 9 | 1
+ 26 | 1 | 1
+ 29 | 6 | 2
+ 33 | 2 | 2
+ 42 | 3 | 3
+ 38 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 35 | 3 | 3
+ 35 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
+ sum | unique1 | four
+-----+---------+------
+ 0 | 0 | 0
+ 8 | 8 | 0
+ 4 | 4 | 0
+ 17 | 5 | 1
+ 21 | 9 | 1
+ 13 | 1 | 1
+ 33 | 6 | 2
+ 29 | 2 | 2
+ 38 | 3 | 3
+ 42 | 7 | 3
+(10 rows)
+
SELECT first_value(unique1) over w,
nth_value(unique1, 2) over w AS nth_2,
last_value(unique1) over w, unique1, four
@@ -958,6 +1075,1644 @@ SELECT pg_get_viewdef('v_window');
FROM generate_series(1, 10) i(i);
(1 row)
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude current row) as sum_rows FROM generate_series(1, 10) i;
+SELECT * FROM v_window;
+ i | sum_rows
+----+----------
+ 1 | 2
+ 2 | 4
+ 3 | 6
+ 4 | 8
+ 5 | 10
+ 6 | 12
+ 7 | 14
+ 8 | 16
+ 9 | 18
+ 10 | 9
+(10 rows)
+
+SELECT pg_get_viewdef('v_window');
+ pg_get_viewdef
+-----------------------------------------------------------------------------------------------------------
+ SELECT i.i, +
+ sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) AS sum_rows+
+ FROM generate_series(1, 10) i(i);
+(1 row)
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude group) as sum_rows FROM generate_series(1, 10) i;
+SELECT * FROM v_window;
+ i | sum_rows
+----+----------
+ 1 | 2
+ 2 | 4
+ 3 | 6
+ 4 | 8
+ 5 | 10
+ 6 | 12
+ 7 | 14
+ 8 | 16
+ 9 | 18
+ 10 | 9
+(10 rows)
+
+SELECT pg_get_viewdef('v_window');
+ pg_get_viewdef
+-----------------------------------------------------------------------------------------------------
+ SELECT i.i, +
+ sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE GROUP) AS sum_rows+
+ FROM generate_series(1, 10) i(i);
+(1 row)
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude ties) as sum_rows FROM generate_series(1, 10) i;
+SELECT * FROM v_window;
+ i | sum_rows
+----+----------
+ 1 | 3
+ 2 | 6
+ 3 | 9
+ 4 | 12
+ 5 | 15
+ 6 | 18
+ 7 | 21
+ 8 | 24
+ 9 | 27
+ 10 | 19
+(10 rows)
+
+SELECT pg_get_viewdef('v_window');
+ pg_get_viewdef
+----------------------------------------------------------------------------------------------------
+ SELECT i.i, +
+ sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE TIES) AS sum_rows+
+ FROM generate_series(1, 10) i(i);
+(1 row)
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude no others) as sum_rows FROM generate_series(1, 10) i;
+SELECT * FROM v_window;
+ i | sum_rows
+----+----------
+ 1 | 3
+ 2 | 6
+ 3 | 9
+ 4 | 12
+ 5 | 15
+ 6 | 18
+ 7 | 21
+ 8 | 24
+ 9 | 27
+ 10 | 19
+(10 rows)
+
+SELECT pg_get_viewdef('v_window');
+ pg_get_viewdef
+---------------------------------------------------------------------------------------------------------
+ SELECT i.i, +
+ sum(i.i) OVER (ORDER BY i.i ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS sum_rows+
+ FROM generate_series(1, 10) i(i);
+(1 row)
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i groups between 1 preceding and 1 following
+ exclude no others) as sum_rows FROM generate_series(1, 10) i;
+SELECT * FROM v_window;
+ i | sum_rows
+----+----------
+ 1 | 3
+ 2 | 6
+ 3 | 9
+ 4 | 12
+ 5 | 15
+ 6 | 18
+ 7 | 21
+ 8 | 24
+ 9 | 27
+ 10 | 19
+(10 rows)
+
+SELECT pg_get_viewdef('v_window');
+ pg_get_viewdef
+-----------------------------------------------------------------------------------------------------------
+ SELECT i.i, +
+ sum(i.i) OVER (ORDER BY i.i GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING EXCLUDE NO OTHERS) AS sum_rows+
+ FROM generate_series(1, 10) i(i);
+(1 row)
+
+-- RANGE BETWEEN with values tests
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four desc range between 2::int8 preceding and 1::int2 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 3 | 3
+ | 7 | 3
+ 10 | 6 | 2
+ 10 | 2 | 2
+ 18 | 9 | 1
+ 18 | 5 | 1
+ 18 | 1 | 1
+ 23 | 0 | 0
+ 23 | 8 | 0
+ 23 | 4 | 0
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude no others),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 33 | 0 | 0
+ 41 | 8 | 0
+ 37 | 4 | 0
+ 35 | 5 | 1
+ 39 | 9 | 1
+ 31 | 1 | 1
+ 43 | 6 | 2
+ 39 | 2 | 2
+ 26 | 3 | 3
+ 30 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 33 | 0 | 0
+ 33 | 8 | 0
+ 33 | 4 | 0
+ 30 | 5 | 1
+ 30 | 9 | 1
+ 30 | 1 | 1
+ 37 | 6 | 2
+ 37 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 4 | 0 | 0
+ 12 | 4 | 0
+ 12 | 8 | 0
+ 6 | 1 | 1
+ 15 | 5 | 1
+ 14 | 9 | 1
+ 8 | 2 | 2
+ 8 | 6 | 2
+ 10 | 3 | 3
+ 10 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following
+ exclude current row),unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 4 | 0 | 0
+ 8 | 4 | 0
+ 4 | 8 | 0
+ 5 | 1 | 1
+ 10 | 5 | 1
+ 5 | 9 | 1
+ 6 | 2 | 2
+ 2 | 6 | 2
+ 7 | 3 | 3
+ 3 | 7 | 3
+(10 rows)
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 34900 | 5000 | 10-01-2006
+ 34900 | 6000 | 10-01-2006
+ 38400 | 3900 | 12-23-2006
+ 47100 | 4800 | 08-01-2007
+ 47100 | 5200 | 08-01-2007
+ 47100 | 4800 | 08-08-2007
+ 47100 | 5200 | 08-15-2007
+ 36100 | 3500 | 12-10-2007
+ 32200 | 4500 | 01-01-2008
+ 32200 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over (order by enroll_date desc range between '1 year'::interval preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 32200 | 4200 | 01-01-2008
+ 32200 | 4500 | 01-01-2008
+ 36100 | 3500 | 12-10-2007
+ 47100 | 5200 | 08-15-2007
+ 47100 | 4800 | 08-08-2007
+ 47100 | 4800 | 08-01-2007
+ 47100 | 5200 | 08-01-2007
+ 38400 | 3900 | 12-23-2006
+ 34900 | 5000 | 10-01-2006
+ 34900 | 6000 | 10-01-2006
+(10 rows)
+
+select sum(salary) over (order by enroll_date desc range between '1 year'::interval following and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-----+--------+-------------
+ | 4200 | 01-01-2008
+ | 4500 | 01-01-2008
+ | 3500 | 12-10-2007
+ | 5200 | 08-15-2007
+ | 4800 | 08-08-2007
+ | 4800 | 08-01-2007
+ | 5200 | 08-01-2007
+ | 3900 | 12-23-2006
+ | 5000 | 10-01-2006
+ | 6000 | 10-01-2006
+(10 rows)
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude current row), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 29900 | 5000 | 10-01-2006
+ 28900 | 6000 | 10-01-2006
+ 34500 | 3900 | 12-23-2006
+ 42300 | 4800 | 08-01-2007
+ 41900 | 5200 | 08-01-2007
+ 42300 | 4800 | 08-08-2007
+ 41900 | 5200 | 08-15-2007
+ 32600 | 3500 | 12-10-2007
+ 27700 | 4500 | 01-01-2008
+ 28000 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude group), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 23900 | 5000 | 10-01-2006
+ 23900 | 6000 | 10-01-2006
+ 34500 | 3900 | 12-23-2006
+ 37100 | 4800 | 08-01-2007
+ 37100 | 5200 | 08-01-2007
+ 42300 | 4800 | 08-08-2007
+ 41900 | 5200 | 08-15-2007
+ 32600 | 3500 | 12-10-2007
+ 23500 | 4500 | 01-01-2008
+ 23500 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude ties), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 28900 | 5000 | 10-01-2006
+ 29900 | 6000 | 10-01-2006
+ 38400 | 3900 | 12-23-2006
+ 41900 | 4800 | 08-01-2007
+ 42300 | 5200 | 08-01-2007
+ 47100 | 4800 | 08-08-2007
+ 47100 | 5200 | 08-15-2007
+ 36100 | 3500 | 12-10-2007
+ 28000 | 4500 | 01-01-2008
+ 27700 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 13700 | 5200 | 11:00:00
+ 18500 | 5000 | 12:00:00
+ 21400 | 3500 | 13:00:00
+ 16400 | 4800 | 14:00:00
+ 17400 | 3900 | 15:00:00
+ 17400 | 4200 | 15:00:00
+ 15300 | 4500 | 17:00:00
+ 20500 | 4800 | 18:00:00
+ 16000 | 6000 | 19:00:00
+ 11200 | 5200 | 20:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_time desc range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 16000 | 5200 | 20:00:00
+ 20500 | 6000 | 19:00:00
+ 15300 | 4800 | 18:00:00
+ 17400 | 4500 | 17:00:00
+ 16400 | 4200 | 15:00:00
+ 16400 | 3900 | 15:00:00
+ 21400 | 4800 | 14:00:00
+ 18500 | 3500 | 13:00:00
+ 13700 | 5000 | 12:00:00
+ 10200 | 5200 | 11:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_time desc range between '1 hour'::interval following and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 10800 | 5200 | 20:00:00
+ 9300 | 6000 | 19:00:00
+ 4500 | 4800 | 18:00:00
+ 8100 | 4500 | 17:00:00
+ 8300 | 4200 | 15:00:00
+ 8300 | 3900 | 15:00:00
+ 8500 | 4800 | 14:00:00
+ 10200 | 3500 | 13:00:00
+ 5200 | 5000 | 12:00:00
+ | 5200 | 11:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude current row), salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 8500 | 5200 | 11:00:00
+ 13500 | 5000 | 12:00:00
+ 17900 | 3500 | 13:00:00
+ 11600 | 4800 | 14:00:00
+ 13500 | 3900 | 15:00:00
+ 13200 | 4200 | 15:00:00
+ 10800 | 4500 | 17:00:00
+ 15700 | 4800 | 18:00:00
+ 10000 | 6000 | 19:00:00
+ 6000 | 5200 | 20:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude group), salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 8500 | 5200 | 11:00:00
+ 13500 | 5000 | 12:00:00
+ 17900 | 3500 | 13:00:00
+ 11600 | 4800 | 14:00:00
+ 9300 | 3900 | 15:00:00
+ 9300 | 4200 | 15:00:00
+ 10800 | 4500 | 17:00:00
+ 15700 | 4800 | 18:00:00
+ 10000 | 6000 | 19:00:00
+ 6000 | 5200 | 20:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude ties), salary, enroll_time from empsalary;
+ sum | salary | enroll_time
+-------+--------+-------------
+ 13700 | 5200 | 11:00:00
+ 18500 | 5000 | 12:00:00
+ 21400 | 3500 | 13:00:00
+ 16400 | 4800 | 14:00:00
+ 13200 | 3900 | 15:00:00
+ 13500 | 4200 | 15:00:00
+ 15300 | 4500 | 17:00:00
+ 20500 | 4800 | 18:00:00
+ 16000 | 6000 | 19:00:00
+ 11200 | 5200 | 20:00:00
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 13700 | 5200 | 11:00:00+01
+ 18500 | 5000 | 12:00:00+01
+ 21400 | 3500 | 13:00:00+01
+ 16400 | 4800 | 14:00:00+01
+ 17400 | 3900 | 15:00:00+01
+ 17400 | 4200 | 15:00:00+01
+ 15300 | 4500 | 17:00:00+01
+ 20500 | 4800 | 18:00:00+01
+ 16000 | 6000 | 19:00:00+01
+ 11200 | 5200 | 20:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz desc range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 16000 | 5200 | 20:00:00+01
+ 20500 | 6000 | 19:00:00+01
+ 15300 | 4800 | 18:00:00+01
+ 17400 | 4500 | 17:00:00+01
+ 16400 | 4200 | 15:00:00+01
+ 16400 | 3900 | 15:00:00+01
+ 21400 | 4800 | 14:00:00+01
+ 18500 | 3500 | 13:00:00+01
+ 13700 | 5000 | 12:00:00+01
+ 10200 | 5200 | 11:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz desc range between '1 hour'::interval following and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 10800 | 5200 | 20:00:00+01
+ 9300 | 6000 | 19:00:00+01
+ 4500 | 4800 | 18:00:00+01
+ 8100 | 4500 | 17:00:00+01
+ 8300 | 4200 | 15:00:00+01
+ 8300 | 3900 | 15:00:00+01
+ 8500 | 4800 | 14:00:00+01
+ 10200 | 3500 | 13:00:00+01
+ 5200 | 5000 | 12:00:00+01
+ | 5200 | 11:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude current row), salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 8500 | 5200 | 11:00:00+01
+ 13500 | 5000 | 12:00:00+01
+ 17900 | 3500 | 13:00:00+01
+ 11600 | 4800 | 14:00:00+01
+ 13500 | 3900 | 15:00:00+01
+ 13200 | 4200 | 15:00:00+01
+ 10800 | 4500 | 17:00:00+01
+ 15700 | 4800 | 18:00:00+01
+ 10000 | 6000 | 19:00:00+01
+ 6000 | 5200 | 20:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude group), salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 8500 | 5200 | 11:00:00+01
+ 13500 | 5000 | 12:00:00+01
+ 17900 | 3500 | 13:00:00+01
+ 11600 | 4800 | 14:00:00+01
+ 9300 | 3900 | 15:00:00+01
+ 9300 | 4200 | 15:00:00+01
+ 10800 | 4500 | 17:00:00+01
+ 15700 | 4800 | 18:00:00+01
+ 10000 | 6000 | 19:00:00+01
+ 6000 | 5200 | 20:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude ties), salary, enroll_timetz from empsalary;
+ sum | salary | enroll_timetz
+-------+--------+---------------
+ 13700 | 5200 | 11:00:00+01
+ 18500 | 5000 | 12:00:00+01
+ 21400 | 3500 | 13:00:00+01
+ 16400 | 4800 | 14:00:00+01
+ 13200 | 3900 | 15:00:00+01
+ 13500 | 4200 | 15:00:00+01
+ 15300 | 4500 | 17:00:00+01
+ 20500 | 4800 | 18:00:00+01
+ 16000 | 6000 | 19:00:00+01
+ 11200 | 5200 | 20:00:00+01
+(10 rows)
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 13700 | 5200 | @ 1 year
+ 18500 | 5000 | @ 2 years
+ 21400 | 3500 | @ 3 years
+ 16400 | 4800 | @ 4 years
+ 17400 | 3900 | @ 5 years
+ 17400 | 4200 | @ 5 years
+ 15300 | 4500 | @ 7 years
+ 20500 | 4800 | @ 8 years
+ 16000 | 6000 | @ 9 years
+ 11200 | 5200 | @ 10 years
+(10 rows)
+
+select sum(salary) over (order by enroll_interval desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 16000 | 5200 | @ 10 years
+ 20500 | 6000 | @ 9 years
+ 15300 | 4800 | @ 8 years
+ 17400 | 4500 | @ 7 years
+ 16400 | 4200 | @ 5 years
+ 16400 | 3900 | @ 5 years
+ 21400 | 4800 | @ 4 years
+ 18500 | 3500 | @ 3 years
+ 13700 | 5000 | @ 2 years
+ 10200 | 5200 | @ 1 year
+(10 rows)
+
+select sum(salary) over (order by enroll_interval desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 10800 | 5200 | @ 10 years
+ 9300 | 6000 | @ 9 years
+ 4500 | 4800 | @ 8 years
+ 8100 | 4500 | @ 7 years
+ 8300 | 4200 | @ 5 years
+ 8300 | 3900 | @ 5 years
+ 8500 | 4800 | @ 4 years
+ 10200 | 3500 | @ 3 years
+ 5200 | 5000 | @ 2 years
+ | 5200 | @ 1 year
+(10 rows)
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 8500 | 5200 | @ 1 year
+ 13500 | 5000 | @ 2 years
+ 17900 | 3500 | @ 3 years
+ 11600 | 4800 | @ 4 years
+ 13500 | 3900 | @ 5 years
+ 13200 | 4200 | @ 5 years
+ 10800 | 4500 | @ 7 years
+ 15700 | 4800 | @ 8 years
+ 10000 | 6000 | @ 9 years
+ 6000 | 5200 | @ 10 years
+(10 rows)
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 8500 | 5200 | @ 1 year
+ 13500 | 5000 | @ 2 years
+ 17900 | 3500 | @ 3 years
+ 11600 | 4800 | @ 4 years
+ 9300 | 3900 | @ 5 years
+ 9300 | 4200 | @ 5 years
+ 10800 | 4500 | @ 7 years
+ 15700 | 4800 | @ 8 years
+ 10000 | 6000 | @ 9 years
+ 6000 | 5200 | @ 10 years
+(10 rows)
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_interval from empsalary;
+ sum | salary | enroll_interval
+-------+--------+-----------------
+ 13700 | 5200 | @ 1 year
+ 18500 | 5000 | @ 2 years
+ 21400 | 3500 | @ 3 years
+ 16400 | 4800 | @ 4 years
+ 13200 | 3900 | @ 5 years
+ 13500 | 4200 | @ 5 years
+ 15300 | 4500 | @ 7 years
+ 20500 | 4800 | @ 8 years
+ 16000 | 6000 | @ 9 years
+ 11200 | 5200 | @ 10 years
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 18500 | 5200 | Thu Oct 19 10:23:54 2000 PDT
+ 22400 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ 22400 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 21400 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 17400 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 19500 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 20500 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 16000 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 11200 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 16000 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+ 20500 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 19500 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 17400 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 21400 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 22400 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 18500 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 18500 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ 13700 | 5200 | Thu Oct 19 10:23:54 2000 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 10800 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+ 9300 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 8700 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 8100 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 8700 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 13300 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 13700 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 5200 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 5200 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ | 5200 | Thu Oct 19 10:23:54 2000 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 13300 | 5200 | Thu Oct 19 10:23:54 2000 PDT
+ 17400 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ 18900 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 16600 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 13500 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 13200 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 15000 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 15700 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 10000 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 6000 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 13300 | 5200 | Thu Oct 19 10:23:54 2000 PDT
+ 13900 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ 13900 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 16600 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 13500 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 13200 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 15000 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 15700 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 10000 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 6000 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamptz from empsalary;
+ sum | salary | enroll_timestamptz
+-------+--------+------------------------------
+ 18500 | 5200 | Thu Oct 19 10:23:54 2000 PDT
+ 18900 | 5000 | Fri Oct 19 10:23:54 2001 PDT
+ 17400 | 3500 | Fri Oct 19 10:23:54 2001 PDT
+ 21400 | 4800 | Sat Oct 19 10:23:54 2002 PDT
+ 17400 | 3900 | Sun Oct 19 10:23:54 2003 PDT
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004 PDT
+ 19500 | 4500 | Wed Oct 19 10:23:54 2005 PDT
+ 20500 | 4800 | Thu Oct 19 10:23:54 2006 PDT
+ 16000 | 6000 | Fri Oct 19 10:23:54 2007 PDT
+ 11200 | 5200 | Sun Oct 19 10:23:54 2008 PDT
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 18500 | 5200 | Thu Oct 19 10:23:54 2000
+ 22400 | 5000 | Fri Oct 19 10:23:54 2001
+ 22400 | 3500 | Fri Oct 19 10:23:54 2001
+ 21400 | 4800 | Sat Oct 19 10:23:54 2002
+ 17400 | 3900 | Sun Oct 19 10:23:54 2003
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004
+ 19500 | 4500 | Wed Oct 19 10:23:54 2005
+ 20500 | 4800 | Thu Oct 19 10:23:54 2006
+ 16000 | 6000 | Fri Oct 19 10:23:54 2007
+ 11200 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 16000 | 5200 | Sun Oct 19 10:23:54 2008
+ 20500 | 6000 | Fri Oct 19 10:23:54 2007
+ 19500 | 4800 | Thu Oct 19 10:23:54 2006
+ 17400 | 4500 | Wed Oct 19 10:23:54 2005
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004
+ 21400 | 3900 | Sun Oct 19 10:23:54 2003
+ 22400 | 4800 | Sat Oct 19 10:23:54 2002
+ 18500 | 3500 | Fri Oct 19 10:23:54 2001
+ 18500 | 5000 | Fri Oct 19 10:23:54 2001
+ 13700 | 5200 | Thu Oct 19 10:23:54 2000
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 10800 | 5200 | Sun Oct 19 10:23:54 2008
+ 9300 | 6000 | Fri Oct 19 10:23:54 2007
+ 8700 | 4800 | Thu Oct 19 10:23:54 2006
+ 8100 | 4500 | Wed Oct 19 10:23:54 2005
+ 8700 | 4200 | Tue Oct 19 10:23:54 2004
+ 13300 | 3900 | Sun Oct 19 10:23:54 2003
+ 13700 | 4800 | Sat Oct 19 10:23:54 2002
+ 5200 | 3500 | Fri Oct 19 10:23:54 2001
+ 5200 | 5000 | Fri Oct 19 10:23:54 2001
+ | 5200 | Thu Oct 19 10:23:54 2000
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 13300 | 5200 | Thu Oct 19 10:23:54 2000
+ 17400 | 5000 | Fri Oct 19 10:23:54 2001
+ 18900 | 3500 | Fri Oct 19 10:23:54 2001
+ 16600 | 4800 | Sat Oct 19 10:23:54 2002
+ 13500 | 3900 | Sun Oct 19 10:23:54 2003
+ 13200 | 4200 | Tue Oct 19 10:23:54 2004
+ 15000 | 4500 | Wed Oct 19 10:23:54 2005
+ 15700 | 4800 | Thu Oct 19 10:23:54 2006
+ 10000 | 6000 | Fri Oct 19 10:23:54 2007
+ 6000 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 13300 | 5200 | Thu Oct 19 10:23:54 2000
+ 13900 | 5000 | Fri Oct 19 10:23:54 2001
+ 13900 | 3500 | Fri Oct 19 10:23:54 2001
+ 16600 | 4800 | Sat Oct 19 10:23:54 2002
+ 13500 | 3900 | Sun Oct 19 10:23:54 2003
+ 13200 | 4200 | Tue Oct 19 10:23:54 2004
+ 15000 | 4500 | Wed Oct 19 10:23:54 2005
+ 15700 | 4800 | Thu Oct 19 10:23:54 2006
+ 10000 | 6000 | Fri Oct 19 10:23:54 2007
+ 6000 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 18500 | 5200 | Thu Oct 19 10:23:54 2000
+ 18900 | 5000 | Fri Oct 19 10:23:54 2001
+ 17400 | 3500 | Fri Oct 19 10:23:54 2001
+ 21400 | 4800 | Sat Oct 19 10:23:54 2002
+ 17400 | 3900 | Sun Oct 19 10:23:54 2003
+ 17400 | 4200 | Tue Oct 19 10:23:54 2004
+ 19500 | 4500 | Wed Oct 19 10:23:54 2005
+ 20500 | 4800 | Thu Oct 19 10:23:54 2006
+ 16000 | 6000 | Fri Oct 19 10:23:54 2007
+ 11200 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between current row and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 18500 | 5200 | Thu Oct 19 10:23:54 2000
+ 17200 | 5000 | Fri Oct 19 10:23:54 2001
+ 17200 | 3500 | Fri Oct 19 10:23:54 2001
+ 12900 | 4800 | Sat Oct 19 10:23:54 2002
+ 12600 | 3900 | Sun Oct 19 10:23:54 2003
+ 13500 | 4200 | Tue Oct 19 10:23:54 2004
+ 15300 | 4500 | Wed Oct 19 10:23:54 2005
+ 16000 | 4800 | Thu Oct 19 10:23:54 2006
+ 11200 | 6000 | Fri Oct 19 10:23:54 2007
+ 5200 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and current row),
+ salary, enroll_timestamp from empsalary;
+ sum | salary | enroll_timestamp
+-------+--------+--------------------------
+ 5200 | 5200 | Thu Oct 19 10:23:54 2000
+ 13700 | 5000 | Fri Oct 19 10:23:54 2001
+ 13700 | 3500 | Fri Oct 19 10:23:54 2001
+ 13300 | 4800 | Sat Oct 19 10:23:54 2002
+ 8700 | 3900 | Sun Oct 19 10:23:54 2003
+ 8100 | 4200 | Tue Oct 19 10:23:54 2004
+ 8700 | 4500 | Wed Oct 19 10:23:54 2005
+ 9300 | 4800 | Thu Oct 19 10:23:54 2006
+ 10800 | 6000 | Fri Oct 19 10:23:54 2007
+ 11200 | 5200 | Sun Oct 19 10:23:54 2008
+(10 rows)
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 34900 | 5000 | 10-01-2006
+ 34900 | 6000 | 10-01-2006
+ 38400 | 3900 | 12-23-2006
+ 47100 | 4800 | 08-01-2007
+ 47100 | 5200 | 08-01-2007
+ 47100 | 4800 | 08-08-2007
+ 47100 | 5200 | 08-15-2007
+ 47100 | 3500 | 12-10-2007
+ 47100 | 4500 | 01-01-2008
+ 47100 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude current row), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 29900 | 5000 | 10-01-2006
+ 28900 | 6000 | 10-01-2006
+ 34500 | 3900 | 12-23-2006
+ 42300 | 4800 | 08-01-2007
+ 41900 | 5200 | 08-01-2007
+ 42300 | 4800 | 08-08-2007
+ 41900 | 5200 | 08-15-2007
+ 43600 | 3500 | 12-10-2007
+ 42600 | 4500 | 01-01-2008
+ 42900 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude group), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 23900 | 5000 | 10-01-2006
+ 23900 | 6000 | 10-01-2006
+ 34500 | 3900 | 12-23-2006
+ 37100 | 4800 | 08-01-2007
+ 37100 | 5200 | 08-01-2007
+ 42300 | 4800 | 08-08-2007
+ 41900 | 5200 | 08-15-2007
+ 43600 | 3500 | 12-10-2007
+ 38400 | 4500 | 01-01-2008
+ 38400 | 4200 | 01-01-2008
+(10 rows)
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude ties), salary, enroll_date from empsalary;
+ sum | salary | enroll_date
+-------+--------+-------------
+ 28900 | 5000 | 10-01-2006
+ 29900 | 6000 | 10-01-2006
+ 38400 | 3900 | 12-23-2006
+ 41900 | 4800 | 08-01-2007
+ 42300 | 5200 | 08-01-2007
+ 47100 | 4800 | 08-08-2007
+ 47100 | 5200 | 08-15-2007
+ 47100 | 3500 | 12-10-2007
+ 42900 | 4500 | 01-01-2008
+ 42600 | 4200 | 01-01-2008
+(10 rows)
+
+select first_value(salary) over(order by salary range between 1000 preceding and 1000 following),
+ lead(salary) over(order by salary range between 1000 preceding and 1000 following),
+ nth_value(salary, 1) over(order by salary range between 1000 preceding and 1000 following),
+ salary from empsalary;
+ first_value | lead | nth_value | salary
+-------------+------+-----------+--------
+ 3500 | 3900 | 3500 | 3500
+ 3500 | 4200 | 3500 | 3900
+ 3500 | 4500 | 3500 | 4200
+ 3500 | 4800 | 3500 | 4500
+ 3900 | 4800 | 3900 | 4800
+ 3900 | 5000 | 3900 | 4800
+ 4200 | 5200 | 4200 | 5000
+ 4200 | 5200 | 4200 | 5200
+ 4200 | 6000 | 4200 | 5200
+ 5000 | | 5000 | 6000
+(10 rows)
+
+select last_value(salary) over(order by salary range between 1000 preceding and 1000 following),
+ lag(salary) over(order by salary range between 1000 preceding and 1000 following),
+ salary from empsalary;
+ last_value | lag | salary
+------------+------+--------
+ 4500 | | 3500
+ 4800 | 3500 | 3900
+ 5200 | 3900 | 4200
+ 5200 | 4200 | 4500
+ 5200 | 4500 | 4800
+ 5200 | 4800 | 4800
+ 6000 | 4800 | 5000
+ 6000 | 5000 | 5200
+ 6000 | 5200 | 5200
+ 6000 | 5200 | 6000
+(10 rows)
+
+select first_value(salary) over(order by salary range between 1000 following and 3000 following
+ exclude current row),
+ lead(salary) over(order by salary range between 1000 following and 3000 following exclude ties),
+ nth_value(salary, 1) over(order by salary range between 1000 following and 3000 following
+ exclude ties),
+ salary from empsalary;
+ first_value | lead | nth_value | salary
+-------------+------+-----------+--------
+ 4500 | 3900 | 4500 | 3500
+ 5000 | 4200 | 5000 | 3900
+ 5200 | 4500 | 5200 | 4200
+ 6000 | 4800 | 6000 | 4500
+ 6000 | 4800 | 6000 | 4800
+ 6000 | 5000 | 6000 | 4800
+ 6000 | 5200 | 6000 | 5000
+ | 5200 | | 5200
+ | 6000 | | 5200
+ | | | 6000
+(10 rows)
+
+select last_value(salary) over(order by salary range between 1000 following and 3000 following
+ exclude group),
+ lag(salary) over(order by salary range between 1000 following and 3000 following exclude group),
+ salary from empsalary;
+ last_value | lag | salary
+------------+------+--------
+ 6000 | | 3500
+ 6000 | 3500 | 3900
+ 6000 | 3900 | 4200
+ 6000 | 4200 | 4500
+ 6000 | 4500 | 4800
+ 6000 | 4800 | 4800
+ 6000 | 4800 | 5000
+ | 5000 | 5200
+ | 5200 | 5200
+ | 5200 | 6000
+(10 rows)
+
+select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude ties),
+ last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+ first_value | last_value | salary | enroll_date
+-------------+------------+--------+-------------
+ 5000 | 5200 | 5000 | 10-01-2006
+ | 5200 | 6000 | 10-01-2006
+ 5000 | 3500 | 3900 | 12-23-2006
+ 5000 | 4200 | 4800 | 08-01-2007
+ 5000 | 4200 | 5200 | 08-01-2007
+ 5000 | 4200 | 4800 | 08-08-2007
+ 5000 | 4200 | 5200 | 08-15-2007
+ 5000 | 4200 | 3500 | 12-10-2007
+ 5000 | 4200 | 4500 | 01-01-2008
+ 5000 | 4200 | 4200 | 01-01-2008
+(10 rows)
+
+-- RANGE BETWEEN with values negative tests
+select sum(salary) over (order by enroll_timestamp, enroll_date range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: RANGE clause requires exactly one ORDER BY column
+LINE 1: select sum(salary) over (order by enroll_timestamp, enroll_d...
+ ^
+select sum(salary) over (range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: RANGE clause requires exactly one ORDER BY column
+LINE 1: select sum(salary) over (range between '1 year'::interval pr...
+ ^
+select sum(salary) over (order by depname range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: no in_range function for given types
+LINE 1: select sum(salary) over (order by depname range between '1 y...
+ ^
+select max(enroll_date) over (order by enroll_timestamp range between 1 preceding and 2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: no in_range function for given types
+LINE 1: select max(enroll_date) over (order by enroll_timestamp rang...
+ ^
+select max(enroll_date) over (order by salary range between -1 preceding and 2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: RANGE offsets cannot be negative. invalid value -1
+select max(enroll_date) over (order by salary range between 1 preceding and -2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: RANGE offsets cannot be negative. invalid value -2
+select max(enroll_date) over (order by salary range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+ERROR: no in_range function for given types
+LINE 1: select max(enroll_date) over (order by salary range between ...
+ ^
+-- GROUPS tests
+SELECT sum(unique1) over (order by four groups between unbounded preceding and current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 12 | 0 | 0
+ 12 | 8 | 0
+ 12 | 4 | 0
+ 27 | 5 | 1
+ 27 | 9 | 1
+ 27 | 1 | 1
+ 35 | 6 | 2
+ 35 | 2 | 2
+ 45 | 3 | 3
+ 45 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between unbounded preceding and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 45 | 0 | 0
+ 45 | 8 | 0
+ 45 | 4 | 0
+ 45 | 5 | 1
+ 45 | 9 | 1
+ 45 | 1 | 1
+ 45 | 6 | 2
+ 45 | 2 | 2
+ 45 | 3 | 3
+ 45 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between current row and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 45 | 0 | 0
+ 45 | 8 | 0
+ 45 | 4 | 0
+ 33 | 5 | 1
+ 33 | 9 | 1
+ 33 | 1 | 1
+ 18 | 6 | 2
+ 18 | 2 | 2
+ 10 | 3 | 3
+ 10 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 1 preceding and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 45 | 0 | 0
+ 45 | 8 | 0
+ 45 | 4 | 0
+ 45 | 5 | 1
+ 45 | 9 | 1
+ 45 | 1 | 1
+ 33 | 6 | 2
+ 33 | 2 | 2
+ 18 | 3 | 3
+ 18 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 1 following and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 33 | 0 | 0
+ 33 | 8 | 0
+ 33 | 4 | 0
+ 18 | 5 | 1
+ 18 | 9 | 1
+ 18 | 1 | 1
+ 10 | 6 | 2
+ 10 | 2 | 2
+ | 3 | 3
+ | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between unbounded preceding and 2 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 35 | 0 | 0
+ 35 | 8 | 0
+ 35 | 4 | 0
+ 45 | 5 | 1
+ 45 | 9 | 1
+ 45 | 1 | 1
+ 45 | 6 | 2
+ 45 | 2 | 2
+ 45 | 3 | 3
+ 45 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ | 0 | 0
+ | 8 | 0
+ | 4 | 0
+ 12 | 5 | 1
+ 12 | 9 | 1
+ 12 | 1 | 1
+ 27 | 6 | 2
+ 27 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 27 | 0 | 0
+ 27 | 8 | 0
+ 27 | 4 | 0
+ 35 | 5 | 1
+ 35 | 9 | 1
+ 35 | 1 | 1
+ 45 | 6 | 2
+ 45 | 2 | 2
+ 33 | 3 | 3
+ 33 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 0 preceding and 0 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 12 | 0 | 0
+ 12 | 8 | 0
+ 12 | 4 | 0
+ 15 | 5 | 1
+ 15 | 9 | 1
+ 15 | 1 | 1
+ 8 | 6 | 2
+ 8 | 2 | 2
+ 10 | 3 | 3
+ 10 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude current row), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 27 | 0 | 0
+ 19 | 8 | 0
+ 23 | 4 | 0
+ 30 | 5 | 1
+ 26 | 9 | 1
+ 34 | 1 | 1
+ 39 | 6 | 2
+ 43 | 2 | 2
+ 30 | 3 | 3
+ 26 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude group), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 15 | 0 | 0
+ 15 | 8 | 0
+ 15 | 4 | 0
+ 20 | 5 | 1
+ 20 | 9 | 1
+ 20 | 1 | 1
+ 37 | 6 | 2
+ 37 | 2 | 2
+ 23 | 3 | 3
+ 23 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude ties), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four
+-----+---------+------
+ 15 | 0 | 0
+ 23 | 8 | 0
+ 19 | 4 | 0
+ 25 | 5 | 1
+ 29 | 9 | 1
+ 21 | 1 | 1
+ 43 | 6 | 2
+ 39 | 2 | 2
+ 26 | 3 | 3
+ 30 | 7 | 3
+(10 rows)
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following),unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four | ten
+-----+---------+------+-----
+ 0 | 0 | 0 | 0
+ 1 | 1 | 1 | 1
+ 2 | 2 | 2 | 2
+ 3 | 3 | 3 | 3
+ 4 | 4 | 0 | 4
+ 5 | 5 | 1 | 5
+ 6 | 6 | 2 | 6
+ 7 | 7 | 3 | 7
+ 8 | 8 | 0 | 8
+ 9 | 9 | 1 | 9
+(10 rows)
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude current row), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four | ten
+-----+---------+------+-----
+ | 0 | 0 | 0
+ | 1 | 1 | 1
+ | 2 | 2 | 2
+ | 3 | 3 | 3
+ | 4 | 0 | 4
+ | 5 | 1 | 5
+ | 6 | 2 | 6
+ | 7 | 3 | 7
+ | 8 | 0 | 8
+ | 9 | 1 | 9
+(10 rows)
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude group), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four | ten
+-----+---------+------+-----
+ | 0 | 0 | 0
+ | 1 | 1 | 1
+ | 2 | 2 | 2
+ | 3 | 3 | 3
+ | 4 | 0 | 4
+ | 5 | 1 | 5
+ | 6 | 2 | 6
+ | 7 | 3 | 7
+ | 8 | 0 | 8
+ | 9 | 1 | 9
+(10 rows)
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude ties), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+ sum | unique1 | four | ten
+-----+---------+------+-----
+ 0 | 0 | 0 | 0
+ 1 | 1 | 1 | 1
+ 2 | 2 | 2 | 2
+ 3 | 3 | 3 | 3
+ 4 | 4 | 0 | 4
+ 5 | 5 | 1 | 5
+ 6 | 6 | 2 | 6
+ 7 | 7 | 3 | 7
+ 8 | 8 | 0 | 8
+ 9 | 9 | 1 | 9
+(10 rows)
+
+select first_value(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ lead(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ nth_value(salary, 1) over(order by enroll_date groups between 1 preceding and 1 following),
+ salary, enroll_date from empsalary;
+ first_value | lead | nth_value | salary | enroll_date
+-------------+------+-----------+--------+-------------
+ 5000 | 6000 | 5000 | 5000 | 10-01-2006
+ 5000 | 3900 | 5000 | 6000 | 10-01-2006
+ 5000 | 4800 | 5000 | 3900 | 12-23-2006
+ 3900 | 5200 | 3900 | 4800 | 08-01-2007
+ 3900 | 4800 | 3900 | 5200 | 08-01-2007
+ 4800 | 5200 | 4800 | 4800 | 08-08-2007
+ 4800 | 3500 | 4800 | 5200 | 08-15-2007
+ 5200 | 4500 | 5200 | 3500 | 12-10-2007
+ 3500 | 4200 | 3500 | 4500 | 01-01-2008
+ 3500 | | 3500 | 4200 | 01-01-2008
+(10 rows)
+
+select last_value(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ lag(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ salary, enroll_date from empsalary;
+ last_value | lag | salary | enroll_date
+------------+------+--------+-------------
+ 3900 | | 5000 | 10-01-2006
+ 3900 | 5000 | 6000 | 10-01-2006
+ 5200 | 6000 | 3900 | 12-23-2006
+ 4800 | 3900 | 4800 | 08-01-2007
+ 4800 | 4800 | 5200 | 08-01-2007
+ 5200 | 5200 | 4800 | 08-08-2007
+ 3500 | 4800 | 5200 | 08-15-2007
+ 4200 | 5200 | 3500 | 12-10-2007
+ 4200 | 3500 | 4500 | 01-01-2008
+ 4200 | 4500 | 4200 | 01-01-2008
+(10 rows)
+
+select first_value(salary) over(order by enroll_date groups between 1 following and 3 following
+ exclude current row),
+ lead(salary) over(order by enroll_date groups between 1 following and 3 following exclude ties),
+ nth_value(salary, 1) over(order by enroll_date groups between 1 following and 3 following
+ exclude ties),
+ salary, enroll_date from empsalary;
+ first_value | lead | nth_value | salary | enroll_date
+-------------+------+-----------+--------+-------------
+ 3900 | 6000 | 3900 | 5000 | 10-01-2006
+ 3900 | 3900 | 3900 | 6000 | 10-01-2006
+ 4800 | 4800 | 4800 | 3900 | 12-23-2006
+ 4800 | 5200 | 4800 | 4800 | 08-01-2007
+ 4800 | 4800 | 4800 | 5200 | 08-01-2007
+ 5200 | 5200 | 5200 | 4800 | 08-08-2007
+ 3500 | 3500 | 3500 | 5200 | 08-15-2007
+ 4500 | 4500 | 4500 | 3500 | 12-10-2007
+ | 4200 | | 4500 | 01-01-2008
+ | | | 4200 | 01-01-2008
+(10 rows)
+
+select last_value(salary) over(order by enroll_date groups between 1 following and 3 following
+ exclude group),
+ lag(salary) over(order by enroll_date groups between 1 following and 3 following exclude group),
+ salary, enroll_date from empsalary;
+ last_value | lag | salary | enroll_date
+------------+------+--------+-------------
+ 4800 | | 5000 | 10-01-2006
+ 4800 | 5000 | 6000 | 10-01-2006
+ 5200 | 6000 | 3900 | 12-23-2006
+ 3500 | 3900 | 4800 | 08-01-2007
+ 3500 | 4800 | 5200 | 08-01-2007
+ 4200 | 5200 | 4800 | 08-08-2007
+ 4200 | 4800 | 5200 | 08-15-2007
+ 4200 | 5200 | 3500 | 12-10-2007
+ | 3500 | 4500 | 01-01-2008
+ | 4500 | 4200 | 01-01-2008
+(10 rows)
+
+-- Show differences in values mode between ROWS, RANGE, and GROUPS
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 4
+ 3 | 9
+ 5 | 15
+ 7 | 21
+ 9 | 27
+ 11 | 33
+ 13 | 39
+ 15 | 45
+ 17 | 51
+ 19 | 57
+ 21 | 63
+ 23 | 69
+ 25 | 75
+ 27 | 81
+ 29 | 87
+ 31 | 93
+ 33 | 99
+ 35 | 68
+(18 rows)
+
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x range between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 1
+ 3 | 3
+ 5 | 5
+ 7 | 7
+ 9 | 9
+ 11 | 11
+ 13 | 13
+ 15 | 15
+ 17 | 17
+ 19 | 19
+ 21 | 21
+ 23 | 23
+ 25 | 25
+ 27 | 27
+ 29 | 29
+ 31 | 31
+ 33 | 33
+ 35 | 35
+(18 rows)
+
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 4
+ 3 | 9
+ 5 | 15
+ 7 | 21
+ 9 | 27
+ 11 | 33
+ 13 | 39
+ 15 | 45
+ 17 | 51
+ 19 | 57
+ 21 | 63
+ 23 | 69
+ 25 | 75
+ 27 | 81
+ 29 | 87
+ 31 | 93
+ 33 | 99
+ 35 | 68
+(18 rows)
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 2
+ 1 | 3
+ 1 | 7
+ 5 | 13
+ 7 | 21
+ 9 | 27
+ 11 | 33
+ 13 | 39
+ 15 | 45
+ 17 | 51
+ 19 | 57
+ 21 | 63
+ 23 | 69
+ 25 | 75
+ 27 | 81
+ 29 | 87
+ 31 | 93
+ 33 | 99
+ 35 | 105
+ 37 | 111
+ 39 | 117
+ 41 | 123
+ 43 | 129
+ 45 | 135
+ 47 | 141
+ 49 | 96
+(26 rows)
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x range between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 3
+ 1 | 3
+ 1 | 3
+ 5 | 5
+ 7 | 7
+ 9 | 9
+ 11 | 11
+ 13 | 13
+ 15 | 15
+ 17 | 17
+ 19 | 19
+ 21 | 21
+ 23 | 23
+ 25 | 25
+ 27 | 27
+ 29 | 29
+ 31 | 31
+ 33 | 33
+ 35 | 35
+ 37 | 37
+ 39 | 39
+ 41 | 41
+ 43 | 43
+ 45 | 45
+ 47 | 47
+ 49 | 49
+(26 rows)
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following);
+ x | sum
+----+-----
+ 1 | 8
+ 1 | 8
+ 1 | 8
+ 5 | 15
+ 7 | 21
+ 9 | 27
+ 11 | 33
+ 13 | 39
+ 15 | 45
+ 17 | 51
+ 19 | 57
+ 21 | 63
+ 23 | 69
+ 25 | 75
+ 27 | 81
+ 29 | 87
+ 31 | 93
+ 33 | 99
+ 35 | 105
+ 37 | 111
+ 39 | 117
+ 41 | 123
+ 43 | 129
+ 45 | 135
+ 47 | 141
+ 49 | 96
+(26 rows)
+
-- with UNION
SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0;
count
diff --git a/src/test/regress/sql/window.sql b/src/test/regress/sql/window.sql
index e2a1a1cdd5..7826f41afd 100644
--- a/src/test/regress/sql/window.sql
+++ b/src/test/regress/sql/window.sql
@@ -6,20 +6,25 @@ CREATE TEMPORARY TABLE empsalary (
depname varchar,
empno bigint,
salary int,
- enroll_date date
+ enroll_date date,
+ enroll_time time,
+ enroll_timetz timetz,
+ enroll_interval interval,
+ enroll_timestamptz timestamptz,
+ enroll_timestamp timestamp
);
INSERT INTO empsalary VALUES
-('develop', 10, 5200, '2007-08-01'),
-('sales', 1, 5000, '2006-10-01'),
-('personnel', 5, 3500, '2007-12-10'),
-('sales', 4, 4800, '2007-08-08'),
-('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');
+('develop', 10, 5200, '2007-08-01', '11:00', '11:00 BST', '1 year'::interval, TIMESTAMP '2000-10-19 10:23:54+01', TIMESTAMP '2000-10-19 10:23:54'),
+('sales', 1, 5000, '2006-10-01', '12:00', '12:00 BST', '2 years'::interval, TIMESTAMP '2001-10-19 10:23:54+01', TIMESTAMP '2001-10-19 10:23:54'),
+('personnel', 5, 3500, '2007-12-10', '13:00', '13:00 BST', '3 years'::interval, TIMESTAMP '2001-10-19 10:23:54+01', TIMESTAMP '2001-10-19 10:23:54'),
+('sales', 4, 4800, '2007-08-08', '14:00', '14:00 BST', '4 years'::interval, TIMESTAMP '2002-10-19 10:23:54+01', TIMESTAMP '2002-10-19 10:23:54'),
+('personnel', 2, 3900, '2006-12-23', '15:00', '15:00 BST', '5 years'::interval, TIMESTAMP '2003-10-19 10:23:54+01', TIMESTAMP '2003-10-19 10:23:54'),
+('develop', 7, 4200, '2008-01-01', '15:00', '15:00 BST', '5 years'::interval, TIMESTAMP '2004-10-19 10:23:54+01', TIMESTAMP '2004-10-19 10:23:54'),
+('develop', 9, 4500, '2008-01-01', '17:00', '17:00 BST', '7 years'::interval, TIMESTAMP '2005-10-19 10:23:54+01', TIMESTAMP '2005-10-19 10:23:54'),
+('sales', 3, 4800, '2007-08-01', '18:00', '18:00 BST', '8 years'::interval, TIMESTAMP '2006-10-19 10:23:54+01', TIMESTAMP '2006-10-19 10:23:54'),
+('develop', 8, 6000, '2006-10-01', '19:00', '19:00 BST', '9 years'::interval, TIMESTAMP '2007-10-19 10:23:54+01', TIMESTAMP '2007-10-19 10:23:54'),
+('develop', 11, 5200, '2007-08-15', '20:00', '20:00 BST', '10 years'::interval, TIMESTAMP '2008-10-19 10:23:54+01', TIMESTAMP '2008-10-19 10:23:54');
SELECT depname, empno, salary, sum(salary) OVER (PARTITION BY depname) FROM empsalary ORDER BY depname, salary;
@@ -189,6 +194,22 @@ SELECT sum(unique1) over (rows between 2 preceding and 2 following),
unique1, four
FROM tenk1 WHERE unique1 < 10;
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude no others),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (rows between 2 preceding and 2 following exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
SELECT sum(unique1) over (rows between 2 preceding and 1 preceding),
unique1, four
FROM tenk1 WHERE unique1 < 10;
@@ -205,10 +226,17 @@ SELECT sum(unique1) over (w range between current row and unbounded following),
unique1, four
FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
--- fail: not implemented yet
-SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude current row),
unique1, four
-FROM tenk1 WHERE unique1 < 10;
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
+
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
+
+SELECT sum(unique1) over (w range between unbounded preceding and current row exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10 WINDOW w AS (order by four);
SELECT first_value(unique1) over w,
nth_value(unique1, 2) over w AS nth_2,
@@ -230,6 +258,394 @@ SELECT * FROM v_window;
SELECT pg_get_viewdef('v_window');
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude current row) as sum_rows FROM generate_series(1, 10) i;
+
+SELECT * FROM v_window;
+
+SELECT pg_get_viewdef('v_window');
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude group) as sum_rows FROM generate_series(1, 10) i;
+
+SELECT * FROM v_window;
+
+SELECT pg_get_viewdef('v_window');
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude ties) as sum_rows FROM generate_series(1, 10) i;
+
+SELECT * FROM v_window;
+
+SELECT pg_get_viewdef('v_window');
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i rows between 1 preceding and 1 following
+ exclude no others) as sum_rows FROM generate_series(1, 10) i;
+
+SELECT * FROM v_window;
+
+SELECT pg_get_viewdef('v_window');
+
+CREATE OR REPLACE TEMP VIEW v_window AS
+ SELECT i, sum(i) over (order by i groups between 1 preceding and 1 following
+ exclude no others) as sum_rows FROM generate_series(1, 10) i;
+
+SELECT * FROM v_window;
+
+SELECT pg_get_viewdef('v_window');
+
+-- RANGE BETWEEN with values tests
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four desc range between 2::int8 preceding and 1::int2 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude no others),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 1::int2 preceding exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude ties),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four range between 2::int8 preceding and 6::int2 following exclude group),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by four order by unique1 range between 5::int8 preceding and 6::int2 following
+ exclude current row),unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_date desc range between '1 year'::interval preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_date desc range between '1 year'::interval following and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude current row), salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude group), salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_date range between '1 year'::interval preceding and '1 year'::interval following
+ exclude ties), salary, enroll_date from empsalary;
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_time desc range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_time desc range between '1 hour'::interval following and '2 hours'::interval following),
+ salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude current row), salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude group), salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_time range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude ties), salary, enroll_time from empsalary;
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_timetz desc range between '1 hour'::interval preceding and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_timetz desc range between '1 hour'::interval following and '2 hours'::interval following),
+ salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude current row), salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude group), salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_timetz range between '1 hour'::interval preceding and '2 hours'::interval following
+ exclude ties), salary, enroll_timetz from empsalary;
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_interval desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_interval desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_interval range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_interval from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamptz range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamptz from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp desc range between '1 year'::interval preceding and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp desc range between '1 year'::interval following and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude current row), salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude group), salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between current row and '2 years'::interval following),
+ salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by enroll_timestamp range between '1 year'::interval preceding and current row),
+ salary, enroll_timestamp from empsalary;
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude current row), salary, enroll_date from empsalary;
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude group), salary, enroll_date from empsalary;
+
+select sum(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude ties), salary, enroll_date from empsalary;
+
+select first_value(salary) over(order by salary range between 1000 preceding and 1000 following),
+ lead(salary) over(order by salary range between 1000 preceding and 1000 following),
+ nth_value(salary, 1) over(order by salary range between 1000 preceding and 1000 following),
+ salary from empsalary;
+
+select last_value(salary) over(order by salary range between 1000 preceding and 1000 following),
+ lag(salary) over(order by salary range between 1000 preceding and 1000 following),
+ salary from empsalary;
+
+select first_value(salary) over(order by salary range between 1000 following and 3000 following
+ exclude current row),
+ lead(salary) over(order by salary range between 1000 following and 3000 following exclude ties),
+ nth_value(salary, 1) over(order by salary range between 1000 following and 3000 following
+ exclude ties),
+ salary from empsalary;
+
+select last_value(salary) over(order by salary range between 1000 following and 3000 following
+ exclude group),
+ lag(salary) over(order by salary range between 1000 following and 3000 following exclude group),
+ salary from empsalary;
+
+select first_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following
+ exclude ties),
+ last_value(salary) over(order by enroll_date range between unbounded preceding and '1 year'::interval following),
+ salary, enroll_date from empsalary;
+
+-- RANGE BETWEEN with values negative tests
+select sum(salary) over (order by enroll_timestamp, enroll_date range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select sum(salary) over (order by depname range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select max(enroll_date) over (order by enroll_timestamp range between 1 preceding and 2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select max(enroll_date) over (order by salary range between -1 preceding and 2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select max(enroll_date) over (order by salary range between 1 preceding and -2 following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+select max(enroll_date) over (order by salary range between '1 year'::interval preceding and '2 years'::interval following
+ exclude ties), salary, enroll_timestamp from empsalary;
+
+-- GROUPS tests
+
+SELECT sum(unique1) over (order by four groups between unbounded preceding and current row),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between unbounded preceding and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between current row and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 1 preceding and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 1 following and unbounded following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between unbounded preceding and 2 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 preceding),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 0 preceding and 0 following),
+ unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude current row), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude group), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (order by four groups between 2 preceding and 1 following
+ exclude ties), unique1, four
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following),unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude current row), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude group), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+
+SELECT sum(unique1) over (partition by ten
+ order by four groups between 0 preceding and 0 following exclude ties), unique1, four, ten
+FROM tenk1 WHERE unique1 < 10;
+
+select first_value(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ lead(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ nth_value(salary, 1) over(order by enroll_date groups between 1 preceding and 1 following),
+ salary, enroll_date from empsalary;
+
+select last_value(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ lag(salary) over(order by enroll_date groups between 1 preceding and 1 following),
+ salary, enroll_date from empsalary;
+
+select first_value(salary) over(order by enroll_date groups between 1 following and 3 following
+ exclude current row),
+ lead(salary) over(order by enroll_date groups between 1 following and 3 following exclude ties),
+ nth_value(salary, 1) over(order by enroll_date groups between 1 following and 3 following
+ exclude ties),
+ salary, enroll_date from empsalary;
+
+select last_value(salary) over(order by enroll_date groups between 1 following and 3 following
+ exclude group),
+ lag(salary) over(order by enroll_date groups between 1 following and 3 following exclude group),
+ salary, enroll_date from empsalary;
+
+-- Show differences in values mode between ROWS, RANGE, and GROUPS
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following);
+
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x range between 1 preceding and 1 following);
+
+WITH cte (x) AS (
+ SELECT * FROM generate_series(1, 35, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following);
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x rows between 1 preceding and 1 following);
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x range between 1 preceding and 1 following);
+
+WITH cte (x) AS (
+ select 1 union all select 1 union all select 1 union all
+ SELECT * FROM generate_series(5, 49, 2)
+)
+SELECT x, (sum(x) over w)
+FROM cte
+WINDOW w AS (ORDER BY x groups between 1 preceding and 1 following);
+
-- with UNION
SELECT count(*) OVER (PARTITION BY four) FROM (SELECT * FROM tenk1 UNION ALL SELECT * FROM tenk2)s LIMIT 0;