提交 c53f1270 authored 作者: Thomas Mueller's avatar Thomas Mueller

Large result sets now always create temporary tables instead of temporary files.

上级 864b1958
...@@ -269,7 +269,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -269,7 +269,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, true, sort); external = new ResultTempTable(session, expressions, true, sort);
rowCount = external.addRows(distinctRows.values()); rowCount = external.addRows(distinctRows.values());
distinctRows = null; distinctRows = null;
} }
...@@ -282,7 +282,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -282,7 +282,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, false, sort); external = new ResultTempTable(session, expressions, false, sort);
} }
addRowsToDisk(); addRowsToDisk();
} }
...@@ -319,7 +319,7 @@ public class LocalResult implements ResultInterface, ResultTarget { ...@@ -319,7 +319,7 @@ public class LocalResult implements ResultInterface, ResultTarget {
break; break;
} }
if (external == null) { if (external == null) {
external = new ResultTempTable(session, true, sort); external = new ResultTempTable(session, expressions, true, sort);
} }
rows.add(list); rows.add(list);
if (rows.size() > maxMemoryRows) { if (rows.size() > maxMemoryRows) {
......
...@@ -7,10 +7,13 @@ ...@@ -7,10 +7,13 @@
package org.h2.result; package org.h2.result;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
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;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.Index; import org.h2.index.Index;
import org.h2.index.IndexType; import org.h2.index.IndexType;
...@@ -21,35 +24,44 @@ import org.h2.table.IndexColumn; ...@@ -21,35 +24,44 @@ import org.h2.table.IndexColumn;
import org.h2.table.RegularTable; import org.h2.table.RegularTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueArray; import org.h2.value.ValueNull;
/** /**
* This class implements the temp table buffer for the LocalResult class. * This class implements the temp table buffer for the LocalResult class.
*/ */
public class ResultTempTable implements ResultExternal { public class ResultTempTable implements ResultExternal {
private static final String COLUMN_NAME = "DATA"; private static final String COLUMN_NAME = "DATA";
private final boolean distinct; private final boolean distinct;
private final SortOrder sort; private final SortOrder sort;
private final Index index; private Index index;
private Session session; private Session session;
private Table table; private Table table;
private Cursor resultCursor; private Cursor resultCursor;
private int rowCount; private int rowCount;
private int columnCount;
private final ResultTempTable parent; private final ResultTempTable parent;
private boolean closed; private boolean closed;
private int childCount; private int childCount;
private boolean containsLob;
ResultTempTable(Session session, boolean distinct, SortOrder sort) { ResultTempTable(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
this.session = session; this.session = session;
this.distinct = distinct; this.distinct = distinct;
this.sort = sort; this.sort = sort;
this.columnCount = expressions.length;
Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN); Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
Column column = new Column(COLUMN_NAME, Value.ARRAY);
column.setNullable(false);
CreateTableData data = new CreateTableData(); CreateTableData data = new CreateTableData();
data.columns.add(column); for (int i = 0; i < expressions.length; i++) {
int type = expressions[i].getType();
Column col = new Column(COLUMN_NAME + i,
type);
if (type == Value.CLOB || type == Value.BLOB) {
containsLob = true;
}
data.columns.add(col);
}
data.id = session.getDatabase().allocateObjectId(); data.id = session.getDatabase().allocateObjectId();
data.tableName = "TEMP_RESULT_SET_" + data.id; data.tableName = "TEMP_RESULT_SET_" + data.id;
data.temporary = true; data.temporary = true;
...@@ -58,38 +70,61 @@ public class ResultTempTable implements ResultExternal { ...@@ -58,38 +70,61 @@ public class ResultTempTable implements ResultExternal {
data.create = true; data.create = true;
data.session = session; data.session = session;
table = schema.createTable(data); table = schema.createTable(data);
int indexId = session.getDatabase().allocateObjectId(); if (sort != null || distinct) {
IndexColumn indexColumn = new IndexColumn(); createIndex();
indexColumn.column = column;
indexColumn.columnName = COLUMN_NAME;
IndexType indexType;
IndexColumn[] indexCols = { indexColumn };
if (session.getDatabase().getMvStore() != null) {
indexType = IndexType.createNonUnique(true);
index = table.addIndex(session, data.tableName, indexId, indexCols,
indexType, true, null);
index.setTemporary(true);
} else {
indexType = IndexType.createPrimaryKey(true, false);
index = new PageBtreeIndex((RegularTable) table, indexId,
data.tableName, indexCols, indexType, true, session);
index.setTemporary(true);
table.getIndexes().add(index);
} }
parent = null; parent = null;
} }
private ResultTempTable(ResultTempTable parent) { private ResultTempTable(ResultTempTable parent) {
this.parent = parent; this.parent = parent;
this.columnCount = parent.columnCount;
this.distinct = parent.distinct; this.distinct = parent.distinct;
this.session = parent.session; this.session = parent.session;
this.table = parent.table; this.table = parent.table;
this.index = parent.index; this.index = parent.index;
this.rowCount = parent.rowCount; this.rowCount = parent.rowCount;
// sort is only used when adding rows this.sort = parent.sort;
this.sort = null; this.containsLob = parent.containsLob;
reset(); reset();
} }
private void createIndex() {
IndexColumn[] indexCols = null;
if (sort != null) {
int[] colInd = sort.getQueryColumnIndexes();
indexCols = new IndexColumn[colInd.length];
for (int i = 0; i < colInd.length; i++) {
IndexColumn indexColumn = new IndexColumn();
indexColumn.column = table.getColumn(colInd[i]);
indexColumn.sortType = sort.getSortTypes()[i];
indexColumn.columnName = COLUMN_NAME + i;
indexCols[i] = indexColumn;
}
} else {
indexCols = new IndexColumn[columnCount];
for (int i = 0; i < columnCount; i++) {
IndexColumn indexColumn = new IndexColumn();
indexColumn.column = table.getColumn(i);
indexColumn.columnName = COLUMN_NAME + i;
indexCols[i] = indexColumn;
}
}
String indexName = table.getSchema().getUniqueIndexName(session,
table, Constants.PREFIX_INDEX);
int indexId = session.getDatabase().allocateObjectId();
IndexType indexType = IndexType.createNonUnique(true);
if (session.getDatabase().getMvStore() != null) {
index = table.addIndex(session, indexName, indexId, indexCols,
indexType, true, null);
index.setTemporary(true);
} else {
index = new PageBtreeIndex((RegularTable) table, indexId,
indexName, indexCols, indexType, true, session);
index.setTemporary(true);
table.getIndexes().add(index);
}
}
@Override @Override
public synchronized ResultExternal createShallowCopy() { public synchronized ResultExternal createShallowCopy() {
...@@ -138,6 +173,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -138,6 +173,7 @@ public class ResultTempTable implements ResultExternal {
@Override @Override
public int addRows(ArrayList<Value[]> rows) { public int addRows(ArrayList<Value[]> rows) {
// speeds up inserting, but not really needed:
if (sort != null) { if (sort != null) {
sort.sort(rows); sort.sort(rows);
} }
...@@ -172,6 +208,11 @@ public class ResultTempTable implements ResultExternal { ...@@ -172,6 +208,11 @@ public class ResultTempTable implements ResultExternal {
if (table == null) { if (table == null) {
return; return;
} }
if (containsLob) {
// contains BLOB or CLOB: can not truncate now,
// otherwise the BLOB and CLOB entries are removed
return;
}
try { try {
Database database = session.getDatabase(); Database database = session.getDatabase();
// Need to lock because not all of the code-paths // Need to lock because not all of the code-paths
...@@ -189,7 +230,9 @@ public class ResultTempTable implements ResultExternal { ...@@ -189,7 +230,9 @@ public class ResultTempTable implements ResultExternal {
// time. (the table is truncated, so this is just one record) // time. (the table is truncated, so this is just one record)
if (!database.isSysTableLocked()) { if (!database.isSysTableLocked()) {
Session sysSession = database.getSystemSession(); Session sysSession = database.getSystemSession();
index.removeChildrenAndResources(sysSession); if (index != null) {
index.removeChildrenAndResources(sysSession);
}
table.removeChildrenAndResources(sysSession); table.removeChildrenAndResources(sysSession);
// the transaction must be committed immediately // the transaction must be committed immediately
sysSession.commit(false); sysSession.commit(false);
...@@ -207,13 +250,13 @@ public class ResultTempTable implements ResultExternal { ...@@ -207,13 +250,13 @@ public class ResultTempTable implements ResultExternal {
@Override @Override
public Value[] next() { public Value[] next() {
if (resultCursor == null) { if (resultCursor == null) {
Index idx;
if (distinct || sort != null) {
idx = index;
} else {
idx = table.getScanIndex(session);
}
if (session.getDatabase().getMvStore() != null) { if (session.getDatabase().getMvStore() != null) {
Index idx;
if (distinct || sort != null) {
idx = index;
} else {
idx = table.getScanIndex(session);
}
// sometimes the transaction is already committed, // sometimes the transaction is already committed,
// in which case we can't use the session // in which case we can't use the session
if (idx.getRowCount(session) == 0 && rowCount > 0) { if (idx.getRowCount(session) == 0 && rowCount > 0) {
...@@ -224,15 +267,14 @@ public class ResultTempTable implements ResultExternal { ...@@ -224,15 +267,14 @@ public class ResultTempTable implements ResultExternal {
resultCursor = idx.find(session, null, null); resultCursor = idx.find(session, null, null);
} }
} else { } else {
resultCursor = index.find(session, null, null); resultCursor = idx.find(session, null, null);
} }
} }
if (!resultCursor.next()) { if (!resultCursor.next()) {
return null; return null;
} }
Row row = resultCursor.get(); Row row = resultCursor.get();
ValueArray data = (ValueArray) row.getValue(0); return row.getValueList();
return data.getList();
} }
@Override @Override
...@@ -240,19 +282,36 @@ public class ResultTempTable implements ResultExternal { ...@@ -240,19 +282,36 @@ public class ResultTempTable implements ResultExternal {
resultCursor = null; resultCursor = null;
} }
private static Row convertToRow(Value[] values) { private Row convertToRow(Value[] values) {
ValueArray data = ValueArray.get(values); if (values.length < columnCount) {
return new Row(new Value[]{data}, Row.MEMORY_CALCULATE); Value[] v2 = Arrays.copyOf(values, columnCount);
for (int i = values.length; i < columnCount; i++) {
v2[i] = ValueNull.INSTANCE;
}
values = v2;
}
return new Row(values, Row.MEMORY_CALCULATE);
} }
private Cursor find(Row row) { private Cursor find(Row row) {
if (index == null) {
// for the case "in(select ...)", the query might
// use an optimization and not create the index
// up front
createIndex();
}
Cursor cursor = index.find(session, row, row); Cursor cursor = index.find(session, row, row);
Value a = row.getValue(0); Database db = session.getDatabase();
while (cursor.next()) { while (cursor.next()) {
SearchRow found; SearchRow found = cursor.getSearchRow();
found = cursor.getSearchRow(); boolean ok = true;
Value b = found.getValue(0); for (int i = 0; i < row.getColumnCount(); i++) {
if (session.getDatabase().areEqual(a, b)) { if (!db.areEqual(row.getValue(i), found.getValue(i))) {
ok = false;
break;
}
}
if (ok) {
return cursor; return cursor;
} }
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论