Hi everyone,
Here is one PR #1424
<https://github.com/h2database/h2database/pull/1424>
Issue #1405 <https://github.com/h2database/h2database/issues/1405>
Add ability to external implementation of the LocalResult. It may be used
to track allocated RAM, custom results storage,
custom manage of externalize results.
I wrote the code, it's mine, and I'm contributing it to H2 for distribution
multiple-licensed under the MPL 2.0, and the EPL 1.0 (
http://h2database.com/html/license.html).
--
You received this message because you are subscribed to the Google Groups "H2
Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.
Index: h2/src/main/org/h2/result/LocalResult.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/result/LocalResult.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/result/LocalResult.java (date 1536655889000)
@@ -5,604 +5,75 @@
*/
package org.h2.result;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import org.h2.engine.Database;
-import org.h2.engine.Session;
-import org.h2.engine.SessionInterface;
-import org.h2.expression.Expression;
-import org.h2.message.DbException;
-import org.h2.mvstore.db.MVTempResult;
-import org.h2.util.Utils;
-import org.h2.util.ValueHashMap;
-import org.h2.value.DataType;
+import org.h2.engine.SysProperties;
import org.h2.value.Value;
-import org.h2.value.ValueArray;
/**
* A local result set contains all row data of a result set.
- * This is the object generated by engine,
+ * The object is generated by {@link LocalResultFactory},
* and it is also used directly by the ResultSet class in the embedded mode.
- * If the result does not fit in memory, it is written to a temporary file.
+ * The memory usage and other policies are defined by implementation.
*/
-public class LocalResult implements ResultInterface, ResultTarget {
-
- private int maxMemoryRows;
- private Session session;
- private int visibleColumnCount;
- private Expression[] expressions;
- private int rowId, rowCount;
- private ArrayList<Value[]> rows;
- private SortOrder sort;
- private ValueHashMap<Value[]> distinctRows;
- private Value[] currentRow;
- private int offset;
- private int limit = -1;
- private boolean fetchPercent;
- private boolean withTies;
- private boolean limitsWereApplied;
- private ResultExternal external;
- private boolean distinct;
- private int[] distinctIndexes;
- private boolean closed;
- private boolean containsLobs;
-
- /**
- * Construct a local result object.
- */
- public LocalResult() {
- // nothing to do
- }
-
+public interface LocalResult extends ResultInterface, ResultTarget {
/**
- * Construct a local result object.
+ * Redefine count of maximum rows holds in memory for the result.
*
- * @param session the session
- * @param expressions the expression array
- * @param visibleColumnCount the number of visible columns
- */
- public LocalResult(Session session, Expression[] expressions,
- int visibleColumnCount) {
- this.session = session;
- if (session == null) {
- this.maxMemoryRows = Integer.MAX_VALUE;
- } else {
- Database db = session.getDatabase();
- if (db.isPersistent() && !db.isReadOnly()) {
- this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
- } else {
- this.maxMemoryRows = Integer.MAX_VALUE;
- }
- }
- rows = Utils.newSmallArrayList();
- this.visibleColumnCount = visibleColumnCount;
- rowId = -1;
- this.expressions = expressions;
- }
-
- @Override
- public boolean isLazy() {
- return false;
- }
-
- public void setMaxMemoryRows(int maxValue) {
- this.maxMemoryRows = maxValue;
- }
-
- /**
- * Construct a local result set by reading all data from a regular result
- * set.
+ * @param maxValue Maximum rows count in memory.
*
- * @param session the session
- * @param rs the result set
- * @param maxrows the maximum number of rows to read (0 for no limit)
- * @return the local result set
+ * @see SysProperties#MAX_MEMORY_ROWS
*/
- public static LocalResult read(Session session, ResultSet rs, int maxrows) {
- Expression[] cols = Expression.getExpressionColumns(session, rs);
- int columnCount = cols.length;
- LocalResult result = new LocalResult(session, cols, columnCount);
- try {
- for (int i = 0; (maxrows == 0 || i < maxrows) && rs.next(); i++) {
- Value[] list = new Value[columnCount];
- for (int j = 0; j < columnCount; j++) {
- int type = result.getColumnType(j);
- list[j] = DataType.readValue(session, rs, j + 1, type);
- }
- result.addRow(list);
- }
- } catch (SQLException e) {
- throw DbException.convert(e);
- }
- result.done();
- return result;
- }
+ public void setMaxMemoryRows(int maxValue);
/**
- * Create a shallow copy of the result set. The data and a temporary table
- * (if there is any) is not copied.
- *
- * @param targetSession the session of the copy
- * @return the copy if possible, or null if copying is not possible
+ * @param sort Sort order.
*/
- @Override
- public LocalResult createShallowCopy(SessionInterface targetSession) {
- if (external == null && (rows == null || rows.size() < rowCount)) {
- return null;
- }
- if (containsLobs) {
- return null;
- }
- ResultExternal e2 = null;
- if (external != null) {
- e2 = external.createShallowCopy();
- if (e2 == null) {
- return null;
- }
- }
- LocalResult copy = new LocalResult();
- copy.maxMemoryRows = this.maxMemoryRows;
- copy.session = (Session) targetSession;
- copy.visibleColumnCount = this.visibleColumnCount;
- copy.expressions = this.expressions;
- copy.rowId = -1;
- copy.rowCount = this.rowCount;
- copy.rows = this.rows;
- copy.sort = this.sort;
- copy.distinctRows = this.distinctRows;
- copy.distinct = distinct;
- copy.distinctIndexes = distinctIndexes;
- copy.currentRow = null;
- copy.offset = 0;
- copy.limit = -1;
- copy.external = e2;
- return copy;
- }
-
- /**
- * Set the sort order.
- *
- * @param sort the sort order
- */
- public void setSortOrder(SortOrder sort) {
- this.sort = sort;
- }
+ public void setSortOrder(SortOrder sort);
/**
* Remove duplicate rows.
*/
- public void setDistinct() {
- assert distinctIndexes == null;
- distinct = true;
- distinctRows = ValueHashMap.newInstance();
- }
+ public void setDistinct();
/**
* Remove rows with duplicates in columns with specified indexes.
*
* @param distinctIndexes distinct indexes
*/
- public void setDistinct(int[] distinctIndexes) {
- assert !distinct;
- this.distinctIndexes = distinctIndexes;
- distinctRows = ValueHashMap.newInstance();
- }
-
- /**
- * @return whether this result is a distinct result
- */
- public boolean isAnyDistinct() {
- return distinct || distinctIndexes != null;
- }
+ public void setDistinct(int[] distinctIndexes);
/**
* Remove the row from the result set if it exists.
*
* @param values the row
*/
- public void removeDistinct(Value[] values) {
- if (!distinct) {
- DbException.throwInternalError();
- }
- assert values.length == visibleColumnCount;
- if (distinctRows != null) {
- ValueArray array = ValueArray.get(values);
- distinctRows.remove(array);
- rowCount = distinctRows.size();
- } else {
- rowCount = external.removeRow(values);
- }
- }
-
- /**
- * Check if this result set contains the given row.
- *
- * @param values the row
- * @return true if the row exists
- */
- @Override
- public boolean containsDistinct(Value[] values) {
- assert values.length == visibleColumnCount;
- if (external != null) {
- return external.contains(values);
- }
- if (distinctRows == null) {
- distinctRows = ValueHashMap.newInstance();
- for (Value[] row : rows) {
- ValueArray array = getArrayOfDistinct(row);
- distinctRows.put(array, array.getList());
- }
- }
- ValueArray array = ValueArray.get(values);
- return distinctRows.get(array) != null;
- }
-
- @Override
- public void reset() {
- rowId = -1;
- currentRow = null;
- if (external != null) {
- external.reset();
- }
- }
-
- @Override
- public Value[] currentRow() {
- return currentRow;
- }
-
- @Override
- public boolean next() {
- if (!closed && rowId < rowCount) {
- rowId++;
- if (rowId < rowCount) {
- if (external != null) {
- currentRow = external.next();
- } else {
- currentRow = rows.get(rowId);
- }
- return true;
- }
- currentRow = null;
- }
- return false;
- }
-
- @Override
- public int getRowId() {
- return rowId;
- }
-
- @Override
- public boolean isAfterLast() {
- return rowId >= rowCount;
- }
-
- private void cloneLobs(Value[] values) {
- for (int i = 0; i < values.length; i++) {
- Value v = values[i];
- Value v2 = v.copyToResult();
- if (v2 != v) {
- containsLobs = true;
- session.addTemporaryLob(v2);
- values[i] = v2;
- }
- }
- }
-
- private ValueArray getArrayOfDistinct(Value[] values) {
- if (distinctIndexes != null) {
- int cnt = distinctIndexes.length;
- Value[] newValues = new Value[cnt];
- for (int i = 0; i < cnt; i++) {
- newValues[i] = values[distinctIndexes[i]];
- }
- values = newValues;
- } else if (values.length > visibleColumnCount) {
- values = Arrays.copyOf(values, visibleColumnCount);
- }
- return ValueArray.get(values);
- }
-
- private void createExternalResult() {
- Database database = session.getDatabase();
- external = database.isMVStore()
- || /* not supported by ResultTempTable */ distinct && expressions.length != visibleColumnCount
- || distinctIndexes != null
- ? MVTempResult.of(database, expressions, distinct, distinctIndexes, visibleColumnCount, sort)
- : new ResultTempTable(session, expressions, distinct, sort);
- }
-
- /**
- * Add a row to this object.
- *
- * @param values the row to add
- */
- @Override
- public void addRow(Value[] values) {
- cloneLobs(values);
- if (isAnyDistinct()) {
- if (distinctRows != null) {
- ValueArray array = getArrayOfDistinct(values);
- distinctRows.putIfAbsent(array, values);
- rowCount = distinctRows.size();
- if (rowCount > maxMemoryRows) {
- createExternalResult();
- rowCount = external.addRows(distinctRows.values());
- distinctRows = null;
- }
- } else {
- rowCount = external.addRow(values);
- }
- } else {
- rows.add(values);
- rowCount++;
- if (rows.size() > maxMemoryRows) {
- addRowsToDisk();
- }
- }
- }
-
- private void addRowsToDisk() {
- if (external == null) {
- createExternalResult();
- }
- rowCount = external.addRows(rows);
- rows.clear();
- }
-
- @Override
- public int getVisibleColumnCount() {
- return visibleColumnCount;
- }
+ public void removeDistinct(Value[] values);
/**
* This method is called after all rows have been added.
*/
- public void done() {
- if (external != null) {
- addRowsToDisk();
- } else {
- if (isAnyDistinct()) {
- rows = distinctRows.values();
- }
- if (sort != null && limit != 0) {
- boolean withLimit = limit > 0 && !withTies;
- if (offset > 0 || withLimit) {
- sort.sort(rows, offset, withLimit ? limit : rows.size());
- } else {
- sort.sort(rows);
- }
- }
- }
- applyOffsetAndLimit();
- reset();
- }
-
- private void applyOffsetAndLimit() {
- if (limitsWereApplied) {
- return;
- }
- int offset = Math.max(this.offset, 0);
- int limit = this.limit;
- if (offset == 0 && limit < 0 && !fetchPercent || rowCount == 0) {
- return;
- }
- if (fetchPercent) {
- if (limit < 0 || limit > 100) {
- throw DbException.getInvalidValueException("FETCH PERCENT", limit);
- }
- // Oracle rounds percent up, do the same for now
- limit = (int) (((long) limit * rowCount + 99) / 100);
- }
- boolean clearAll = offset >= rowCount || limit == 0;
- if (!clearAll) {
- int remaining = rowCount - offset;
- limit = limit < 0 ? remaining : Math.min(remaining, limit);
- if (offset == 0 && remaining <= limit) {
- return;
- }
- } else {
- limit = 0;
- }
- distinctRows = null;
- rowCount = limit;
- if (external == null) {
- if (clearAll) {
- rows.clear();
- return;
- }
- int to = offset + limit;
- if (withTies && sort != null) {
- Value[] expected = rows.get(to - 1);
- while (to < rows.size() && sort.compare(expected, rows.get(to)) == 0) {
- to++;
- rowCount++;
- }
- }
- if (offset != 0 || to != rows.size()) {
- // avoid copying the whole array for each row
- rows = new ArrayList<>(rows.subList(offset, to));
- }
- } else {
- if (clearAll) {
- external.close();
- external = null;
- return;
- }
- trimExternal(offset, limit);
- }
- }
-
- private void trimExternal(int offset, int limit) {
- ResultExternal temp = external;
- external = null;
- temp.reset();
- while (--offset >= 0) {
- temp.next();
- }
- Value[] row = null;
- while (--limit >= 0) {
- row = temp.next();
- rows.add(row);
- if (rows.size() > maxMemoryRows) {
- addRowsToDisk();
- }
- }
- if (withTies && sort != null && row != null) {
- Value[] expected = row;
- while ((row = temp.next()) != null && sort.compare(expected, row) == 0) {
- rows.add(row);
- rowCount++;
- if (rows.size() > maxMemoryRows) {
- addRowsToDisk();
- }
- }
- }
- if (external != null) {
- addRowsToDisk();
- }
- temp.close();
- }
-
- @Override
- public int getRowCount() {
- return rowCount;
- }
-
- @Override
- public void limitsWereApplied() {
- this.limitsWereApplied = true;
- }
-
- @Override
- public boolean hasNext() {
- return !closed && rowId < rowCount - 1;
- }
+ public void done();
/**
* Set the number of rows that this result will return at the maximum.
*
* @param limit the limit (-1 means no limit, 0 means no rows)
*/
- public void setLimit(int limit) {
- this.limit = limit;
- }
+ public void setLimit(int limit);
/**
* @param fetchPercent whether limit expression specifies percentage of rows
*/
- public void setFetchPercent(boolean fetchPercent) {
- this.fetchPercent = fetchPercent;
- }
+ public void setFetchPercent(boolean fetchPercent);
/**
* @param withTies whether tied rows should be included in result too
*/
- public void setWithTies(boolean withTies) {
- this.withTies = withTies;
- }
-
- @Override
- public boolean needToClose() {
- return external != null;
- }
-
- @Override
- public void close() {
- if (external != null) {
- external.close();
- external = null;
- closed = true;
- }
- }
-
- @Override
- public String getAlias(int i) {
- return expressions[i].getAlias();
- }
-
- @Override
- public String getTableName(int i) {
- return expressions[i].getTableName();
- }
-
- @Override
- public String getSchemaName(int i) {
- return expressions[i].getSchemaName();
- }
-
- @Override
- public int getDisplaySize(int i) {
- return expressions[i].getDisplaySize();
- }
-
- @Override
- public String getColumnName(int i) {
- return expressions[i].getColumnName();
- }
-
- @Override
- public int getColumnType(int i) {
- return expressions[i].getType();
- }
-
- @Override
- public long getColumnPrecision(int i) {
- return expressions[i].getPrecision();
- }
-
- @Override
- public int getNullable(int i) {
- return expressions[i].getNullable();
- }
-
- @Override
- public boolean isAutoIncrement(int i) {
- return expressions[i].isAutoIncrement();
- }
-
- @Override
- public int getColumnScale(int i) {
- return expressions[i].getScale();
- }
+ public void setWithTies(boolean withTies);
/**
* Set the offset of the first row to return.
*
* @param offset the offset
*/
- public void setOffset(int offset) {
- this.offset = offset;
- }
-
- @Override
- public String toString() {
- return super.toString() + " columns: " + visibleColumnCount +
- " rows: " + rowCount + " pos: " + rowId;
- }
-
- /**
- * Check if this result set is closed.
- *
- * @return true if it is
- */
- @Override
- public boolean isClosed() {
- return closed;
- }
-
- @Override
- public int getFetchSize() {
- return 0;
- }
-
- @Override
- public void setFetchSize(int fetchSize) {
- // ignore
- }
-
+ public void setOffset(int offset);
}
Index: h2/src/main/org/h2/command/dml/Call.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/Call.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/Call.java (date 1536655889000)
@@ -12,6 +12,7 @@
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.result.LocalResult;
+import org.h2.result.LocalResultFactory;
import org.h2.result.ResultInterface;
import org.h2.value.Value;
@@ -34,9 +35,9 @@
LocalResult result;
if (isResultSet) {
Expression[] expr = expression.getExpressionColumns(session);
- result = new LocalResult(session, expr, expr.length);
+ result = session.getDatabase().getResultFactory().create(session, expr, expr.length);
} else {
- result = new LocalResult(session, expressions, 1);
+ result = session.getDatabase().getResultFactory().create(session, expressions, 1);
}
result.done();
return result;
@@ -66,9 +67,9 @@
if (isResultSet) {
v = v.convertTo(Value.RESULT_SET);
ResultSet rs = v.getResultSet();
- return LocalResult.read(session, rs, maxrows);
+ return LocalResultFactory.read(session, rs, maxrows);
}
- LocalResult result = new LocalResult(session, expressions, 1);
+ LocalResult result = session.getDatabase().getResultFactory().create(session, expressions, 1);
Value[] row = { v };
result.addRow(row);
result.done();
Index: h2/src/main/org/h2/command/dml/ScriptCommand.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/ScriptCommand.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/ScriptCommand.java (date 1536655889000)
@@ -137,7 +137,7 @@
private LocalResult createResult() {
Expression[] expressions = { new ExpressionColumn(
session.getDatabase(), new Column("SCRIPT", Value.STRING)) };
- return new LocalResult(session, expressions, 1);
+ return session.getDatabase().getResultFactory().create(session, expressions, 1);
}
@Override
Index: h2/src/main/org/h2/engine/Database.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/engine/Database.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/engine/Database.java (date 1536655889000)
@@ -41,6 +41,7 @@
import org.h2.message.TraceSystem;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.db.MVTableEngine;
+import org.h2.result.LocalResultFactory;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SearchRow;
@@ -230,6 +231,7 @@
private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES;
private QueryStatisticsData queryStatisticsData;
private RowFactory rowFactory = RowFactory.DEFAULT;
+ private LocalResultFactory resultFactory = LocalResultFactory.DEFAULT;
private Authenticator authenticator;
@@ -369,6 +371,14 @@
this.rowFactory = rowFactory;
}
+ public LocalResultFactory getResultFactory() {
+ return resultFactory;
+ }
+
+ public void setResultFactory(LocalResultFactory resultFactory) {
+ this.resultFactory = resultFactory;
+ }
+
public static void setInitialPowerOffCount(int count) {
initialPowerOffCount = count;
}
Index: h2/src/main/org/h2/engine/GeneratedKeys.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/engine/GeneratedKeys.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/engine/GeneratedKeys.java (date 1536655889000)
@@ -128,7 +128,7 @@
Database db = session.getDatabase();
if (Boolean.FALSE.equals(generatedKeysRequest)) {
clear(null);
- return new LocalResult();
+ return db.getResultFactory().create();
}
ArrayList<ExpressionColumn> expressionColumns;
if (Boolean.TRUE.equals(generatedKeysRequest)) {
@@ -152,7 +152,7 @@
}
} else {
clear(null);
- return new LocalResult();
+ return db.getResultFactory().create();
}
} else if (generatedKeysRequest instanceof String[]) {
if (table != null) {
@@ -182,18 +182,19 @@
}
} else {
clear(null);
- return new LocalResult();
+ return db.getResultFactory().create();
}
} else {
clear(null);
- return new LocalResult();
+ return db.getResultFactory().create();
}
int columnCount = expressionColumns.size();
if (columnCount == 0) {
clear(null);
- return new LocalResult();
+ return db.getResultFactory().create();
}
- LocalResult result = new LocalResult(session, expressionColumns.toArray(new Expression[0]), columnCount);
+ LocalResult result = db.getResultFactory().create(session,
+ expressionColumns.toArray(new Expression[0]), columnCount);
for (Map<Column, Value> map : data) {
Value[] row = new Value[columnCount];
for (Map.Entry<Column, Value> entry : map.entrySet()) {
Index: h2/src/main/org/h2/command/dml/Explain.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/Explain.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/Explain.java (date 1536655889000)
@@ -72,7 +72,7 @@
Database db = session.getDatabase();
ExpressionColumn expr = new ExpressionColumn(db, column);
Expression[] expressions = { expr };
- result = new LocalResult(session, expressions, 1);
+ result = db.getResultFactory().create(session, expressions, 1);
if (maxrows >= 0) {
String plan;
if (executeCommand) {
Index: h2/src/main/org/h2/table/FunctionTable.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/table/FunctionTable.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/table/FunctionTable.java (date 1536655889000)
@@ -19,6 +19,7 @@
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.result.LocalResult;
+import org.h2.result.LocalResultFactory;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.schema.Schema;
@@ -194,7 +195,7 @@
cachedResult.reset();
return cachedResult;
}
- LocalResult result = LocalResult.read(session, v.getResultSet(), 0);
+ LocalResult result = LocalResultFactory.read(session, v.getResultSet(), 0);
if (function.isDeterministic()) {
cachedResult = result;
cachedValue = v;
Index: h2/src/main/org/h2/result/LocalResultFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/result/LocalResultFactory.java (date 1536655889000)
+++ h2/src/main/org/h2/result/LocalResultFactory.java (date 1536655889000)
@@ -0,0 +1,91 @@
+/*
+ * 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.result;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.h2.engine.Session;
+import org.h2.expression.Expression;
+import org.h2.message.DbException;
+import org.h2.value.DataType;
+import org.h2.value.Value;
+
+/**
+ * Creates local result.
+ */
+public abstract class LocalResultFactory {
+ /**
+ * Default implementation of local result factory.
+ */
+ public static final LocalResultFactory DEFAULT = new DefaultLocalResultFactory();
+
+ /**
+ * Create a local result object.
+ *
+ * @param session the session
+ * @param expressions the expression array
+ * @param visibleColumnCount the number of visible columns
+ * @return object to collect local result.
+ */
+ public abstract LocalResult create(Session session, Expression[] expressions, int visibleColumnCount);
+
+ /**
+ * Create a local result object.
+ * @return object to collect local result.
+ */
+ public abstract LocalResult create();
+
+ /**
+ * Construct a local result set by reading all data from a regular result
+ * set.
+ *
+ * @param session the session
+ * @param rs the result set
+ * @param maxrows the maximum number of rows to read (0 for no limit)
+ * @return the local result set
+ */
+ public static LocalResult read(Session session, ResultSet rs, int maxrows) {
+ Expression[] cols = Expression.getExpressionColumns(session, rs);
+ int columnCount = cols.length;
+ LocalResult result = session.getDatabase().getResultFactory().create(session, cols, columnCount);
+ try {
+ for (int i = 0; (maxrows == 0 || i < maxrows) && rs.next(); i++) {
+ Value[] list = new Value[columnCount];
+ for (int j = 0; j < columnCount; j++) {
+ int type = result.getColumnType(j);
+ list[j] = DataType.readValue(session, rs, j + 1, type);
+ }
+ result.addRow(list);
+ }
+ } catch (SQLException e) {
+ throw DbException.convert(e);
+ }
+ result.done();
+ return result;
+ }
+
+ /**
+ * Default implementation of local result factory.
+ */
+ private static final class DefaultLocalResultFactory extends LocalResultFactory {
+ /**
+ *
+ */
+ DefaultLocalResultFactory() {
+ //No-op.
+ }
+
+ @Override
+ public LocalResult create(Session session, Expression[] expressions, int visibleColumnCount) {
+ return new LocalResultImpl(session, expressions, visibleColumnCount);
+ }
+
+ @Override
+ public LocalResult create() {
+ return new LocalResultImpl();
+ }
+ }
+}
Index: h2/src/main/org/h2/result/LocalResultImpl.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/result/LocalResultImpl.java (date 1536655889000)
+++ h2/src/main/org/h2/result/LocalResultImpl.java (date 1536655889000)
@@ -0,0 +1,575 @@
+/*
+ * 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.result;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.h2.engine.Database;
+import org.h2.engine.Session;
+import org.h2.engine.SessionInterface;
+import org.h2.expression.Expression;
+import org.h2.message.DbException;
+import org.h2.mvstore.db.MVTempResult;
+import org.h2.util.Utils;
+import org.h2.util.ValueHashMap;
+import org.h2.value.Value;
+import org.h2.value.ValueArray;
+
+/**
+ * A local result set contains all row data of a result set.
+ * This is the object generated by engine,
+ * and it is also used directly by the ResultSet class in the embedded mode.
+ * If the result does not fit in memory, it is written to a temporary file.
+ */
+public class LocalResultImpl implements LocalResult {
+
+ private int maxMemoryRows;
+ private Session session;
+ private int visibleColumnCount;
+ private Expression[] expressions;
+ private int rowId, rowCount;
+ private ArrayList<Value[]> rows;
+ private SortOrder sort;
+ private ValueHashMap<Value[]> distinctRows;
+ private Value[] currentRow;
+ private int offset;
+ private int limit = -1;
+ private boolean fetchPercent;
+ private boolean withTies;
+ private boolean limitsWereApplied;
+ private ResultExternal external;
+ private boolean distinct;
+ private int[] distinctIndexes;
+ private boolean closed;
+ private boolean containsLobs;
+
+ /**
+ * Construct a local result object.
+ */
+ public LocalResultImpl() {
+ // nothing to do
+ }
+
+ /**
+ * Construct a local result object.
+ *
+ * @param session the session
+ * @param expressions the expression array
+ * @param visibleColumnCount the number of visible columns
+ */
+ public LocalResultImpl(Session session, Expression[] expressions,
+ int visibleColumnCount) {
+ this.session = session;
+ if (session == null) {
+ this.maxMemoryRows = Integer.MAX_VALUE;
+ } else {
+ Database db = session.getDatabase();
+ if (db.isPersistent() && !db.isReadOnly()) {
+ this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
+ } else {
+ this.maxMemoryRows = Integer.MAX_VALUE;
+ }
+ }
+ rows = Utils.newSmallArrayList();
+ this.visibleColumnCount = visibleColumnCount;
+ rowId = -1;
+ this.expressions = expressions;
+ }
+
+ @Override
+ public boolean isLazy() {
+ return false;
+ }
+
+ public void setMaxMemoryRows(int maxValue) {
+ this.maxMemoryRows = maxValue;
+ }
+
+ /**
+ * Create a shallow copy of the result set. The data and a temporary table
+ * (if there is any) is not copied.
+ *
+ * @param targetSession the session of the copy
+ * @return the copy if possible, or null if copying is not possible
+ */
+ @Override
+ public LocalResultImpl createShallowCopy(SessionInterface targetSession) {
+ if (external == null && (rows == null || rows.size() < rowCount)) {
+ return null;
+ }
+ if (containsLobs) {
+ return null;
+ }
+ ResultExternal e2 = null;
+ if (external != null) {
+ e2 = external.createShallowCopy();
+ if (e2 == null) {
+ return null;
+ }
+ }
+ LocalResultImpl copy = new LocalResultImpl();
+ copy.maxMemoryRows = this.maxMemoryRows;
+ copy.session = (Session) targetSession;
+ copy.visibleColumnCount = this.visibleColumnCount;
+ copy.expressions = this.expressions;
+ copy.rowId = -1;
+ copy.rowCount = this.rowCount;
+ copy.rows = this.rows;
+ copy.sort = this.sort;
+ copy.distinctRows = this.distinctRows;
+ copy.distinct = distinct;
+ copy.distinctIndexes = distinctIndexes;
+ copy.currentRow = null;
+ copy.offset = 0;
+ copy.limit = -1;
+ copy.external = e2;
+ return copy;
+ }
+
+ /**
+ * Set the sort order.
+ *
+ * @param sort the sort order
+ */
+ public void setSortOrder(SortOrder sort) {
+ this.sort = sort;
+ }
+
+ /**
+ * Remove duplicate rows.
+ */
+ public void setDistinct() {
+ assert distinctIndexes == null;
+ distinct = true;
+ distinctRows = ValueHashMap.newInstance();
+ }
+
+ /**
+ * Remove rows with duplicates in columns with specified indexes.
+ *
+ * @param distinctIndexes distinct indexes
+ */
+ public void setDistinct(int[] distinctIndexes) {
+ assert !distinct;
+ this.distinctIndexes = distinctIndexes;
+ distinctRows = ValueHashMap.newInstance();
+ }
+
+ /**
+ * @return whether this result is a distinct result
+ */
+ private boolean isAnyDistinct() {
+ return distinct || distinctIndexes != null;
+ }
+
+ /**
+ * Remove the row from the result set if it exists.
+ *
+ * @param values the row
+ */
+ public void removeDistinct(Value[] values) {
+ if (!distinct) {
+ DbException.throwInternalError();
+ }
+ assert values.length == visibleColumnCount;
+ if (distinctRows != null) {
+ ValueArray array = ValueArray.get(values);
+ distinctRows.remove(array);
+ rowCount = distinctRows.size();
+ } else {
+ rowCount = external.removeRow(values);
+ }
+ }
+
+ /**
+ * Check if this result set contains the given row.
+ *
+ * @param values the row
+ * @return true if the row exists
+ */
+ @Override
+ public boolean containsDistinct(Value[] values) {
+ assert values.length == visibleColumnCount;
+ if (external != null) {
+ return external.contains(values);
+ }
+ if (distinctRows == null) {
+ distinctRows = ValueHashMap.newInstance();
+ for (Value[] row : rows) {
+ ValueArray array = getArrayOfDistinct(row);
+ distinctRows.put(array, array.getList());
+ }
+ }
+ ValueArray array = ValueArray.get(values);
+ return distinctRows.get(array) != null;
+ }
+
+ @Override
+ public void reset() {
+ rowId = -1;
+ currentRow = null;
+ if (external != null) {
+ external.reset();
+ }
+ }
+
+ @Override
+ public Value[] currentRow() {
+ return currentRow;
+ }
+
+ @Override
+ public boolean next() {
+ if (!closed && rowId < rowCount) {
+ rowId++;
+ if (rowId < rowCount) {
+ if (external != null) {
+ currentRow = external.next();
+ } else {
+ currentRow = rows.get(rowId);
+ }
+ return true;
+ }
+ currentRow = null;
+ }
+ return false;
+ }
+
+ @Override
+ public int getRowId() {
+ return rowId;
+ }
+
+ @Override
+ public boolean isAfterLast() {
+ return rowId >= rowCount;
+ }
+
+ private void cloneLobs(Value[] values) {
+ for (int i = 0; i < values.length; i++) {
+ Value v = values[i];
+ Value v2 = v.copyToResult();
+ if (v2 != v) {
+ containsLobs = true;
+ session.addTemporaryLob(v2);
+ values[i] = v2;
+ }
+ }
+ }
+
+ private ValueArray getArrayOfDistinct(Value[] values) {
+ if (distinctIndexes != null) {
+ int cnt = distinctIndexes.length;
+ Value[] newValues = new Value[cnt];
+ for (int i = 0; i < cnt; i++) {
+ newValues[i] = values[distinctIndexes[i]];
+ }
+ values = newValues;
+ } else if (values.length > visibleColumnCount) {
+ values = Arrays.copyOf(values, visibleColumnCount);
+ }
+ return ValueArray.get(values);
+ }
+
+ private void createExternalResult() {
+ Database database = session.getDatabase();
+ external = database.isMVStore()
+ || /* not supported by ResultTempTable */ distinct && expressions.length != visibleColumnCount
+ || distinctIndexes != null
+ ? MVTempResult.of(database, expressions, distinct, distinctIndexes, visibleColumnCount, sort)
+ : new ResultTempTable(session, expressions, distinct, sort);
+ }
+
+ /**
+ * Add a row to this object.
+ *
+ * @param values the row to add
+ */
+ @Override
+ public void addRow(Value[] values) {
+ cloneLobs(values);
+ if (isAnyDistinct()) {
+ if (distinctRows != null) {
+ ValueArray array = getArrayOfDistinct(values);
+ distinctRows.putIfAbsent(array, values);
+ rowCount = distinctRows.size();
+ if (rowCount > maxMemoryRows) {
+ createExternalResult();
+ rowCount = external.addRows(distinctRows.values());
+ distinctRows = null;
+ }
+ } else {
+ rowCount = external.addRow(values);
+ }
+ } else {
+ rows.add(values);
+ rowCount++;
+ if (rows.size() > maxMemoryRows) {
+ addRowsToDisk();
+ }
+ }
+ }
+
+ private void addRowsToDisk() {
+ if (external == null) {
+ createExternalResult();
+ }
+ rowCount = external.addRows(rows);
+ rows.clear();
+ }
+
+ @Override
+ public int getVisibleColumnCount() {
+ return visibleColumnCount;
+ }
+
+ /**
+ * This method is called after all rows have been added.
+ */
+ public void done() {
+ if (external != null) {
+ addRowsToDisk();
+ } else {
+ if (isAnyDistinct()) {
+ rows = distinctRows.values();
+ }
+ if (sort != null && limit != 0) {
+ boolean withLimit = limit > 0 && !withTies;
+ if (offset > 0 || withLimit) {
+ sort.sort(rows, offset, withLimit ? limit : rows.size());
+ } else {
+ sort.sort(rows);
+ }
+ }
+ }
+ applyOffsetAndLimit();
+ reset();
+ }
+
+ private void applyOffsetAndLimit() {
+ if (limitsWereApplied) {
+ return;
+ }
+ int offset = Math.max(this.offset, 0);
+ int limit = this.limit;
+ if (offset == 0 && limit < 0 && !fetchPercent || rowCount == 0) {
+ return;
+ }
+ if (fetchPercent) {
+ if (limit < 0 || limit > 100) {
+ throw DbException.getInvalidValueException("FETCH PERCENT", limit);
+ }
+ // Oracle rounds percent up, do the same for now
+ limit = (int) (((long) limit * rowCount + 99) / 100);
+ }
+ boolean clearAll = offset >= rowCount || limit == 0;
+ if (!clearAll) {
+ int remaining = rowCount - offset;
+ limit = limit < 0 ? remaining : Math.min(remaining, limit);
+ if (offset == 0 && remaining <= limit) {
+ return;
+ }
+ } else {
+ limit = 0;
+ }
+ distinctRows = null;
+ rowCount = limit;
+ if (external == null) {
+ if (clearAll) {
+ rows.clear();
+ return;
+ }
+ int to = offset + limit;
+ if (withTies && sort != null) {
+ Value[] expected = rows.get(to - 1);
+ while (to < rows.size() && sort.compare(expected, rows.get(to)) == 0) {
+ to++;
+ rowCount++;
+ }
+ }
+ if (offset != 0 || to != rows.size()) {
+ // avoid copying the whole array for each row
+ rows = new ArrayList<>(rows.subList(offset, to));
+ }
+ } else {
+ if (clearAll) {
+ external.close();
+ external = null;
+ return;
+ }
+ trimExternal(offset, limit);
+ }
+ }
+
+ private void trimExternal(int offset, int limit) {
+ ResultExternal temp = external;
+ external = null;
+ temp.reset();
+ while (--offset >= 0) {
+ temp.next();
+ }
+ Value[] row = null;
+ while (--limit >= 0) {
+ row = temp.next();
+ rows.add(row);
+ if (rows.size() > maxMemoryRows) {
+ addRowsToDisk();
+ }
+ }
+ if (withTies && sort != null && row != null) {
+ Value[] expected = row;
+ while ((row = temp.next()) != null && sort.compare(expected, row) == 0) {
+ rows.add(row);
+ rowCount++;
+ if (rows.size() > maxMemoryRows) {
+ addRowsToDisk();
+ }
+ }
+ }
+ if (external != null) {
+ addRowsToDisk();
+ }
+ temp.close();
+ }
+
+ @Override
+ public int getRowCount() {
+ return rowCount;
+ }
+
+ @Override
+ public void limitsWereApplied() {
+ this.limitsWereApplied = true;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return !closed && rowId < rowCount - 1;
+ }
+
+ /**
+ * Set the number of rows that this result will return at the maximum.
+ *
+ * @param limit the limit (-1 means no limit, 0 means no rows)
+ */
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ /**
+ * @param fetchPercent whether limit expression specifies percentage of rows
+ */
+ public void setFetchPercent(boolean fetchPercent) {
+ this.fetchPercent = fetchPercent;
+ }
+
+ /**
+ * @param withTies whether tied rows should be included in result too
+ */
+ public void setWithTies(boolean withTies) {
+ this.withTies = withTies;
+ }
+
+ @Override
+ public boolean needToClose() {
+ return external != null;
+ }
+
+ @Override
+ public void close() {
+ if (external != null) {
+ external.close();
+ external = null;
+ closed = true;
+ }
+ }
+
+ @Override
+ public String getAlias(int i) {
+ return expressions[i].getAlias();
+ }
+
+ @Override
+ public String getTableName(int i) {
+ return expressions[i].getTableName();
+ }
+
+ @Override
+ public String getSchemaName(int i) {
+ return expressions[i].getSchemaName();
+ }
+
+ @Override
+ public int getDisplaySize(int i) {
+ return expressions[i].getDisplaySize();
+ }
+
+ @Override
+ public String getColumnName(int i) {
+ return expressions[i].getColumnName();
+ }
+
+ @Override
+ public int getColumnType(int i) {
+ return expressions[i].getType();
+ }
+
+ @Override
+ public long getColumnPrecision(int i) {
+ return expressions[i].getPrecision();
+ }
+
+ @Override
+ public int getNullable(int i) {
+ return expressions[i].getNullable();
+ }
+
+ @Override
+ public boolean isAutoIncrement(int i) {
+ return expressions[i].isAutoIncrement();
+ }
+
+ @Override
+ public int getColumnScale(int i) {
+ return expressions[i].getScale();
+ }
+
+ /**
+ * Set the offset of the first row to return.
+ *
+ * @param offset the offset
+ */
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " columns: " + visibleColumnCount +
+ " rows: " + rowCount + " pos: " + rowId;
+ }
+
+ /**
+ * Check if this result set is closed.
+ *
+ * @return true if it is
+ */
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public int getFetchSize() {
+ return 0;
+ }
+
+ @Override
+ public void setFetchSize(int fetchSize) {
+ // ignore
+ }
+
+}
Index: h2/src/test/org/h2/test/TestAll.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/test/org/h2/test/TestAll.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/test/org/h2/test/TestAll.java (date 1536655889000)
@@ -193,6 +193,7 @@
import org.h2.test.unit.TestIntPerfectHash;
import org.h2.test.unit.TestInterval;
import org.h2.test.unit.TestJmx;
+import org.h2.test.unit.TestLocalResultFactory;
import org.h2.test.unit.TestLocale;
import org.h2.test.unit.TestMathUtils;
import org.h2.test.unit.TestMemoryUnmapper;
@@ -980,6 +981,7 @@
addTest(new TestTraceSystem());
addTest(new TestUtils());
addTest(new TestValueHashMap());
+ addTest(new TestLocalResultFactory());
runAddedTests();
Index: h2/src/main/org/h2/command/dml/SelectUnion.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/SelectUnion.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/SelectUnion.java (date 1536655889000)
@@ -130,14 +130,14 @@
@Override
public ResultInterface queryMeta() {
int columnCount = left.getColumnCount();
- LocalResult result = new LocalResult(session, expressionArray, columnCount);
+ LocalResult result = session.getDatabase().getResultFactory().create(session, expressionArray, columnCount);
result.done();
return result;
}
public LocalResult getEmptyResult() {
int columnCount = left.getColumnCount();
- return new LocalResult(session, expressionArray, columnCount);
+ return session.getDatabase().getResultFactory().create(session, expressionArray, columnCount);
}
@Override
@@ -189,7 +189,7 @@
return lazyResult;
}
}
- LocalResult result = new LocalResult(session, expressionArray, columnCount);
+ LocalResult result = session.getDatabase().getResultFactory().create(session, expressionArray, columnCount);
if (sort != null) {
result.setSortOrder(sort);
}
@@ -239,7 +239,7 @@
break;
}
case INTERSECT: {
- LocalResult temp = new LocalResult(session, expressionArray, columnCount);
+ LocalResult temp = session.getDatabase().getResultFactory().create(session, expressionArray, columnCount);
temp.setDistinct();
while (l.next()) {
temp.addRow(convert(l.currentRow(), columnCount));
Index: h2/src/main/org/h2/command/dml/Select.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/Select.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/Select.java (date 1536655889000)
@@ -687,7 +687,7 @@
@Override
public ResultInterface queryMeta() {
- LocalResult result = new LocalResult(session, expressionArray,
+ LocalResult result = session.getDatabase().getResultFactory().create(session, expressionArray,
visibleColumnCount);
result.done();
return result;
@@ -858,12 +858,13 @@
}
private LocalResult createLocalResult(LocalResult old) {
- return old != null ? old : new LocalResult(session, expressionArray,
+ return old != null ? old : session.getDatabase().getResultFactory().create(session, expressionArray,
visibleColumnCount);
}
private LocalResult convertToDistinct(ResultInterface result) {
- LocalResult distinctResult = new LocalResult(session, expressionArray, visibleColumnCount);
+ LocalResult distinctResult = session.getDatabase().getResultFactory().create(session,
+ expressionArray, visibleColumnCount);
distinctResult.setDistinct();
result.reset();
while (result.next()) {
Index: h2/src/main/org/h2/expression/TableFunction.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/expression/TableFunction.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/expression/TableFunction.java (date 1536655889000)
@@ -86,7 +86,7 @@
ExpressionColumn col = new ExpressionColumn(db, c);
header[i] = col;
}
- LocalResult result = new LocalResult(session, header, len);
+ LocalResult result = db.getResultFactory().create(session, header, len);
if (distinctRows) {
result.setDistinct();
}
Index: h2/src/main/org/h2/command/dml/Set.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/Set.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/Set.java (date 1536655889000)
@@ -19,6 +19,7 @@
import org.h2.expression.ValueExpression;
import org.h2.message.DbException;
import org.h2.message.Trace;
+import org.h2.result.LocalResultFactory;
import org.h2.result.ResultInterface;
import org.h2.result.RowFactory;
import org.h2.schema.Schema;
@@ -556,6 +557,19 @@
throw DbException.convert(e);
}
}
+ break;
+ }
+ case SetTypes.LOCAL_RESULT_FACTORY: {
+ session.getUser().checkAdmin();
+ String localResultFactoryName = expression.getColumnName();
+ Class<LocalResultFactory> localResultFactoryClass = JdbcUtils.loadUserClass(localResultFactoryName);
+ LocalResultFactory localResultFactory;
+ try {
+ localResultFactory = localResultFactoryClass.getDeclaredConstructor().newInstance();
+ database.setResultFactory(localResultFactory);
+ } catch (Exception e) {
+ throw DbException.convert(e);
+ }
break;
}
default:
Index: h2/src/main/org/h2/command/dml/SetTypes.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/main/org/h2/command/dml/SetTypes.java (revision 5f682ecea5dc182df2cd42f4c26c60a72a90e538)
+++ h2/src/main/org/h2/command/dml/SetTypes.java (date 1536655889000)
@@ -252,7 +252,12 @@
*/
public static final int AUTHENTICATOR = 48;
- private static final int COUNT = AUTHENTICATOR + 1;
+ /**
+ * The type of a SET LOCAL_RESULT_FACTORY statement.
+ */
+ public static final int LOCAL_RESULT_FACTORY = 49;
+
+ private static final int COUNT = LOCAL_RESULT_FACTORY + 1;
private static final ArrayList<String> TYPES;
@@ -311,6 +316,7 @@
list.add(BUILTIN_ALIAS_OVERRIDE, "BUILTIN_ALIAS_OVERRIDE");
list.add(COLUMN_NAME_RULES, "COLUMN_NAME_RULES");
list.add(AUTHENTICATOR, "AUTHENTICATOR");
+ list.add(LOCAL_RESULT_FACTORY, "LOCAL_RESULT_FACTORY");
TYPES = list;
}
Index: h2/src/test/org/h2/test/unit/TestLocalResultFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- h2/src/test/org/h2/test/unit/TestLocalResultFactory.java (date 1536655889000)
+++ h2/src/test/org/h2/test/unit/TestLocalResultFactory.java (date 1536655889000)
@@ -0,0 +1,65 @@
+/*
+ * 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.test.unit;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.Statement;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.h2.engine.Session;
+import org.h2.expression.Expression;
+import org.h2.result.LocalResult;
+import org.h2.result.LocalResultFactory;
+import org.h2.test.TestBase;
+
+/**
+ * Test {@link LocalResultFactory} setting.
+ */
+public class TestLocalResultFactory extends TestBase {
+
+ /**
+ * Run just this test.
+ *
+ * @param a ignored
+ */
+ public static void main(String[] a) throws Exception {
+ TestBase.createCaller().init().test();
+ }
+
+ @Override
+ public void test() throws Exception {
+ try (Connection conn = DriverManager.getConnection("jdbc:h2:mem:localResultFactory;LOCAL_RESULT_FACTORY=\""
+ + MyTestLocalResultFactory.class.getName() + '"')) {
+ Statement stat = conn.createStatement();
+
+ stat.execute("create table t1(id int, name varchar)");
+ for (int i = 0; i < 1000; i++) {
+ stat.execute("insert into t1 values(" + i + ", 'name')");
+ }
+ assertEquals(MyTestLocalResultFactory.COUNTER.get(), 0);
+
+ stat.execute("select * from t1");
+ assertEquals(MyTestLocalResultFactory.COUNTER.get(), 1);
+ }
+ }
+
+ /**
+ * Test local result factory.
+ */
+ public static class MyTestLocalResultFactory extends LocalResultFactory {
+ static final AtomicInteger COUNTER = new AtomicInteger();
+
+ @Override public LocalResult create(Session session, Expression[] expressions, int visibleColumnCount) {
+ COUNTER.incrementAndGet();
+ return LocalResultFactory.DEFAULT.create(session, expressions, visibleColumnCount);
+ }
+
+ @Override public LocalResult create() {
+ COUNTER.incrementAndGet();
+ return LocalResultFactory.DEFAULT.create();
+ }
+ }
+}