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

Parse remaining types of window frames

上级 a1314402
...@@ -176,8 +176,10 @@ import org.h2.expression.aggregate.Aggregate.AggregateType; ...@@ -176,8 +176,10 @@ import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.JavaAggregate; import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window; import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFrame; import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrame.SimpleExtent; import org.h2.expression.aggregate.WindowFrameBound;
import org.h2.expression.aggregate.WindowFrame.WindowFrameExclusion; import org.h2.expression.aggregate.WindowFrameBound.WindowFrameBoundType;
import org.h2.expression.aggregate.WindowFrameExclusion;
import org.h2.expression.aggregate.WindowFrameUnits;
import org.h2.expression.aggregate.WindowFunction; import org.h2.expression.aggregate.WindowFunction;
import org.h2.expression.aggregate.WindowFunction.WindowFunctionType; import org.h2.expression.aggregate.WindowFunction.WindowFunctionType;
import org.h2.index.Index; import org.h2.index.Index;
...@@ -3074,8 +3076,7 @@ public class Parser { ...@@ -3074,8 +3076,7 @@ public class Parser {
frame = readWindowFrame(); frame = readWindowFrame();
break; break;
default: default:
frame = new WindowFrame(SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW, frame = null;
WindowFrameExclusion.EXCLUDE_NO_OTHERS);
} }
} else { } else {
frame = readWindowFrame(); frame = readWindowFrame();
...@@ -3092,46 +3093,68 @@ public class Parser { ...@@ -3092,46 +3093,68 @@ public class Parser {
} }
private WindowFrame readWindowFrame() { private WindowFrame readWindowFrame() {
SimpleExtent extent; WindowFrameUnits units;
if (readIf("ROWS")) {
units = WindowFrameUnits.ROWS;
} else if (readIf("RANGE")) {
units = WindowFrameUnits.RANGE;
} else if (readIf("GROUPS")) {
units = WindowFrameUnits.GROUPS;
} else {
return null;
}
WindowFrameBound starting, following;
if (readIf("BETWEEN")) {
starting = readWindowFrameStarting();
read("AND");
following = readWindowFrameFollowing();
} else {
starting = readWindowFrameStarting();
following = null;
}
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS; WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("RANGE")) { if (readIf("EXCLUDE")) {
read("BETWEEN"); if (readIf("CURRENT")) {
if (readIf("UNBOUNDED")) {
read("PRECEDING");
read("AND");
if (readIf("CURRENT")) {
read("ROW");
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
} else {
read("UNBOUNDED");
read("FOLLOWING");
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING;
}
} else {
read("CURRENT");
read("ROW"); read("ROW");
read("AND"); exclusion = WindowFrameExclusion.EXCLUDE_CURRENT_ROW;
read("UNBOUNDED"); } else if (readIf(GROUP)) {
read("FOLLOWING"); exclusion = WindowFrameExclusion.EXCLUDE_GROUP;
extent = SimpleExtent.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING; } else if (readIf("TIES")) {
} exclusion = WindowFrameExclusion.EXCLUDE_TIES;
if (readIf("EXCLUDE")) { } else {
if (readIf("CURRENT")) { read("NO");
read("ROW"); read("OTHERS");
exclusion = WindowFrameExclusion.EXCLUDE_CURRENT_ROW;
} else if (readIf(GROUP)) {
exclusion = WindowFrameExclusion.EXCLUDE_GROUP;
} else if (readIf("TIES")) {
exclusion = WindowFrameExclusion.EXCLUDE_TIES;
} else {
read("NO");
read("OTHERS");
}
} }
} else {
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
} }
return new WindowFrame(extent, exclusion); return new WindowFrame(units, starting, following, exclusion);
}
private WindowFrameBound readWindowFrameStarting() {
if (readIf("UNBOUNDED")) {
read("PRECEDING");
return new WindowFrameBound(WindowFrameBoundType.UNBOUNDED, 0);
}
if (readIf("CURRENT")) {
read("ROW");
return new WindowFrameBound(WindowFrameBoundType.CURRENT_ROW, 0);
}
int value = readNonNegativeInt();
read("PRECEDING");
return new WindowFrameBound(WindowFrameBoundType.VALUE, value);
}
private WindowFrameBound readWindowFrameFollowing() {
if (readIf("UNBOUNDED")) {
read("FOLLOWING");
return new WindowFrameBound(WindowFrameBoundType.UNBOUNDED, 0);
}
if (readIf("CURRENT")) {
read("ROW");
return new WindowFrameBound(WindowFrameBoundType.CURRENT_ROW, 0);
}
int value = readNonNegativeInt();
read("FOLLOWING");
return new WindowFrameBound(WindowFrameBoundType.VALUE, value);
} }
private AggregateType getAggregateType(String name) { private AggregateType getAggregateType(String name) {
......
...@@ -10,6 +10,7 @@ import java.util.ArrayList; ...@@ -10,6 +10,7 @@ import java.util.ArrayList;
import org.h2.command.dml.SelectOrderBy; import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.expression.aggregate.WindowFrameBound.WindowFrameBoundType;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
...@@ -64,6 +65,10 @@ public final class Window { ...@@ -64,6 +65,10 @@ public final class Window {
public Window(ArrayList<Expression> partitionBy, ArrayList<SelectOrderBy> orderBy, WindowFrame frame) { public Window(ArrayList<Expression> partitionBy, ArrayList<SelectOrderBy> orderBy, WindowFrame frame) {
this.partitionBy = partitionBy; this.partitionBy = partitionBy;
this.orderBy = orderBy; this.orderBy = orderBy;
if (frame == null) {
frame = new WindowFrame(WindowFrameUnits.RANGE, new WindowFrameBound(WindowFrameBoundType.UNBOUNDED, 0),
null, WindowFrameExclusion.EXCLUDE_NO_OTHERS);
}
this.frame = frame; this.frame = frame;
} }
......
...@@ -11,6 +11,7 @@ import java.util.Collections; ...@@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import org.h2.expression.aggregate.WindowFrameBound.WindowFrameBoundType;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -20,92 +21,6 @@ import org.h2.value.Value; ...@@ -20,92 +21,6 @@ import org.h2.value.Value;
*/ */
public final class WindowFrame { public final class WindowFrame {
/**
* Simple extent.
*/
public enum SimpleExtent {
/**
* RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW frame specification.
*/
RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW("RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"),
/**
* RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING frame specification.
*/
RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING("RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING"),
/**
* RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING frame
* specification.
*/
RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING(
"RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"),
;
private final String sql;
private SimpleExtent(String sql) {
this.sql = sql;
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return sql;
}
}
/**
* Window frame exclusion clause.
*/
public enum WindowFrameExclusion {
/**
* EXCLUDE CURRENT ROW exclusion clause.
*/
EXCLUDE_CURRENT_ROW("EXCLUDE CURRENT ROW"),
/**
* EXCLUDE GROUP exclusion clause.
*/
EXCLUDE_GROUP("EXCLUDE GROUP"),
/**
* EXCLUDE TIES exclusion clause.
*/
EXCLUDE_TIES("EXCLUDE TIES"),
/**
* EXCLUDE NO OTHERS exclusion clause.
*/
EXCLUDE_NO_OTHERS("EXCLUDE NO OTHERS"),
;
private final String sql;
private WindowFrameExclusion(String sql) {
this.sql = sql;
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return sql;
}
}
private abstract class Itr implements Iterator<Value[]> { private abstract class Itr implements Iterator<Value[]> {
final ArrayList<Value[]> orderedRows; final ArrayList<Value[]> orderedRows;
...@@ -231,20 +146,34 @@ public final class WindowFrame { ...@@ -231,20 +146,34 @@ public final class WindowFrame {
} }
private final SimpleExtent extent; private final WindowFrameUnits units;
private final WindowFrameBound starting;
private final WindowFrameBound following;
private final WindowFrameExclusion exclusion; private final WindowFrameExclusion exclusion;
/** /**
* Creates new instance of window frame clause. * Creates new instance of window frame clause.
* *
* @param extent * @param units
* window frame extent * units
* @param starting
* starting clause
* @param following
* following clause
* @param exclusion * @param exclusion
* window frame exclusion * exclusion clause
*/ */
public WindowFrame(SimpleExtent extent, WindowFrameExclusion exclusion) { public WindowFrame(WindowFrameUnits units, WindowFrameBound starting, WindowFrameBound following,
this.extent = extent; WindowFrameExclusion exclusion) {
this.units = units;
this.starting = starting;
if (following != null && following.getType() == WindowFrameBoundType.CURRENT_ROW) {
following = null;
}
this.following = following;
this.exclusion = exclusion; this.exclusion = exclusion;
} }
...@@ -254,7 +183,7 @@ public final class WindowFrame { ...@@ -254,7 +183,7 @@ public final class WindowFrame {
* @return whether window frame specification can be omitted * @return whether window frame specification can be omitted
*/ */
public boolean isDefault() { public boolean isDefault() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW return starting.getType() == WindowFrameBoundType.UNBOUNDED && following == null
&& exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS; && exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS;
} }
...@@ -265,7 +194,8 @@ public final class WindowFrame { ...@@ -265,7 +194,8 @@ public final class WindowFrame {
* @return whether window frame specification contains all rows in partition * @return whether window frame specification contains all rows in partition
*/ */
public boolean isFullPartition() { public boolean isFullPartition() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING return starting.getType() == WindowFrameBoundType.UNBOUNDED && following != null
&& following.getType() == WindowFrameBoundType.UNBOUNDED
&& exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS; && exclusion == WindowFrameExclusion.EXCLUDE_NO_OTHERS;
} }
...@@ -285,22 +215,57 @@ public final class WindowFrame { ...@@ -285,22 +215,57 @@ public final class WindowFrame {
public Iterator<Value[]> iterator(ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow, public Iterator<Value[]> iterator(ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow,
boolean reverse) { boolean reverse) {
int size = orderedRows.size(); int size = orderedRows.size();
final int startIndex, endIndex; final int startIndex;
switch (extent) { switch (starting.getType()) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW: case UNBOUNDED:
startIndex = 0; startIndex = 0;
endIndex = currentRow;
break; break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING: case CURRENT_ROW:
startIndex = currentRow; startIndex = currentRow;
endIndex = size - 1;
break; break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING: case VALUE:
startIndex = 0; switch (units) {
endIndex = size - 1; case ROWS: {
int value = starting.getValue();
startIndex = value > currentRow ? 0 : currentRow - value;
break;
}
default:
// TODO
throw DbException.getUnsupportedException("units=" + units);
}
break; break;
default: default:
throw DbException.getUnsupportedException("window frame extent =" + extent); throw DbException.getUnsupportedException("window frame bound type=" + starting.getType());
}
int endIndex;
if (following == null) {
endIndex = currentRow;
} else {
switch (following.getType()) {
case UNBOUNDED:
endIndex = size - 1;
break;
case CURRENT_ROW:
endIndex = currentRow;
break;
case VALUE:
switch (units) {
case ROWS: {
int value = following.getValue();
int rem = size - currentRow - 1;
endIndex = value > rem ? size - 1 : currentRow + value;
break;
}
default:
// TODO
throw DbException.getUnsupportedException("units=" + units);
}
break;
default:
throw DbException.getUnsupportedException("window frame bound type=" + following.getType());
}
} }
if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) { if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) {
return complexIterator(orderedRows, sortOrder, currentRow, startIndex, endIndex, reverse); return complexIterator(orderedRows, sortOrder, currentRow, startIndex, endIndex, reverse);
...@@ -350,11 +315,17 @@ public final class WindowFrame { ...@@ -350,11 +315,17 @@ public final class WindowFrame {
* @see org.h2.expression.Expression#getSQL() * @see org.h2.expression.Expression#getSQL()
*/ */
public String getSQL() { public String getSQL() {
String sql = extent.getSQL(); StringBuilder builder = new StringBuilder();
builder.append(units.getSQL());
if (following == null) {
builder.append(' ').append(starting.getSQL(false));
} else {
builder.append(" BETWEEN ").append(starting.getSQL(false)).append(" AND ").append(following.getSQL(true));
}
if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) { if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) {
sql = sql + ' ' + exclusion.getSQL(); builder.append(' ').append(exclusion.getSQL());
} }
return sql; return builder.toString();
} }
} }
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
import org.h2.expression.Expression;
import org.h2.message.DbException;
/**
* Window frame bound.
*/
public class WindowFrameBound {
/**
* Window frame bound type.
*/
public enum WindowFrameBoundType {
/**
* UNBOUNDED PRECEDING or UNBOUNDED FOLLOWING clause.
*/
UNBOUNDED,
/**
* CURRENT_ROW clause.
*/
CURRENT_ROW,
/**
* PRECEDING or FOLLOWING clause.
*/
VALUE;
}
private final WindowFrameBoundType type;
private final int value;
/**
* Creates new instance of window frame bound.
*
* @param type
* bound type
* @param value
* bound value, if any
*/
public WindowFrameBound(WindowFrameBoundType type, int value) {
this.type = type;
if (type == WindowFrameBoundType.VALUE) {
if (value < 0) {
throw DbException.getInvalidValueException("unsigned", value);
}
this.value = value;
} else {
this.value = 0;
}
}
/**
* Returns the type
*
* @return the type
*/
public WindowFrameBoundType getType() {
return type;
}
/**
* Returns the value.
*
* @return the value
*/
public int getValue() {
return value;
}
/**
* Returns SQL representation.
*
* @param following
* if false return SQL for starting clause, if true return SQL
* for following clause
* @return SQL representation.
* @see Expression#getSQL()
*/
public String getSQL(boolean following) {
switch (type) {
case UNBOUNDED:
return following ? "UNBOUNDED FOLLOWING" : "UNBOUNDED PRECEDING";
case CURRENT_ROW:
return "CURRENT ROW";
case VALUE:
return value + (following ? " FOLLOWING" : " PRECEDING");
default:
throw DbException.throwInternalError("type=" + type);
}
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
/**
* Window frame exclusion clause.
*/
public enum WindowFrameExclusion {
/**
* EXCLUDE CURRENT ROW exclusion clause.
*/
EXCLUDE_CURRENT_ROW("EXCLUDE CURRENT ROW"),
/**
* EXCLUDE GROUP exclusion clause.
*/
EXCLUDE_GROUP("EXCLUDE GROUP"),
/**
* EXCLUDE TIES exclusion clause.
*/
EXCLUDE_TIES("EXCLUDE TIES"),
/**
* EXCLUDE NO OTHERS exclusion clause.
*/
EXCLUDE_NO_OTHERS("EXCLUDE NO OTHERS"),
;
private final String sql;
private WindowFrameExclusion(String sql) {
this.sql = sql;
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return sql;
}
}
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
/**
* Window frame units.
*/
public enum WindowFrameUnits {
/**
* ROWS unit.
*/
ROWS,
/**
* RANGE unit.
*/
RANGE,
/**
* GROUPS unit.
*/
GROUPS,
;
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return name();
}
}
...@@ -139,8 +139,8 @@ SELECT ID, CATEGORY, ...@@ -139,8 +139,8 @@ SELECT ID, CATEGORY,
NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) C, NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) C,
NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW)
FROM TEST FETCH FIRST 3 ROWS ONLY; FROM TEST FETCH FIRST 3 ROWS ONLY;
> ID CATEGORY C NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) > ID CATEGORY C NTH_VALUE(CATEGORY, 2) OVER (ORDER BY CATEGORY RANGE UNBOUNDED PRECEDING EXCLUDE CURRENT ROW)
> -- -------- ---- --------------------------------------------------------------------------------------------------------------------- > -- -------- ---- ---------------------------------------------------------------------------------------------
> 1 1 null null > 1 1 null null
> 2 1 1 null > 2 1 1 null
> 3 1 1 1 > 3 1 1 1
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论