提交 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;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrame.SimpleExtent;
import org.h2.expression.aggregate.WindowFrame.WindowFrameExclusion;
import org.h2.expression.aggregate.WindowFrameBound;
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.WindowFunctionType;
import org.h2.index.Index;
......@@ -3074,8 +3076,7 @@ public class Parser {
frame = readWindowFrame();
break;
default:
frame = new WindowFrame(SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW,
WindowFrameExclusion.EXCLUDE_NO_OTHERS);
frame = null;
}
} else {
frame = readWindowFrame();
......@@ -3092,29 +3093,26 @@ public class Parser {
}
private WindowFrame readWindowFrame() {
SimpleExtent extent;
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("RANGE")) {
read("BETWEEN");
if (readIf("UNBOUNDED")) {
read("PRECEDING");
read("AND");
if (readIf("CURRENT")) {
read("ROW");
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
WindowFrameUnits units;
if (readIf("ROWS")) {
units = WindowFrameUnits.ROWS;
} else if (readIf("RANGE")) {
units = WindowFrameUnits.RANGE;
} else if (readIf("GROUPS")) {
units = WindowFrameUnits.GROUPS;
} else {
read("UNBOUNDED");
read("FOLLOWING");
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING;
return null;
}
} else {
read("CURRENT");
read("ROW");
WindowFrameBound starting, following;
if (readIf("BETWEEN")) {
starting = readWindowFrameStarting();
read("AND");
read("UNBOUNDED");
read("FOLLOWING");
extent = SimpleExtent.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING;
following = readWindowFrameFollowing();
} else {
starting = readWindowFrameStarting();
following = null;
}
WindowFrameExclusion exclusion = WindowFrameExclusion.EXCLUDE_NO_OTHERS;
if (readIf("EXCLUDE")) {
if (readIf("CURRENT")) {
read("ROW");
......@@ -3128,10 +3126,35 @@ public class Parser {
read("OTHERS");
}
}
} else {
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
return new WindowFrame(units, starting, following, exclusion);
}
return new WindowFrame(extent, 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) {
......
......@@ -10,6 +10,7 @@ import java.util.ArrayList;
import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.aggregate.WindowFrameBound.WindowFrameBoundType;
import org.h2.result.SortOrder;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
......@@ -64,6 +65,10 @@ public final class Window {
public Window(ArrayList<Expression> partitionBy, ArrayList<SelectOrderBy> orderBy, WindowFrame frame) {
this.partitionBy = partitionBy;
this.orderBy = orderBy;
if (frame == null) {
frame = new WindowFrame(WindowFrameUnits.RANGE, new WindowFrameBound(WindowFrameBoundType.UNBOUNDED, 0),
null, WindowFrameExclusion.EXCLUDE_NO_OTHERS);
}
this.frame = frame;
}
......
......@@ -11,6 +11,7 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.h2.expression.aggregate.WindowFrameBound.WindowFrameBoundType;
import org.h2.message.DbException;
import org.h2.result.SortOrder;
import org.h2.value.Value;
......@@ -20,92 +21,6 @@ import org.h2.value.Value;
*/
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[]> {
final ArrayList<Value[]> orderedRows;
......@@ -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;
/**
* Creates new instance of window frame clause.
*
* @param extent
* window frame extent
* @param units
* units
* @param starting
* starting clause
* @param following
* following clause
* @param exclusion
* window frame exclusion
* exclusion clause
*/
public WindowFrame(SimpleExtent extent, WindowFrameExclusion exclusion) {
this.extent = extent;
public WindowFrame(WindowFrameUnits units, WindowFrameBound starting, WindowFrameBound following,
WindowFrameExclusion exclusion) {
this.units = units;
this.starting = starting;
if (following != null && following.getType() == WindowFrameBoundType.CURRENT_ROW) {
following = null;
}
this.following = following;
this.exclusion = exclusion;
}
......@@ -254,7 +183,7 @@ public final class WindowFrame {
* @return whether window frame specification can be omitted
*/
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;
}
......@@ -265,7 +194,8 @@ public final class WindowFrame {
* @return whether window frame specification contains all rows in partition
*/
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;
}
......@@ -285,22 +215,57 @@ public final class WindowFrame {
public Iterator<Value[]> iterator(ArrayList<Value[]> orderedRows, SortOrder sortOrder, int currentRow,
boolean reverse) {
int size = orderedRows.size();
final int startIndex, endIndex;
switch (extent) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW:
final int startIndex;
switch (starting.getType()) {
case UNBOUNDED:
startIndex = 0;
endIndex = currentRow;
break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING:
case CURRENT_ROW:
startIndex = currentRow;
endIndex = size - 1;
break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING:
startIndex = 0;
case VALUE:
switch (units) {
case ROWS: {
int value = starting.getValue();
startIndex = value > currentRow ? 0 : currentRow - value;
break;
}
default:
// TODO
throw DbException.getUnsupportedException("units=" + units);
}
break;
default:
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:
throw DbException.getUnsupportedException("window frame extent =" + extent);
// TODO
throw DbException.getUnsupportedException("units=" + units);
}
break;
default:
throw DbException.getUnsupportedException("window frame bound type=" + following.getType());
}
}
if (exclusion != WindowFrameExclusion.EXCLUDE_NO_OTHERS) {
return complexIterator(orderedRows, sortOrder, currentRow, startIndex, endIndex, reverse);
......@@ -350,11 +315,17 @@ public final class WindowFrame {
* @see org.h2.expression.Expression#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) {
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,
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)
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
> 2 1 1 null
> 3 1 1 1
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论