提交 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
<h2>Next Version (unreleased)</h2>
<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>
<li>The condition "in(select...)" did not work correctly in some cases if the subquery had an "order by".
......
......@@ -6,7 +6,6 @@
package org.h2.command.dml;
import java.text.Collator;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
import org.h2.command.Prepared;
......@@ -20,9 +19,11 @@ import org.h2.expression.Expression;
import org.h2.expression.ValueExpression;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.RowFactory;
import org.h2.schema.Schema;
import org.h2.table.Table;
import org.h2.tools.CompressTool;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
import org.h2.value.CompareMode;
import org.h2.value.ValueInt;
......@@ -483,6 +484,19 @@ public class Set extends Prepared {
addOrUpdateSetting(name, null, getIntValue());
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:
DbException.throwInternalError("type="+type);
}
......
......@@ -223,6 +223,11 @@ public class SetTypes {
*/
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 SetTypes() {
......@@ -274,6 +279,7 @@ public class SetTypes {
list.add(RETENTION_TIME, "RETENTION_TIME");
list.add(QUERY_STATISTICS, "QUERY_STATISTICS");
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;
import org.h2.message.TraceSystem;
import org.h2.mvstore.db.MVTableEngine;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SearchRow;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
......@@ -191,6 +192,7 @@ public class Database implements DataHandler {
private boolean queryStatistics;
private int queryStatisticsMaxEntries = Constants.QUERY_STATISTICS_MAX_ENTRIES;
private QueryStatisticsData queryStatisticsData;
private RowFactory rowFactory = RowFactory.DEFAULT;
public Database(ConnectionInfo ci, String cipher) {
String name = ci.getName();
......@@ -249,7 +251,6 @@ public class Database implements DataHandler {
ci.getProperty("JAVA_OBJECT_SERIALIZER", null);
this.multiThreaded =
ci.getProperty("MULTI_THREADED", false);
boolean closeAtVmShutdown =
dbSettings.dbCloseOnExit;
int traceLevelFile =
......@@ -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) {
initialPowerOffCount = count;
}
......
......@@ -148,6 +148,10 @@ public class Session extends SessionWithState {
this.currentSchemaName = Constants.SCHEMA_MAIN;
}
public Row createRow(Value[] data, int memory) {
return database.createRow(data, memory);
}
public void setSubQueryInfo(SubQueryInfo subQueryInfo) {
this.subQueryInfo = subQueryInfo;
}
......@@ -680,7 +684,7 @@ public class Session extends SessionWithState {
row = t.getRow(this, key);
} else {
op = UndoLogRecord.DELETE;
row = new Row(value.getList(), Row.MEMORY_CALCULATE);
row = createRow(value.getList(), Row.MEMORY_CALCULATE);
}
row.setKey(key);
UndoLogRecord log = new UndoLogRecord(t, op, row);
......@@ -1164,6 +1168,10 @@ public class Session extends SessionWithState {
if (SysProperties.CHECK && !v.isLinkedToTable()) {
DbException.throwInternalError();
}
if (removeLobMap == null) {
removeLobMap = New.hashMap();
removeLobMap.put(v.toString(), v);
}
}
/**
......
......@@ -221,7 +221,7 @@ public class UndoLogRecord {
for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue();
}
row = new Row(values, Row.MEMORY_CALCULATE);
row = getTable().getDatabase().createRow(values, Row.MEMORY_CALCULATE);
row.setKey(key);
row.setDeleted(deleted);
row.setSessionId(sessionId);
......
......@@ -5,6 +5,7 @@
*/
package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
......@@ -16,11 +17,13 @@ import org.h2.value.Value;
*/
public class FunctionCursor implements Cursor {
private Session session;
private final ResultInterface result;
private Value[] values;
private Row row;
FunctionCursor(ResultInterface result) {
FunctionCursor(Session session, ResultInterface result) {
this.session = session;
this.result = result;
}
......@@ -30,7 +33,7 @@ public class FunctionCursor implements Cursor {
return null;
}
if (row == null) {
row = new Row(values, 1);
row = session.createRow(values, 1);
}
return row;
}
......
......@@ -42,7 +42,7 @@ public class FunctionCursorResultSet implements Cursor {
return null;
}
if (row == null) {
row = new Row(values, 1);
row = session.createRow(values, 1);
}
return row;
}
......
......@@ -45,7 +45,7 @@ public class FunctionIndex extends BaseIndex {
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
if (functionTable.isBufferResultSetToLocalTemp()) {
return new FunctionCursor(functionTable.getResult(session));
return new FunctionCursor(session, functionTable.getResult(session));
}
return new FunctionCursorResultSet(session,
functionTable.getResultSet(session));
......
......@@ -17,7 +17,6 @@ import org.h2.result.Row;
import org.h2.store.Data;
import org.h2.store.Page;
import org.h2.store.PageStore;
import org.h2.table.RegularTable;
import org.h2.value.Value;
/**
......@@ -617,7 +616,7 @@ public class PageDataLeaf extends PageData {
* @param columnCount the number of columns
* @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];
synchronized (data) {
data.setPos(pos);
......@@ -625,7 +624,7 @@ public class PageDataLeaf extends PageData {
values[i] = data.readValue();
}
}
return RegularTable.createRow(values);
return index.getDatabase().createRow(values, Row.MEMORY_CALCULATE);
}
}
......@@ -5,6 +5,7 @@
*/
package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -16,16 +17,18 @@ import org.h2.value.ValueLong;
*/
class RangeCursor implements Cursor {
private Session session;
private boolean beforeFirst;
private long current;
private Row currentRow;
private final long start, end, step;
RangeCursor(long start, long end) {
this(start, end, 1);
RangeCursor(Session session, long start, long end) {
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.end = end;
this.step = step;
......@@ -50,7 +53,7 @@ class RangeCursor implements Cursor {
} else {
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;
}
......
......@@ -58,7 +58,7 @@ public class RangeIndex extends BaseIndex {
} catch (Exception e) {
// error when converting the value - ignore
}
return new RangeCursor(start, end, step);
return new RangeCursor(session, start, end, step);
}
@Override
......@@ -100,7 +100,7 @@ public class RangeIndex extends BaseIndex {
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
long pos = first ? rangeTable.getMin(session) : rangeTable.getMax(session);
return new RangeCursor(pos, pos);
return new RangeCursor(session, pos, pos);
}
@Override
......
......@@ -143,7 +143,7 @@ public class ScanIndex extends BaseIndex {
rows = New.arrayList();
firstFree = -1;
} else {
Row free = new Row(null, 1);
Row free = session.createRow(null, 1);
free.setKey(firstFree);
long key = row.getKey();
if (rows.size() <= key) {
......
......@@ -197,7 +197,7 @@ public class MVPrimaryIndex extends BaseIndex {
}
}
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(map.entryIterator(min), max);
return new MVStoreCursor(session, map.entryIterator(min), max);
}
@Override
......@@ -210,7 +210,7 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session);
Value v = map.get(ValueLong.get(key));
ValueArray array = (ValueArray) v;
Row row = new Row(array.getList(), 0);
Row row = session.createRow(array.getList(), 0);
row.setKey(key);
return row;
}
......@@ -260,14 +260,14 @@ public class MVPrimaryIndex extends BaseIndex {
TransactionMap<Value, Value> map = getMap(session);
ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
if (v == null) {
return new MVStoreCursor(Collections
return new MVStoreCursor(session, Collections
.<Entry<Value, Value>> emptyList().iterator(), null);
}
Value value = map.get(v);
Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
@SuppressWarnings("unchecked")
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();
return c;
}
......@@ -347,7 +347,7 @@ public class MVPrimaryIndex extends BaseIndex {
*/
Cursor find(Session session, ValueLong first, ValueLong last) {
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(map.entryIterator(first), last);
return new MVStoreCursor(session, map.entryIterator(first), last);
}
@Override
......@@ -374,12 +374,14 @@ public class MVPrimaryIndex extends BaseIndex {
*/
class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<Entry<Value, Value>> it;
private final ValueLong last;
private Entry<Value, Value> current;
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.last = last;
}
......@@ -389,7 +391,7 @@ public class MVPrimaryIndex extends BaseIndex {
if (row == null) {
if (current != null) {
ValueArray array = (ValueArray) current.getValue();
row = new Row(array.getList(), 0);
row = session.createRow(array.getList(), 0);
row.setKey(current.getKey().getLong());
}
}
......
......@@ -288,7 +288,7 @@ public class ResultTempTable implements ResultExternal {
}
values = v2;
}
return new Row(values, Row.MEMORY_CALCULATE);
return session.createRow(values, Row.MEMORY_CALCULATE);
}
private Cursor find(Row row) {
......
......@@ -5,77 +5,31 @@
*/
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;
/**
* 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 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.
* 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);
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;
}
public abstract Row getCopy();
@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];
}
/**
* Set version.
*
* @param version row version
*/
public abstract void setVersion(int version);
/**
* Get the number of bytes required for the data.
......@@ -83,97 +37,52 @@ public class Row implements SearchRow {
* @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;
}
public abstract int getByteCount(Data dummy);
@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();
}
/**
* Check if this is an empty row.
*
* @return {@code true} if the row is empty
*/
public abstract boolean isEmpty();
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.
*/
public void commit() {
this.sessionId = 0;
}
public boolean isDeleted() {
return deleted;
}
public abstract void commit();
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 {
}
values[i] = v;
}
Row row = new Row(values, mem);
Row row = session.createRow(values, mem);
row.setKey(key);
row.setVersion(version);
row.setDeleted(deleted);
......
......@@ -104,7 +104,7 @@ public class LobStorageMap implements LobStorageInterface {
}
// search for the last block
// (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)
long lastUsedKey = -1;
for (Entry<Long, Object[]> e : lobMap.entrySet()) {
......
......@@ -9,7 +9,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import org.h2.api.ErrorCode;
import org.h2.compress.CompressLZF;
import org.h2.engine.Session;
......@@ -17,6 +16,7 @@ import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.util.BitField;
import org.h2.util.IntArray;
import org.h2.util.IntIntHashMap;
......@@ -316,7 +316,7 @@ public class PageLog {
} else if (x == ADD) {
int sessionId = in.readVarInt();
int tableId = in.readVarInt();
Row row = readRow(in, data);
Row row = readRow(store.getDatabase().getRowFactory(), in, data);
if (stage == RECOVERY_STAGE_UNDO) {
store.allocateIfIndexRoot(pos, tableId, row);
} else if (stage == RECOVERY_STAGE_REDO) {
......@@ -452,11 +452,12 @@ public class PageLog {
/**
* Read a row from an input stream.
*
* @param rowFactory the row factory
* @param in the input stream
* @param data a temporary buffer
* @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();
int len = in.readVarInt();
data.reset();
......@@ -467,7 +468,7 @@ public class PageLog {
for (int i = 0; i < columnCount; i++) {
values[i] = data.readValue();
}
Row row = new Row(values, Row.MEMORY_CALCULATE);
Row row = rowFactory.createRow(values, Row.MEMORY_CALCULATE);
row.setKey(key);
return row;
}
......
......@@ -1978,7 +1978,7 @@ public class MetaTable extends Table {
v = col.convert(v);
values[i] = v;
}
Row row = new Row(values, 1);
Row row = database.createRow(values, 1);
row.setKey(rows.size());
rows.add(row);
}
......
......@@ -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.
*
......
......@@ -606,7 +606,7 @@ public abstract class Table extends SchemaObjectBase {
}
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 {
// Here can be concurrently produced more than one row, but it must be ok.
Value[] values = new Value[columns.length];
Arrays.fill(values, ValueNull.INSTANCE);
nullRow = row = new Row(values, 1);
nullRow = row = database.createRow(values, 1);
}
return row;
}
......
......@@ -44,6 +44,7 @@ import org.h2.mvstore.db.TransactionStore;
import org.h2.mvstore.db.TransactionStore.TransactionMap;
import org.h2.mvstore.db.ValueDataType;
import org.h2.result.Row;
import org.h2.result.RowFactory;
import org.h2.result.SimpleRow;
import org.h2.security.SHA256;
import org.h2.store.Data;
......@@ -948,7 +949,7 @@ public class Recover extends Tool implements DataHandler {
} else if (x == PageLog.ADD) {
int sessionId = in.readVarInt();
setStorage(in.readVarInt());
Row row = PageLog.readRow(in, s);
Row row = PageLog.readRow(RowFactory.DEFAULT, in, s);
writer.println("-- session " + sessionId +
" table " + storageId +
" + " + row.toString());
......
......@@ -19,10 +19,8 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.SysProperties;
......@@ -129,7 +127,8 @@ public class JdbcUtils {
* @param className the name of the class
* @return the class object
*/
public static Class<?> loadUserClass(String className) {
@SuppressWarnings("unchecked")
public static <Z> Class<Z> loadUserClass(String className) {
if (allowedClassNames == null) {
// initialize the static fields
String s = SysProperties.ALLOWED_CLASSES;
......@@ -168,7 +167,7 @@ public class JdbcUtils {
try {
Class<?> userClass = classFactory.loadClass(className);
if (!(userClass == null)) {
return userClass;
return (Class<Z>) userClass;
}
} catch (Exception e) {
throw DbException.get(
......@@ -178,10 +177,10 @@ public class JdbcUtils {
}
// Use local ClassLoader
try {
return Class.forName(className);
return (Class<Z>) Class.forName(className);
} catch (ClassNotFoundException e) {
try {
return Class.forName(
return (Class<Z>) Class.forName(
className, true,
Thread.currentThread().getContextClassLoader());
} catch (Exception e2) {
......
......@@ -53,6 +53,7 @@ import org.h2.test.db.TestQueryCache;
import org.h2.test.db.TestReadOnly;
import org.h2.test.db.TestRecursiveQueries;
import org.h2.test.db.TestRights;
import org.h2.test.db.TestRowFactory;
import org.h2.test.db.TestRunscript;
import org.h2.test.db.TestSQLInjection;
import org.h2.test.db.TestScript;
......@@ -696,6 +697,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestSpatial());
addTest(new TestSpeed());
addTest(new TestTableEngines());
addTest(new TestRowFactory());
addTest(new TestTempTables());
addTest(new TestTransaction());
addTest(new TestTriggersConstraints());
......
......@@ -28,7 +28,9 @@ import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.tools.Recover;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.StringUtils;
......@@ -57,6 +59,7 @@ public class TestLob extends TestBase {
@Override
public void test() throws Exception {
testRemoveAfterDeleteAndClose();
testRemovedAfterTimeout();
testConcurrentRemoveRead();
testCloseLobTwice();
......@@ -109,6 +112,33 @@ public class TestLob extends TestBase {
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 {
deleteDb("lob");
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 {
EndlessTable(CreateTableData 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);
}
......
......@@ -20,6 +20,7 @@ import java.util.TreeSet;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.result.Row;
import org.h2.result.RowImpl;
import org.h2.store.Page;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
......@@ -272,14 +273,14 @@ public class TestPageStore extends TestBase {
private void testInsertDelete() {
Row[] x = new Row[0];
Row r = new Row(null, 0);
Row r = new RowImpl(null, 0);
x = Page.insert(x, 0, 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);
assertTrue(x[0] == r2);
assertTrue(x[1] == r);
Row r3 = new Row(null, 0);
Row r3 = new RowImpl(null, 0);
x = Page.insert(x, 2, 1, r3);
assertTrue(x[0] == r2);
assertTrue(x[1] == r3);
......
......@@ -9,7 +9,7 @@ import java.lang.instrument.Instrumentation;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.h2.engine.Constants;
import org.h2.result.Row;
import org.h2.result.RowImpl;
import org.h2.store.Data;
import org.h2.util.Profiler;
import org.h2.value.Value;
......@@ -34,7 +34,7 @@ public class MemoryFootprint {
print("BigInteger", new BigInteger("0"));
print("String", new String("Hello"));
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();
for (int i = 1; i < 128; i += i) {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论