提交 4ab34a53 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Fix sorting with distinct in ResultTempTable

上级 3ab43d18
...@@ -289,6 +289,10 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -289,6 +289,10 @@ public class LocalResult implements ResultInterface, ResultTarget {
return ValueArray.get(values); return ValueArray.get(values);
} }
private void createExternalResult() {
external = new ResultTempTable(session, expressions, distinct, sort);
}
/** /**
* Add a row to this object. * Add a row to this object.
* *
...@@ -303,7 +307,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -303,7 +307,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
distinctRows.put(array, values); distinctRows.put(array, values);
rowCount = distinctRows.size(); rowCount = distinctRows.size();
if (rowCount > maxMemoryRows) { if (rowCount > maxMemoryRows) {
external = new ResultTempTable(session, expressions, true, sort); createExternalResult();
rowCount = external.addRows(distinctRows.values()); rowCount = external.addRows(distinctRows.values());
distinctRows = null; distinctRows = null;
} }
...@@ -316,7 +320,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -316,7 +320,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
rowCount++; rowCount++;
if (rows.size() > maxMemoryRows) { if (rows.size() > maxMemoryRows) {
if (external == null) { if (external == null) {
external = new ResultTempTable(session, expressions, false, sort); createExternalResult();
} }
addRowsToDisk(); addRowsToDisk();
} }
...@@ -353,7 +357,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -353,7 +357,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
break; break;
} }
if (external == null) { if (external == null) {
external = new ResultTempTable(session, expressions, true, sort); createExternalResult();
} }
rows.add(list); rows.add(list);
if (rows.size() > maxMemoryRows) { if (rows.size() > maxMemoryRows) {
......
...@@ -7,6 +7,8 @@ package org.h2.result; ...@@ -7,6 +7,8 @@ package org.h2.result;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
import org.h2.command.ddl.CreateTableData; import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
...@@ -89,25 +91,40 @@ public class ResultTempTable implements ResultExternal { ...@@ -89,25 +91,40 @@ public class ResultTempTable implements ResultExternal {
private void createIndex() { private void createIndex() {
IndexColumn[] indexCols = null; IndexColumn[] indexCols = null;
// If we need to do distinct, the distinct columns may not match the if (sort != null) {
// sort columns. So we need to disregard the sort. Not ideal.
if (sort != null && !distinct) {
int[] colIndex = sort.getQueryColumnIndexes(); int[] colIndex = sort.getQueryColumnIndexes();
indexCols = new IndexColumn[colIndex.length]; int len = colIndex.length;
for (int i = 0; i < colIndex.length; i++) { int totalLen;
IndexColumn indexColumn = new IndexColumn(); BitSet used;
indexColumn.column = table.getColumn(colIndex[i]); if (distinct) {
totalLen = columnCount;
used = new BitSet();
} else {
totalLen = len;
used = null;
}
indexCols = new IndexColumn[totalLen];
for (int i = 0; i < len; i++) {
int idx = colIndex[i];
if (used != null) {
used.set(idx);
}
IndexColumn indexColumn = createIndexColumn(idx);
indexColumn.sortType = sort.getSortTypes()[i]; indexColumn.sortType = sort.getSortTypes()[i];
indexColumn.columnName = COLUMN_NAME + i;
indexCols[i] = indexColumn; indexCols[i] = indexColumn;
} }
if (used != null) {
int idx = 0;
for (int i = len; i < totalLen; i++) {
idx = used.nextClearBit(idx);
indexCols[i] = createIndexColumn(idx);
idx++;
}
}
} else { } else {
indexCols = new IndexColumn[columnCount]; indexCols = new IndexColumn[columnCount];
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
IndexColumn indexColumn = new IndexColumn(); indexCols[i] = createIndexColumn(i);
indexColumn.column = table.getColumn(i);
indexColumn.columnName = COLUMN_NAME + i;
indexCols[i] = indexColumn;
} }
} }
String indexName = table.getSchema().getUniqueIndexName(session, String indexName = table.getSchema().getUniqueIndexName(session,
...@@ -118,6 +135,13 @@ public class ResultTempTable implements ResultExternal { ...@@ -118,6 +135,13 @@ public class ResultTempTable implements ResultExternal {
indexType, true, null); indexType, true, null);
} }
private IndexColumn createIndexColumn(int index) {
IndexColumn indexColumn = new IndexColumn();
indexColumn.column = table.getColumn(index);
indexColumn.columnName = COLUMN_NAME + index;
return indexColumn;
}
@Override @Override
public synchronized ResultExternal createShallowCopy() { public synchronized ResultExternal createShallowCopy() {
if (parent != null) { if (parent != null) {
......
...@@ -11,6 +11,7 @@ import java.sql.ResultSet; ...@@ -11,6 +11,7 @@ import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.BitSet;
import org.h2.store.FileLister; import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -35,6 +36,7 @@ public class TestBigResult extends TestBase { ...@@ -35,6 +36,7 @@ public class TestBigResult extends TestBase {
return; return;
} }
testLargeSubquery(); testLargeSubquery();
testSortingAndDistinct();
testLargeUpdateDelete(); testLargeUpdateDelete();
testCloseConnectionDelete(); testCloseConnectionDelete();
testOrderGroup(); testOrderGroup();
...@@ -66,6 +68,104 @@ public class TestBigResult extends TestBase { ...@@ -66,6 +68,104 @@ public class TestBigResult extends TestBase {
conn.close(); conn.close();
} }
private void testSortingAndDistinct() throws SQLException {
deleteDb("bigResult");
Connection conn = getConnection("bigResult");
Statement stat = conn.createStatement();
int count = getSize(1000, 4000);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, VALUE INT NOT NULL)");
PreparedStatement ps = conn.prepareStatement("INSERT INTO TEST VALUES (?, ?)");
for (int i = 0; i < count; i++) {
ps.setInt(1, i);
ps.setInt(2, count - i);
ps.executeUpdate();
}
// local result
testSortintAndDistinct1(stat, count, count);
// external result
testSortintAndDistinct1(stat, 10, count);
stat.execute("DROP TABLE TEST");
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, VALUE1 INT NOT NULL, VALUE2 INT NOT NULL)");
ps = conn.prepareStatement("INSERT INTO TEST VALUES (?, ?, ?)");
int partCount = count / 10;
for (int i = 0; i < count; i++) {
ps.setInt(1, i);
int a = i / 10;
int b = i % 10;
ps.setInt(2, partCount - a);
ps.setInt(3, 10 - b);
ps.executeUpdate();
}
String sql;
/*
* Sorting only
*/
sql = "SELECT VALUE2, VALUE1 FROM (SELECT ID, VALUE2, VALUE1 FROM TEST ORDER BY VALUE2)";
// local result
testSortingAndDistinct2(stat, sql, count, partCount);
// external result
testSortingAndDistinct2(stat, sql, 10, partCount);
/*
* Distinct only
*/
sql = "SELECT VALUE2, VALUE1 FROM (SELECT DISTINCT ID, VALUE2, VALUE1 FROM TEST)";
// local result
testSortingAndDistinct2DistinctOnly(stat, sql, count, partCount);
// external result
testSortingAndDistinct2DistinctOnly(stat, sql, 10, partCount);
/*
* Sorting and distinct
*/
sql = "SELECT VALUE2, VALUE1 FROM (SELECT DISTINCT ID, VALUE2, VALUE1 FROM TEST ORDER BY VALUE2)";
// local result
testSortingAndDistinct2(stat, sql, count, partCount);
// external result
testSortingAndDistinct2(stat, sql, 10, partCount);
conn.close();
}
private void testSortintAndDistinct1(Statement stat, int maxRows, int count) throws SQLException {
stat.execute("SET MAX_MEMORY_ROWS " + maxRows);
ResultSet rs = stat.executeQuery("SELECT VALUE FROM (SELECT DISTINCT ID, VALUE FROM TEST ORDER BY VALUE)");
for (int i = 1; i <= count; i++) {
assertTrue(rs.next());
assertEquals(rs.getInt(1), i);
}
assertFalse(rs.next());
}
private void testSortingAndDistinct2(Statement stat, String sql, int maxRows, int partCount) throws SQLException {
ResultSet rs;
stat.execute("SET MAX_MEMORY_ROWS " + maxRows);
rs = stat.executeQuery(sql);
BitSet set = new BitSet(partCount);
for (int i = 1; i <= 10; i++) {
set.clear();
for (int j = 1; j <= partCount; j++) {
assertTrue(rs.next());
assertEquals(i, rs.getInt(1));
set.set(rs.getInt(2));
}
assertEquals(partCount + 1, set.nextClearBit(1));
}
assertFalse(rs.next());
}
private void testSortingAndDistinct2DistinctOnly(Statement stat, String sql, int maxRows, int partCount) throws SQLException {
ResultSet rs;
stat.execute("SET MAX_MEMORY_ROWS " + maxRows);
rs = stat.executeQuery(sql);
BitSet set = new BitSet(partCount * 10);
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= partCount; j++) {
assertTrue(rs.next());
set.set(rs.getInt(1) * partCount + rs.getInt(2));
}
}
assertEquals(partCount * 11 + 1, set.nextClearBit(partCount + 1));
assertFalse(rs.next());
}
private void testLargeUpdateDelete() throws SQLException { private void testLargeUpdateDelete() throws SQLException {
deleteDb("bigResult"); deleteDb("bigResult");
Connection conn = getConnection("bigResult"); Connection conn = getConnection("bigResult");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论