提交 bced9290 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Detect overflow in window frame bounds

上级 122fa953
...@@ -10,10 +10,11 @@ import java.util.Collections; ...@@ -10,10 +10,11 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.h2.api.ErrorCode;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.BinaryOperation; import org.h2.expression.BinaryOperation;
import org.h2.expression.Expression;
import org.h2.expression.BinaryOperation.OpType; import org.h2.expression.BinaryOperation.OpType;
import org.h2.expression.Expression;
import org.h2.expression.ValueExpression; import org.h2.expression.ValueExpression;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
...@@ -297,14 +298,32 @@ public final class WindowFrame { ...@@ -297,14 +298,32 @@ public final class WindowFrame {
return value; return value;
} }
/**
* Appends bound value to the current row and produces row for comparison
* operations.
*
* @param session
* the session
* @param orderedRows
* rows in partition
* @param sortOrder
* the sort order
* @param currentRow
* index of the current row
* @param bound
* window frame bound
* @param add
* false for PRECEDING, true for FOLLOWING
* @return row for comparison operations, or null if result is out of range
* and should be treated as UNLIMITED
*/
private static Value[] getCompareRow(Session session, ArrayList<Value[]> orderedRows, SortOrder sortOrder, private static Value[] getCompareRow(Session session, ArrayList<Value[]> orderedRows, SortOrder sortOrder,
int currentRow, WindowFrameBound bound, boolean add) { int currentRow, WindowFrameBound bound, boolean add) {
int sortIndex = sortOrder.getQueryColumnIndexes()[0]; int sortIndex = sortOrder.getQueryColumnIndexes()[0];
OpType opType = add ^ (sortOrder.getSortTypes()[0] & SortOrder.DESCENDING) != 0 ? OpType.PLUS : OpType.MINUS;
Value[] row = orderedRows.get(currentRow); Value[] row = orderedRows.get(currentRow);
Value[] newRow = row.clone();
Value currentValue = row[sortIndex]; Value currentValue = row[sortIndex];
switch (currentValue.getType()) { int type = currentValue.getType();
switch (type) {
case Value.BYTE: case Value.BYTE:
case Value.SHORT: case Value.SHORT:
case Value.INT: case Value.INT:
...@@ -334,8 +353,20 @@ public final class WindowFrame { ...@@ -334,8 +353,20 @@ public final class WindowFrame {
throw DbException.getInvalidValueException("ORDER BY value for RANGE frame", currentValue.getTraceSQL()); throw DbException.getInvalidValueException("ORDER BY value for RANGE frame", currentValue.getTraceSQL());
} }
Value range = getValueOffset(bound, orderedRows.get(currentRow), session); Value range = getValueOffset(bound, orderedRows.get(currentRow), session);
Value newValue = new BinaryOperation(opType, ValueExpression.get(currentValue), ValueExpression.get(range)) OpType opType = add ^ (sortOrder.getSortTypes()[0] & SortOrder.DESCENDING) != 0 ? OpType.PLUS : OpType.MINUS;
.optimize(session).getValue(session); Value newValue;
try {
newValue = new BinaryOperation(opType, ValueExpression.get(currentValue), ValueExpression.get(range))
.optimize(session).getValue(session).convertTo(type);
} catch (DbException ex) {
switch (ex.getErrorCode()) {
case ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_1:
case ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE_2:
return null;
}
throw ex;
}
Value[] newRow = row.clone();
newRow[sortIndex] = newValue; newRow[sortIndex] = newValue;
return newRow; return newRow;
} }
...@@ -575,6 +606,25 @@ public final class WindowFrame { ...@@ -575,6 +606,25 @@ public final class WindowFrame {
return endIndex; return endIndex;
} }
/**
* Returns starting or ending index of a window frame.
*
* @param session
* the session
* @param orderedRows
* rows in partition
* @param sortOrder
* the sort order
* @param currentRow
* index of the current row
* @param bound
* window frame bound
* @param forFollowing
* false for start index, true for end index
* @return starting or ending index of a window frame (inclusive), can be 0
* or be equal to the number of rows if frame is not limited from
* that side
*/
private int getIndex(Session session, ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow, private int getIndex(Session session, ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow,
WindowFrameBound bound, boolean forFollowing) { WindowFrameBound bound, boolean forFollowing) {
int size = orderedRows.size(); int size = orderedRows.size();
...@@ -618,6 +668,7 @@ public final class WindowFrame { ...@@ -618,6 +668,7 @@ public final class WindowFrame {
case RANGE: { case RANGE: {
index = currentRow; index = currentRow;
Value[] row = getCompareRow(session, orderedRows, sortOrder, index, bound, false); Value[] row = getCompareRow(session, orderedRows, sortOrder, index, bound, false);
if (row != null) {
index = Collections.binarySearch(orderedRows, row, sortOrder); index = Collections.binarySearch(orderedRows, row, sortOrder);
if (index >= 0) { if (index >= 0) {
if (!forFollowing) { if (!forFollowing) {
...@@ -639,6 +690,9 @@ public final class WindowFrame { ...@@ -639,6 +690,9 @@ public final class WindowFrame {
index--; index--;
} }
} }
} else {
index = -1;
}
break; break;
} }
default: default:
...@@ -694,6 +748,7 @@ public final class WindowFrame { ...@@ -694,6 +748,7 @@ public final class WindowFrame {
case RANGE: { case RANGE: {
index = currentRow; index = currentRow;
Value[] row = getCompareRow(session, orderedRows, sortOrder, index, bound, true); Value[] row = getCompareRow(session, orderedRows, sortOrder, index, bound, true);
if (row != null) {
index = Collections.binarySearch(orderedRows, row, sortOrder); index = Collections.binarySearch(orderedRows, row, sortOrder);
if (index >= 0) { if (index >= 0) {
if (forFollowing) { if (forFollowing) {
...@@ -713,6 +768,9 @@ public final class WindowFrame { ...@@ -713,6 +768,9 @@ public final class WindowFrame {
} }
} }
} }
} else {
index = size;
}
break; break;
} }
default: default:
......
...@@ -121,5 +121,31 @@ SELECT SUM(V) OVER (ORDER BY V RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM V ...@@ -121,5 +121,31 @@ SELECT SUM(V) OVER (ORDER BY V RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM V
SELECT SUM(V) OVER (ORDER BY V RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES (TRUE) T(V); SELECT SUM(V) OVER (ORDER BY V RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM VALUES (TRUE) T(V);
> exception INVALID_VALUE_2 > exception INVALID_VALUE_2
SELECT
SUM(ID) OVER (ORDER BY ID RANGE BETWEEN 10000000000 PRECEDING AND CURRENT ROW) P,
SUM(ID) OVER (ORDER BY ID RANGE BETWEEN 10000000001 PRECEDING AND 10000000000 PRECEDING) P2,
SUM(ID) OVER (ORDER BY ID RANGE BETWEEN CURRENT ROW AND 2147483647 FOLLOWING) F,
SUM(ID) OVER (ORDER BY ID RANGE BETWEEN 2147483647 FOLLOWING AND 2147483648 FOLLOWING) F2,
ID FROM TEST ORDER BY ID;
> P P2 F F2 ID
> -- ---- -- ---- --
> 1 null 63 null 1
> 3 null 62 null 2
> 7 null 60 null 4
> 15 null 56 null 8
> 31 null 48 null 16
> 63 null 32 null 32
> rows (ordered): 6
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
SELECT
ARRAY_AGG(T) OVER (ORDER BY T RANGE BETWEEN INTERVAL 1 DAY PRECEDING AND CURRENT ROW) C,
ARRAY_AGG(T) OVER (ORDER BY T RANGE BETWEEN INTERVAL 2 HOUR PRECEDING AND INTERVAL 1 HOUR PRECEDING) P,
T FROM VALUES (TIME '00:00:00'), (TIME '01:30:00') TEST(T) ORDER BY T;
> C P T
> -------------------- ---------- --------
> [00:00:00] null 00:00:00
> [00:00:00, 01:30:00] [00:00:00] 01:30:00
> rows (ordered): 2
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论