提交 32d3ca3f authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add WindowFrame class and move some logic into it

上级 249129a2
......@@ -175,7 +175,8 @@ import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.Window.SimpleWindowFrame;
import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrame.SimpleExtent;
import org.h2.expression.aggregate.WindowFunction;
import org.h2.expression.aggregate.WindowFunction.WindowFunctionType;
import org.h2.index.Index;
......@@ -3062,7 +3063,7 @@ public class Parser {
} else if (!isAggregate) {
orderBy = new ArrayList<>(0);
}
SimpleWindowFrame frame;
WindowFrame frame;
if (aggregate instanceof WindowFunction) {
WindowFunction w = (WindowFunction) aggregate;
switch (w.getFunctionType()) {
......@@ -3072,7 +3073,7 @@ public class Parser {
frame = readWindowFrame();
break;
default:
frame = SimpleWindowFrame.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
frame = new WindowFrame(SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW);
}
} else {
frame = readWindowFrame();
......@@ -3088,8 +3089,8 @@ public class Parser {
}
}
private SimpleWindowFrame readWindowFrame() {
SimpleWindowFrame frame;
private WindowFrame readWindowFrame() {
SimpleExtent extent;
if (readIf("RANGE")) {
read("BETWEEN");
if (readIf("UNBOUNDED")) {
......@@ -3097,11 +3098,11 @@ public class Parser {
read("AND");
if (readIf("CURRENT")) {
read("ROW");
frame = SimpleWindowFrame.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
} else {
read("UNBOUNDED");
read("FOLLOWING");
frame = SimpleWindowFrame.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING;
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING;
}
} else {
read("CURRENT");
......@@ -3109,12 +3110,12 @@ public class Parser {
read("AND");
read("UNBOUNDED");
read("FOLLOWING");
frame = SimpleWindowFrame.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING;
extent = SimpleExtent.RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING;
}
} else {
frame = SimpleWindowFrame.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
extent = SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
}
return frame;
return new WindowFrame(extent);
}
private AggregateType getAggregateType(String name) {
......
......@@ -424,16 +424,23 @@ public abstract class AbstractAggregate extends Expression {
*/
protected void getOrderedResultLoop(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered,
int rowIdColumn) {
switch (over.getWindowFrame()) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW: {
WindowFrame frame = over.getWindowFrame();
if (frame.isDefault()) {
Object aggregateData = createAggregateData();
for (Value[] row : ordered) {
updateFromExpressions(session, aggregateData, row);
result.put(row[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData));
}
break;
}
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING: {
} else if (frame.isFullPartition()) {
Object aggregateData = createAggregateData();
for (Value[] row : ordered) {
updateFromExpressions(session, aggregateData, row);
}
Value value = getAggregatedValue(session, aggregateData);
for (Value[] row : ordered) {
result.put(row[rowIdColumn].getInt(), value);
}
} else {
// TODO optimize unordered aggregates
int size = ordered.size();
for (int i = 0; i < size; i++) {
......@@ -443,18 +450,6 @@ public abstract class AbstractAggregate extends Expression {
}
result.put(ordered.get(i)[rowIdColumn].getInt(), getAggregatedValue(session, aggregateData));
}
break;
}
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING: {
Object aggregateData = createAggregateData();
for (Value[] row : ordered) {
updateFromExpressions(session, aggregateData, row);
}
Value value = getAggregatedValue(session, aggregateData);
for (Value[] row : ordered) {
result.put(row[rowIdColumn].getInt(), value);
}
}
}
}
......
......@@ -22,53 +22,11 @@ import org.h2.value.ValueArray;
*/
public final class Window {
/**
* Simple window frame.
*/
public enum SimpleWindowFrame {
/**
* 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 SimpleWindowFrame(String sql) {
this.sql = sql;
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see Expression#getSQL()
*/
public String getSQL() {
return sql;
}
}
private final ArrayList<Expression> partitionBy;
private final ArrayList<SelectOrderBy> orderBy;
private final SimpleWindowFrame frame;
private final WindowFrame frame;
/**
* @param builder
......@@ -100,7 +58,7 @@ public final class Window {
* @param frame
* window frame clause
*/
public Window(ArrayList<Expression> partitionBy, ArrayList<SelectOrderBy> orderBy, SimpleWindowFrame frame) {
public Window(ArrayList<Expression> partitionBy, ArrayList<SelectOrderBy> orderBy, WindowFrame frame) {
this.partitionBy = partitionBy;
this.orderBy = orderBy;
this.frame = frame;
......@@ -184,7 +142,7 @@ public final class Window {
*
* @return window frame
*/
public SimpleWindowFrame getWindowFrame() {
public WindowFrame getWindowFrame() {
return frame;
}
......@@ -229,7 +187,7 @@ public final class Window {
builder.append(StringUtils.unEnclose(partitionBy.get(i).getSQL()));
}
}
if (frame != SimpleWindowFrame.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW) {
if (!frame.isDefault()) {
builder.append(' ').append(frame.getSQL());
}
appendOrderBy(builder, orderBy);
......
/*
* 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 java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.h2.message.DbException;
import org.h2.value.Value;
/**
* Window frame clause.
*/
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;
}
}
private final SimpleExtent extent;
/**
* Creates new instance of window frame clause.
*
* @param extent
* window frame extent
*/
public WindowFrame(SimpleExtent extent) {
this.extent = extent;
}
/**
* Returns whether window frame specification can be omitted.
*
* @return whether window frame specification can be omitted
*/
public boolean isDefault() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW;
}
/**
* Returns whether window frame specification contains all rows in
* partition.
*
* @return whether window frame specification contains all rows in partition
*/
public boolean isFullPartition() {
return extent == SimpleExtent.RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING;
}
/**
* Returns iterator.
*
* @param orderedRows
* ordered rows
* @param currentRow
* index of the current row
* @return iterator
*/
public Iterator<Value[]> iterator(final ArrayList<Value[]> orderedRows, int currentRow) {
int size = orderedRows.size();
final int startIndex, endIndex;
switch (extent) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW:
startIndex = 0;
endIndex = currentRow;
break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING:
startIndex = currentRow;
endIndex = size - 1;
break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING:
startIndex = 0;
endIndex = size - 1;
break;
default:
throw DbException.getUnsupportedException("window frame extent =" + extent);
}
return new Iterator<Value[]>() {
private int cursor = startIndex;
@Override
public boolean hasNext() {
return cursor <= endIndex;
}
@Override
public Value[] next() {
if (cursor > endIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor++);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Returns iterator in descending order.
*
* @param orderedRows
* ordered rows
* @param currentRow
* index of the current row
* @return iterator in descending order
*/
public Iterator<Value[]> reverseIterator(final ArrayList<Value[]> orderedRows, int currentRow) {
int size = orderedRows.size();
final int startIndex, endIndex;
switch (extent) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW:
startIndex = 0;
endIndex = currentRow;
break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING:
startIndex = currentRow;
endIndex = size - 1;
break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING:
startIndex = 0;
endIndex = size - 1;
break;
default:
throw DbException.getUnsupportedException("window frame extent =" + extent);
}
return new Iterator<Value[]>() {
private int cursor = endIndex;
@Override
public boolean hasNext() {
return cursor >= startIndex;
}
@Override
public Value[] next() {
if (cursor < startIndex) {
throw new NoSuchElementException();
}
return orderedRows.get(cursor--);
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* Returns SQL representation.
*
* @return SQL representation.
* @see org.h2.expression.Expression#getSQL()
*/
public String getSQL() {
return extent.getSQL();
}
}
......@@ -7,6 +7,7 @@ package org.h2.expression.aggregate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import org.h2.command.dml.Select;
import org.h2.engine.Session;
......@@ -130,34 +131,15 @@ public class WindowFunction extends AbstractAggregate {
}
}
private static Value getNthValue(ArrayList<Value[]> ordered, int startIndex, int endIndex, int number,
boolean fromLast, boolean ignoreNulls) {
return ignoreNulls ? getNthValueIgnoreNulls(ordered, startIndex, endIndex, number, fromLast)
: ordered.get(fromLast ? endIndex - number : startIndex + number)[0];
}
private static Value getNthValueIgnoreNulls(ArrayList<Value[]> ordered, int startIndex, int endIndex, int number,
boolean fromLast) {
private static Value getNthValue(Iterator<Value[]> iterator, int number, boolean ignoreNulls) {
Value v = ValueNull.INSTANCE;
int cnt = 0;
if (fromLast) {
for (int i = endIndex; i >= startIndex; i--) {
Value t = ordered.get(i)[0];
if (t != ValueNull.INSTANCE) {
if (cnt++ == number) {
v = t;
break;
}
}
}
} else {
for (int i = startIndex; i <= endIndex; i++) {
Value t = ordered.get(i)[0];
if (t != ValueNull.INSTANCE) {
if (cnt++ == number) {
v = t;
break;
}
while (iterator.hasNext()) {
Value t = iterator.next()[0];
if (!ignoreNulls || t != ValueNull.INSTANCE) {
if (cnt++ == number) {
v = t;
break;
}
}
}
......@@ -333,33 +315,17 @@ public class WindowFunction extends AbstractAggregate {
private void getNth(Session session, HashMap<Integer, Value> result, ArrayList<Value[]> ordered, int rowIdColumn) {
int size = ordered.size();
for (int i = 0; i < size; i++) {
int startIndex, endIndex;
switch (over.getWindowFrame()) {
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW:
startIndex = 0;
endIndex = i;
break;
case RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING:
startIndex = i;
endIndex = size - 1;
break;
case RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING:
startIndex = 0;
endIndex = size - 1;
break;
default:
throw DbException.getUnsupportedException("window frame=" + over.getWindowFrame());
}
WindowFrame frame = over.getWindowFrame();
Value[] row = ordered.get(i);
int rowId = row[rowIdColumn].getInt();
Value v;
switch (type) {
case FIRST_VALUE: {
v = getNthValue(ordered, startIndex, endIndex, 0, false, ignoreNulls);
v = getNthValue(frame.iterator(ordered, i), 0, ignoreNulls);
break;
}
case LAST_VALUE:
v = getNthValue(ordered, startIndex, endIndex, 0, true, ignoreNulls);
v = getNthValue(frame.reverseIterator(ordered, i), 0, ignoreNulls);
break;
case NTH_VALUE: {
int n = row[1].getInt();
......@@ -367,11 +333,8 @@ public class WindowFunction extends AbstractAggregate {
throw DbException.getInvalidValueException("nth row", n);
}
n--;
if (n > endIndex - startIndex) {
v = ValueNull.INSTANCE;
} else {
v = getNthValue(ordered, startIndex, endIndex, n, fromLast, ignoreNulls);
}
Iterator<Value[]> iter = fromLast ? frame.reverseIterator(ordered, i) : frame.iterator(ordered, i);
v = getNthValue(iter, n, ignoreNulls);
break;
}
default:
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论