提交 0581dbd0 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Merge branch 'master' into generated_keys

......@@ -2636,7 +2636,7 @@ GEOMETRY
"
"Functions (Aggregate)","AVG","
AVG ( [ DISTINCT ] { numeric } )
AVG ( [ DISTINCT ] { numeric } ) [ FILTER ( WHERE expression ) ]
","
The average (mean) value.
If no rows are selected, the result is NULL.
......@@ -2647,7 +2647,7 @@ AVG(X)
"
"Functions (Aggregate)","BIT_AND","
BIT_AND(expression)
BIT_AND(expression) [ FILTER ( WHERE expression ) ]
","
The bitwise AND of all non-null values.
If no rows are selected, the result is NULL.
......@@ -2657,7 +2657,7 @@ BIT_AND(ID)
"
"Functions (Aggregate)","BIT_OR","
BIT_OR(expression)
BIT_OR(expression) [ FILTER ( WHERE expression ) ]
","
The bitwise OR of all non-null values.
If no rows are selected, the result is NULL.
......@@ -2667,7 +2667,7 @@ BIT_OR(ID)
"
"Functions (Aggregate)","BOOL_AND","
BOOL_AND(boolean)
BOOL_AND(boolean) [ FILTER ( WHERE expression ) ]
","
Returns true if all expressions are true.
If no rows are selected, the result is NULL.
......@@ -2677,7 +2677,7 @@ BOOL_AND(ID>10)
"
"Functions (Aggregate)","BOOL_OR","
BOOL_OR(boolean)
BOOL_OR(boolean) [ FILTER ( WHERE expression ) ]
","
Returns true if any expression is true.
If no rows are selected, the result is NULL.
......@@ -2687,7 +2687,7 @@ BOOL_OR(NAME LIKE 'W%')
"
"Functions (Aggregate)","COUNT","
COUNT( { * | { [ DISTINCT ] expression } } )
COUNT( { * | { [ DISTINCT ] expression } } ) [ FILTER ( WHERE expression ) ]
","
The count of all row, or of the non-null values.
This method returns a long.
......@@ -2700,7 +2700,7 @@ COUNT(*)
"Functions (Aggregate)","GROUP_CONCAT","
GROUP_CONCAT ( [ DISTINCT ] string
[ ORDER BY { expression [ ASC | DESC ] } [,...] ]
[ SEPARATOR expression ] )
[ SEPARATOR expression ] ) [ FILTER ( WHERE expression ) ]
","
Concatenates strings with a separator.
The default separator is a ',' (without space).
......@@ -2712,7 +2712,7 @@ GROUP_CONCAT(NAME ORDER BY ID SEPARATOR ', ')
"
"Functions (Aggregate)","MAX","
MAX(value)
MAX(value) [ FILTER ( WHERE expression ) ]
","
The highest value.
If no rows are selected, the result is NULL.
......@@ -2723,7 +2723,7 @@ MAX(NAME)
"
"Functions (Aggregate)","MIN","
MIN(value)
MIN(value) [ FILTER ( WHERE expression ) ]
","
The lowest value.
If no rows are selected, the result is NULL.
......@@ -2734,7 +2734,7 @@ MIN(NAME)
"
"Functions (Aggregate)","SUM","
SUM( [ DISTINCT ] { numeric } )
SUM( [ DISTINCT ] { numeric } ) [ FILTER ( WHERE expression ) ]
","
The sum of all values.
If no rows are selected, the result is NULL.
......@@ -2746,7 +2746,7 @@ SUM(X)
"
"Functions (Aggregate)","SELECTIVITY","
SELECTIVITY(value)
SELECTIVITY(value) [ FILTER ( WHERE expression ) ]
","
Estimates the selectivity (0-100) of a value.
The value is defined as (100 * distinctCount / rowCount).
......@@ -2758,7 +2758,7 @@ SELECT SELECTIVITY(FIRSTNAME), SELECTIVITY(NAME) FROM TEST WHERE ROWNUM()<20000
"
"Functions (Aggregate)","STDDEV_POP","
STDDEV_POP( [ DISTINCT ] numeric )
STDDEV_POP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
","
The population standard deviation.
This method returns a double.
......@@ -2769,7 +2769,7 @@ STDDEV_POP(X)
"
"Functions (Aggregate)","STDDEV_SAMP","
STDDEV_SAMP( [ DISTINCT ] numeric )
STDDEV_SAMP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
","
The sample standard deviation.
This method returns a double.
......@@ -2780,7 +2780,7 @@ STDDEV(X)
"
"Functions (Aggregate)","VAR_POP","
VAR_POP( [ DISTINCT ] numeric )
VAR_POP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
","
The population variance (square of the population standard deviation).
This method returns a double.
......@@ -2791,7 +2791,7 @@ VAR_POP(X)
"
"Functions (Aggregate)","VAR_SAMP","
VAR_SAMP( [ DISTINCT ] numeric )
VAR_SAMP( [ DISTINCT ] numeric ) [ FILTER ( WHERE expression ) ]
","
The sample variance (square of the sample standard deviation).
This method returns a double.
......@@ -2802,7 +2802,7 @@ VAR_SAMP(X)
"
"Functions (Aggregate)","MEDIAN","
MEDIAN( [ DISTINCT ] value )
MEDIAN( [ DISTINCT ] value ) [ FILTER ( WHERE expression ) ]
","
The value separating the higher half of a values from the lower half.
Returns the middle value or an interpolated value between two middle values if number of values is even.
......
......@@ -2627,7 +2627,7 @@ public class Parser {
throw getSyntaxError();
}
currentSelect.setGroupQuery();
Expression r;
Aggregate r;
if (aggregateType == AggregateType.COUNT) {
if (readIf("*")) {
r = new Aggregate(AggregateType.COUNT_ALL, null, currentSelect,
......@@ -2645,38 +2645,45 @@ public class Parser {
}
}
} else if (aggregateType == AggregateType.GROUP_CONCAT) {
Aggregate agg = null;
boolean distinct = readIf("DISTINCT");
if (equalsToken("GROUP_CONCAT", aggregateName)) {
agg = new Aggregate(AggregateType.GROUP_CONCAT,
r = new Aggregate(AggregateType.GROUP_CONCAT,
readExpression(), currentSelect, distinct);
if (readIf("ORDER")) {
read("BY");
agg.setGroupConcatOrder(parseSimpleOrderList());
r.setGroupConcatOrder(parseSimpleOrderList());
}
if (readIf("SEPARATOR")) {
agg.setGroupConcatSeparator(readExpression());
r.setGroupConcatSeparator(readExpression());
}
} else if (equalsToken("STRING_AGG", aggregateName)) {
// PostgreSQL compatibility: string_agg(expression, delimiter)
agg = new Aggregate(AggregateType.GROUP_CONCAT,
r = new Aggregate(AggregateType.GROUP_CONCAT,
readExpression(), currentSelect, distinct);
read(",");
agg.setGroupConcatSeparator(readExpression());
r.setGroupConcatSeparator(readExpression());
if (readIf("ORDER")) {
read("BY");
agg.setGroupConcatOrder(parseSimpleOrderList());
r.setGroupConcatOrder(parseSimpleOrderList());
}
} else {
r = null;
}
r = agg;
} else {
boolean distinct = readIf("DISTINCT");
r = new Aggregate(aggregateType, readExpression(), currentSelect,
distinct);
}
read(")");
if (r != null && readIf("FILTER")) {
read("(");
read("WHERE");
Expression condition = readExpression();
read(")");
r.setFilterCondition(condition);
}
return r;
}
......@@ -2727,8 +2734,17 @@ public class Parser {
params.add(readExpression());
} while (readIf(","));
read(")");
Expression filterCondition;
if (readIf("FILTER")) {
read("(");
read("WHERE");
filterCondition = readExpression();
read(")");
} else {
filterCondition = null;
}
Expression[] list = params.toArray(new Expression[0]);
JavaAggregate agg = new JavaAggregate(aggregate, list, currentSelect);
JavaAggregate agg = new JavaAggregate(aggregate, list, currentSelect, filterCondition);
currentSelect.setGroupQuery();
return agg;
}
......
......@@ -209,6 +209,8 @@ public class Database implements DataHandler {
private RowFactory rowFactory = RowFactory.DEFAULT;
public Database(ConnectionInfo ci, String cipher) {
META_LOCK_DEBUGGING.set(null);
META_LOCK_DEBUGGING_STACK.set(null);
String name = ci.getName();
this.dbSettings = ci.getDbSettings();
this.reconnectCheckDelayNs = TimeUnit.MILLISECONDS.toNanos(dbSettings.reconnectCheckDelay);
......@@ -1256,110 +1258,112 @@ public class Database implements DataHandler {
* hook
*/
void close(boolean fromShutdownHook) {
synchronized (this) {
if (closing) {
return;
}
throwLastBackgroundException();
if (fileLockMethod == FileLockMethod.SERIALIZED &&
!reconnectChangePending) {
// another connection may have written something - don't write
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
// ignore
}
traceSystem.close();
Engine.getInstance().close(databaseName);
return;
}
closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
try {
synchronized (this) {
if (closing) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (!userSessions.isEmpty()) {
// if a connection was opened, we can't close the database
throwLastBackgroundException();
if (fileLockMethod == FileLockMethod.SERIALIZED &&
!reconnectChangePending) {
// another connection may have written something - don't write
try {
closeOpenFilesAndUnlock(false);
} catch (DbException e) {
// ignore
}
traceSystem.close();
return;
}
closing = true;
stopServer();
if (!userSessions.isEmpty()) {
if (!fromShutdownHook) {
return;
}
trace.info("closing {0} from shutdown hook", databaseName);
closeAllSessionsException(null);
}
trace.info("closing {0}", databaseName);
if (eventListener != null) {
// allow the event listener to connect to the database
closing = false;
DatabaseEventListener e = eventListener;
// set it to null, to make sure it's called only once
eventListener = null;
e.closingDatabase();
if (!userSessions.isEmpty()) {
// if a connection was opened, we can't close the database
return;
}
closing = true;
}
}
}
removeOrphanedLobs();
try {
if (systemSession != null) {
if (powerOffCount != -1) {
for (Table table : getAllTablesAndViews(false)) {
if (table.isGlobalTemporary()) {
table.removeChildrenAndResources(systemSession);
} else {
table.close(systemSession);
removeOrphanedLobs();
try {
if (systemSession != null) {
if (powerOffCount != -1) {
for (Table table : getAllTablesAndViews(false)) {
if (table.isGlobalTemporary()) {
table.removeChildrenAndResources(systemSession);
} else {
table.close(systemSession);
}
}
for (SchemaObject obj : getAllSchemaObjects(
DbObject.SEQUENCE)) {
Sequence sequence = (Sequence) obj;
sequence.close();
}
}
for (SchemaObject obj : getAllSchemaObjects(
DbObject.SEQUENCE)) {
Sequence sequence = (Sequence) obj;
sequence.close();
DbObject.TRIGGER)) {
TriggerObject trigger = (TriggerObject) obj;
try {
trigger.close();
} catch (SQLException e) {
trace.error(e, "close");
}
}
}
for (SchemaObject obj : getAllSchemaObjects(
DbObject.TRIGGER)) {
TriggerObject trigger = (TriggerObject) obj;
try {
trigger.close();
} catch (SQLException e) {
trace.error(e, "close");
if (powerOffCount != -1) {
meta.close(systemSession);
systemSession.commit(true);
}
}
if (powerOffCount != -1) {
meta.close(systemSession);
systemSession.commit(true);
}
} catch (DbException e) {
trace.error(e, "close");
}
} catch (DbException e) {
trace.error(e, "close");
}
tempFileDeleter.deleteAll();
try {
closeOpenFilesAndUnlock(true);
} catch (DbException e) {
trace.error(e, "close");
}
trace.info("closed");
traceSystem.close();
if (closeOnExit != null) {
closeOnExit.reset();
tempFileDeleter.deleteAll();
try {
Runtime.getRuntime().removeShutdownHook(closeOnExit);
} catch (IllegalStateException e) {
// ignore
} catch (SecurityException e) {
// applets may not do that - ignore
closeOpenFilesAndUnlock(true);
} catch (DbException e) {
trace.error(e, "close");
}
closeOnExit = null;
}
Engine.getInstance().close(databaseName);
if (deleteFilesOnDisconnect && persistent) {
deleteFilesOnDisconnect = false;
try {
String directory = FileUtils.getParent(databaseName);
String name = FileUtils.getName(databaseName);
DeleteDbFiles.execute(directory, name, true);
} catch (Exception e) {
// ignore (the trace is closed already)
trace.info("closed");
traceSystem.close();
if (closeOnExit != null) {
closeOnExit.reset();
try {
Runtime.getRuntime().removeShutdownHook(closeOnExit);
} catch (IllegalStateException e) {
// ignore
} catch (SecurityException e) {
// applets may not do that - ignore
}
closeOnExit = null;
}
if (deleteFilesOnDisconnect && persistent) {
deleteFilesOnDisconnect = false;
try {
String directory = FileUtils.getParent(databaseName);
String name = FileUtils.getName(databaseName);
DeleteDbFiles.execute(directory, name, true);
} catch (Exception e) {
// ignore (the trace is closed already)
}
}
} finally {
Engine.getInstance().close(databaseName);
}
}
......
......@@ -69,7 +69,8 @@ class DatabaseCloser extends Thread {
trace.error(e, "could not close the database");
// if this was successful, we ignore the exception
// otherwise not
} catch (RuntimeException e2) {
} catch (Throwable e2) {
e.addSuppressed(e2);
throw e;
}
}
......
......@@ -6,6 +6,7 @@
package org.h2.engine;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.h2.api.ErrorCode;
import org.h2.command.CommandInterface;
......@@ -27,7 +28,7 @@ import org.h2.util.Utils;
public class Engine implements SessionFactory {
private static final Engine INSTANCE = new Engine();
private static final HashMap<String, Database> DATABASES = new HashMap<>();
private static final Map<String, Database> DATABASES = new HashMap<>();
private volatile long wrongPasswordDelay =
SysProperties.DELAY_WRONG_PASSWORD_MIN;
......@@ -50,30 +51,32 @@ public class Engine implements SessionFactory {
Database database;
ci.removeProperty("NO_UPGRADE", false);
boolean openNew = ci.getProperty("OPEN_NEW", false);
if (openNew || ci.isUnnamedInMemory()) {
database = null;
} else {
database = DATABASES.get(name);
}
User user = null;
boolean opened = false;
if (database == null) {
if (ifExists && !Database.exists(name)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, name);
}
database = new Database(ci, cipher);
opened = true;
if (database.getAllUsers().isEmpty()) {
// users is the last thing we add, so if no user is around,
// the database is new (or not initialized correctly)
user = new User(database, database.allocateObjectId(),
ci.getUserName(), false);
user.setAdmin(true);
user.setUserPasswordHash(ci.getUserPasswordHash());
database.setMasterUser(user);
User user = null;
synchronized (DATABASES) {
if (openNew || ci.isUnnamedInMemory()) {
database = null;
} else {
database = DATABASES.get(name);
}
if (!ci.isUnnamedInMemory()) {
DATABASES.put(name, database);
if (database == null) {
if (ifExists && !Database.exists(name)) {
throw DbException.get(ErrorCode.DATABASE_NOT_FOUND_1, name);
}
database = new Database(ci, cipher);
opened = true;
if (database.getAllUsers().isEmpty()) {
// users is the last thing we add, so if no user is around,
// the database is new (or not initialized correctly)
user = new User(database, database.allocateObjectId(),
ci.getUserName(), false);
user.setAdmin(true);
user.setUserPasswordHash(ci.getUserPasswordHash());
database.setMasterUser(user);
}
if (!ci.isUnnamedInMemory()) {
DATABASES.put(name, database);
}
}
}
if (opened) {
......@@ -272,7 +275,9 @@ public class Engine implements SessionFactory {
throw DbException.get(ErrorCode.FEATURE_NOT_SUPPORTED_1, e, "JMX");
}
}
DATABASES.remove(name);
synchronized (DATABASES) {
DATABASES.remove(name);
}
}
/**
......
......@@ -146,6 +146,8 @@ public class Aggregate extends Expression {
private int displaySize;
private int lastGroupRowId;
private Expression filterCondition;
/**
* Create a new aggregate object.
*
......@@ -228,6 +230,15 @@ public class Aggregate extends Expression {
this.groupConcatSeparator = separator;
}
/**
* Sets the FILTER condition.
*
* @param filterCondition condition
*/
public void setFilterCondition(Expression filterCondition) {
this.filterCondition = filterCondition;
}
private SortOrder initOrder(Session session) {
int size = groupConcatOrderList.size();
int[] index = new int[size];
......@@ -281,6 +292,11 @@ public class Aggregate extends Expression {
}
}
}
if (filterCondition != null) {
if (!filterCondition.getBooleanValue(session)) {
return;
}
}
data.add(session.getDatabase(), dataType, distinct, v);
}
......@@ -383,6 +399,9 @@ public class Aggregate extends Expression {
if (groupConcatSeparator != null) {
groupConcatSeparator.mapColumns(resolver, level);
}
if (filterCondition != null) {
filterCondition.mapColumns(resolver, level);
}
}
@Override
......@@ -403,6 +422,9 @@ public class Aggregate extends Expression {
if (groupConcatSeparator != null) {
groupConcatSeparator = groupConcatSeparator.optimize(session);
}
if (filterCondition != null) {
filterCondition = filterCondition.optimize(session);
}
switch (type) {
case GROUP_CONCAT:
dataType = Value.STRING;
......@@ -487,6 +509,9 @@ public class Aggregate extends Expression {
if (groupConcatSeparator != null) {
groupConcatSeparator.setEvaluatable(tableFilter, b);
}
if (filterCondition != null) {
filterCondition.setEvaluatable(tableFilter, b);
}
}
@Override
......@@ -523,7 +548,11 @@ public class Aggregate extends Expression {
if (groupConcatSeparator != null) {
buff.append(" SEPARATOR ").append(groupConcatSeparator.getSQL());
}
return buff.append(')').toString();
buff.append(')');
if (filterCondition != null) {
buff.append(" FILTER (WHERE ").append(filterCondition.getSQL()).append(')');
}
return buff.toString();
}
@Override
......@@ -586,9 +615,14 @@ public class Aggregate extends Expression {
throw DbException.throwInternalError("type=" + type);
}
if (distinct) {
return text + "(DISTINCT " + on.getSQL() + ")";
text += "(DISTINCT " + on.getSQL() + ')';
} else {
text += StringUtils.enclose(on.getSQL());
}
return text + StringUtils.enclose(on.getSQL());
if (filterCondition != null) {
text += " FILTER (WHERE " + filterCondition.getSQL() + ')';
}
return text;
}
private Index getMinMaxColumnIndex() {
......@@ -607,6 +641,9 @@ public class Aggregate extends Expression {
@Override
public boolean isEverything(ExpressionVisitor visitor) {
if (filterCondition != null && !filterCondition.isEverything(visitor)) {
return false;
}
if (visitor.getType() == ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL) {
switch (type) {
case COUNT:
......@@ -649,7 +686,14 @@ public class Aggregate extends Expression {
@Override
public int getCost() {
return (on == null) ? 1 : on.getCost() + 1;
int cost = 1;
if (on != null) {
cost += on.getCost();
}
if (filterCondition != null) {
cost += filterCondition.getCost();
}
return cost;
}
}
......@@ -31,15 +31,17 @@ public class JavaAggregate extends Expression {
private final Select select;
private final Expression[] args;
private int[] argTypes;
private Expression filterCondition;
private int dataType;
private Connection userConnection;
private int lastGroupRowId;
public JavaAggregate(UserAggregate userAggregate, Expression[] args,
Select select) {
Select select, Expression filterCondition) {
this.userAggregate = userAggregate;
this.args = args;
this.select = select;
this.filterCondition = filterCondition;
}
@Override
......@@ -48,6 +50,9 @@ public class JavaAggregate extends Expression {
for (Expression e : args) {
cost += e.getCost();
}
if (filterCondition != null) {
cost += filterCondition.getCost();
}
return cost;
}
......@@ -74,7 +79,11 @@ public class JavaAggregate extends Expression {
buff.appendExceptFirst(", ");
buff.append(e.getSQL());
}
return buff.append(')').toString();
buff.append(')');
if (filterCondition != null) {
buff.append(" FILTER (WHERE ").append(filterCondition.getSQL()).append(')');
}
return buff.toString();
}
@Override
......@@ -101,6 +110,9 @@ public class JavaAggregate extends Expression {
return false;
}
}
if (filterCondition != null && !filterCondition.isEverything(visitor)) {
return false;
}
return true;
}
......@@ -109,6 +121,9 @@ public class JavaAggregate extends Expression {
for (Expression arg : args) {
arg.mapColumns(resolver, level);
}
if (filterCondition != null) {
filterCondition.mapColumns(resolver, level);
}
}
@Override
......@@ -128,6 +143,9 @@ public class JavaAggregate extends Expression {
} catch (SQLException e) {
throw DbException.convert(e);
}
if (filterCondition != null) {
filterCondition = filterCondition.optimize(session);
}
return this;
}
......@@ -136,6 +154,9 @@ public class JavaAggregate extends Expression {
for (Expression e : args) {
e.setEvaluatable(tableFilter, b);
}
if (filterCondition != null) {
filterCondition.setEvaluatable(tableFilter, b);
}
}
private Aggregate getInstance() throws SQLException {
......@@ -180,6 +201,12 @@ public class JavaAggregate extends Expression {
}
lastGroupRowId = groupRowId;
if (filterCondition != null) {
if (!filterCondition.getBooleanValue(session)) {
return;
}
}
Aggregate agg = (Aggregate) group.get(this);
try {
if (agg == null) {
......
......@@ -415,7 +415,7 @@ public class JdbcConnection extends TraceObject
session = null;
}
}
} catch (Exception e) {
} catch (Throwable e) {
throw logAndConvert(e);
}
}
......
......@@ -264,7 +264,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements
} finally {
afterWriting();
}
} catch (Exception e) {
} catch (Throwable e) {
throw logAndConvert(e);
}
}
......
......@@ -273,7 +273,7 @@ public class DbException extends RuntimeException {
* @param e the root cause
* @return the SQL exception object
*/
public static SQLException toSQLException(Exception e) {
public static SQLException toSQLException(Throwable e) {
if (e instanceof SQLException) {
return (SQLException) e;
}
......
......@@ -351,17 +351,25 @@ public class TraceObject {
* @param ex the exception
* @return the SQL exception object
*/
protected SQLException logAndConvert(Exception ex) {
SQLException e = DbException.toSQLException(ex);
if (trace == null) {
DbException.traceThrowable(e);
} else {
int errorCode = e.getErrorCode();
if (errorCode >= 23000 && errorCode < 24000) {
trace.info(e, "exception");
protected SQLException logAndConvert(Throwable ex) {
SQLException e = null;
try {
e = DbException.toSQLException(ex);
if (trace == null) {
DbException.traceThrowable(e);
} else {
trace.error(e, "exception");
int errorCode = e.getErrorCode();
if (errorCode >= 23000 && errorCode < 24000) {
trace.info(e, "exception");
} else {
trace.error(e, "exception");
}
}
} catch(Throwable ignore) {
if (e == null) {
e = new SQLException("", "HY000", ex);
}
e.addSuppressed(ignore);
}
return e;
}
......
......@@ -380,9 +380,7 @@ public final class MVStore {
}
private void panic(IllegalStateException e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
handleException(e);
panicException = e;
closeImmediately();
throw e;
......@@ -862,10 +860,8 @@ public final class MVStore {
public void closeImmediately() {
try {
closeStore(false);
} catch (Exception e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
} catch (Throwable e) {
handleException(e);
}
}
......@@ -2468,11 +2464,9 @@ public final class MVStore {
if (hasUnsavedChanges()) {
try {
commitAndSave();
} catch (Exception e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
return;
}
} catch (Throwable e) {
handleException(e);
return;
}
}
if (autoCompactFillRate > 0) {
......@@ -2492,10 +2486,18 @@ public final class MVStore {
// in the bookkeeping?
compact(fillRate, autoCommitMemory);
autoCompactLastFileOpCount = fileStore.getWriteCount() + fileStore.getReadCount();
} catch (Exception e) {
if (backgroundExceptionHandler != null) {
backgroundExceptionHandler.uncaughtException(null, e);
}
} catch (Throwable e) {
handleException(e);
}
}
}
private void handleException(Throwable ex) {
if (backgroundExceptionHandler != null) {
try {
backgroundExceptionHandler.uncaughtException(null, ex);
} catch(Throwable ignore) {
ex.addSuppressed(ignore);
}
}
}
......
......@@ -79,6 +79,8 @@ public abstract class TestBase {
private final LinkedList<byte[]> memory = new LinkedList<>();
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
/**
* Get the test directory for this test.
*
......@@ -545,7 +547,6 @@ public abstract class TestBase {
* @param s the message
*/
static synchronized void printlnWithTime(long millis, String s) {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
s = dateFormat.format(new java.util.Date()) + " " +
formatTime(millis) + " " + s;
System.out.println(s);
......@@ -1470,6 +1471,12 @@ public abstract class TestBase {
*/
protected void freeMemory() {
memory.clear();
for (int i = 0; i < 5; i++) {
System.gc();
try {
Thread.sleep(20);
} catch (InterruptedException ignore) {/**/}
}
}
/**
......
......@@ -747,6 +747,10 @@ public class TestFunctions extends TestBase implements AggregateFunction {
"SELECT SIMPLE_MEDIAN(X) FROM SYSTEM_RANGE(1, 9)");
rs.next();
assertEquals("5", rs.getString(1));
rs = stat.executeQuery(
"SELECT SIMPLE_MEDIAN(X) FILTER (WHERE X > 2) FROM SYSTEM_RANGE(1, 9)");
rs.next();
assertEquals("6", rs.getString(1));
conn.close();
if (config.memory) {
......
......@@ -13,7 +13,9 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.mvstore.MVStore;
import org.h2.store.fs.FilePath;
import org.h2.store.fs.FilePathMem;
......@@ -37,10 +39,6 @@ public class TestOutOfMemory extends TestBase {
@Override
public void test() throws SQLException, InterruptedException {
if (config.travis) {
// fails regularly under Travis, not sure why
return;
}
if (config.vmlens) {
// running out of memory will cause the vmlens agent to stop working
return;
......@@ -60,7 +58,16 @@ public class TestOutOfMemory extends TestBase {
private void testMVStoreUsingInMemoryFileSystem() {
FilePath.register(new FilePathMem());
String fileName = "memFS:" + getTestName();
MVStore store = MVStore.open(fileName);
final AtomicReference<Throwable> exRef = new AtomicReference<>();
MVStore store = new MVStore.Builder()
.fileName(fileName)
.backgroundExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
exRef.compareAndSet(null, e);
}
})
.open();
try {
Map<Integer, byte[]> map = store.openMap("test");
Random r = new Random(1);
......@@ -70,10 +77,11 @@ public class TestOutOfMemory extends TestBase {
r.nextBytes(data);
map.put(i, data);
}
Throwable throwable = exRef.get();
if(throwable instanceof OutOfMemoryError) throw (OutOfMemoryError)throwable;
if(throwable instanceof IllegalStateException) throw (IllegalStateException)throwable;
fail();
} catch (OutOfMemoryError e) {
// expected
} catch (IllegalStateException e) {
} catch (OutOfMemoryError | IllegalStateException e) {
// expected
}
try {
......@@ -83,7 +91,7 @@ public class TestOutOfMemory extends TestBase {
}
store.closeImmediately();
store = MVStore.open(fileName);
map = store.openMap("test");
store.openMap("test");
store.close();
} finally {
// just in case, otherwise if this test suffers a spurious failure,
......@@ -95,47 +103,55 @@ public class TestOutOfMemory extends TestBase {
private void testDatabaseUsingInMemoryFileSystem() throws SQLException, InterruptedException {
String filename = "memFS:" + getTestName();
String url = "jdbc:h2:" + filename;
Connection conn = DriverManager.getConnection(url);
Statement stat = conn.createStatement();
try {
stat.execute("create table test(id int, name varchar) as " +
"select x, space(10000000) from system_range(1, 1000)");
fail();
} catch (SQLException e) {
int err = e.getErrorCode();
assertTrue(e.getMessage(), err == ErrorCode.GENERAL_ERROR_1
|| err == ErrorCode.OUT_OF_MEMORY);
}
String url = "jdbc:h2:" + filename + "/test";
try {
Connection conn = DriverManager.getConnection(url);
Statement stat = conn.createStatement();
try {
stat.execute("create table test(id int, name varchar) as " +
"select x, space(10000000+x) from system_range(1, 1000)");
fail();
} catch (SQLException e) {
assertTrue("Unexpected error code: " + e.getErrorCode(),
ErrorCode.OUT_OF_MEMORY == e.getErrorCode() ||
ErrorCode.FILE_CORRUPTED_1 == e.getErrorCode() ||
ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() ||
ErrorCode.GENERAL_ERROR_1 == e.getErrorCode());
}
recoverAfterOOM();
try {
conn.close();
fail();
} catch (SQLException e) {
assertTrue("Unexpected error code: " + e.getErrorCode(),
ErrorCode.OUT_OF_MEMORY == e.getErrorCode() ||
ErrorCode.FILE_CORRUPTED_1 == e.getErrorCode() ||
ErrorCode.DATABASE_IS_CLOSED == e.getErrorCode() ||
ErrorCode.GENERAL_ERROR_1 == e.getErrorCode());
}
recoverAfterOOM();
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("SELECT 1");
conn.close();
fail();
} catch (SQLException e) {
int err = e.getErrorCode();
assertTrue(e.getMessage(), err == ErrorCode.GENERAL_ERROR_1
|| err == ErrorCode.OUT_OF_MEMORY
|| err == ErrorCode.DATABASE_IS_CLOSED);
} finally {
// release the static data this test generates
FileUtils.deleteRecursive(filename, true);
}
}
private static void recoverAfterOOM() throws InterruptedException {
for (int i = 0; i < 5; i++) {
System.gc();
Thread.sleep(20);
}
conn = DriverManager.getConnection(url);
stat = conn.createStatement();
stat.execute("select 1");
conn.close();
// release the static data this test generates
FileUtils.delete(filename);
}
private void testUpdateWhenNearlyOutOfMemory() throws SQLException, InterruptedException {
if (config.memory || config.mvcc || config.mvStore) {
if (config.memory) {
return;
}
for (int i = 0; i < 5; i++) {
System.gc();
Thread.sleep(20);
}
recoverAfterOOM();
deleteDb("outOfMemory");
Connection conn = getConnection("outOfMemory;MAX_OPERATION_MEMORY=1000000");
Statement stat = conn.createStatement();
......@@ -148,8 +164,27 @@ public class TestOutOfMemory extends TestBase {
stat.execute("checkpoint");
eatMemory(80);
try {
assertThrows(ErrorCode.OUT_OF_MEMORY, prep).execute();
assertThrows(ErrorCode.DATABASE_IS_CLOSED, conn).close();
try {
prep.execute();
fail();
} catch(DbException ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
} catch (SQLException ex) {
freeMemory();
assertTrue(ErrorCode.OUT_OF_MEMORY == ex.getErrorCode() || ErrorCode.GENERAL_ERROR_1 == ex.getErrorCode());
}
recoverAfterOOM();
try {
conn.close();
fail();
} catch(DbException ex) {
freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode());
} catch (SQLException ex) {
freeMemory();
assertEquals(ErrorCode.DATABASE_IS_CLOSED, ex.getErrorCode());
}
freeMemory();
conn = null;
conn = getConnection("outOfMemory");
......@@ -160,7 +195,7 @@ public class TestOutOfMemory extends TestBase {
} catch (OutOfMemoryError e) {
freeMemory();
// out of memory not detected
throw (Error) new AssertionError("Out of memory not detected").initCause(e);
throw new AssertionError("Out of memory not detected", e);
} finally {
freeMemory();
if (conn != null) {
......
......@@ -2,3 +2,28 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v int);
> ok
insert into test values (10), (20), (30), (40), (50), (60), (70), (80), (90), (100), (110), (120);
> update count: 12
select avg(v), avg(v) filter (where v >= 40) from test where v <= 100;
> AVG(V) AVG(V) FILTER (WHERE (V >= 40))
> ------ -------------------------------
> 55 70
> rows: 1
create index test_idx on test(v);
select avg(v), avg(v) filter (where v >= 40) from test where v <= 100;
> AVG(V) AVG(V) FILTER (WHERE (V >= 40))
> ------ -------------------------------
> 55 70
> rows: 1
drop table test;
> ok
......@@ -2,3 +2,31 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v bigint);
> ok
insert into test values
(0xfffffffffff0), (0xffffffffff0f), (0xfffffffff0ff), (0xffffffff0fff),
(0xfffffff0ffff), (0xffffff0fffff), (0xfffff0ffffff), (0xffff0fffffff),
(0xfff0ffffffff), (0xff0fffffffff), (0xf0ffffffffff), (0x0fffffffffff);
> update count: 12
select bit_and(v), bit_and(v) filter (where v <= 0xffffffff0fff) from test where v >= 0xff0fffffffff;
> BIT_AND(V) BIT_AND(V) FILTER (WHERE (V <= 281474976649215))
> --------------- ------------------------------------------------
> 280375465082880 280375465086975
> rows: 1
create index test_idx on test(v);
select bit_and(v), bit_and(v) filter (where v <= 0xffffffff0fff) from test where v >= 0xff0fffffffff;
> BIT_AND(V) BIT_AND(V) FILTER (WHERE (V <= 281474976649215))
> --------------- ------------------------------------------------
> 280375465082880 280375465086975
> rows: 1
drop table test;
> ok
......@@ -2,3 +2,30 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
-- with filter condition
create table test(v bigint);
> ok
insert into test values (1), (2), (4), (8), (16), (32), (64), (128), (256), (512), (1024), (2048);
> update count: 12
select bit_or(v), bit_or(v) filter (where v >= 8) from test where v <= 512;
> BIT_OR(V) BIT_OR(V) FILTER (WHERE (V >= 8))
> --------- ---------------------------------
> 1023 1016
> rows: 1
create index test_idx on test(v);
select bit_or(v), bit_or(v) filter (where v >= 8) from test where v <= 512;
> BIT_OR(V) BIT_OR(V) FILTER (WHERE (V >= 8))
> --------- ---------------------------------
> 1023 1016
> rows: 1
drop table test;
> ok
......@@ -2,3 +2,34 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v int);
> ok
insert into test values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12);
> update count: 12
select count(v), count(v) filter (where v >= 4) from test where v <= 10;
> COUNT(V) COUNT(V) FILTER (WHERE (V >= 4))
> -------- --------------------------------
> 10 7
> rows: 1
create index test_idx on test(v);
select count(v), count(v) filter (where v >= 4) from test where v <= 10;
> COUNT(V) COUNT(V) FILTER (WHERE (V >= 4))
> -------- --------------------------------
> 10 7
> rows: 1
select count(v), count(v) filter (where v >= 4) from test;
> COUNT(V) COUNT(V) FILTER (WHERE (V >= 4))
> -------- --------------------------------
> 12 9
> rows: 1
drop table test;
> ok
......@@ -2,3 +2,41 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v varchar);
> ok
insert into test values ('1'), ('2'), ('3'), ('4'), ('5'), ('6'), ('7'), ('8'), ('9');
> update count: 9
select group_concat(v order by v asc separator '-'),
group_concat(v order by v desc separator '-') filter (where v >= '4')
from test where v >= '2';
> GROUP_CONCAT(V ORDER BY V SEPARATOR '-') GROUP_CONCAT(V ORDER BY V DESC SEPARATOR '-') FILTER (WHERE (V >= '4'))
> ---------------------------------------- -----------------------------------------------------------------------
> 2-3-4-5-6-7-8-9 9-8-7-6-5-4
> rows (ordered): 1
create index test_idx on test(v);
select group_concat(v order by v asc separator '-'),
group_concat(v order by v desc separator '-') filter (where v >= '4')
from test where v >= '2';
> GROUP_CONCAT(V ORDER BY V SEPARATOR '-') GROUP_CONCAT(V ORDER BY V DESC SEPARATOR '-') FILTER (WHERE (V >= '4'))
> ---------------------------------------- -----------------------------------------------------------------------
> 2-3-4-5-6-7-8-9 9-8-7-6-5-4
> rows (ordered): 1
select group_concat(v order by v asc separator '-'),
group_concat(v order by v desc separator '-') filter (where v >= '4')
from test;
> GROUP_CONCAT(V ORDER BY V SEPARATOR '-') GROUP_CONCAT(V ORDER BY V DESC SEPARATOR '-') FILTER (WHERE (V >= '4'))
> ---------------------------------------- -----------------------------------------------------------------------
> 1-2-3-4-5-6-7-8-9 9-8-7-6-5-4
> rows (ordered): 1
drop table test;
> ok
......@@ -2,3 +2,34 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v int);
> ok
insert into test values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12);
> update count: 12
select max(v), max(v) filter (where v <= 8) from test where v <= 10;
> MAX(V) MAX(V) FILTER (WHERE (V <= 8))
> ------ ------------------------------
> 10 8
> rows: 1
create index test_idx on test(v);
select max(v), max(v) filter (where v <= 8) from test where v <= 10;
> MAX(V) MAX(V) FILTER (WHERE (V <= 8))
> ------ ------------------------------
> 10 8
> rows: 1
select max(v), max(v) filter (where v <= 8) from test;
> MAX(V) MAX(V) FILTER (WHERE (V <= 8))
> ------ ------------------------------
> 12 8
> rows: 1
drop table test;
> ok
......@@ -734,3 +734,72 @@ select median(v) from test;
drop table test;
> ok
-- with filter condition
create table test(v int);
> ok
insert into test values (10), (20), (30), (40), (50), (60), (70), (80), (90), (100), (110), (120);
> update count: 12
select median(v), median(v) filter (where v >= 40) from test where v <= 100;
> MEDIAN(V) MEDIAN(V) FILTER (WHERE (V >= 40))
> --------- ----------------------------------
> 55 70
> rows: 1
create index test_idx on test(v);
select median(v), median(v) filter (where v >= 40) from test where v <= 100;
> MEDIAN(V) MEDIAN(V) FILTER (WHERE (V >= 40))
> --------- ----------------------------------
> 55 70
> rows: 1
select median(v), median(v) filter (where v >= 40) from test;
> MEDIAN(V) MEDIAN(V) FILTER (WHERE (V >= 40))
> --------- ----------------------------------
> 65 80
> rows: 1
drop table test;
> ok
-- with filter and group by
create table test(dept varchar, amount int);
> ok
insert into test values
('First', 10), ('First', 10), ('First', 20), ('First', 30), ('First', 30),
('Second', 5), ('Second', 4), ('Second', 20), ('Second', 22), ('Second', 300),
('Third', 3), ('Third', 100), ('Third', 150), ('Third', 170), ('Third', 400);
select dept, median(amount) from test group by dept order by dept;
> DEPT MEDIAN(AMOUNT)
> ------ --------------
> First 20
> Second 20
> Third 150
> rows (ordered): 3
select dept, median(amount) filter (where amount >= 20) from test group by dept order by dept;
> DEPT MEDIAN(AMOUNT) FILTER (WHERE (AMOUNT >= 20))
> ------ --------------------------------------------
> First 30
> Second 22
> Third 160
> rows (ordered): 3
select dept, median(amount) filter (where amount >= 20) from test
where (amount < 200) group by dept order by dept;
> DEPT MEDIAN(AMOUNT) FILTER (WHERE (AMOUNT >= 20))
> ------ --------------------------------------------
> First 30
> Second 21
> Third 150
> rows (ordered): 3
drop table test;
> ok
......@@ -2,3 +2,34 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v int);
> ok
insert into test values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12);
> update count: 12
select min(v), min(v) filter (where v >= 4) from test where v >= 2;
> MIN(V) MIN(V) FILTER (WHERE (V >= 4))
> ------ ------------------------------
> 2 4
> rows: 1
create index test_idx on test(v);
select min(v), min(v) filter (where v >= 4) from test where v >= 2;
> MIN(V) MIN(V) FILTER (WHERE (V >= 4))
> ------ ------------------------------
> 2 4
> rows: 1
select min(v), min(v) filter (where v >= 4) from test;
> MIN(V) MIN(V) FILTER (WHERE (V >= 4))
> ------ ------------------------------
> 1 4
> rows: 1
drop table test;
> ok
......@@ -2,3 +2,28 @@
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- with filter condition
create table test(v int);
> ok
insert into test values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12);
> update count: 12
select sum(v), sum(v) filter (where v >= 4) from test where v <= 10;
> SUM(V) SUM(V) FILTER (WHERE (V >= 4))
> ------ ------------------------------
> 55 49
> rows: 1
create index test_idx on test(v);
select sum(v), sum(v) filter (where v >= 4) from test where v <= 10;
> SUM(V) SUM(V) FILTER (WHERE (V >= 4))
> ------ ------------------------------
> 55 49
> rows: 1
drop table test;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论