提交 6d02b26f authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 9c01059b
#Fri Jan 18 08:04:40 CET 2008 #Mon Jan 28 07:19:50 CET 2008
javac=javac javac=javac
benchmark.drivers.dir=C\:/data/java benchmark.drivers.dir=C\:/data/java
path.servlet.jar=C\:/data/classpath/servlet-api.jar path.servlet.jar=C\:/data/classpath/servlet-api.jar
......
...@@ -224,7 +224,7 @@ ...@@ -224,7 +224,7 @@
</target> </target>
<target name="jarDb" depends="compileResources, manifest"> <target name="jarDb" depends="compileResources, manifest">
<javac executable="${javac}" srcdir="src/main" destdir="bin" debug="true"> <javac executable="${javac}" srcdir="src/main" destdir="bin" debug="false">
<include name="org/h2/*" /> <include name="org/h2/*" />
<include name="org/h2/engine/**" /> <include name="org/h2/engine/**" />
<include name="org/h2/jdbc/**" /> <include name="org/h2/jdbc/**" />
......
...@@ -135,7 +135,6 @@ public abstract class Command implements CommandInterface { ...@@ -135,7 +135,6 @@ public abstract class Command implements CommandInterface {
cancel = false; cancel = false;
throw Message.getSQLException(ErrorCode.STATEMENT_WAS_CANCELLED); throw Message.getSQLException(ErrorCode.STATEMENT_WAS_CANCELLED);
} }
session.throttle();
} }
private void stop() throws SQLException { private void stop() throws SQLException {
......
...@@ -233,11 +233,10 @@ public abstract class Prepared { ...@@ -233,11 +233,10 @@ public abstract class Prepared {
* @throws SQLException if it was cancelled * @throws SQLException if it was cancelled
*/ */
public void checkCancelled() throws SQLException { public void checkCancelled() throws SQLException {
// TODO strange code: probably checkCancelled should always be called on the session. fix & test after release 1.0 session.checkCancelled();
if (command != null) { Command c = command != null ? command : session.getCurrentCommand();
command.checkCancelled(); if (c != null) {
} else { c.checkCancelled();
session.checkCancelled();
} }
} }
......
...@@ -74,7 +74,8 @@ public class Session implements SessionInterface { ...@@ -74,7 +74,8 @@ public class Session implements SessionInterface {
private boolean undoLogEnabled = true; private boolean undoLogEnabled = true;
private boolean autoCommitAtTransactionEnd; private boolean autoCommitAtTransactionEnd;
private String currentTransactionName; private String currentTransactionName;
private boolean isClosed; private volatile long cancelAt;
private boolean closed;
private boolean rollbackMode; private boolean rollbackMode;
private long sessionStart = System.currentTimeMillis(); private long sessionStart = System.currentTimeMillis();
private long currentCommandStart; private long currentCommandStart;
...@@ -150,7 +151,7 @@ public class Session implements SessionInterface { ...@@ -150,7 +151,7 @@ public class Session implements SessionInterface {
if (!SysProperties.runFinalize) { if (!SysProperties.runFinalize) {
return; return;
} }
if (!isClosed) { if (!closed) {
throw Message.getInternalError("not closed", stackTrace); throw Message.getInternalError("not closed", stackTrace);
} }
} }
...@@ -205,7 +206,7 @@ public class Session implements SessionInterface { ...@@ -205,7 +206,7 @@ public class Session implements SessionInterface {
} }
public Command prepareLocal(String sql) throws SQLException { public Command prepareLocal(String sql) throws SQLException {
if (isClosed) { if (closed) {
throw Message.getSQLException(ErrorCode.CONNECTION_BROKEN); throw Message.getSQLException(ErrorCode.CONNECTION_BROKEN);
} }
Parser parser = new Parser(this); Parser parser = new Parser(this);
...@@ -320,13 +321,17 @@ public class Session implements SessionInterface { ...@@ -320,13 +321,17 @@ public class Session implements SessionInterface {
return id; return id;
} }
public void cancel() {
cancelAt = System.currentTimeMillis();
}
public void close() throws SQLException { public void close() throws SQLException {
if (!isClosed) { if (!closed) {
try { try {
cleanTempTables(true); cleanTempTables(true);
database.removeSession(this); database.removeSession(this);
} finally { } finally {
isClosed = true; closed = true;
} }
} }
} }
...@@ -420,7 +425,7 @@ public class Session implements SessionInterface { ...@@ -420,7 +425,7 @@ public class Session implements SessionInterface {
if (traceModuleName == null) { if (traceModuleName == null) {
traceModuleName = Trace.JDBC + "[" + id + "]"; traceModuleName = Trace.JDBC + "[" + id + "]";
} }
if (isClosed) { if (closed) {
return new TraceSystem(null, false).getTrace(traceModuleName); return new TraceSystem(null, false).getTrace(traceModuleName);
} }
return database.getTrace(traceModuleName); return database.getTrace(traceModuleName);
...@@ -512,7 +517,7 @@ public class Session implements SessionInterface { ...@@ -512,7 +517,7 @@ public class Session implements SessionInterface {
} }
public boolean isClosed() { public boolean isClosed() {
return isClosed; return closed;
} }
public void setThrottle(int throttle) { public void setThrottle(int throttle) {
...@@ -541,8 +546,14 @@ public class Session implements SessionInterface { ...@@ -541,8 +546,14 @@ public class Session implements SessionInterface {
} }
public void checkCancelled() throws SQLException { public void checkCancelled() throws SQLException {
if (currentCommand != null) { throttle();
currentCommand.checkCancelled(); if (cancelAt == 0) {
return;
}
long time = System.currentTimeMillis();
if (time >= cancelAt) {
cancelAt = 0;
throw Message.getSQLException(ErrorCode.STATEMENT_WAS_CANCELLED);
} }
} }
......
...@@ -73,4 +73,9 @@ public interface SessionInterface { ...@@ -73,4 +73,9 @@ public interface SessionInterface {
* @return the data handler * @return the data handler
*/ */
DataHandler getDataHandler(); DataHandler getDataHandler();
/**
* Cancel the current or next command (called when closing a connection).
*/
void cancel();
} }
...@@ -388,4 +388,8 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -388,4 +388,8 @@ public class SessionRemote implements SessionInterface, DataHandler {
return lobSyncObject; return lobSyncObject;
} }
public void cancel() {
// TODO open another remote connection and cancel this session using a unique id (like PostgreSQL)
}
} }
...@@ -9,6 +9,7 @@ import java.util.Collections; ...@@ -9,6 +9,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.log.UndoLogRecord; import org.h2.log.UndoLogRecord;
...@@ -21,7 +22,6 @@ import org.h2.table.IndexColumn; ...@@ -21,7 +22,6 @@ import org.h2.table.IndexColumn;
import org.h2.table.TableData; import org.h2.table.TableData;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils; import org.h2.util.ObjectUtils;
import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueLob; import org.h2.value.ValueLob;
...@@ -35,7 +35,6 @@ public class ScanIndex extends BaseIndex { ...@@ -35,7 +35,6 @@ public class ScanIndex extends BaseIndex {
private ObjectArray rows = new ObjectArray(); private ObjectArray rows = new ObjectArray();
private Storage storage; private Storage storage;
private TableData tableData; private TableData tableData;
private boolean containsLargeObject;
private int rowCountDiff; private int rowCountDiff;
private HashMap sessionRowCount; private HashMap sessionRowCount;
private HashSet delta; private HashSet delta;
...@@ -54,11 +53,6 @@ public class ScanIndex extends BaseIndex { ...@@ -54,11 +53,6 @@ public class ScanIndex extends BaseIndex {
rowCount = count; rowCount = count;
table.setRowCount(count); table.setRowCount(count);
trace.info("open existing " + table.getName() + " rows: " + count); trace.info("open existing " + table.getName() + " rows: " + count);
for (int i = 0; i < columns.length; i++) {
if (DataType.isLargeObject(columns[i].column.getType())) {
containsLargeObject = true;
}
}
} }
public void remove(Session session) throws SQLException { public void remove(Session session) throws SQLException {
...@@ -75,7 +69,7 @@ public class ScanIndex extends BaseIndex { ...@@ -75,7 +69,7 @@ public class ScanIndex extends BaseIndex {
} else { } else {
storage.truncate(session); storage.truncate(session);
} }
if (containsLargeObject && tableData.isPersistent()) { if (tableData.getContainsLargeObject() && tableData.isPersistent()) {
ValueLob.removeAllForTable(database, table.getId()); ValueLob.removeAllForTable(database, table.getId());
} }
tableData.setRowCount(0); tableData.setRowCount(0);
...@@ -104,7 +98,7 @@ public class ScanIndex extends BaseIndex { ...@@ -104,7 +98,7 @@ public class ScanIndex extends BaseIndex {
public void add(Session session, Row row) throws SQLException { public void add(Session session, Row row) throws SQLException {
if (storage != null) { if (storage != null) {
if (containsLargeObject) { if (tableData.getContainsLargeObject()) {
for (int i = 0; i < row.getColumnCount(); i++) { for (int i = 0; i < row.getColumnCount(); i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
Value v2 = v.link(database, getId()); Value v2 = v.link(database, getId());
...@@ -164,7 +158,7 @@ public class ScanIndex extends BaseIndex { ...@@ -164,7 +158,7 @@ public class ScanIndex extends BaseIndex {
public void remove(Session session, Row row) throws SQLException { public void remove(Session session, Row row) throws SQLException {
if (storage != null) { if (storage != null) {
storage.removeRecord(session, row.getPos()); storage.removeRecord(session, row.getPos());
if (containsLargeObject) { if (tableData.getContainsLargeObject()) {
for (int i = 0; i < row.getColumnCount(); i++) { for (int i = 0; i < row.getColumnCount(); i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
if (v.isLinked()) { if (v.isLinked()) {
......
...@@ -234,6 +234,7 @@ public class JdbcConnection extends TraceObject implements Connection { ...@@ -234,6 +234,7 @@ public class JdbcConnection extends TraceObject implements Connection {
if (session == null) { if (session == null) {
return; return;
} }
session.cancel();
synchronized (session) { synchronized (session) {
try { try {
if (!session.isClosed()) { if (!session.isClosed()) {
......
...@@ -15,6 +15,7 @@ import org.h2.value.Value; ...@@ -15,6 +15,7 @@ import org.h2.value.Value;
* Represents a row in a table. * Represents a row in a table.
*/ */
public class Row extends Record implements SearchRow { public class Row extends Record implements SearchRow {
public static final int MEMORY_CALCULATE = -1;
private final Value[] data; private final Value[] data;
private final int memory; private final int memory;
...@@ -63,7 +64,14 @@ public class Row extends Record implements SearchRow { ...@@ -63,7 +64,14 @@ public class Row extends Record implements SearchRow {
} }
public int getMemorySize() { public int getMemorySize() {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4; if (memory != MEMORY_CALCULATE) {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
}
int m = blockCount * (DiskFile.BLOCK_SIZE / 16);
for (int i = 0; data != null && i < data.length; i++) {
m += data[i].getMemory();
}
return m;
} }
public String toString() { public String toString() {
......
...@@ -34,6 +34,7 @@ import org.h2.store.RecordReader; ...@@ -34,6 +34,7 @@ import org.h2.store.RecordReader;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.ObjectArray; import org.h2.util.ObjectArray;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
/** /**
...@@ -42,6 +43,7 @@ import org.h2.value.Value; ...@@ -42,6 +43,7 @@ import org.h2.value.Value;
* instead it is kept in the indexes. There is at least one index, the scan index. * instead it is kept in the indexes. There is at least one index, the scan index.
*/ */
public class TableData extends Table implements RecordReader { public class TableData extends Table implements RecordReader {
private final boolean clustered;
private ScanIndex scanIndex; private ScanIndex scanIndex;
private long rowCount; private long rowCount;
private Session lockExclusive; private Session lockExclusive;
...@@ -50,7 +52,7 @@ public class TableData extends Table implements RecordReader { ...@@ -50,7 +52,7 @@ public class TableData extends Table implements RecordReader {
private boolean globalTemporary; private boolean globalTemporary;
private final ObjectArray indexes = new ObjectArray(); private final ObjectArray indexes = new ObjectArray();
private long lastModificationId; private long lastModificationId;
private final boolean clustered; private boolean containsLargeObject;
public TableData(Schema schema, String tableName, int id, ObjectArray columns, public TableData(Schema schema, String tableName, int id, ObjectArray columns,
boolean persistent, boolean clustered) throws SQLException { boolean persistent, boolean clustered) throws SQLException {
...@@ -63,6 +65,11 @@ public class TableData extends Table implements RecordReader { ...@@ -63,6 +65,11 @@ public class TableData extends Table implements RecordReader {
scanIndex = new ScanIndex(this, id, IndexColumn.wrap(cols), IndexType.createScan(persistent)); scanIndex = new ScanIndex(this, id, IndexColumn.wrap(cols), IndexType.createScan(persistent));
indexes.add(scanIndex); indexes.add(scanIndex);
} }
for (int i = 0; i < cols.length; i++) {
if (DataType.isLargeObject(cols[i].getType())) {
containsLargeObject = true;
}
}
traceLock = database.getTrace(Trace.LOCK); traceLock = database.getTrace(Trace.LOCK);
} }
...@@ -457,7 +464,9 @@ public class TableData extends Table implements RecordReader { ...@@ -457,7 +464,9 @@ public class TableData extends Table implements RecordReader {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
data[i] = s.readValue(); data[i] = s.readValue();
} }
return new Row(data, memoryPerRow); int memory = containsLargeObject ? Row.MEMORY_CALCULATE : memoryPerRow;
Row row = new Row(data, memory);
return row;
} }
public void setRowCount(int count) { public void setRowCount(int count) {
...@@ -530,4 +539,8 @@ public class TableData extends Table implements RecordReader { ...@@ -530,4 +539,8 @@ public class TableData extends Table implements RecordReader {
return clustered; return clustered;
} }
public boolean getContainsLargeObject() {
return containsLargeObject;
}
} }
...@@ -357,7 +357,7 @@ public class TableLink extends Table { ...@@ -357,7 +357,7 @@ public class TableLink extends Table {
boolean deleteInsert; boolean deleteInsert;
if (emitUpdates) { if (emitUpdates) {
for (rows.reset(); rows.hasNext();) { for (rows.reset(); rows.hasNext();) {
session.checkCancelled(); prepared.checkCancelled();
Row oldRow = rows.next(); Row oldRow = rows.next();
Row newRow = rows.next(); Row newRow = rows.next();
linkedIndex.update(session, oldRow, newRow); linkedIndex.update(session, oldRow, newRow);
......
...@@ -13,6 +13,11 @@ public class MemoryUtils { ...@@ -13,6 +13,11 @@ public class MemoryUtils {
private static final int GC_DELAY = 50; private static final int GC_DELAY = 50;
private static final int MAX_GC = 8; private static final int MAX_GC = 8;
/**
* Get the used memory in KB.
*
* @return the used memory
*/
public static int getMemoryUsed() { public static int getMemoryUsed() {
collectGarbage(); collectGarbage();
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
...@@ -20,6 +25,11 @@ public class MemoryUtils { ...@@ -20,6 +25,11 @@ public class MemoryUtils {
return (int) (mem >> 10); return (int) (mem >> 10);
} }
/**
* Get the free memory in KB.
*
* @return the used memory
*/
public static int getMemoryFree() { public static int getMemoryFree() {
collectGarbage(); collectGarbage();
Runtime rt = Runtime.getRuntime(); Runtime rt = Runtime.getRuntime();
......
...@@ -42,15 +42,15 @@ public class TempFileDeleter { ...@@ -42,15 +42,15 @@ public class TempFileDeleter {
} catch (Exception e) { } catch (Exception e) {
// TODO log such errors? // TODO log such errors?
} }
deleteUnused();
} }
deleteUnused();
} }
public static void deleteUnused() { public static void deleteUnused() {
// Mystery: I don't know how QUEUE could get null, but two independent // Mystery: I don't know how QUEUE could get null, but two independent
// people reported NullPointerException here - if somebody understands // people reported NullPointerException here - if somebody understands
// how it could happen please report it! // how it could happen please report it!
// Setup: webapp under Tomcat, exception occurs during undeploy // Environment: web application under Tomcat, exception occurs during undeploy
while (QUEUE != null) { while (QUEUE != null) {
Reference ref = QUEUE.poll(); Reference ref = QUEUE.poll();
if (ref == null) { if (ref == null) {
......
...@@ -116,103 +116,103 @@ public class DataType { ...@@ -116,103 +116,103 @@ public class DataType {
add(Value.SHORT, Types.SMALLINT, "Short", add(Value.SHORT, Types.SMALLINT, "Short",
createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, ValueShort.DISPLAY_SIZE, false, false), createDecimal(ValueShort.PRECISION, ValueShort.PRECISION, 0, ValueShort.DISPLAY_SIZE, false, false),
new String[]{"SMALLINT", "YEAR", "INT2"}, new String[]{"SMALLINT", "YEAR", "INT2"},
1 5
); );
add(Value.INT, Types.INTEGER, "Int", add(Value.INT, Types.INTEGER, "Int",
createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, ValueInt.DISPLAY_SIZE, false, false), createDecimal(ValueInt.PRECISION, ValueInt.PRECISION, 0, ValueInt.DISPLAY_SIZE, false, false),
new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"}, new String[]{"INTEGER", "INT", "MEDIUMINT", "INT4", "SIGNED"},
1 5
); );
add(Value.LONG, Types.BIGINT, "Long", add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, false), createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, false),
new String[]{"BIGINT", "INT8"}, new String[]{"BIGINT", "INT8"},
1 5
); );
add(Value.LONG, Types.BIGINT, "Long", add(Value.LONG, Types.BIGINT, "Long",
createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, true), createDecimal(ValueLong.PRECISION, ValueLong.PRECISION, 0, ValueLong.DISPLAY_SIZE, false, true),
new String[]{"IDENTITY", "SERIAL"}, new String[]{"IDENTITY", "SERIAL"},
1 5
); );
add(Value.DECIMAL, Types.DECIMAL, "BigDecimal", add(Value.DECIMAL, Types.DECIMAL, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false),
new String[]{"DECIMAL", "DEC"}, new String[]{"DECIMAL", "DEC"},
7 17
// TODO value: are NaN, Inf, -Inf,... supported as well? // TODO value: are NaN, Inf, -Inf,... supported as well?
); );
add(Value.DECIMAL, Types.NUMERIC, "BigDecimal", add(Value.DECIMAL, Types.NUMERIC, "BigDecimal",
createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false), createDecimal(Integer.MAX_VALUE, ValueDecimal.DEFAULT_PRECISION, ValueDecimal.DEFAULT_SCALE, ValueDecimal.DEFAULT_DISPLAY_SIZE, true, false),
new String[]{"NUMERIC", "NUMBER"}, new String[]{"NUMERIC", "NUMBER"},
7 17
// TODO value: are NaN, Inf, -Inf,... supported as well? // TODO value: are NaN, Inf, -Inf,... supported as well?
); );
add(Value.FLOAT, Types.REAL, "Float", add(Value.FLOAT, Types.REAL, "Float",
createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, 0, ValueFloat.DISPLAY_SIZE, false, false), createDecimal(ValueFloat.PRECISION, ValueFloat.PRECISION, 0, ValueFloat.DISPLAY_SIZE, false, false),
new String[] {"REAL", "FLOAT4"}, new String[] {"REAL", "FLOAT4"},
1 5
); );
add(Value.DOUBLE, Types.DOUBLE, "Double", add(Value.DOUBLE, Types.DOUBLE, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false), createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false),
new String[] { "DOUBLE", "DOUBLE PRECISION" }, new String[] { "DOUBLE", "DOUBLE PRECISION" },
1 4
); );
add(Value.DOUBLE, Types.FLOAT, "Double", add(Value.DOUBLE, Types.FLOAT, "Double",
createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false), createDecimal(ValueDouble.PRECISION, ValueDouble.PRECISION, 0, ValueDouble.DISPLAY_SIZE, false, false),
new String[] {"FLOAT", "FLOAT8" }, new String[] {"FLOAT", "FLOAT8" },
1 4
// TODO value: show min and max values, E format if supported // TODO value: show min and max values, E format if supported
); );
add(Value.TIME, Types.TIME, "Time", add(Value.TIME, Types.TIME, "Time",
createDate(ValueTime.PRECISION, "TIME", 0, ValueTime.DISPLAY_SIZE), createDate(ValueTime.PRECISION, "TIME", 0, ValueTime.DISPLAY_SIZE),
new String[]{"TIME"}, new String[]{"TIME"},
4 10
// TODO value: min / max for time // TODO value: min / max for time
); );
add(Value.DATE, Types.DATE, "Date", add(Value.DATE, Types.DATE, "Date",
createDate(ValueDate.PRECISION, "DATE", 0, ValueDate.DISPLAY_SIZE), createDate(ValueDate.PRECISION, "DATE", 0, ValueDate.DISPLAY_SIZE),
new String[]{"DATE"}, new String[]{"DATE"},
4 10
// TODO value: min / max for date // TODO value: min / max for date
); );
add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp", add(Value.TIMESTAMP, Types.TIMESTAMP, "Timestamp",
createDate(ValueTimestamp.PRECISION, "TIMESTAMP", ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE), createDate(ValueTimestamp.PRECISION, "TIMESTAMP", ValueTimestamp.DEFAULT_SCALE, ValueTimestamp.DISPLAY_SIZE),
new String[]{"TIMESTAMP", "DATETIME", "SMALLDATETIME"}, new String[]{"TIMESTAMP", "DATETIME", "SMALLDATETIME"},
4 12
// TODO value: min / max for timestamp // TODO value: min / max for timestamp
); );
add(Value.BYTES, Types.VARBINARY, "Bytes", add(Value.BYTES, Types.VARBINARY, "Bytes",
createString(false), createString(false),
new String[]{"VARBINARY"}, new String[]{"VARBINARY"},
4 8
); );
add(Value.BYTES, Types.BINARY, "Bytes", add(Value.BYTES, Types.BINARY, "Bytes",
createString(false), createString(false),
new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"}, new String[]{"BINARY", "RAW", "BYTEA", "LONG RAW"},
4 8
); );
add(Value.BYTES, Types.LONGVARBINARY, "Bytes", add(Value.BYTES, Types.LONGVARBINARY, "Bytes",
createString(false), createString(false),
new String[]{"LONGVARBINARY"}, new String[]{"LONGVARBINARY"},
4 8
); );
add(Value.UUID, Types.BINARY, "Bytes", add(Value.UUID, Types.BINARY, "Bytes",
createString(false), createString(false),
new String[]{"UUID"}, new String[]{"UUID"},
4 8
); );
add(Value.JAVA_OBJECT, Types.OTHER, "Object", add(Value.JAVA_OBJECT, Types.OTHER, "Object",
createString(false), createString(false),
new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"}, new String[]{"OTHER", "OBJECT", "JAVA_OBJECT"},
4 8
); );
add(Value.BLOB, Types.BLOB, "Bytes", add(Value.BLOB, Types.BLOB, "Bytes",
createString(false), createString(false),
new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "IMAGE", "OID"}, new String[]{"BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "IMAGE", "OID"},
4 8
); );
add(Value.CLOB, Types.CLOB, "String", add(Value.CLOB, Types.CLOB, "String",
createString(true), createString(true),
new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"}, new String[]{"CLOB", "TINYTEXT", "TEXT", "MEDIUMTEXT", "LONGTEXT", "NTEXT", "NCLOB"},
4 10
); );
DataType dataType = new DataType(); DataType dataType = new DataType();
dataType.prefix = "("; dataType.prefix = "(";
...@@ -220,13 +220,13 @@ public class DataType { ...@@ -220,13 +220,13 @@ public class DataType {
add(Value.ARRAY, Types.ARRAY, "Array", add(Value.ARRAY, Types.ARRAY, "Array",
dataType, dataType,
new String[]{"ARRAY"}, new String[]{"ARRAY"},
2 10
); );
dataType = new DataType(); dataType = new DataType();
add(Value.RESULT_SET, 0, "ResultSet", add(Value.RESULT_SET, 0, "ResultSet",
dataType, dataType,
new String[]{"RESULT_SET"}, new String[]{"RESULT_SET"},
2 20
); );
for (int i = 0; i < typesByValueType.length; i++) { for (int i = 0; i < typesByValueType.length; i++) {
DataType dt = typesByValueType[i]; DataType dt = typesByValueType[i];
......
...@@ -76,6 +76,15 @@ public abstract class Value { ...@@ -76,6 +76,15 @@ public abstract class Value {
*/ */
public abstract int getDisplaySize(); public abstract int getDisplaySize();
/**
* Get the memory used by this object.
*
* @return the memory used in bytes
*/
public int getMemory() {
return DataType.getDataType(getType()).memory * 4;
}
/** /**
* Get the value as a string. * Get the value as a string.
* *
......
...@@ -133,4 +133,12 @@ public class ValueArray extends Value { ...@@ -133,4 +133,12 @@ public class ValueArray extends Value {
return true; return true;
} }
public int getMemory() {
int memory = 0;
for (int i = 0; i < values.length; i++) {
memory += values[i].getMemory();
}
return memory;
}
} }
...@@ -70,4 +70,8 @@ abstract class ValueBytesBase extends Value { ...@@ -70,4 +70,8 @@ abstract class ValueBytesBase extends Value {
return v instanceof ValueBytesBase && ByteUtils.compareNotNull(value, ((ValueBytesBase) v).value) == 0; return v instanceof ValueBytesBase && ByteUtils.compareNotNull(value, ((ValueBytesBase) v).value) == 0;
} }
public int getMemory() {
return value.length + 4;
}
} }
...@@ -185,4 +185,8 @@ public class ValueDecimal extends Value { ...@@ -185,4 +185,8 @@ public class ValueDecimal extends Value {
return v instanceof ValueDecimal && value.equals(((ValueDecimal) v).value); return v instanceof ValueDecimal && value.equals(((ValueDecimal) v).value);
} }
public int getMemory() {
return getString().length() * 3 + 120;
}
} }
...@@ -642,4 +642,11 @@ public class ValueLob extends Value { ...@@ -642,4 +642,11 @@ public class ValueLob extends Value {
this.fileName = fileName; this.fileName = fileName;
} }
public int getMemory() {
if (small != null) {
return small.length + 32;
}
return 128;
}
} }
...@@ -60,4 +60,8 @@ abstract class ValueStringBase extends Value { ...@@ -60,4 +60,8 @@ abstract class ValueStringBase extends Value {
return v instanceof ValueString && value.equals(((ValueString) v).value); return v instanceof ValueString && value.equals(((ValueString) v).value);
} }
public int getMemory() {
return value.length() * 2 + 30;
}
} }
...@@ -104,6 +104,7 @@ import org.h2.test.unit.TestStringUtils; ...@@ -104,6 +104,7 @@ import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestTools; import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestValue; import org.h2.test.unit.TestValue;
import org.h2.test.unit.TestValueHashMap; import org.h2.test.unit.TestValueHashMap;
import org.h2.test.unit.TestValueMemory;
import org.h2.tools.DeleteDbFiles; import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server; import org.h2.tools.Server;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -121,12 +122,12 @@ Random test: ...@@ -121,12 +122,12 @@ Random test:
cd bin cd bin
del *.db del *.db
start cmd /k "java -cp .;%H2DRIVERS% org.h2.test.TestAll join >testJoin.txt" start cmd /k "java -cp .;%H2DRIVERS% org.h2.test.TestAll join >testJoin.txt"
start cmd /k "java -cp . org.h2.test.TestAll crash >testCrash.txt"
start cmd /k "java -cp . org.h2.test.TestAll synth >testSynth.txt" start cmd /k "java -cp . org.h2.test.TestAll synth >testSynth.txt"
start cmd /k "java -cp . org.h2.test.TestAll all >testAll.txt" start cmd /k "java -cp . org.h2.test.TestAll all >testAll.txt"
start cmd /k "java -cp . org.h2.test.TestAll random >testRandom.txt" start cmd /k "java -cp . org.h2.test.TestAll random >testRandom.txt"
start cmd /k "java -cp . org.h2.test.TestAll btree >testBtree.txt" start cmd /k "java -cp . org.h2.test.TestAll btree >testBtree.txt"
start cmd /k "java -cp . org.h2.test.TestAll halt >testHalt.txt" start cmd /k "java -cp . org.h2.test.TestAll halt >testHalt.txt"
java -cp . org.h2.test.TestAll crash >testCrash.txt
java org.h2.test.TestAll timer java org.h2.test.TestAll timer
...@@ -150,6 +151,51 @@ java org.h2.test.TestAll timer ...@@ -150,6 +151,51 @@ java org.h2.test.TestAll timer
/* /*
adjust cache memory usage
simple pure java config file (interpreted)
DROP ALL OBJECTS;
SET MAX_LENGTH_INPLACE_LOB 32768;
CREATE TABLE TEST(ID IDENTITY, DATA CLOB);
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
@LOOP 1000 INSERT INTO TEST(DATA) VALUES(SPACE(32000));
CALL MEMORY_USED();
ResultRemote.close()
public void close() {
result = null;
// NULL OUT THE SESSION REFERENCE
sendClose();
if (lobValues != null) {
for (int i = 0; i < lobValues.size(); i++) {
Value v = (Value) lobValues.get(i);
try {
v.close();
} catch (SQLException e) {
// BANG NullPointerException
session.getTrace().error("delete lob " + v.getSQL(), e);
}
}
lobValues = null;
}
}
orphan? orphan?
javadoc: design patterns javadoc: design patterns
...@@ -166,6 +212,8 @@ Roadmap: ...@@ -166,6 +212,8 @@ Roadmap:
Move Maven 2 repository from hsql.sf.net to h2database.sf.net Move Maven 2 repository from hsql.sf.net to h2database.sf.net
History: History:
The cache size was not correctly calculated for tables with large objects (specially if compression is used).
This could lead to out-of-memory exceptions.
The exception "Hexadecimal string contains non-hex character" was not always thrown when it should have been. Fixed. The exception "Hexadecimal string contains non-hex character" was not always thrown when it should have been. Fixed.
The H2 Console now provides a link to the documentation when an error occurs (H2 databases only so far). The H2 Console now provides a link to the documentation when an error occurs (H2 databases only so far).
...@@ -592,6 +640,7 @@ Features of H2 ...@@ -592,6 +640,7 @@ Features of H2
new TestTools().runTest(this); new TestTools().runTest(this);
new TestValue().runTest(this); new TestValue().runTest(this);
new TestValueHashMap().runTest(this); new TestValueHashMap().runTest(this);
new TestValueMemory().runTest(this);
afterTest(); afterTest();
} }
......
...@@ -183,7 +183,7 @@ public class TestCases extends TestBase { ...@@ -183,7 +183,7 @@ public class TestCases extends TestBase {
} }
}); });
t.start(); t.start();
Thread.sleep(500); Thread.sleep(300);
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
conn.close(); conn.close();
t.join(5000); t.join(5000);
......
...@@ -49,12 +49,12 @@ public class TestExclusive extends TestBase { ...@@ -49,12 +49,12 @@ public class TestExclusive extends TestBase {
t.start(); t.start();
state[0] = 1; state[0] = 1;
stat.execute("set exclusive false"); stat.execute("set exclusive false");
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
Thread.sleep(100); Thread.sleep(100);
if (state[0] == 2) { if (state[0] == 2) {
break; break;
} }
} }
check(state[0], 2); check(state[0], 2);
stat.execute("set exclusive true"); stat.execute("set exclusive true");
conn.close(); conn.close();
......
...@@ -79,7 +79,7 @@ public class TestMemoryUsage extends TestBase { ...@@ -79,7 +79,7 @@ public class TestMemoryUsage extends TestBase {
// update // update
time = System.currentTimeMillis(); time = System.currentTimeMillis();
prep = conn.prepareStatement("UPDATE TEST SET NAME='Hallo Welt' WHERE ID = ?"); prep = conn.prepareStatement("UPDATE TEST SET NAME='Hallo Welt' || ID WHERE ID = ?");
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
prep.setInt(1, i); prep.setInt(1, i);
prep.execute(); prep.execute();
......
...@@ -103,12 +103,12 @@ public class TestSessionsLocks extends TestBase { ...@@ -103,12 +103,12 @@ public class TestSessionsLocks extends TestBase {
rs = stat.executeQuery("CALL CANCEL_SESSION(" + otherId + ")"); rs = stat.executeQuery("CALL CANCEL_SESSION(" + otherId + ")");
rs.next(); rs.next();
if (rs.getBoolean(1)) { if (rs.getBoolean(1)) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
Thread.sleep(100); Thread.sleep(100);
if (done[0]) { if (done[0]) {
break; break;
} }
} }
check(done[0]); check(done[0]);
break; break;
} else { } else {
......
package org.h2.test.unit;
import java.io.ByteArrayInputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Random;
import org.h2.store.DataHandler;
import org.h2.store.FileStore;
import org.h2.test.TestBase;
import org.h2.util.MemoryUtils;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueByte;
import org.h2.value.ValueBytes;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
import org.h2.value.ValueDouble;
import org.h2.value.ValueFloat;
import org.h2.value.ValueInt;
import org.h2.value.ValueJavaObject;
import org.h2.value.ValueLob;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.h2.value.ValueShort;
import org.h2.value.ValueString;
import org.h2.value.ValueStringFixed;
import org.h2.value.ValueStringIgnoreCase;
import org.h2.value.ValueTime;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueUuid;
public class TestValueMemory extends TestBase implements DataHandler {
private Random random = new Random(1);
public void test() throws Exception {
for (int i = 0; i < Value.TYPE_COUNT; i++) {
testType(i);
}
}
private void testType(int type) throws SQLException {
System.gc();
System.gc();
long first = MemoryUtils.getMemoryUsed();
ArrayList list = new ArrayList();
long memory = 0;
for (int i = 0; memory < 1000000; i++) {
Value v = create(type);
memory += v.getMemory();
list.add(v);
}
Object[] array = list.toArray();
list = null;
System.gc();
System.gc();
long used = MemoryUtils.getMemoryUsed() - first;
memory /= 1024;
System.out.println("Type: " + type + " Used memory: " + used + " calculated: " + memory + " " + array.length);
if (Math.abs(used - memory) > used / 10) {
int todoMaybeThrowError;
System.out.println("ERROR");
}
}
Value create(int type) throws SQLException {
switch (type) {
case Value.NULL:
return ValueNull.INSTANCE;
case Value.BOOLEAN:
return ValueBoolean.get(false);
case Value.BYTE:
return ValueByte.get((byte) random.nextInt());
case Value.SHORT:
return ValueShort.get((short) random.nextInt());
case Value.INT:
return ValueInt.get(random.nextInt());
case Value.LONG:
return ValueLong.get(random.nextLong());
case Value.DECIMAL:
return ValueDecimal.get(new BigDecimal(random.nextInt() /*+ "12123344563456345634565234523451312312" */));
case Value.DOUBLE:
return ValueDouble.get(random.nextDouble());
case Value.FLOAT:
return ValueFloat.get(random.nextFloat());
case Value.TIME:
return ValueTime.get(new java.sql.Time(random.nextLong()));
case Value.DATE:
return ValueDate.get(new java.sql.Date(random.nextLong()));
case Value.TIMESTAMP:
return ValueTimestamp.get(new java.sql.Timestamp(random.nextLong()));
case Value.BYTES:
return ValueBytes.get(randomBytes(random.nextInt(1000)));
case Value.STRING:
return ValueString.get(randomString(random.nextInt(100)));
case Value.STRING_IGNORECASE:
return ValueStringIgnoreCase.get(randomString(random.nextInt(100)));
case Value.BLOB: {
int len = (int) Math.abs(random.nextGaussian() * 100);
byte[] data = randomBytes(len);
return ValueLob.createBlob(new ByteArrayInputStream(data), len, this);
}
case Value.CLOB: {
int len = (int) Math.abs(random.nextGaussian() * 100);
String s = randomString(len);
return ValueLob.createClob(new StringReader(s), len, this);
}
case Value.ARRAY: {
int len = random.nextInt(20);
Value[] list = new Value[len];
for (int i = 0; i < list.length; i++) {
list[i] = create(Value.STRING);
}
return ValueArray.get(list);
}
case Value.RESULT_SET:
// not supported currently
return ValueNull.INSTANCE;
case Value.JAVA_OBJECT:
return ValueJavaObject.getNoCopy(randomBytes(random.nextInt(100)));
case Value.UUID:
return ValueUuid.get(random.nextLong(), random.nextLong());
case Value.STRING_FIXED:
return ValueStringFixed.get(randomString(random.nextInt(100)));
default:
throw new Error("type=" + type);
}
}
byte[] randomBytes(int len) {
byte[] data = new byte[len];
if (random.nextBoolean()) {
// don't initialize always (compression)
random.nextBytes(data);
}
return data;
}
String randomString(int len) {
char[] chars = new char[len];
if (random.nextBoolean()) {
// don't initialize always (compression)
for (int i = 0; i < chars.length; i++) {
chars[i] = (char) (random.nextGaussian() * 100);
}
}
return new String(chars);
}
public int allocateObjectId(boolean needFresh, boolean dataFile) {
return 0;
}
public void checkPowerOff() throws SQLException {
}
public void checkWritingAllowed() throws SQLException {
}
public int compareTypeSave(Value a, Value b) throws SQLException {
return 0;
}
public String createTempFile() throws SQLException {
return baseDir + "/valueMemory/data";
// try {
// return File.createTempFile("temp", ".tmp", new File(baseDir + "/valueMemory/data")).getAbsolutePath();
// } catch (IOException e) {
// throw new SQLException();
// }
}
public void freeUpDiskSpace() throws SQLException {
}
public int getChecksum(byte[] data, int start, int end) {
return 0;
}
public String getDatabasePath() {
return baseDir + "/valueMemory";
}
public String getLobCompressionAlgorithm(int type) {
return "LZF";
}
public Object getLobSyncObject() {
return this;
}
public int getMaxLengthInplaceLob() {
return 100;
}
public boolean getTextStorage() {
return false;
}
public void handleInvalidChecksum() throws SQLException {
}
public FileStore openFile(String name, String mode, boolean mustExist) throws SQLException {
return FileStore.open(this, name, mode, null);
}
}
/* /*
* The contents of this file are subject to the terms * The contents of this file are subject to the terms
* of the Common Development and Distribution License * of the Common Development and Distribution License
* (the "License"). You may not use this file except * (the "License"). You may not use this file except
* in compliance with the License. * in compliance with the License.
* *
* You can obtain a copy of the license at * You can obtain a copy of the license at
* glassfish/bootstrap/legal/CDDLv1.0.txt or * glassfish/bootstrap/legal/CDDLv1.0.txt or
* https://glassfish.dev.java.net/public/CDDLv1.0.html. * https://glassfish.dev.java.net/public/CDDLv1.0.html.
* See the License for the specific language governing * See the License for the specific language governing
* permissions and limitations under the License. * permissions and limitations under the License.
* *
* When distributing Covered Code, include this CDDL * When distributing Covered Code, include this CDDL
* HEADER in each file and include the License file at * HEADER in each file and include the License file at
* glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
* add the following below this CDDL HEADER, with the * add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your * fields enclosed by brackets "[]" replaced with your
* own identifying information: Portions Copyright [yyyy] * own identifying information: Portions Copyright [yyyy]
* [name of copyright owner] * [name of copyright owner]
*/ */
// Copyright (c) 1998, 2006, Oracle. All rights reserved. // Copyright (c) 1998, 2006, Oracle. All rights reserved.
package oracle.toplink.essentials.platform.database; package oracle.toplink.essentials.platform.database;
import java.util.*; import java.util.*;
...@@ -33,7 +33,13 @@ import oracle.toplink.essentials.internal.databaseaccess.*; ...@@ -33,7 +33,13 @@ import oracle.toplink.essentials.internal.databaseaccess.*;
import oracle.toplink.essentials.internal.sessions.AbstractSession; import oracle.toplink.essentials.internal.sessions.AbstractSession;
/** /**
* <p><b>Purpose</b>: Provides H2 specific behaviour. * This platform provides H2 specific behaviour.
* Use the following setting to enable this platform:
* <pre>
* &lt;property
* name="toplink.platform.class.name"
* value="oracle.toplink.essentials.platform.database.H2Platform"/>
* </pre>
*/ */
public class H2Platform extends DatabasePlatform { public class H2Platform extends DatabasePlatform {
public H2Platform() { public H2Platform() {
...@@ -58,18 +64,18 @@ public class H2Platform extends DatabasePlatform { ...@@ -58,18 +64,18 @@ public class H2Platform extends DatabasePlatform {
fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("BINARY", false)); fieldTypeMapping.put(byte[].class, new FieldTypeDefinition("BINARY", false));
fieldTypeMapping.put(char[].class, new FieldTypeDefinition("LONGVARCHAR", false)); fieldTypeMapping.put(char[].class, new FieldTypeDefinition("LONGVARCHAR", false));
fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("BINARY", false)); fieldTypeMapping.put(java.sql.Blob.class, new FieldTypeDefinition("BINARY", false));
fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("LONGVARCHAR", false)); fieldTypeMapping.put(java.sql.Clob.class, new FieldTypeDefinition("LONGVARCHAR", false));
fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE", false)); fieldTypeMapping.put(java.sql.Date.class, new FieldTypeDefinition("DATE", false));
fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("TIME", false)); fieldTypeMapping.put(java.sql.Time.class, new FieldTypeDefinition("TIME", false));
fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("TIMESTAMP", false)); fieldTypeMapping.put(java.sql.Timestamp.class, new FieldTypeDefinition("TIMESTAMP", false));
return fieldTypeMapping; return fieldTypeMapping;
} }
// public boolean isHSQL() { // public boolean isHSQL() {
// return true; // return true;
// } // }
public boolean isH2() { public boolean isH2() {
return true; return true;
...@@ -78,16 +84,16 @@ public class H2Platform extends DatabasePlatform { ...@@ -78,16 +84,16 @@ public class H2Platform extends DatabasePlatform {
public boolean supportsForeignKeyConstraints() { public boolean supportsForeignKeyConstraints() {
return true; return true;
} }
public ValueReadQuery buildSelectQueryForNativeSequence(String seqName, Integer size) { public ValueReadQuery buildSelectQueryForNativeSequence(String seqName, Integer size) {
return new ValueReadQuery("CALL NEXT VALUE FOR " + getQualifiedSequenceName(seqName)); return new ValueReadQuery("CALL NEXT VALUE FOR " + getQualifiedSequenceName(seqName));
// return new ValueReadQuery("SELECT " + getQualifiedSequenceName(seqName) + ".NEXTVAL FROM DUAL"); // return new ValueReadQuery("SELECT " + getQualifiedSequenceName(seqName) + ".NEXTVAL FROM DUAL");
} }
public boolean supportsNativeSequenceNumbers() { public boolean supportsNativeSequenceNumbers() {
return true; return true;
} }
protected String getQualifiedSequenceName(String seqName) { protected String getQualifiedSequenceName(String seqName) {
if (getTableQualifier().equals("")) { if (getTableQualifier().equals("")) {
return seqName; return seqName;
...@@ -95,22 +101,22 @@ public class H2Platform extends DatabasePlatform { ...@@ -95,22 +101,22 @@ public class H2Platform extends DatabasePlatform {
return getTableQualifier() + "." + seqName; return getTableQualifier() + "." + seqName;
} }
} }
public boolean supportsSelectForUpdateNoWait() { public boolean supportsSelectForUpdateNoWait() {
return true; return true;
} }
protected ExpressionOperator todayOperator() { protected ExpressionOperator todayOperator() {
return ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.Today, "SYSDATE"); return ExpressionOperator.simpleFunctionNoParentheses(ExpressionOperator.Today, "SYSDATE");
} }
protected void initializePlatformOperators() { protected void initializePlatformOperators() {
super.initializePlatformOperators(); super.initializePlatformOperators();
addOperator(ExpressionOperator.simpleMath(ExpressionOperator.Concat, "||")); addOperator(ExpressionOperator.simpleMath(ExpressionOperator.Concat, "||"));
} }
public boolean shouldUseJDBCOuterJoinSyntax() { public boolean shouldUseJDBCOuterJoinSyntax() {
return false; return false;
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论