提交 8753a6b0 authored 作者: Andrei Tokar's avatar Andrei Tokar

Merge remote-tracking branch 'h2database/master' into undo-log-split

......@@ -21,6 +21,66 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #1177: Resource leak in Recover tool
</li>
<li>PR #1183: Improve concurrency of connection pool with wait-free implement
</li>
<li>Issue #1073: H2 v1.4.197 fails to open an existing database with the error [Unique index or primary key violation: "PRIMARY KEY ON """".PAGE_INDEX"]
</li>
<li>PR #1179: Drop TransactionMap.readLogId
</li>
<li>PR #1181: Improve CURRENT_TIMESTAMP and add LOCALTIME and LOCALTIMESTAMP
</li>
<li>PR #1176: Magic value replacement with constant
</li>
<li>PR #1171: Introduce last commited value into a VersionedValue
</li>
<li>PR #1175: tighten test conditions - do not ignore any exceptions
</li>
<li>PR #1174: Remove mapid
</li>
<li>PR #1173: protect first background exception encountered and relate it to clients
</li>
<li>PR #1172: Yet another attempt to tighten that testing loop
</li>
<li>PR #1170: Add support of CONTINUE | RESTART IDENTITY to TRUNCATE TABLE
</li>
<li>Issue #1168: ARRAY_CONTAINS() returning incorrect results when inside subquery with Long elements.
</li>
<li>PR #1167: MVStore: Undo log synchronization removal
</li>
<li>PR #1166: Add SRID support to EWKT format
</li>
<li>PR #1165: Optimize isTargetRowFound() and buildColumnListFromOnCondition() in MergeUsing
</li>
<li>PR #1164: More fixes for parsing of MERGE USING and other changes in Parser
</li>
<li>PR #1154: Support for external authentication
</li>
<li>PR #1162: Reduce allocation of temporary strings
</li>
<li>PR #1158: make fields final
</li>
<li>Issue #1129: TestCrashAPI / TestFuzzOptimizations throw OOME on Travis in PageStore mode
</li>
<li>PR #1156: Add support for SQL:2003 WITH [NO] DATA to CREATE TABLE AS
</li>
<li>PR #1149: fix deadlock between OnExitDatabaseCloser.DATABASES and Engine.DATABASES
</li>
<li>PR #1152: skip intermediate DbException object when creating SQLException
</li>
<li>PR #1144: Add missing schema name with recursive view
</li>
<li>Issue #1091: get rid of the "New" class
</li>
<li>PR #1147: Assorted minor optimizations
</li>
<li>PR #1145: Reduce code duplication
</li>
<li>PR #1142: Misc small fixes
</li>
<li>PR #1141: Assorted optimizations and fixes
</li>
<li>PR #1138, #1139: Fix a memory leak caused by DatabaseCloser objects
</li>
<li>PR #1137: Step toward making transaction commit atomic
......
......@@ -54,7 +54,12 @@ public abstract class Prepared {
private long modificationMetaId;
private Command command;
private int objectId;
/**
* Used to preserve object identities on database startup. {@code 0} if
* object is not stored, {@code -1} if object is stored and its ID is
* already read, {@code >0} if object is stored and its id is not yet read.
*/
private int persistedObjectId;
private int currentRowNumber;
private int rowScanCount;
/**
......@@ -239,28 +244,31 @@ public abstract class Prepared {
/**
* Get the object id to use for the database object that is created in this
* statement. This id is only set when the object is persistent.
* statement. This id is only set when the object is already persisted.
* If not set, this method returns 0.
*
* @return the object id or 0 if not set
*/
protected int getCurrentObjectId() {
return objectId;
protected int getPersistedObjectId() {
int id = persistedObjectId;
return id >= 0 ? id : 0;
}
/**
* Get the current object id, or get a new id from the database. The object
* id is used when creating new database object (CREATE statement).
* id is used when creating new database object (CREATE statement). This
* method may be called only once.
*
* @return the object id
*/
protected int getObjectId() {
int id = objectId;
int id = persistedObjectId;
if (id == 0) {
id = session.getDatabase().allocateObjectId();
} else {
objectId = 0;
} else if (id < 0) {
throw DbException.throwInternalError("Prepared.getObjectId() was called before");
}
persistedObjectId = -1;
return id;
}
......@@ -287,12 +295,12 @@ public abstract class Prepared {
}
/**
* Set the object id for this statement.
* Set the persisted object id for this statement.
*
* @param i the object id
*/
public void setObjectId(int i) {
this.objectId = i;
public void setPersistedObjectId(int i) {
this.persistedObjectId = i;
this.create = false;
}
......
......@@ -137,25 +137,24 @@ public class AlterTableAddConstraint extends SchemaCommand {
throw DbException.get(ErrorCode.SECOND_PRIMARY_KEY);
}
}
}
if (index == null) {
} else {
IndexType indexType = IndexType.createPrimaryKey(
table.isPersistIndexes(), primaryKeyHash);
String indexName = table.getSchema().getUniqueIndexName(
session, table, Constants.PREFIX_PRIMARY_KEY);
int id = getObjectId();
int indexId = session.getDatabase().allocateObjectId();
try {
index = table.addIndex(session, indexName, id,
index = table.addIndex(session, indexName, indexId,
indexColumns, indexType, true, null);
} finally {
getSchema().freeUniqueName(indexName);
}
}
index.getIndexType().setBelongsToConstraint(true);
int constraintId = getObjectId();
int id = getObjectId();
String name = generateConstraintName(table);
ConstraintUnique pk = new ConstraintUnique(getSchema(),
constraintId, name, table, true);
id, name, table, true);
pk.setColumns(indexColumns);
pk.setIndex(index, true);
constraint = pk;
......@@ -277,7 +276,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
private Index createIndex(Table t, IndexColumn[] cols, boolean unique) {
int indexId = getObjectId();
int indexId = session.getDatabase().allocateObjectId();
IndexType indexType;
if (unique) {
// for unique constraints
......
......@@ -103,7 +103,7 @@ public abstract class CommandWithColumns extends SchemaCommand {
if (columns != null) {
for (Column c : columns) {
if (c.isAutoIncrement()) {
int objId = getObjectId();
int objId = session.getDatabase().allocateObjectId();
c.convertAutoIncrementToSequence(session, getSchema(), objId, temporary);
if (!Constants.CLUSTERING_DISABLED.equals(session.getDatabase().getCluster())) {
throw DbException.getUnsupportedException("CLUSTERING && auto-increment columns");
......
......@@ -418,7 +418,7 @@ public class Set extends Prepared {
}
case SetTypes.TRACE_LEVEL_FILE:
session.getUser().checkAdmin();
if (getCurrentObjectId() == 0) {
if (getPersistedObjectId() == 0) {
// don't set the property when opening the database
// this is for compatibility with older versions, because
// this setting was persistent
......@@ -427,7 +427,7 @@ public class Set extends Prepared {
break;
case SetTypes.TRACE_LEVEL_SYSTEM_OUT:
session.getUser().checkAdmin();
if (getCurrentObjectId() == 0) {
if (getPersistedObjectId() == 0) {
// don't set the property when opening the database
// this is for compatibility with older versions, because
// this setting was persistent
......@@ -552,7 +552,7 @@ public class Set extends Prepared {
}
addOrUpdateSetting(name,expression.getValue(session).getString(),0);
} catch (Exception e) {
//Errors during start are ignored to allow to open the database
//Errors during start are ignored to allow to open the database
if (database.isStarting()) {
database.getTrace(Trace.DATABASE).error(e, "{0}: failed to set authenticator during database start ",expression.toString());
} else {
......
......@@ -54,7 +54,7 @@ public class MetaRecord implements Comparable<MetaRecord> {
DatabaseEventListener listener) {
try {
Prepared command = systemSession.prepare(sql);
command.setObjectId(id);
command.setPersistedObjectId(id);
command.update();
} catch (DbException e) {
e = e.addSQL(sql);
......
......@@ -22,8 +22,11 @@ package org.h2.jdbcx;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
import javax.sql.ConnectionEvent;
......@@ -33,7 +36,6 @@ import javax.sql.DataSource;
import javax.sql.PooledConnection;
import org.h2.message.DbException;
import org.h2.util.Utils;
/**
* A simple standalone JDBC connection pool.
......@@ -69,12 +71,12 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
private static final int DEFAULT_MAX_CONNECTIONS = 10;
private final ConnectionPoolDataSource dataSource;
private final ArrayList<PooledConnection> recycledConnections = Utils.newSmallArrayList();
private final Queue<PooledConnection> recycledConnections = new ConcurrentLinkedQueue<>();
private PrintWriter logWriter;
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
private int timeout = DEFAULT_TIMEOUT;
private int activeConnections;
private boolean isDisposed;
private volatile int maxConnections = DEFAULT_MAX_CONNECTIONS;
private volatile int timeout = DEFAULT_TIMEOUT;
private AtomicInteger activeConnections = new AtomicInteger(0);
private AtomicBoolean isDisposed = new AtomicBoolean(false);
protected JdbcConnectionPool(ConnectionPoolDataSource dataSource) {
this.dataSource = dataSource;
......@@ -120,13 +122,11 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
*
* @param max the maximum number of connections
*/
public synchronized void setMaxConnections(int max) {
public void setMaxConnections(int max) {
if (max < 1) {
throw new IllegalArgumentException("Invalid maxConnections value: " + max);
}
this.maxConnections = max;
// notify waiting threads if the value was increased
notifyAll();
}
/**
......@@ -134,7 +134,7 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
*
* @return the max the maximum number of connections
*/
public synchronized int getMaxConnections() {
public int getMaxConnections() {
return maxConnections;
}
......@@ -144,7 +144,7 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
* @return the timeout in seconds
*/
@Override
public synchronized int getLoginTimeout() {
public int getLoginTimeout() {
return timeout;
}
......@@ -156,7 +156,7 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
* @param seconds the timeout, 0 meaning the default
*/
@Override
public synchronized void setLoginTimeout(int seconds) {
public void setLoginTimeout(int seconds) {
if (seconds == 0) {
seconds = DEFAULT_TIMEOUT;
}
......@@ -167,13 +167,12 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
* Closes all unused pooled connections.
* Exceptions while closing are written to the log stream (if set).
*/
public synchronized void dispose() {
if (isDisposed) {
return;
}
isDisposed = true;
for (PooledConnection aList : recycledConnections) {
closeConnection(aList);
public void dispose() {
isDisposed.set(true);
PooledConnection pc;
while ((pc = recycledConnections.poll()) != null) {
closeConnection(pc);
}
}
......@@ -193,18 +192,28 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
@Override
public Connection getConnection() throws SQLException {
long max = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeout);
int spin = 0;
do {
synchronized (this) {
if (activeConnections < maxConnections) {
return getConnectionNow();
}
if (activeConnections.incrementAndGet() <= maxConnections) {
try {
wait(1000);
} catch (InterruptedException e) {
// ignore
return getConnectionNow();
} catch (Throwable t) {
activeConnections.decrementAndGet();
throw t;
}
} else {
activeConnections.decrementAndGet();
}
} while (System.nanoTime() <= max);
if (--spin >= 0) {
continue;
}
try {
spin = 3;
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} while (System.nanoTime() - max <= 0);
throw new SQLException("Login timeout", "08001", 8001);
}
......@@ -217,17 +226,14 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
}
private Connection getConnectionNow() throws SQLException {
if (isDisposed) {
if (isDisposed.get()) {
throw new IllegalStateException("Connection pool has been disposed.");
}
PooledConnection pc;
if (!recycledConnections.isEmpty()) {
pc = recycledConnections.remove(recycledConnections.size() - 1);
} else {
PooledConnection pc = recycledConnections.poll();
if (pc == null) {
pc = dataSource.getPooledConnection();
}
Connection conn = pc.getConnection();
activeConnections++;
pc.addConnectionEventListener(this);
return conn;
}
......@@ -239,19 +245,20 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
*
* @param pc the pooled connection
*/
synchronized void recycleConnection(PooledConnection pc) {
if (activeConnections <= 0) {
private void recycleConnection(PooledConnection pc) {
int active = activeConnections.decrementAndGet();
if (active < 0) {
activeConnections.incrementAndGet();
throw new AssertionError();
}
activeConnections--;
if (!isDisposed && activeConnections < maxConnections) {
if (!isDisposed.get() && active < maxConnections) {
recycledConnections.add(pc);
if (isDisposed.get()) {
dispose();
}
} else {
closeConnection(pc);
}
if (activeConnections >= maxConnections - 1) {
notifyAll();
}
}
private void closeConnection(PooledConnection pc) {
......@@ -290,8 +297,8 @@ public class JdbcConnectionPool implements DataSource, ConnectionEventListener,
*
* @return the number of active connections.
*/
public synchronized int getActiveConnections() {
return activeConnections;
public int getActiveConnections() {
return activeConnections.get();
}
/**
......
......@@ -340,14 +340,13 @@ public class Recover extends Tool implements DataHandler {
} else if (fileName.endsWith(Constants.SUFFIX_MV_FILE)) {
String f = fileName.substring(0, fileName.length() -
Constants.SUFFIX_PAGE_FILE.length());
PrintWriter writer;
writer = getWriter(fileName, ".txt");
MVStoreTool.dump(fileName, writer, true);
MVStoreTool.info(fileName, writer);
writer.close();
writer = getWriter(f + ".h2.db", ".sql");
dumpMVStoreFile(writer, fileName);
writer.close();
try (PrintWriter writer = getWriter(fileName, ".txt")) {
MVStoreTool.dump(fileName, writer, true);
MVStoreTool.info(fileName, writer);
}
try (PrintWriter writer = getWriter(f + ".h2.db", ".sql")) {
dumpMVStoreFile(writer, fileName);
}
}
}
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论