提交 508ed18e authored 作者: S.Vladykin's avatar S.Vladykin

Merge branch 'master' of https://github.com/h2database/h2database into batchview2

...@@ -21,6 +21,8 @@ Change Log ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>CLOB and BLOB objects of removed rows were sometimes kept in the database file.
</li>
<li>Server mode: executing "shutdown" left a thread on the server. <li>Server mode: executing "shutdown" left a thread on the server.
</li> </li>
<li>The condition "in(select...)" did not work correctly in some cases if the subquery had an "order by". <li>The condition "in(select...)" did not work correctly in some cases if the subquery had an "order by".
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
package org.h2.command.dml; package org.h2.command.dml;
import java.text.Collator; import java.text.Collator;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
import org.h2.command.Prepared; import org.h2.command.Prepared;
...@@ -20,9 +19,11 @@ import org.h2.expression.Expression; ...@@ -20,9 +19,11 @@ import org.h2.expression.Expression;
import org.h2.expression.ValueExpression; import org.h2.expression.ValueExpression;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.result.RowFactory;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.tools.CompressTool; import org.h2.tools.CompressTool;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.CompareMode; import org.h2.value.CompareMode;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
...@@ -483,6 +484,19 @@ public class Set extends Prepared { ...@@ -483,6 +484,19 @@ public class Set extends Prepared {
addOrUpdateSetting(name, null, getIntValue()); addOrUpdateSetting(name, null, getIntValue());
break; break;
} }
case SetTypes.ROW_FACTORY: {
session.getUser().checkAdmin();
String rowFactoryName = expression.getColumnName();
Class<RowFactory> rowFactoryClass = JdbcUtils.loadUserClass(rowFactoryName);
RowFactory rowFactory;
try {
rowFactory = rowFactoryClass.newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
database.setRowFactory(rowFactory);
break;
}
default: default:
DbException.throwInternalError("type="+type); DbException.throwInternalError("type="+type);
} }
......
...@@ -223,6 +223,11 @@ public class SetTypes { ...@@ -223,6 +223,11 @@ public class SetTypes {
*/ */
public static final int QUERY_STATISTICS_MAX_ENTRIES = 42; public static final int QUERY_STATISTICS_MAX_ENTRIES = 42;
/**
* The type of a SET ROW_FACTORY statement.
*/
public static final int ROW_FACTORY = 43;
private static final ArrayList<String> TYPES = New.arrayList(); private static final ArrayList<String> TYPES = New.arrayList();
private SetTypes() { private SetTypes() {
...@@ -274,6 +279,7 @@ public class SetTypes { ...@@ -274,6 +279,7 @@ public class SetTypes {
list.add(RETENTION_TIME, "RETENTION_TIME"); list.add(RETENTION_TIME, "RETENTION_TIME");
list.add(QUERY_STATISTICS, "QUERY_STATISTICS"); list.add(QUERY_STATISTICS, "QUERY_STATISTICS");
list.add(QUERY_STATISTICS_MAX_ENTRIES, "QUERY_STATISTICS_MAX_ENTRIES"); list.add(QUERY_STATISTICS_MAX_ENTRIES, "QUERY_STATISTICS_MAX_ENTRIES");
list.add(ROW_FACTORY, "ROW_FACTORY");
} }
/** /**
......
...@@ -31,6 +31,7 @@ import org.h2.message.Trace; ...@@ -31,6 +31,7 @@ import org.h2.message.Trace;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.mvstore.db.MVTableEngine; import org.h2.mvstore.db.MVTableEngine;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.schema.SchemaObject; import org.h2.schema.SchemaObject;
...@@ -191,6 +192,7 @@ public class Database implements DataHandler { ...@@ -191,6 +192,7 @@ public class Database implements DataHandler {
private boolean queryStatistics; private boolean queryStatistics;
private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES; private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES;
private QueryStatisticsData queryStatisticsData; private QueryStatisticsData queryStatisticsData;
private RowFactory rowFactory = RowFactory.DEFAULT;
public Database(ConnectionInfo ci, String cipher) { public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName(); String name = ci.getName();
...@@ -249,7 +251,6 @@ public class Database implements DataHandler { ...@@ -249,7 +251,6 @@ public class Database implements DataHandler {
ci.getProperty("JAVA_OBJECT_SERIALIZER", null); ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
this.multiThreaded = this.multiThreaded =
ci.getProperty("MULTI_THREADED", false); ci.getProperty("MULTI_THREADED", false);
boolean closeAtVmShutdown = boolean closeAtVmShutdown =
dbSettings.dbCloseOnExit; dbSettings.dbCloseOnExit;
int traceLevelFile = int traceLevelFile =
...@@ -301,6 +302,18 @@ public class Database implements DataHandler { ...@@ -301,6 +302,18 @@ public class Database implements DataHandler {
} }
} }
public Row createRow(Value[] data, int memory) {
return rowFactory.createRow(data, memory);
}
public RowFactory getRowFactory() {
return rowFactory;
}
public void setRowFactory(RowFactory rowFactory) {
this.rowFactory = rowFactory;
}
public static void setInitialPowerOffCount(int count) { public static void setInitialPowerOffCount(int count) {
initialPowerOffCount = count; initialPowerOffCount = count;
} }
......
...@@ -148,6 +148,10 @@ public class Session extends SessionWithState { ...@@ -148,6 +148,10 @@ public class Session extends SessionWithState {
this.currentSchemaName = Constants.SCHEMA_MAIN; this.currentSchemaName = Constants.SCHEMA_MAIN;
} }
public Row createRow(Value[] data, int memory) {
return database.createRow(data, memory);
}
public void setSubQueryInfo(SubQueryInfo subQueryInfo) { public void setSubQueryInfo(SubQueryInfo subQueryInfo) {
this.subQueryInfo = subQueryInfo; this.subQueryInfo = subQueryInfo;
} }
...@@ -680,7 +684,7 @@ public class Session extends SessionWithState { ...@@ -680,7 +684,7 @@ public class Session extends SessionWithState {
row = t.getRow(this, key); row = t.getRow(this, key);
} else { } else {
op = UndoLogRecord.DELETE; op = UndoLogRecord.DELETE;
row = new Row(value.getList(), Row.MEMORY_CALCULATE); row = createRow(value.getList(), Row.MEMORY_CALCULATE);
} }
row.setKey(key); row.setKey(key);
UndoLogRecord log = new UndoLogRecord(t, op, row); UndoLogRecord log = new UndoLogRecord(t, op, row);
...@@ -1164,6 +1168,10 @@ public class Session extends SessionWithState { ...@@ -1164,6 +1168,10 @@ public class Session extends SessionWithState {
if (SysProperties.CHECK && !v.isLinkedToTable()) { if (SysProperties.CHECK && !v.isLinkedToTable()) {
DbException.throwInternalError(); DbException.throwInternalError();
} }
if (removeLobMap == null) {
removeLobMap = New.hashMap();
removeLobMap.put(v.toString(), v);
}
} }
/** /**
......
...@@ -221,7 +221,7 @@ public class UndoLogRecord { ...@@ -221,7 +221,7 @@ public class UndoLogRecord {
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue(); values[i] = buff.readValue();
} }
row = new Row(values, Row.MEMORY_CALCULATE); row = getTable().getDatabase().createRow(values, Row.MEMORY_CALCULATE);
row.setKey(key); row.setKey(key);
row.setDeleted(deleted); row.setDeleted(deleted);
row.setSessionId(sessionId); row.setSessionId(sessionId);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.index; package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.result.Row; import org.h2.result.Row;
...@@ -16,11 +17,13 @@ import org.h2.value.Value; ...@@ -16,11 +17,13 @@ import org.h2.value.Value;
*/ */
public class FunctionCursor implements Cursor { public class FunctionCursor implements Cursor {
private Session session;
private final ResultInterface result; private final ResultInterface result;
private Value[] values; private Value[] values;
private Row row; private Row row;
FunctionCursor(ResultInterface result) { FunctionCursor(Session session, ResultInterface result) {
this.session = session;
this.result = result; this.result = result;
} }
...@@ -30,7 +33,7 @@ public class FunctionCursor implements Cursor { ...@@ -30,7 +33,7 @@ public class FunctionCursor implements Cursor {
return null; return null;
} }
if (row == null) { if (row == null) {
row = new Row(values, 1); row = session.createRow(values, 1);
} }
return row; return row;
} }
......
...@@ -42,7 +42,7 @@ public class FunctionCursorResultSet implements Cursor { ...@@ -42,7 +42,7 @@ public class FunctionCursorResultSet implements Cursor {
return null; return null;
} }
if (row == null) { if (row == null) {
row = new Row(values, 1); row = session.createRow(values, 1);
} }
return row; return row;
} }
......
...@@ -45,7 +45,7 @@ public class FunctionIndex extends BaseIndex { ...@@ -45,7 +45,7 @@ public class FunctionIndex extends BaseIndex {
@Override @Override
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
if (functionTable.isBufferResultSetToLocalTemp()) { if (functionTable.isBufferResultSetToLocalTemp()) {
return new FunctionCursor(functionTable.getResult(session)); return new FunctionCursor(session, functionTable.getResult(session));
} }
return new FunctionCursorResultSet(session, return new FunctionCursorResultSet(session,
functionTable.getResultSet(session)); functionTable.getResultSet(session));
......
...@@ -17,7 +17,6 @@ import org.h2.result.Row; ...@@ -17,7 +17,6 @@ import org.h2.result.Row;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.store.Page; import org.h2.store.Page;
import org.h2.store.PageStore; import org.h2.store.PageStore;
import org.h2.table.RegularTable;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -617,7 +616,7 @@ public class PageDataLeaf extends PageData { ...@@ -617,7 +616,7 @@ public class PageDataLeaf extends PageData {
* @param columnCount the number of columns * @param columnCount the number of columns
* @return the row * @return the row
*/ */
private static Row readRow(Data data, int pos, int columnCount) { private Row readRow(Data data, int pos, int columnCount) {
Value[] values = new Value[columnCount]; Value[] values = new Value[columnCount];
synchronized (data) { synchronized (data) {
data.setPos(pos); data.setPos(pos);
...@@ -625,7 +624,7 @@ public class PageDataLeaf extends PageData { ...@@ -625,7 +624,7 @@ public class PageDataLeaf extends PageData {
values[i] = data.readValue(); values[i] = data.readValue();
} }
} }
return RegularTable.createRow(values); return index.getDatabase().createRow(values, Row.MEMORY_CALCULATE);
} }
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.index; package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
...@@ -16,16 +17,18 @@ import org.h2.value.ValueLong; ...@@ -16,16 +17,18 @@ import org.h2.value.ValueLong;
*/ */
class RangeCursor implements Cursor { class RangeCursor implements Cursor {
private Session session;
private boolean beforeFirst; private boolean beforeFirst;
private long current; private long current;
private Row currentRow; private Row currentRow;
private final long start, end, step; private final long start, end, step;
RangeCursor(long start, long end) { RangeCursor(Session session, long start, long end) {
this(start, end, 1); this(session, start, end, 1);
} }
RangeCursor(long start, long end, long step) { RangeCursor(Session session, long start, long end, long step) {
this.session = session;
this.start = start; this.start = start;
this.end = end; this.end = end;
this.step = step; this.step = step;
...@@ -50,7 +53,7 @@ class RangeCursor implements Cursor { ...@@ -50,7 +53,7 @@ class RangeCursor implements Cursor {
} else { } else {
current += step; current += step;
} }
currentRow = new Row(new Value[]{ValueLong.get(current)}, 1); currentRow = session.createRow(new Value[]{ValueLong.get(current)}, 1);
return step > 0 ? current <= end : current >= end; return step > 0 ? current <= end : current >= end;
} }
......
...@@ -58,7 +58,7 @@ public class RangeIndex extends BaseIndex { ...@@ -58,7 +58,7 @@ public class RangeIndex extends BaseIndex {
} catch (Exception e) { } catch (Exception e) {
// error when converting the value - ignore // error when converting the value - ignore
} }
return new RangeCursor(start, end, step); return new RangeCursor(session, start, end, step);
} }
@Override @Override
...@@ -100,7 +100,7 @@ public class RangeIndex extends BaseIndex { ...@@ -100,7 +100,7 @@ public class RangeIndex extends BaseIndex {
@Override @Override
public Cursor findFirstOrLast(Session session, boolean first) { public Cursor findFirstOrLast(Session session, boolean first) {
long pos = first ? rangeTable.getMin(session) : rangeTable.getMax(session); long pos = first ? rangeTable.getMin(session) : rangeTable.getMax(session);
return new RangeCursor(pos, pos); return new RangeCursor(session, pos, pos);
} }
@Override @Override
......
...@@ -143,7 +143,7 @@ public class ScanIndex extends BaseIndex { ...@@ -143,7 +143,7 @@ public class ScanIndex extends BaseIndex {
rows = New.arrayList(); rows = New.arrayList();
firstFree = -1; firstFree = -1;
} else { } else {
Row free = new Row(null, 1); Row free = session.createRow(null, 1);
free.setKey(firstFree); free.setKey(firstFree);
long key = row.getKey(); long key = row.getKey();
if (rows.size() <= key) { if (rows.size() <= key) {
......
...@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex {
} }
} }
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(map.entryIterator(min), max); return new MVStoreCursor(session, map.entryIterator(min), max);
} }
@Override @Override
...@@ -210,7 +210,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -210,7 +210,7 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
Value v = map.get(ValueLong.get(key)); Value v = map.get(ValueLong.get(key));
ValueArray array = (ValueArray) v; ValueArray array = (ValueArray) v;
Row row = new Row(array.getList(), 0); Row row = session.createRow(array.getList(), 0);
row.setKey(key); row.setKey(key);
return row; return row;
} }
...@@ -260,14 +260,14 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -260,14 +260,14 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey()); ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
if (v == null) { if (v == null) {
return new MVStoreCursor(Collections return new MVStoreCursor(session, Collections
.<Entry<Value, Value>> emptyList().iterator(), null); .<Entry<Value, Value>> emptyList().iterator(), null);
} }
Value value = map.get(v); Value value = map.get(v);
Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value); Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<Entry<Value, Value>> list = Arrays.asList(e); List<Entry<Value, Value>> list = Arrays.asList(e);
MVStoreCursor c = new MVStoreCursor(list.iterator(), v); MVStoreCursor c = new MVStoreCursor(session, list.iterator(), v);
c.next(); c.next();
return c; return c;
} }
...@@ -347,7 +347,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -347,7 +347,7 @@ public class MVPrimaryIndex extends BaseIndex {
*/ */
Cursor find(Session session, ValueLong first, ValueLong last) { Cursor find(Session session, ValueLong first, ValueLong last) {
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(map.entryIterator(first), last); return new MVStoreCursor(session, map.entryIterator(first), last);
} }
@Override @Override
...@@ -374,12 +374,14 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -374,12 +374,14 @@ public class MVPrimaryIndex extends BaseIndex {
*/ */
class MVStoreCursor implements Cursor { class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<Entry<Value, Value>> it; private final Iterator<Entry<Value, Value>> it;
private final ValueLong last; private final ValueLong last;
private Entry<Value, Value> current; private Entry<Value, Value> current;
private Row row; private Row row;
public MVStoreCursor(Iterator<Entry<Value, Value>> it, ValueLong last) { public MVStoreCursor(Session session, Iterator<Entry<Value, Value>> it, ValueLong last) {
this.session = session;
this.it = it; this.it = it;
this.last = last; this.last = last;
} }
...@@ -389,7 +391,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -389,7 +391,7 @@ public class MVPrimaryIndex extends BaseIndex {
if (row == null) { if (row == null) {
if (current != null) { if (current != null) {
ValueArray array = (ValueArray) current.getValue(); ValueArray array = (ValueArray) current.getValue();
row = new Row(array.getList(), 0); row = session.createRow(array.getList(), 0);
row.setKey(current.getKey().getLong()); row.setKey(current.getKey().getLong());
} }
} }
......
...@@ -288,7 +288,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -288,7 +288,7 @@ public class ResultTempTable implements ResultExternal {
} }
values = v2; values = v2;
} }
return new Row(values, Row.MEMORY_CALCULATE); return session.createRow(values, Row.MEMORY_CALCULATE);
} }
private Cursor find(Row row) { private Cursor find(Row row) {
......
...@@ -5,77 +5,31 @@ ...@@ -5,77 +5,31 @@
*/ */
package org.h2.result; package org.h2.result;
import org.h2.engine.Constants;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.util.StatementBuilder;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueLong;
/** /**
* Represents a row in a table. * Represents a row in a table.
*/ */
public class Row implements SearchRow { public abstract class Row implements SearchRow {
public static final int MEMORY_CALCULATE = -1; public static final int MEMORY_CALCULATE = -1;
public static final Row[] EMPTY_ARRAY = {}; public static final Row[] EMPTY_ARRAY = {};
private long key;
private final Value[] data;
private int memory;
private int version;
private boolean deleted;
private int sessionId;
public Row(Value[] data, int memory) {
this.data = data;
this.memory = memory;
}
/** /**
* Get a copy of the row that is distinct from (not equal to) this row. * Get a copy of the row that is distinct from (not equal to) this row.
* This is used for FOR UPDATE to allow pseudo-updating a row. * This is used for FOR UPDATE to allow pseudo-updating a row.
* *
* @return a new row with the same data * @return a new row with the same data
*/ */
public Row getCopy() { public abstract Row getCopy();
Value[] d2 = new Value[data.length];
System.arraycopy(data, 0, d2, 0, data.length);
Row r2 = new Row(d2, memory);
r2.key = key;
r2.version = version + 1;
r2.sessionId = sessionId;
return r2;
}
@Override
public void setKeyAndVersion(SearchRow row) {
setKey(row.getKey());
setVersion(row.getVersion());
}
@Override
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Override /**
public long getKey() { * Set version.
return key; *
} * @param version row version
*/
@Override public abstract void setVersion(int version);
public void setKey(long key) {
this.key = key;
}
@Override
public Value getValue(int i) {
return i == -1 ? ValueLong.get(key) : data[i];
}
/** /**
* Get the number of bytes required for the data. * Get the number of bytes required for the data.
...@@ -83,97 +37,52 @@ public class Row implements SearchRow { ...@@ -83,97 +37,52 @@ public class Row implements SearchRow {
* @param dummy the template buffer * @param dummy the template buffer
* @return the number of bytes * @return the number of bytes
*/ */
public int getByteCount(Data dummy) { public abstract int getByteCount(Data dummy);
int size = 0;
for (Value v : data) {
size += dummy.getValueLen(v);
}
return size;
}
@Override
public void setValue(int i, Value v) {
if (i == -1) {
this.key = v.getLong();
} else {
data[i] = v;
}
}
public boolean isEmpty() {
return data == null;
}
@Override
public int getColumnCount() {
return data.length;
}
@Override /**
public int getMemory() { * Check if this is an empty row.
if (memory != MEMORY_CALCULATE) { *
return memory; * @return {@code true} if the row is empty
} */
int m = Constants.MEMORY_ROW; public abstract boolean isEmpty();
if (data != null) {
int len = data.length;
m += Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER;
for (int i = 0; i < len; i++) {
Value v = data[i];
if (v != null) {
m += v.getMemory();
}
}
}
this.memory = m;
return m;
}
@Override
public String toString() {
StatementBuilder buff = new StatementBuilder("( /* key:");
buff.append(getKey());
if (version != 0) {
buff.append(" v:" + version);
}
if (isDeleted()) {
buff.append(" deleted");
}
buff.append(" */ ");
if (data != null) {
for (Value v : data) {
buff.appendExceptFirst(", ");
buff.append(v == null ? "null" : v.getTraceSQL());
}
}
return buff.append(')').toString();
}
public void setDeleted(boolean deleted) { /**
this.deleted = deleted; * Mark the row as deleted.
} *
* @param deleted deleted flag
*/
public abstract void setDeleted(boolean deleted);
public void setSessionId(int sessionId) { /**
this.sessionId = sessionId; * Set session id.
} *
* @param sessionId the session id
*/
public abstract void setSessionId(int sessionId);
public int getSessionId() { /**
return sessionId; * Get session id.
} *
* @return the session id
*/
public abstract int getSessionId();
/** /**
* This record has been committed. The session id is reset. * This record has been committed. The session id is reset.
*/ */
public void commit() { public abstract void commit();
this.sessionId = 0;
}
public boolean isDeleted() {
return deleted;
}
public Value[] getValueList() { /**
return data; * Check if the row is deleted.
} *
* @return {@code true} if the row is deleted
*/
public abstract boolean isDeleted();
/**
* Get values.
*
* @return values
*/
public abstract Value[] getValueList();
} }
package org.h2.result;
import org.h2.value.Value;
/**
* Creates rows.
*
* @author Sergi Vladykin
*/
public abstract class RowFactory {
/**
* Default implementation of row factory.
*/
public static final RowFactory DEFAULT = new DefaultRowFactory();
/**
* Create new row.
*
* @param data row values
* @param memory memory
* @return created row
*/
public abstract Row createRow(Value[] data, int memory);
/**
* Default implementation of row factory.
*/
private static final class DefaultRowFactory extends RowFactory {
@Override
public Row createRow(Value[] data, int memory) {
return new RowImpl(data, memory);
}
}
}
/*
* Copyright 2004-2014 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 org.h2.engine.Constants;
import org.h2.store.Data;
import org.h2.util.StatementBuilder;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* Default row implementation.
*/
public class RowImpl extends Row {
private long key;
private final Value[] data;
private int memory;
private int version;
private boolean deleted;
private int sessionId;
public RowImpl(Value[] data, int memory) {
this.data = data;
this.memory = memory;
}
/**
* Get a copy of the row that is distinct from (not equal to) this row.
* This is used for FOR UPDATE to allow pseudo-updating a row.
*
* @return a new row with the same data
*/
public Row getCopy() {
Value[] d2 = new Value[data.length];
System.arraycopy(data, 0, d2, 0, data.length);
RowImpl r2 = new RowImpl(d2, memory);
r2.key = key;
r2.version = version + 1;
r2.sessionId = sessionId;
return r2;
}
@Override
public void setKeyAndVersion(SearchRow row) {
setKey(row.getKey());
setVersion(row.getVersion());
}
@Override
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
@Override
public long getKey() {
return key;
}
@Override
public void setKey(long key) {
this.key = key;
}
@Override
public Value getValue(int i) {
return i == -1 ? ValueLong.get(key) : data[i];
}
/**
* Get the number of bytes required for the data.
*
* @param dummy the template buffer
* @return the number of bytes
*/
public int getByteCount(Data dummy) {
int size = 0;
for (Value v : data) {
size += dummy.getValueLen(v);
}
return size;
}
@Override
public void setValue(int i, Value v) {
if (i == -1) {
this.key = v.getLong();
} else {
data[i] = v;
}
}
public boolean isEmpty() {
return data == null;
}
@Override
public int getColumnCount() {
return data.length;
}
@Override
public int getMemory() {
if (memory != MEMORY_CALCULATE) {
return memory;
}
int m = Constants.MEMORY_ROW;
if (data != null) {
int len = data.length;
m += Constants.MEMORY_OBJECT + len * Constants.MEMORY_POINTER;
for (int i = 0; i < len; i++) {
Value v = data[i];
if (v != null) {
m += v.getMemory();
}
}
}
this.memory = m;
return m;
}
@Override
public String toString() {
StatementBuilder buff = new StatementBuilder("( /* key:");
buff.append(getKey());
if (version != 0) {
buff.append(" v:" + version);
}
if (isDeleted()) {
buff.append(" deleted");
}
buff.append(" */ ");
if (data != null) {
for (Value v : data) {
buff.appendExceptFirst(", ");
buff.append(v == null ? "null" : v.getTraceSQL());
}
}
return buff.append(')').toString();
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public void setSessionId(int sessionId) {
this.sessionId = sessionId;
}
public int getSessionId() {
return sessionId;
}
/**
* This record has been committed. The session id is reset.
*/
public void commit() {
this.sessionId = 0;
}
public boolean isDeleted() {
return deleted;
}
public Value[] getValueList() {
return data;
}
}
...@@ -191,7 +191,7 @@ public class RowList { ...@@ -191,7 +191,7 @@ public class RowList {
} }
values[i] = v; values[i] = v;
} }
Row row = new Row(values, mem); Row row = session.createRow(values, mem);
row.setKey(key); row.setKey(key);
row.setVersion(version); row.setVersion(version);
row.setDeleted(deleted); row.setDeleted(deleted);
......
...@@ -104,7 +104,7 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -104,7 +104,7 @@ public class LobStorageMap implements LobStorageInterface {
} }
// search for the last block // search for the last block
// (in theory, only the latest lob can have unreferenced blocks, // (in theory, only the latest lob can have unreferenced blocks,
// but the latest lob could by a copy of another one, and // but the latest lob could be a copy of another one, and
// we don't know that, so we iterate over all lobs) // we don't know that, so we iterate over all lobs)
long lastUsedKey = -1; long lastUsedKey = -1;
for (Entry<Long, Object[]> e : lobMap.entrySet()) { for (Entry<Long, Object[]> e : lobMap.entrySet()) {
......
...@@ -9,7 +9,6 @@ import java.io.IOException; ...@@ -9,7 +9,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.compress.CompressLZF; import org.h2.compress.CompressLZF;
import org.h2.engine.Session; import org.h2.engine.Session;
...@@ -17,6 +16,7 @@ import org.h2.engine.SysProperties; ...@@ -17,6 +16,7 @@ import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.util.BitField; import org.h2.util.BitField;
import org.h2.util.IntArray; import org.h2.util.IntArray;
import org.h2.util.IntIntHashMap; import org.h2.util.IntIntHashMap;
...@@ -316,7 +316,7 @@ public class PageLog { ...@@ -316,7 +316,7 @@ public class PageLog {
} else if (x == ADD) { } else if (x == ADD) {
int sessionId = in.readVarInt(); int sessionId = in.readVarInt();
int tableId = in.readVarInt(); int tableId = in.readVarInt();
Row row = readRow(in, data); Row row = readRow(store.getDatabase().getRowFactory(), in, data);
if (stage == RECOVERY_STAGE_UNDO) { if (stage == RECOVERY_STAGE_UNDO) {
store.allocateIfIndexRoot(pos, tableId, row); store.allocateIfIndexRoot(pos, tableId, row);
} else if (stage == RECOVERY_STAGE_REDO) { } else if (stage == RECOVERY_STAGE_REDO) {
...@@ -452,11 +452,12 @@ public class PageLog { ...@@ -452,11 +452,12 @@ public class PageLog {
/** /**
* Read a row from an input stream. * Read a row from an input stream.
* *
* @param rowFactory the row factory
* @param in the input stream * @param in the input stream
* @param data a temporary buffer * @param data a temporary buffer
* @return the row * @return the row
*/ */
public static Row readRow(DataReader in, Data data) throws IOException { public static Row readRow(RowFactory rowFactory, DataReader in, Data data) throws IOException {
long key = in.readVarLong(); long key = in.readVarLong();
int len = in.readVarInt(); int len = in.readVarInt();
data.reset(); data.reset();
...@@ -467,7 +468,7 @@ public class PageLog { ...@@ -467,7 +468,7 @@ public class PageLog {
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
values[i] = data.readValue(); values[i] = data.readValue();
} }
Row row = new Row(values, Row.MEMORY_CALCULATE); Row row = rowFactory.createRow(values, Row.MEMORY_CALCULATE);
row.setKey(key); row.setKey(key);
return row; return row;
} }
......
...@@ -1978,7 +1978,7 @@ public class MetaTable extends Table { ...@@ -1978,7 +1978,7 @@ public class MetaTable extends Table {
v = col.convert(v); v = col.convert(v);
values[i] = v; values[i] = v;
} }
Row row = new Row(values, 1); Row row = database.createRow(values, 1);
row.setKey(rows.size()); row.setKey(rows.size());
rows.add(row); rows.add(row);
} }
......
...@@ -681,16 +681,6 @@ public class RegularTable extends TableBase { ...@@ -681,16 +681,6 @@ public class RegularTable extends TableBase {
} }
} }
/**
* Create a row from the values.
*
* @param data the value list
* @return the row
*/
public static Row createRow(Value[] data) {
return new Row(data, Row.MEMORY_CALCULATE);
}
/** /**
* Set the row count of this table. * Set the row count of this table.
* *
......
...@@ -606,7 +606,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -606,7 +606,7 @@ public abstract class Table extends SchemaObjectBase {
} }
public Row getTemplateRow() { public Row getTemplateRow() {
return new Row(new Value[columns.length], Row.MEMORY_CALCULATE); return database.createRow(new Value[columns.length], Row.MEMORY_CALCULATE);
} }
/** /**
...@@ -628,7 +628,7 @@ public abstract class Table extends SchemaObjectBase { ...@@ -628,7 +628,7 @@ public abstract class Table extends SchemaObjectBase {
// Here can be concurrently produced more than one row, but it must be ok. // Here can be concurrently produced more than one row, but it must be ok.
Value[] values = new Value[columns.length]; Value[] values = new Value[columns.length];
Arrays.fill(values, ValueNull.INSTANCE); Arrays.fill(values, ValueNull.INSTANCE);
nullRow = row = new Row(values, 1); nullRow = row = database.createRow(values, 1);
} }
return row; return row;
} }
......
...@@ -44,6 +44,7 @@ import org.h2.mvstore.db.TransactionStore; ...@@ -44,6 +44,7 @@ import org.h2.mvstore.db.TransactionStore;
import org.h2.mvstore.db.TransactionStore.TransactionMap; import org.h2.mvstore.db.TransactionStore.TransactionMap;
import org.h2.mvstore.db.ValueDataType; import org.h2.mvstore.db.ValueDataType;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SimpleRow; import org.h2.result.SimpleRow;
import org.h2.security.SHA256; import org.h2.security.SHA256;
import org.h2.store.Data; import org.h2.store.Data;
...@@ -948,7 +949,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -948,7 +949,7 @@ public class Recover extends Tool implements DataHandler {
} else if (x == PageLog.ADD) { } else if (x == PageLog.ADD) {
int sessionId = in.readVarInt(); int sessionId = in.readVarInt();
setStorage(in.readVarInt()); setStorage(in.readVarInt());
Row row = PageLog.readRow(in, s); Row row = PageLog.readRow(RowFactory.DEFAULT, in, s);
writer.println("-- session " + sessionId + writer.println("-- session " + sessionId +
" table " + storageId + " table " + storageId +
" + " + row.toString()); " + " + row.toString());
......
...@@ -19,10 +19,8 @@ import java.sql.Statement; ...@@ -19,10 +19,8 @@ import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Properties; import java.util.Properties;
import javax.naming.Context; import javax.naming.Context;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer; import org.h2.api.JavaObjectSerializer;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
...@@ -129,7 +127,8 @@ public class JdbcUtils { ...@@ -129,7 +127,8 @@ public class JdbcUtils {
* @param className the name of the class * @param className the name of the class
* @return the class object * @return the class object
*/ */
public static Class<?> loadUserClass(String className) { @SuppressWarnings("unchecked")
public static <Z> Class<Z> loadUserClass(String className) {
if (allowedClassNames == null) { if (allowedClassNames == null) {
// initialize the static fields // initialize the static fields
String s = SysProperties.ALLOWED_CLASSES; String s = SysProperties.ALLOWED_CLASSES;
...@@ -168,7 +167,7 @@ public class JdbcUtils { ...@@ -168,7 +167,7 @@ public class JdbcUtils {
try { try {
Class<?> userClass = classFactory.loadClass(className); Class<?> userClass = classFactory.loadClass(className);
if (!(userClass == null)) { if (!(userClass == null)) {
return userClass; return (Class<Z>) userClass;
} }
} catch (Exception e) { } catch (Exception e) {
throw DbException.get( throw DbException.get(
...@@ -178,10 +177,10 @@ public class JdbcUtils { ...@@ -178,10 +177,10 @@ public class JdbcUtils {
} }
// Use local ClassLoader // Use local ClassLoader
try { try {
return Class.forName(className); return (Class<Z>) Class.forName(className);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
try { try {
return Class.forName( return (Class<Z>) Class.forName(
className, true, className, true,
Thread.currentThread().getContextClassLoader()); Thread.currentThread().getContextClassLoader());
} catch (Exception e2) { } catch (Exception e2) {
......
...@@ -53,6 +53,7 @@ import org.h2.test.db.TestQueryCache; ...@@ -53,6 +53,7 @@ import org.h2.test.db.TestQueryCache;
import org.h2.test.db.TestReadOnly; import org.h2.test.db.TestReadOnly;
import org.h2.test.db.TestRecursiveQueries; import org.h2.test.db.TestRecursiveQueries;
import org.h2.test.db.TestRights; import org.h2.test.db.TestRights;
import org.h2.test.db.TestRowFactory;
import org.h2.test.db.TestRunscript; import org.h2.test.db.TestRunscript;
import org.h2.test.db.TestSQLInjection; import org.h2.test.db.TestSQLInjection;
import org.h2.test.db.TestScript; import org.h2.test.db.TestScript;
...@@ -696,6 +697,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -696,6 +697,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestSpatial()); addTest(new TestSpatial());
addTest(new TestSpeed()); addTest(new TestSpeed());
addTest(new TestTableEngines()); addTest(new TestTableEngines());
addTest(new TestRowFactory());
addTest(new TestTempTables()); addTest(new TestTempTables());
addTest(new TestTransaction()); addTest(new TestTransaction());
addTest(new TestTriggersConstraints()); addTest(new TestTriggersConstraints());
......
...@@ -28,7 +28,9 @@ import org.h2.api.ErrorCode; ...@@ -28,7 +28,9 @@ import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.Recover;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -57,6 +59,7 @@ public class TestLob extends TestBase { ...@@ -57,6 +59,7 @@ public class TestLob extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testRemoveAfterDeleteAndClose();
testRemovedAfterTimeout(); testRemovedAfterTimeout();
testConcurrentRemoveRead(); testConcurrentRemoveRead();
testCloseLobTwice(); testCloseLobTwice();
...@@ -109,6 +112,33 @@ public class TestLob extends TestBase { ...@@ -109,6 +112,33 @@ public class TestLob extends TestBase {
deleteDb("lob"); deleteDb("lob");
} }
private void testRemoveAfterDeleteAndClose() throws Exception {
if (config.memory) {
return;
}
deleteDb("lob");
Connection conn = getConnection("lob");
Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, data clob)");
for (int i = 0; i < 10; i++) {
stat.execute("insert into test values(1, space(100000))");
if (i > 5) {
ResultSet rs = stat.executeQuery("select * from test");
rs.next();
Clob c = rs.getClob(2);
stat.execute("delete from test where id = 1");
c.getSubString(1, 10);
} else {
stat.execute("delete from test where id = 1");
}
}
// some clobs are removed only here (those that were queries for)
conn.close();
Recover.execute(getBaseDir(), "lob");
long size = FileUtils.size(getBaseDir() + "/lob.h2.sql");
assertTrue("size: " + size, size > 1000 && size < 10000);
}
private void testLargeClob() throws Exception { private void testLargeClob() throws Exception {
deleteDb("lob"); deleteDb("lob");
Connection conn; Connection conn;
......
/*
* Copyright 2004-2014 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.db;
import java.sql.Connection;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicInteger;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.RowImpl;
import org.h2.test.TestBase;
import org.h2.value.Value;
/**
* Test {@link RowFactory} setting.
*
* @author Sergi Vladykin
*/
public class TestRowFactory 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 {
deleteDb("rowFactory");
Connection conn = getConnection("rowFactory;ROW_FACTORY=\"" +
MyTestRowFactory.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')");
}
assertTrue(MyTestRowFactory.COUNTER.get() >= 1000);
deleteDb("rowFactory");
}
/**
* Test row factory.
*/
public static class MyTestRowFactory extends RowFactory {
private static final AtomicInteger COUNTER = new AtomicInteger();
@Override
public Row createRow(Value[] data, int memory) {
COUNTER.incrementAndGet();
return new RowImpl(data, memory);
}
}
}
...@@ -882,7 +882,8 @@ public class TestTableEngines extends TestBase { ...@@ -882,7 +882,8 @@ public class TestTableEngines extends TestBase {
EndlessTable(CreateTableData data) { EndlessTable(CreateTableData data) {
super(data); super(data);
row = new Row(new Value[] { ValueInt.get(1), ValueNull.INSTANCE }, 0); row = data.schema.getDatabase().createRow(
new Value[] { ValueInt.get(1), ValueNull.INSTANCE }, 0);
scanIndex = new Auto(this); scanIndex = new Auto(this);
} }
......
...@@ -20,6 +20,7 @@ import java.util.TreeSet; ...@@ -20,6 +20,7 @@ import java.util.TreeSet;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowImpl;
import org.h2.store.Page; import org.h2.store.Page;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
...@@ -272,14 +273,14 @@ public class TestPageStore extends TestBase { ...@@ -272,14 +273,14 @@ public class TestPageStore extends TestBase {
private void testInsertDelete() { private void testInsertDelete() {
Row[] x = new Row[0]; Row[] x = new Row[0];
Row r = new Row(null, 0); Row r = new RowImpl(null, 0);
x = Page.insert(x, 0, 0, r); x = Page.insert(x, 0, 0, r);
assertTrue(x[0] == r); assertTrue(x[0] == r);
Row r2 = new Row(null, 0); Row r2 = new RowImpl(null, 0);
x = Page.insert(x, 1, 0, r2); x = Page.insert(x, 1, 0, r2);
assertTrue(x[0] == r2); assertTrue(x[0] == r2);
assertTrue(x[1] == r); assertTrue(x[1] == r);
Row r3 = new Row(null, 0); Row r3 = new RowImpl(null, 0);
x = Page.insert(x, 2, 1, r3); x = Page.insert(x, 2, 1, r3);
assertTrue(x[0] == r2); assertTrue(x[0] == r2);
assertTrue(x[1] == r3); assertTrue(x[1] == r3);
......
...@@ -9,7 +9,7 @@ import java.lang.instrument.Instrumentation; ...@@ -9,7 +9,7 @@ import java.lang.instrument.Instrumentation;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.result.Row; import org.h2.result.RowImpl;
import org.h2.store.Data; import org.h2.store.Data;
import org.h2.util.Profiler; import org.h2.util.Profiler;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -34,7 +34,7 @@ public class MemoryFootprint { ...@@ -34,7 +34,7 @@ public class MemoryFootprint {
print("BigInteger", new BigInteger("0")); print("BigInteger", new BigInteger("0"));
print("String", new String("Hello")); print("String", new String("Hello"));
print("Data", Data.create(null, 10)); print("Data", Data.create(null, 10));
print("Row", new Row(new Value[0], 0)); print("Row", new RowImpl(new Value[0], 0));
System.out.println(); System.out.println();
for (int i = 1; i < 128; i += i) { for (int i = 1; i < 128; i += i) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论