Unverified 提交 f84b575c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1299 from katzyn/distinct

Handle distinct results properly
...@@ -2535,7 +2535,7 @@ public class Parser { ...@@ -2535,7 +2535,7 @@ public class Parser {
} }
currentSelect = temp; currentSelect = temp;
if (readIf(DISTINCT)) { if (readIf(DISTINCT)) {
command.setDistinct(true); command.setDistinct();
} else { } else {
readIf(ALL); readIf(ALL);
} }
...@@ -2749,9 +2749,7 @@ public class Parser { ...@@ -2749,9 +2749,7 @@ public class Parser {
} else { } else {
if (isSelect()) { if (isSelect()) {
Query query = parseSelect(); Query query = parseSelect();
// can not be lazy because we have to call // TODO lazy result causes timeout in TestFuzzOptimizations
// method ResultInterface.containsDistinct
// which is not supported for lazy execution
query.setNeverLazy(true); query.setNeverLazy(true);
r = new ConditionInSelect(database, r, query, false, r = new ConditionInSelect(database, r, query, false,
Comparison.EQUAL); Comparison.EQUAL);
......
...@@ -255,13 +255,17 @@ public abstract class Query extends Prepared { ...@@ -255,13 +255,17 @@ public abstract class Query extends Prepared {
/** /**
* Set the distinct flag. * Set the distinct flag.
*
* @param b the new value
*/ */
public void setDistinct(boolean b) { public void setDistinct() {
distinct = b; distinct = true;
} }
/**
* Set the distinct flag only if it is possible, may be used as a possible
* optimization only.
*/
public abstract void setDistinctIfPossible();
public boolean isDistinct() { public boolean isDistinct() {
return distinct; return distinct;
} }
......
...@@ -247,6 +247,13 @@ public class Select extends Query { ...@@ -247,6 +247,13 @@ public class Select extends Query {
return orderList != null || sort != null; return orderList != null || sort != null;
} }
@Override
public void setDistinctIfPossible() {
if (!distinct && offsetExpr == null && limitExpr == null) {
setDistinct();
}
}
/** /**
* Add a condition to the list of conditions. * Add a condition to the list of conditions.
* *
...@@ -667,9 +674,6 @@ public class Select extends Query { ...@@ -667,9 +674,6 @@ public class Select extends Query {
result = createLocalResult(result); result = createLocalResult(result);
result.setDistinct(); result.setDistinct();
} }
if (randomAccessResult) {
result = createLocalResult(result);
}
if (isGroupQuery && !isGroupSortedQuery) { if (isGroupQuery && !isGroupSortedQuery) {
result = createLocalResult(result); result = createLocalResult(result);
} }
...@@ -724,8 +728,12 @@ public class Select extends Query { ...@@ -724,8 +728,12 @@ public class Select extends Query {
if (limitRows > 0) { if (limitRows > 0) {
lazyResult.setLimit(limitRows); lazyResult.setLimit(limitRows);
} }
if (randomAccessResult) {
return convertToDistinct(lazyResult);
} else {
return lazyResult; return lazyResult;
} }
}
if (offsetExpr != null) { if (offsetExpr != null) {
result.setOffset(offsetExpr.getValue(session).getInt()); result.setOffset(offsetExpr.getValue(session).getInt());
} }
...@@ -734,6 +742,9 @@ public class Select extends Query { ...@@ -734,6 +742,9 @@ public class Select extends Query {
} }
if (result != null) { if (result != null) {
result.done(); result.done();
if (randomAccessResult && !distinct) {
result = convertToDistinct(result);
}
if (target != null) { if (target != null) {
while (result.next()) { while (result.next()) {
target.addRow(result.currentRow()); target.addRow(result.currentRow());
...@@ -761,6 +772,18 @@ public class Select extends Query { ...@@ -761,6 +772,18 @@ public class Select extends Query {
visibleColumnCount); visibleColumnCount);
} }
private LocalResult convertToDistinct(ResultInterface result) {
LocalResult distinctResult = new LocalResult(session, expressionArray, visibleColumnCount);
distinctResult.setDistinct();
result.reset();
while (result.next()) {
distinctResult.addRow(result.currentRow());
}
result.close();
distinctResult.done();
return distinctResult;
}
private void expandColumnList() { private void expandColumnList() {
Database db = session.getDatabase(); Database db = session.getDatabase();
......
...@@ -125,6 +125,11 @@ public class SelectUnion extends Query { ...@@ -125,6 +125,11 @@ public class SelectUnion extends Query {
return orderList != null || sort != null; return orderList != null || sort != null;
} }
@Override
public void setDistinctIfPossible() {
setDistinct();
}
private Value[] convert(Value[] values, int columnCount) { private Value[] convert(Value[] values, int columnCount) {
Value[] newValues; Value[] newValues;
if (columnCount == values.length) { if (columnCount == values.length) {
...@@ -210,25 +215,22 @@ public class SelectUnion extends Query { ...@@ -210,25 +215,22 @@ public class SelectUnion extends Query {
result.setSortOrder(sort); result.setSortOrder(sort);
} }
if (distinct) { if (distinct) {
left.setDistinct(true); left.setDistinctIfPossible();
right.setDistinct(true); right.setDistinctIfPossible();
result.setDistinct(); result.setDistinct();
} }
if (randomAccessResult) {
result.setRandomAccess();
}
switch (unionType) { switch (unionType) {
case UNION: case UNION:
case EXCEPT: case EXCEPT:
left.setDistinct(true); left.setDistinctIfPossible();
right.setDistinct(true); right.setDistinctIfPossible();
result.setDistinct(); result.setDistinct();
break; break;
case UNION_ALL: case UNION_ALL:
break; break;
case INTERSECT: case INTERSECT:
left.setDistinct(true); left.setDistinctIfPossible();
right.setDistinct(true); right.setDistinctIfPossible();
break; break;
default: default:
DbException.throwInternalError("type=" + unionType); DbException.throwInternalError("type=" + unionType);
...@@ -260,7 +262,6 @@ public class SelectUnion extends Query { ...@@ -260,7 +262,6 @@ public class SelectUnion extends Query {
case INTERSECT: { case INTERSECT: {
LocalResult temp = new LocalResult(session, expressionArray, columnCount); LocalResult temp = new LocalResult(session, expressionArray, columnCount);
temp.setDistinct(); temp.setDistinct();
temp.setRandomAccess();
while (l.next()) { while (l.next()) {
temp.addRow(convert(l.currentRow(), columnCount)); temp.addRow(convert(l.currentRow(), columnCount));
} }
......
...@@ -43,9 +43,7 @@ public class ConditionInSelect extends Condition { ...@@ -43,9 +43,7 @@ public class ConditionInSelect extends Condition {
@Override @Override
public Value getValue(Session session) { public Value getValue(Session session) {
query.setSession(session); query.setSession(session);
if (!query.hasOrder()) { query.setDistinctIfPossible();
query.setDistinct(true);
}
ResultInterface rows = query.query(0); ResultInterface rows = query.query(0);
Value l = left.getValue(session); Value l = left.getValue(session);
if (!rows.hasNext()) { if (!rows.hasNext()) {
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
*/ */
package org.h2.mvstore.db; package org.h2.mvstore.db;
import java.util.Arrays;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -22,11 +20,6 @@ import org.h2.value.ValueArray; ...@@ -22,11 +20,6 @@ import org.h2.value.ValueArray;
*/ */
class MVPlainTempResult extends MVTempResult { class MVPlainTempResult extends MVTempResult {
/**
* The type of the distinct values.
*/
private final ValueDataType distinctType;
/** /**
* Map with identities of rows as keys rows as values. * Map with identities of rows as keys rows as values.
*/ */
...@@ -39,12 +32,6 @@ class MVPlainTempResult extends MVTempResult { ...@@ -39,12 +32,6 @@ class MVPlainTempResult extends MVTempResult {
*/ */
private long counter; private long counter;
/**
* Optional index. This index is created only if {@link #contains(Value[])}
* method is invoked. Only the root result should have an index if required.
*/
private MVMap<ValueArray, Boolean> index;
/** /**
* Cursor for the {@link #next()} method. * Cursor for the {@link #next()} method.
*/ */
...@@ -58,7 +45,6 @@ class MVPlainTempResult extends MVTempResult { ...@@ -58,7 +45,6 @@ class MVPlainTempResult extends MVTempResult {
*/ */
private MVPlainTempResult(MVPlainTempResult parent) { private MVPlainTempResult(MVPlainTempResult parent) {
super(parent); super(parent);
this.distinctType = null;
this.map = parent.map; this.map = parent.map;
} }
...@@ -75,46 +61,20 @@ class MVPlainTempResult extends MVTempResult { ...@@ -75,46 +61,20 @@ class MVPlainTempResult extends MVTempResult {
MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) { MVPlainTempResult(Database database, Expression[] expressions, int visibleColumnCount) {
super(database, expressions.length, visibleColumnCount); super(database, expressions.length, visibleColumnCount);
ValueDataType valueType = new ValueDataType(database.getCompareMode(), database, new int[columnCount]); ValueDataType valueType = new ValueDataType(database.getCompareMode(), database, new int[columnCount]);
if (columnCount == visibleColumnCount) {
distinctType = valueType;
} else {
distinctType = new ValueDataType(database.getCompareMode(), database, new int[visibleColumnCount]);
}
Builder<Long, ValueArray> builder = new MVMap.Builder<Long, ValueArray>().valueType(valueType); Builder<Long, ValueArray> builder = new MVMap.Builder<Long, ValueArray>().valueType(valueType);
map = store.openMap("tmp", builder); map = store.openMap("tmp", builder);
} }
@Override @Override
public int addRow(Value[] values) { public int addRow(Value[] values) {
assert parent == null && index == null; assert parent == null;
map.put(counter++, ValueArray.get(values)); map.put(counter++, ValueArray.get(values));
return ++rowCount; return ++rowCount;
} }
@Override @Override
public boolean contains(Value[] values) { public boolean contains(Value[] values) {
// Only parent result maintains the index throw DbException.getUnsupportedException("contains()");
if (parent != null) {
return parent.contains(values);
}
if (index == null) {
createIndex();
}
return index.containsKey(ValueArray.get(values));
}
private void createIndex() {
Builder<ValueArray, Boolean> builder = new MVMap.Builder<ValueArray, Boolean>().keyType(distinctType);
index = store.openMap("idx", builder);
Cursor<Long, ValueArray> c = map.cursor(null);
while (c.hasNext()) {
c.next();
ValueArray row = c.getValue();
if (columnCount != visibleColumnCount) {
row = ValueArray.get(Arrays.copyOf(row.getList(), visibleColumnCount));
}
index.putIfAbsent(row, true);
}
} }
@Override @Override
......
...@@ -171,7 +171,9 @@ class MVSortedTempResult extends MVTempResult { ...@@ -171,7 +171,9 @@ class MVSortedTempResult extends MVTempResult {
} else { } else {
distinctType = new ValueDataType(database.getCompareMode(), database, new int[visibleColumnCount]); distinctType = new ValueDataType(database.getCompareMode(), database, new int[visibleColumnCount]);
if (distinct) { if (distinct) {
createIndex(false); Builder<ValueArray, Boolean> indexBuilder = new MVMap.Builder<ValueArray, Boolean>()
.keyType(distinctType);
index = store.openMap("idx", indexBuilder);
} }
} }
} }
...@@ -209,29 +211,13 @@ class MVSortedTempResult extends MVTempResult { ...@@ -209,29 +211,13 @@ class MVSortedTempResult extends MVTempResult {
if (parent != null) { if (parent != null) {
return parent.contains(values); return parent.contains(values);
} }
assert distinct;
if (columnCount != visibleColumnCount) { if (columnCount != visibleColumnCount) {
if (index == null) {
createIndex(true);
}
return index.containsKey(ValueArray.get(values)); return index.containsKey(ValueArray.get(values));
} }
return map.containsKey(getKey(values)); return map.containsKey(getKey(values));
} }
private void createIndex(boolean fill) {
Builder<ValueArray, Boolean> indexBuilder = new MVMap.Builder<ValueArray, Boolean>()
.keyType(distinctType);
index = store.openMap("idx", indexBuilder);
if (fill) {
Cursor<ValueArray, Long> c = map.cursor(null);
while (c.hasNext()) {
Value[] v = getValue(c.next().getList());
ValueArray distinctRow = ValueArray.get(Arrays.copyOf(v, visibleColumnCount));
index.putIfAbsent(distinctRow, true);
}
}
}
@Override @Override
public synchronized ResultExternal createShallowCopy() { public synchronized ResultExternal createShallowCopy() {
if (parent != null) { if (parent != null) {
......
...@@ -43,7 +43,6 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -43,7 +43,6 @@ public class LocalResult implements ResultInterface, ResultTarget {
private int limit = -1; private int limit = -1;
private ResultExternal external; private ResultExternal external;
private boolean distinct; private boolean distinct;
private boolean randomAccess;
private boolean closed; private boolean closed;
private boolean containsLobs; private boolean containsLobs;
...@@ -151,7 +150,6 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -151,7 +150,6 @@ public class LocalResult implements ResultInterface, ResultTarget {
copy.sort = this.sort; copy.sort = this.sort;
copy.distinctRows = this.distinctRows; copy.distinctRows = this.distinctRows;
copy.distinct = distinct; copy.distinct = distinct;
copy.randomAccess = randomAccess;
copy.currentRow = null; copy.currentRow = null;
copy.offset = 0; copy.offset = 0;
copy.limit = -1; copy.limit = -1;
...@@ -176,13 +174,6 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -176,13 +174,6 @@ public class LocalResult implements ResultInterface, ResultTarget {
distinctRows = ValueHashMap.newInstance(); distinctRows = ValueHashMap.newInstance();
} }
/**
* Random access is required (containsDistinct).
*/
public void setRandomAccess() {
this.randomAccess = true;
}
/** /**
* Remove the row from the result set if it exists. * Remove the row from the result set if it exists.
* *
...@@ -288,6 +279,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -288,6 +279,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
private void createExternalResult() { private void createExternalResult() {
Database database = session.getDatabase(); Database database = session.getDatabase();
external = database.isMVStore() external = database.isMVStore()
|| /* not supported by ResultTempTable */ distinct && expressions.length != visibleColumnCount
? MVTempResult.of(database, expressions, distinct, visibleColumnCount, sort) ? MVTempResult.of(database, expressions, distinct, visibleColumnCount, sort)
: new ResultTempTable(session, expressions, distinct, sort); : new ResultTempTable(session, expressions, distinct, sort);
} }
......
...@@ -144,36 +144,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -144,36 +144,7 @@ public class ResultTempTable implements ResultExternal {
closeable = new CloseImpl(session, table); closeable = new CloseImpl(session, table);
fileRef = tempFileDeleter.addFile(closeable, this); fileRef = tempFileDeleter.addFile(closeable, this);
} }
/*
* If ORDER BY or DISTINCT is specified create the index immediately. If
* they are not specified index still may be created later if required
* for IN (SELECT ...) etc.
*/
if (sort != null || distinct) { if (sort != null || distinct) {
getIndex();
}
}
private ResultTempTable(ResultTempTable parent) {
this.parent = parent;
this.columnCount = parent.columnCount;
this.distinct = parent.distinct;
this.session = parent.session;
this.table = parent.table;
this.rowCount = parent.rowCount;
this.sort = parent.sort;
this.tempFileDeleter = null;
this.closeable = null;
this.fileRef = null;
}
private Index getIndex() {
if (parent != null) {
return parent.getIndex();
}
if (index != null) {
return index;
}
IndexColumn[] indexCols; IndexColumn[] indexCols;
if (sort != null) { if (sort != null) {
int[] colIndex = sort.getQueryColumnIndexes(); int[] colIndex = sort.getQueryColumnIndexes();
...@@ -215,6 +186,26 @@ public class ResultTempTable implements ResultExternal { ...@@ -215,6 +186,26 @@ public class ResultTempTable implements ResultExternal {
if (closeable != null) { if (closeable != null) {
closeable.index = index; closeable.index = index;
} }
}
}
private ResultTempTable(ResultTempTable parent) {
this.parent = parent;
this.columnCount = parent.columnCount;
this.distinct = parent.distinct;
this.session = parent.session;
this.table = parent.table;
this.rowCount = parent.rowCount;
this.sort = parent.sort;
this.tempFileDeleter = null;
this.closeable = null;
this.fileRef = null;
}
private Index getIndex() {
if (parent != null) {
return parent.getIndex();
}
return index; return index;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论