提交 8fbe0e15 authored 作者: Noel Grandin's avatar Noel Grandin

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	h2/src/docsrc/html/changelog.html
......@@ -415,9 +415,11 @@ ALTER VIEW ADDRESS_VIEW RECOMPILE
"
"Commands (DDL)","ANALYZE","
ANALYZE [ SAMPLE_SIZE rowCountInt ]
ANALYZE [ TABLE tableName ] [ SAMPLE_SIZE rowCountInt ]
","
Updates the selectivity statistics of all tables. The selectivity is used by the
Updates the selectivity statistics of tables.
If no table name is given, all tables are analyzed.
The selectivity is used by the
cost based optimizer to select the best index for a given query. If no sample
size is set, up to 10000 rows per table are read. The value 0 means all rows are
read. The selectivity can be set manually using ALTER TABLE ALTER COLUMN
......
......@@ -23,6 +23,14 @@ Change Log
<ul>
<li>Fix startup issue when using "CHECK" as a column name.
</li>
<li>Issue #423: ANALYZE performed multiple times on one table during execution of the same statement.
</li>
<li>Issue #426: Support ANALYZE TABLE statement
</li>
<li>Issue #438: Fix slow logging via SLF4J (TRACE_LEVEL_FILE=4).
</li>
<li>Issue #472: Support CREATE SEQUENCE ... ORDER as a NOOP for Oracle compatibility
</li>
</ul>
<h2>Version 1.4.195 (2017-04-23)</h2>
......
......@@ -58,4 +58,5 @@ Export-Package: org.h2;version="${version}",
org.h2.mvstore.db;version="${version}",
org.h2.mvstore.type;version="${version}",
org.h2.mvstore.rtree;version="${version}"
Provide-Capability: osgi.service;objectClass:List<String>=org.osgi.service.jdbc.DataSourceFactory
Premain-Class: org.h2.util.Profiler
......@@ -550,6 +550,11 @@ public class Parser {
private Prepared parseAnalyze() {
Analyze command = new Analyze(session);
if (readIf("TABLE")) {
Table table = readTableOrView();
command.setTable(table);
command.setTop(readPositiveInt());
}
if (readIf("SAMPLE_SIZE")) {
command.setTop(readPositiveInt());
}
......@@ -4660,6 +4665,8 @@ public class Parser {
command.setCacheSize(ValueExpression.get(ValueLong.get(1)));
} else if (readIf("BELONGS_TO_TABLE")) {
command.setBelongsToTable(true);
} else if (readIf("ORDER")) {
// Oracle compatibility
} else {
break;
}
......@@ -4676,6 +4683,10 @@ public class Parser {
return false;
}
private boolean readIfAffinity() {
return readIf("AFFINITY") || readIf("SHARD");
}
private CreateConstant parseCreateConstant() {
boolean ifNotExists = readIfNotExists();
String constantName = readIdentifierWithSchema();
......@@ -5900,6 +5911,7 @@ public class Parser {
String constraintName = null, comment = null;
boolean ifNotExists = false;
boolean allowIndexDefinition = database.getMode().indexDefinitionInCreateTable;
boolean allowAffinityKey = database.getMode().allowAffinityKey;
if (readIf("CONSTRAINT")) {
ifNotExists = readIfNotExists();
constraintName = readIdentifierWithSchema(schema.getName());
......@@ -5951,6 +5963,12 @@ public class Parser {
read("BTREE");
}
return command;
} else if (allowAffinityKey && readIfAffinity()) {
read("KEY");
read("(");
CreateIndex command = createAffinityIndex(schema, tableName, parseIndexColumnList());
command.setIfTableExists(ifTableExists);
return command;
}
AlterTableAddConstraint command;
if (readIf("CHECK")) {
......@@ -6118,6 +6136,9 @@ public class Parser {
if (readIf("CONSTRAINT")) {
constraintName = readColumnIdentifier();
}
// For compatibility with Apache Ignite.
boolean allowAffinityKey = database.getMode().allowAffinityKey;
boolean affinity = allowAffinityKey && readIfAffinity();
if (readIf("PRIMARY")) {
read("KEY");
boolean hash = readIf("HASH");
......@@ -6133,6 +6154,16 @@ public class Parser {
if (readIf("AUTO_INCREMENT")) {
parseAutoIncrement(column);
}
if (affinity) {
CreateIndex idx = createAffinityIndex(schema, tableName, cols);
command.addConstraintCommand(idx);
}
} else if (affinity) {
read("KEY");
IndexColumn[] cols = { new IndexColumn() };
cols[0].columnName = column.getName();
CreateIndex idx = createAffinityIndex(schema, tableName, cols);
command.addConstraintCommand(idx);
} else if (readIf("UNIQUE")) {
AlterTableAddConstraint unique = new AlterTableAddConstraint(
session, schema, false);
......@@ -6251,6 +6282,14 @@ public class Parser {
return command;
}
private CreateIndex createAffinityIndex(Schema schema, String tableName, IndexColumn[] indexColumns) {
CreateIndex idx = new CreateIndex(session, schema);
idx.setTableName(tableName);
idx.setIndexColumns(indexColumns);
idx.setAffinity(true);
return idx;
}
private static int getCompareType(int tokenType) {
switch (tokenType) {
case EQUAL:
......
......@@ -22,8 +22,8 @@ import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
/**
* This class represents the statement
* ANALYZE
* This class represents the statements
* ANALYZE and ANALYZE TABLE
*/
public class Analyze extends DefineCommand {
......@@ -31,20 +31,32 @@ public class Analyze extends DefineCommand {
* The sample size.
*/
private int sampleRows;
/**
* used in ANALYZE TABLE...
*/
private Table table;
public Analyze(Session session) {
super(session);
sampleRows = session.getDatabase().getSettings().analyzeSample;
}
public void setTable(Table table) {
this.table = table;
}
@Override
public int update() {
session.commit(true);
session.getUser().checkAdmin();
Database db = session.getDatabase();
if (table != null) {
analyzeTable(session, table, sampleRows, true);
} else {
for (Table table : db.getAllTablesAndViews(false)) {
analyzeTable(session, table, sampleRows, true);
}
}
return 0;
}
......
......@@ -26,7 +26,7 @@ public class CreateIndex extends SchemaCommand {
private String tableName;
private String indexName;
private IndexColumn[] indexColumns;
private boolean primaryKey, unique, hash, spatial;
private boolean primaryKey, unique, hash, spatial, affinity;
private boolean ifTableExists;
private boolean ifNotExists;
private String comment;
......@@ -98,6 +98,8 @@ public class CreateIndex extends SchemaCommand {
indexType = IndexType.createPrimaryKey(persistent, hash);
} else if (unique) {
indexType = IndexType.createUnique(persistent, hash);
} else if (affinity) {
indexType = IndexType.createAffinity();
} else {
indexType = IndexType.createNonUnique(persistent, hash, spatial);
}
......@@ -123,6 +125,10 @@ public class CreateIndex extends SchemaCommand {
this.spatial = b;
}
public void setAffinity(boolean b) {
this.affinity = b;
}
public void setComment(String comment) {
this.comment = comment;
}
......
......@@ -80,5 +80,4 @@ public class CreateTableData {
* The table is hidden.
*/
public boolean isHidden;
}
......@@ -155,6 +155,11 @@ public class Mode {
*/
public boolean prohibitEmptyInPredicate;
/**
* Whether AFFINITY KEY keywords are supported.
*/
public boolean allowAffinityKey;
private final String name;
static {
......@@ -252,6 +257,12 @@ public class Mode {
Pattern.compile("ApplicationName");
mode.prohibitEmptyInPredicate = true;
add(mode);
mode = new Mode("Ignite");
mode.nullConcatIsNull = true;
mode.allowAffinityKey = true;
mode.indexDefinitionInCreateTable = true;
add(mode);
}
private Mode(String name) {
......
......@@ -13,12 +13,12 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.h2.api.ErrorCode;
import org.h2.command.Command;
import org.h2.command.CommandInterface;
import org.h2.command.Parser;
import org.h2.command.Prepared;
import org.h2.command.ddl.Analyze;
import org.h2.command.dml.Query;
import org.h2.command.dml.SetTypes;
import org.h2.constraint.Constraint;
......@@ -126,6 +126,11 @@ public class Session extends SessionWithState {
private boolean joinBatchEnabled;
private boolean forceJoinOrder;
private boolean lazyQueryExecution;
/**
* Tables marked for ANALYZE after the current transaction is committed.
* Prevents us calling ANALYZE repeatedly in large transactions.
*/
private HashSet<Table> tablesToAnalyze;
/**
* Temporary LOBs from result sets. Those are kept for some time. The
......@@ -659,6 +664,14 @@ public class Session extends SessionWithState {
}
}
endTransaction();
int rows = getDatabase().getSettings().analyzeSample / 10;
if (tablesToAnalyze != null) {
for (Table table : tablesToAnalyze) {
Analyze.analyzeTable(this, table, rows, false);
}
}
tablesToAnalyze = null;
}
private void removeTemporaryLobs(boolean onTimeout) {
......@@ -1682,6 +1695,13 @@ public class Session extends SessionWithState {
return false;
}
public void markTableForAnalyze(Table table) {
if (tablesToAnalyze == null) {
tablesToAnalyze = New.hashSet();
}
tablesToAnalyze.add(table);
}
/**
* Represents a savepoint (a position in a transaction to where one can roll
* back to).
......
......@@ -1234,7 +1234,11 @@ public class Function extends Expression implements FunctionCall {
break;
case ROUND: {
double f = v1 == null ? 1. : Math.pow(10., v1.getDouble());
result = ValueDouble.get(Math.round(v0.getDouble() * f) / f);
double middleResult = v0.getDouble() * f;
int oneWithSymbol = middleResult > 0 ? 1 : -1;
result = ValueDouble.get(Math.round(Math.abs(middleResult)) / f * oneWithSymbol);
break;
}
case TRUNCATE: {
......
......@@ -10,7 +10,7 @@ package org.h2.index;
*/
public class IndexType {
private boolean primaryKey, persistent, unique, hash, scan, spatial;
private boolean primaryKey, persistent, unique, hash, scan, spatial, affinity;
private boolean belongsToConstraint;
/**
......@@ -71,6 +71,16 @@ public class IndexType {
return type;
}
/**
* Create an affinity index.
*
*/
public static IndexType createAffinity() {
IndexType type = new IndexType();
type.affinity = true;
return type;
}
/**
* Create a scan pseudo-index.
*
......@@ -148,6 +158,15 @@ public class IndexType {
return unique;
}
/**
* Does this index represent an affinity key?
*
* @return true if it does
*/
public boolean isAffinity() {
return affinity;
}
/**
* Get the SQL snippet to create such an index.
*
......
......@@ -146,6 +146,9 @@ public class TraceSystem implements TraceWriter {
@Override
public boolean isEnabled(int level) {
if (levelMax == ADAPTER) {
return writer.isEnabled(level);
}
return level <= this.levelMax;
}
......
......@@ -185,7 +185,7 @@ public class WriteBuffer {
* @return this
*/
public WriteBuffer put(ByteBuffer src) {
ensureCapacity(buff.remaining()).put(src);
ensureCapacity(src.remaining()).put(src);
return this;
}
......
......@@ -12,10 +12,8 @@ import java.util.Comparator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.command.ddl.Analyze;
import org.h2.command.ddl.CreateTableData;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential;
......@@ -738,7 +736,7 @@ public class MVTable extends TableBase {
nextAnalyze = n;
}
int rows = session.getDatabase().getSettings().analyzeSample / 10;
Analyze.analyzeTable(session, this, rows, false);
session.markTableForAnalyze(this);
}
@Override
......
......@@ -350,8 +350,12 @@ public class TransactionStore {
if (value == null) {
// nothing to do
} else if (value.value == null) {
int tx = getTransactionId(value.operationId);
if (tx == t.transactionId) {
// remove the value
// only if it's transaction id is same as current transaction id
map.remove(key);
}
} else {
VersionedValue v2 = new VersionedValue();
v2.value = value.value;
......
......@@ -149,9 +149,9 @@ ALTER VIEW [ IF EXISTS ] viewName RECOMPILE
","
Recompiles a view after the underlying tables have been changed or created."
"Commands (DDL)","ANALYZE","
ANALYZE [ SAMPLE_SIZE rowCountInt ]
ANALYZE [ TABLE tableName ] [ SAMPLE_SIZE rowCountInt ]
","
Updates the selectivity statistics of all tables."
Updates the selectivity statistics of tables."
"Commands (DDL)","COMMENT","
COMMENT ON
{ { COLUMN [ schemaName. ] tableName.columnName }
......
......@@ -167,6 +167,9 @@ public final class JoinBatch {
* @return column value for current row
*/
public Value getValue(int filterId, Column column) {
if (current == null) {
return null;
}
Object x = current.row(filterId);
assert x != null;
Row row = current.isRow(filterId) ? (Row) x : ((Cursor) x).get();
......
......@@ -52,7 +52,12 @@ public class LinkSchema {
stat = conn.createStatement();
stat.execute("CREATE SCHEMA IF NOT EXISTS " +
StringUtils.quoteIdentifier(targetSchema));
//Workaround for PostgreSQL to avoid index names
if (url.startsWith("jdbc:postgresql:")) {
rs = c2.getMetaData().getTables(null, sourceSchema, null, new String[]{"TABLE", "LINKED TABLE", "VIEW", "EXTERNAL"});
} else {
rs = c2.getMetaData().getTables(null, sourceSchema, null, null);
}
while (rs.next()) {
String table = rs.getString("TABLE_NAME");
StringBuilder buff = new StringBuilder();
......
......@@ -200,7 +200,8 @@ public class MetaTable extends Table {
"ID INT",
"SORT_TYPE INT",
"CONSTRAINT_NAME",
"INDEX_CLASS"
"INDEX_CLASS",
"AFFINITY BIT"
);
indexColumnName = "TABLE_NAME";
break;
......@@ -912,7 +913,10 @@ public class MetaTable extends Table {
// CONSTRAINT_NAME
constraintName,
// INDEX_CLASS
indexClass
indexClass,
// AFFINITY
index.getIndexType().isAffinity() ?
"TRUE" : "FALSE"
);
}
}
......
......@@ -12,10 +12,8 @@ import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.h2.api.DatabaseEventListener;
import org.h2.api.ErrorCode;
import org.h2.command.ddl.Analyze;
import org.h2.command.ddl.CreateTableData;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintReferential;
......@@ -431,8 +429,7 @@ public class RegularTable extends TableBase {
if (n > 0) {
nextAnalyze = n;
}
int rows = session.getDatabase().getSettings().analyzeSample / 10;
Analyze.analyzeTable(session, this, rows, false);
session.markTableForAnalyze(this);
}
@Override
......
......@@ -445,7 +445,8 @@ public class TableLink extends Table {
@Override
public synchronized long getRowCount(Session session) {
String sql = "SELECT COUNT(*) FROM " + qualifiedTableName;
//The foo alias is used to support the PostgreSQL syntax
String sql = "SELECT COUNT(*) FROM " + qualifiedTableName + " as foo";
try {
PreparedStatement prep = execute(sql, null, false);
ResultSet rs = prep.getResultSet();
......
......@@ -117,6 +117,7 @@ import org.h2.test.mvcc.TestMvcc2;
import org.h2.test.mvcc.TestMvcc3;
import org.h2.test.mvcc.TestMvcc4;
import org.h2.test.mvcc.TestMvccMultiThreaded;
import org.h2.test.mvcc.TestMvccMultiThreaded2;
import org.h2.test.poweroff.TestReorderWrites;
import org.h2.test.recover.RecoverLobTest;
import org.h2.test.rowlock.TestRowLocks;
......@@ -803,6 +804,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestMvcc3());
addTest(new TestMvcc4());
addTest(new TestMvccMultiThreaded());
addTest(new TestMvccMultiThreaded2());
addTest(new TestRowLocks());
// synth
......
......@@ -50,6 +50,7 @@ public class TestCompatibility extends TestBase {
testDB2();
testDerby();
testSybaseAndMSSQLServer();
testIgnite();
conn.close();
deleteDb("compatibility");
......@@ -487,4 +488,26 @@ public class TestCompatibility extends TestBase {
conn.close();
conn = getConnection("compatibility");
}
private void testIgnite() throws SQLException {
Statement stat = conn.createStatement();
stat.execute("SET MODE Ignite");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int affinity key)");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int affinity primary key)");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int, v1 varchar, v2 long affinity key, primary key(v1, id))");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int, v1 varchar, v2 long, primary key(v1, id), affinity key (id))");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int shard key)");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int shard primary key)");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int, v1 varchar, v2 long shard key, primary key(v1, id))");
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("create table test(id int, v1 varchar, v2 long, primary key(v1, id), shard key (id))");
}
}
......@@ -117,6 +117,7 @@ public class TestFunctions extends TestBase implements AggregateFunction {
testThatCurrentTimestampStaysTheSameWithinATransaction();
testThatCurrentTimestampUpdatesOutsideATransaction();
testAnnotationProcessorsOutput();
testRound();
deleteDb("functions");
}
......@@ -2010,6 +2011,28 @@ public class TestFunctions extends TestBase implements AggregateFunction {
}
}
private void testRound() throws SQLException {
deleteDb("functions");
Connection conn = getConnection("functions");
Statement stat = conn.createStatement();
final ResultSet rs = stat.executeQuery(
"select ROUND(-1.2), ROUND(-1.5), ROUND(-1.6), ROUND(2), ROUND(1.5), ROUND(1.8), ROUND(1.1) from dual");
rs.next();
assertEquals(-1, rs.getInt(1));
assertEquals(-2, rs.getInt(2));
assertEquals(-2, rs.getInt(3));
assertEquals(2, rs.getInt(4));
assertEquals(2, rs.getInt(5));
assertEquals(2, rs.getInt(6));
assertEquals(1, rs.getInt(7));
rs.close();
conn.close();
}
private void testThatCurrentTimestampIsSane() throws SQLException,
ParseException {
deleteDb("functions");
......
......@@ -38,30 +38,45 @@ public class TestQueryCache extends TestBase {
private void test1() throws Exception {
Connection conn = getConnection("queryCache;QUERY_CACHE_SIZE=10");
Statement stat = conn.createStatement();
stat.execute("create table test(id int, name varchar) " +
"as select x, space(100) from system_range(1, 1000)");
stat.execute("create table test(id int, name varchar)");
PreparedStatement prep;
conn.prepareStatement("select count(*) from test t1, test t2");
// query execution may be fast here but the parsing must be slow
StringBuilder queryBuilder = new StringBuilder("select count(*) from test t1 where \n");
for (int i = 0; i < 1000; i++) {
if (i != 0) {
queryBuilder.append(" and ");
}
queryBuilder.append(" TIMESTAMP '2005-12-31 23:59:59' = TIMESTAMP '2005-12-31 23:59:59' ");
}
String query = queryBuilder.toString();
conn.prepareStatement(query);
long time;
ResultSet rs;
long first = 0;
for (int i = 0; i < 4; i++) {
// 1000 iterations to warm up and avoid JIT effects
for (int i = 0; i < 1005; i++) {
// this should both ensure results are not re-used
// stat.execute("set mode regular");
// stat.execute("create table x()");
// stat.execute("drop table x");
time = System.nanoTime();
prep = conn.prepareStatement("select count(*) from test t1, test t2");
prep = conn.prepareStatement(query);
execute(prep);
rs = stat.executeQuery("select count(*) from test t1, test t2");
prep.close();
rs = stat.executeQuery(query);
rs.next();
int c = rs.getInt(1);
rs.close();
assertEquals(1000000, c);
assertEquals(0, c);
time = System.nanoTime() - time;
if (first == 0) {
if (i == 1000) {
// take from cache and do not close, so that next iteration will have a cache miss
prep = conn.prepareStatement(query);
} else if (i == 1001) {
first = time;
} else {
// try to avoid pauses in subsequent iterations
System.gc();
} else if (i > 1001) {
assertSmaller(time, first);
}
}
......
......@@ -41,13 +41,7 @@ import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.SubQueryInfo;
import org.h2.table.Table;
import org.h2.table.TableBase;
import org.h2.table.TableFilter;
import org.h2.table.TableType;
import org.h2.table.*;
import org.h2.test.TestBase;
import org.h2.util.DoneFuture;
import org.h2.util.New;
......@@ -82,6 +76,7 @@ public class TestTableEngines extends TestBase {
testSimpleQuery();
testMultiColumnTreeSetIndex();
testBatchedJoin();
testAffinityKey();
}
private void testEarlyFilter() throws SQLException {
......@@ -504,6 +499,25 @@ public class TestTableEngines extends TestBase {
deleteDb("testBatchedJoin");
}
private void testAffinityKey() throws SQLException {
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine;mode=Ignite;MV_STORE=FALSE");
Statement stat = conn.createStatement();
stat.executeUpdate("CREATE TABLE T(ID INT AFFINITY PRIMARY KEY, NAME VARCHAR, AGE INT)" +
" ENGINE \"" + AffinityTableEngine.class.getName() + "\"");
Table tbl = AffinityTableEngine.createdTbl;
assertTrue(tbl != null);
assertEquals(3, tbl.getIndexes().size());
Index aff = tbl.getIndexes().get(2);
assertTrue(aff.getIndexType().isAffinity());
assertEquals("T_AFF", aff.getName());
assertEquals(1, aff.getIndexColumns().length);
assertEquals("ID", aff.getIndexColumns()[0].columnName);
conn.close();
deleteDb("tableEngine");
}
private static void forceJoinOrder(Statement s, boolean force) throws SQLException {
s.executeUpdate("SET FORCE_JOIN_ORDER " + force);
}
......@@ -1098,6 +1112,144 @@ public class TestTableEngines extends TestBase {
}
/**
* A test table factory producing affinity aware tables.
*/
public static class AffinityTableEngine implements TableEngine {
public static Table createdTbl;
/**
* A table able to handle affinity indexes.
*/
private static class AffinityTable extends RegularTable {
/**
* A (no-op) affinity index.
*/
public class AffinityIndex extends BaseIndex {
AffinityIndex(Table table, int id, String name, IndexColumn[] newIndexColumns) {
initBaseIndex(table, id, name, newIndexColumns, IndexType.createAffinity());
}
@Override
public long getRowCountApproximation() {
return table.getRowCountApproximation();
}
@Override
public long getDiskSpaceUsed() {
return table.getDiskSpaceUsed();
}
@Override
public long getRowCount(Session session) {
return table.getRowCount(session);
}
@Override
public void checkRename() {
// do nothing
}
@Override
public void truncate(Session session) {
// do nothing
}
@Override
public void remove(Session session) {
// do nothing
}
@Override
public void remove(Session session, Row r) {
// do nothing
}
@Override
public boolean needRebuild() {
return false;
}
@Override
public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder,
HashSet<Column> allColumnsSet) {
return 0;
}
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
throw DbException.getUnsupportedException("TEST");
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
throw DbException.getUnsupportedException("TEST");
}
@Override
public void close(Session session) {
// do nothing
}
@Override
public boolean canGetFirstOrLast() {
return false;
}
@Override
public void add(Session session, Row r) {
// do nothing
}
}
AffinityTable(CreateTableData data) {
super(data);
}
@Override
public Index addIndex(Session session, String indexName,
int indexId, IndexColumn[] cols, IndexType indexType,
boolean create, String indexComment) {
if (!indexType.isAffinity()) {
return super.addIndex(session, indexName, indexId, cols, indexType, create, indexComment);
}
boolean isSessionTemporary = isTemporary() && !isGlobalTemporary();
if (!isSessionTemporary) {
database.lockMeta(session);
}
AffinityIndex index = new AffinityIndex(this, indexId, getName() + "_AFF", cols);
index.setTemporary(isTemporary());
if (index.getCreateSQL() != null) {
index.setComment(indexComment);
if (isSessionTemporary) {
session.addLocalTempTableIndex(index);
} else {
database.addSchemaObject(session, index);
}
}
getIndexes().add(index);
setModified();
return index;
}
}
/**
* Create a new OneRowTable.
*
* @param data the meta data of the table to create
* @return the new table
*/
@Override
public Table createTable(CreateTableData data) {
return (createdTbl = new AffinityTable(data));
}
}
/**
* A test table factory.
*/
......
/*
* 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.mvcc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.jdbc.JdbcSQLException;
import org.h2.test.TestBase;
import org.h2.util.IOUtils;
/**
* Additional MVCC (multi version concurrency) test cases.
*/
public class TestMvccMultiThreaded2 extends TestBase {
private static final String URL = ";MVCC=TRUE;LOCK_TIMEOUT=120000;MULTI_THREADED=TRUE";
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase test = TestBase.createCaller().init();
test.config.mvcc = true;
test.config.lockTimeout = 120000;
test.config.memory = true;
test.config.multiThreaded = true;
test.test();
}
@Override
public void test() throws SQLException, InterruptedException {
testSelectForUpdateConcurrency();
}
private void testSelectForUpdateConcurrency()
throws SQLException, InterruptedException {
deleteDb(getTestName());
Connection conn = getConnection(getTestName() + URL);
conn.setAutoCommit(false);
String sql = "CREATE TABLE test ("
+ "entity_id INTEGER NOT NULL PRIMARY KEY, "
+ "lastUpdated INTEGER NOT NULL)";
Statement smtm = conn.createStatement();
smtm.executeUpdate(sql);
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO test (entity_id, lastUpdated) VALUES (?, ?)");
ps.setInt(1, 1);
ps.setInt(2, 100);
ps.executeUpdate();
conn.commit();
ArrayList<SelectForUpdate> threads = new ArrayList<SelectForUpdate>();
for (int i = 0; i < 100; i++) {
SelectForUpdate sfu = new SelectForUpdate();
threads.add(sfu);
sfu.start();
}
for (SelectForUpdate sfu : threads) {
sfu.join();
}
IOUtils.closeSilently(conn);
deleteDb(getTestName());
}
private class SelectForUpdate extends Thread {
@Override
public void run() {
final long start = System.currentTimeMillis();
boolean done = false;
Connection conn = null;
try {
conn = getConnection(getTestName() + URL);
conn.setAutoCommit(false);
while (!done) {
try {
PreparedStatement ps = conn.prepareStatement(
"SELECT * FROM test WHERE entity_id = ? FOR UPDATE");
ps.setString(1, "1");
ResultSet rs = ps.executeQuery();
assertTrue(rs.next());
assertTrue(rs.getInt(2) == 100);
conn.commit();
long now = System.currentTimeMillis();
if (now - start > 1000 * 60)
done = true;
} catch (JdbcSQLException e1) {
// skip DUPLICATE_KEY_1 to just focus on this bug.
if (e1.getErrorCode() != ErrorCode.DUPLICATE_KEY_1) {
throw e1;
}
}
}
} catch (SQLException e) {
TestBase.logError("error", e);
}
IOUtils.closeSilently(conn);
}
}
}
......@@ -32,13 +32,19 @@ public class TestTraceSystem extends TestBase {
testAdapter();
}
private static void testAdapter() {
private void testAdapter() {
TraceSystem ts = new TraceSystem(null);
ts.setName("test");
ts.setLevelFile(TraceSystem.ADAPTER);
ts.getTrace("test").debug("test");
ts.getTrace("test").info("test");
ts.getTrace("test").error(new Exception(), "test");
// The used SLF4J-nop logger has all log levels disabled,
// so this should be reflected in the trace system.
assertFalse(ts.isEnabled(TraceSystem.INFO));
assertFalse(ts.getTrace("test").isInfoEnabled());
ts.close();
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论