提交 35b9c26d authored 作者: Noel Grandin's avatar Noel Grandin

Merge remote-tracking branch 'upstream/master'

......@@ -531,7 +531,7 @@ CREATE AGGREGATE [ IF NOT EXISTS ] newAggregateName FOR className
","
Creates a new user-defined aggregate function. The method name must be the full
qualified class name. The class must implement the interface
""org.h2.api.AggregateFunction"".
""org.h2.api.Aggregate"" or ""org.h2.api.AggregateFunction"".
Admin rights are required to execute this command.
This command commits an open transaction in this connection.
......@@ -2152,7 +2152,7 @@ ID<>2
"
"Other Grammar","Condition Right Hand Side","
compare { { { ALL | ANY | SOME } ( select ) } | operand }
compare { { ALL ( select ) } | operand }
| IS [ NOT ] NULL
| IS [ NOT ] [ DISTINCT FROM ] operand
| BETWEEN operand AND operand
......@@ -3444,26 +3444,26 @@ Aggregates are only allowed in select statements.
BIT_OR(ID)
"
"Functions (Aggregate)","BOOL_AND","
BOOL_AND(boolean)
"Functions (Aggregate)","EVERY","
{EVERY|BOOL_AND}(boolean)
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
","
Returns true if all expressions are true.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
BOOL_AND(ID>10)
EVERY(ID>10)
"
"Functions (Aggregate)","BOOL_OR","
BOOL_OR(boolean)
"Functions (Aggregate)","ANY","
{ANY|SOME|BOOL_OR}(boolean)
[FILTER (WHERE expression)] [OVER windowNameOrSpecification]
","
Returns true if any expression is true.
If no rows are selected, the result is NULL.
Aggregates are only allowed in select statements.
","
BOOL_OR(NAME LIKE 'W%')
ANY(NAME LIKE 'W%')
"
"Functions (Aggregate)","COUNT","
......@@ -5215,6 +5215,18 @@ The method returns a value with the same data type as the first parameter.
CALL TRUNCATE_VALUE(X, 10, TRUE);
"
"Functions (System)","UNNEST","
UNNEST(array, [,...]) [WITH ORDINALITY]
","
Returns the result set.
Number of columns is equal to number of arguments,
plus one additional column with row number if WITH ORDINALITY is specified.
Number of rows is equal to length of longest specified array.
If multiple arguments are specified and they have different length, cells with missing values will contain null values.
","
SELECT * FROM UNNEST(ARRAY['a', 'b', 'c']);
"
"Functions (System)","USER","
{ USER | CURRENT_USER } ()
","
......
......@@ -21,6 +21,24 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Issue #1565: SOME / ANY conflict
</li>
<li>PR #1564: Refactor Expression implementations
</li>
<li>Issue #1561: Incorrect documentation and strange fallback value of SysProperties.FILE_ENCODING
</li>
<li>Issue #1566: MVStore implements Closeable/AutoCloseable
</li>
<li>Issue #1550: OutOfMemoryError during "shutdown defrag"
</li>
<li>Issue #1440: OOM when executing "shutdown compact" in server mode
</li>
<li>Issue #1561: Incorrect documentation and strange fallback value of SysProperties.FILE_ENCODING
</li>
<li>PR #1557: increase lock timeout to TestConcurrentUpdate due to Travis failures
</li>
<li>Issue #1554: REGEXP_REPLACE - accept 'g' flag in PostgreSQL compatibility mode
</li>
<li>Issue #950: Comparison between databases in README.md and in features.html
</li>
<li>Issue #1549: [RFE] Implement locking modes (select for update)
......
......@@ -180,7 +180,8 @@ Areas considered experimental are:
<li>The PostgreSQL server
</li><li>Clustering (there are cases were transaction isolation can be broken
due to timing issues, for example one session overtaking another session).
</li><li>Multi-threading within the engine using <code>SET MULTI_THREADED=1</code>.
</li><li>Multi-threading within the old PageStore engine using <code>SET MULTI_THREADED=1</code>.
Default MVStore engine is multi-threaded by default.
</li><li>Compatibility modes for other databases (only some features are implemented).
</li><li>The soft reference cache (<code>CACHE_TYPE=SOFT_LRU</code>). It might not improve performance,
and out of memory issues have been reported.
......
......@@ -950,10 +950,16 @@ or the SQL statement <code>SET MODE PostgreSQL</code>.
</li><li>The system columns <code>CTID</code> and
<code>OID</code> are supported.
</li><li>LOG(x) is base 10 in this mode.
</li><li>REGEXP_REPLACE() uses \ for back-references.
</li><li>REGEXP_REPLACE():
<ul>
<li>uses \ for back-references;</li>
<li>does not throw an exception when the <code>flagsString</code> parameter contains a 'g';</li>
<li>replaces only the first matched substring in the absence of the 'g' flag in the <code>flagsString</code> parameter.</li>
</ul>
</li><li>Fixed-width strings are padded with spaces.
</li><li>MONEY data type is treated like NUMERIC(19, 2) data type.
</li><li>Datetime value functions return the same value within a transaction.
</li><li>ANY and SOME after comparison operators are parsed as array comparison operators.
</li></ul>
<h3>Ignite Compatibility Mode</h3>
......
......@@ -490,7 +490,7 @@ Instead, use a prepared statement with arrays as in the following example:
</p>
<pre>
PreparedStatement prep = conn.prepareStatement(
"SELECT * FROM TEST WHERE ID = ANY(?)");
"SELECT * FROM TEST WHERE ID IN(UNNEST(?))");
prep.setObject(1, new Object[] { "1", "2" });
ResultSet rs = prep.executeQuery();
</pre>
......
......@@ -149,25 +149,13 @@ import org.h2.engine.UserAggregate;
import org.h2.expression.Alias;
import org.h2.expression.BinaryOperation;
import org.h2.expression.BinaryOperation.OpType;
import org.h2.expression.CompareLike;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.ConditionExists;
import org.h2.expression.ConditionIn;
import org.h2.expression.ConditionInParameter;
import org.h2.expression.ConditionInSelect;
import org.h2.expression.ConditionNot;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionList;
import org.h2.expression.Function;
import org.h2.expression.FunctionCall;
import org.h2.expression.JavaFunction;
import org.h2.expression.Parameter;
import org.h2.expression.Rownum;
import org.h2.expression.SequenceValue;
import org.h2.expression.Subquery;
import org.h2.expression.TableFunction;
import org.h2.expression.UnaryOperation;
import org.h2.expression.ValueExpression;
import org.h2.expression.Variable;
......@@ -175,16 +163,29 @@ import org.h2.expression.Wildcard;
import org.h2.expression.aggregate.AbstractAggregate;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Aggregate.AggregateType;
import org.h2.expression.aggregate.DataAnalysisOperation;
import org.h2.expression.aggregate.JavaAggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.aggregate.WindowFrame;
import org.h2.expression.aggregate.WindowFrameBound;
import org.h2.expression.aggregate.WindowFrameBoundType;
import org.h2.expression.aggregate.WindowFrameExclusion;
import org.h2.expression.aggregate.WindowFrameUnits;
import org.h2.expression.aggregate.WindowFunction;
import org.h2.expression.aggregate.WindowFunctionType;
import org.h2.expression.analysis.DataAnalysisOperation;
import org.h2.expression.analysis.Window;
import org.h2.expression.analysis.WindowFrame;
import org.h2.expression.analysis.WindowFrameBound;
import org.h2.expression.analysis.WindowFrameBoundType;
import org.h2.expression.analysis.WindowFrameExclusion;
import org.h2.expression.analysis.WindowFrameUnits;
import org.h2.expression.analysis.WindowFunction;
import org.h2.expression.analysis.WindowFunctionType;
import org.h2.expression.condition.CompareLike;
import org.h2.expression.condition.Comparison;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.expression.condition.ConditionExists;
import org.h2.expression.condition.ConditionIn;
import org.h2.expression.condition.ConditionInParameter;
import org.h2.expression.condition.ConditionInSelect;
import org.h2.expression.condition.ConditionNot;
import org.h2.expression.function.DateTimeFunctions;
import org.h2.expression.function.Function;
import org.h2.expression.function.FunctionCall;
import org.h2.expression.function.JavaFunction;
import org.h2.expression.function.TableFunction;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.result.SortOrder;
......@@ -199,7 +200,6 @@ import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.table.TableView;
import org.h2.util.DateTimeFunctions;
import org.h2.util.IntervalUtils;
import org.h2.util.MathUtils;
import org.h2.util.ParserUtil;
......@@ -2857,7 +2857,7 @@ public class Parser {
r = new ConditionInSelect(database, r, query, true,
compareType);
read(CLOSE_PAREN);
} else if (readIf("ANY") || readIf("SOME")) {
} else if (database.getMode().anyAndSomeAreComparisons && (readIf("ANY") || readIf("SOME"))) {
read(OPEN_PAREN);
if (currentTokenType == PARAMETER && compareType == 0) {
Parameter p = readParameter();
......@@ -3406,6 +3406,23 @@ public class Parser {
tf.setColumns(columns);
break;
}
case Function.UNNEST: {
ArrayList<Column> columns = Utils.newSmallArrayList();
if (!readIf(CLOSE_PAREN)) {
int i = 0;
do {
function.setParameter(i++, readExpression());
columns.add(new Column("C" + i, Value.NULL));
} while (readIfMore(true));
}
if (readIf(WITH)) {
read("ORDINALITY");
columns.add(new Column("NORD", Value.INT));
}
TableFunction tf = (TableFunction) function;
tf.setColumns(columns);
break;
}
default:
if (!readIf(CLOSE_PAREN)) {
int i = 0;
......
......@@ -18,12 +18,12 @@ import org.h2.engine.Mode;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.UndoLogRecord;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.expression.condition.Comparison;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.index.Index;
import org.h2.index.PageDataIndex;
import org.h2.message.DbException;
......@@ -305,15 +305,7 @@ public class Insert extends Prepared implements ResultTarget {
buff.append(",\n");
}
buff.append('(');
buff.resetCount();
for (Expression e : expr) {
buff.appendExceptFirst(", ");
if (e == null) {
buff.append("DEFAULT");
} else {
e.getSQL(buff.builder());
}
}
Expression.writeExpressions(buff.builder(), expr);
buff.append(')');
}
} else {
......
......@@ -242,15 +242,7 @@ public class Merge extends Prepared {
buff.append(", ");
}
buff.append('(');
buff.resetCount();
for (Expression e : expr) {
buff.appendExceptFirst(", ");
if (e == null) {
buff.append("DEFAULT");
} else {
e.getSQL(buff.builder());
}
}
Expression.writeExpressions(buff.builder(), expr);
buff.append(')');
}
} else {
......
......@@ -16,9 +16,9 @@ import org.h2.command.Prepared;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.User;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
......
......@@ -19,9 +19,9 @@ import org.h2.expression.Alias;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Function;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.expression.function.FunctionCall;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.ResultTarget;
......@@ -555,8 +555,8 @@ public abstract class Query extends Prepared {
}
}
int count = expr.getSubexpressionCount();
if (expr instanceof Function) {
if (!((Function) expr).isDeterministic()) {
if (expr instanceof FunctionCall) {
if (!((FunctionCall) expr).isDeterministic()) {
return false;
}
} else if (count <= 0) {
......
......@@ -222,15 +222,7 @@ public class Replace extends Prepared {
buff.append(", ");
}
buff.append('(');
buff.resetCount();
for (Expression e : expr) {
buff.appendExceptFirst(", ");
if (e == null) {
buff.append("DEFAULT");
} else {
e.getSQL(buff.builder());
}
}
Expression.writeExpressions(buff.builder(), expr);
buff.append(')');
}
} else {
......
......@@ -18,15 +18,15 @@ import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.SysProperties;
import org.h2.expression.Alias;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.Wildcard;
import org.h2.expression.aggregate.Aggregate;
import org.h2.expression.aggregate.Window;
import org.h2.expression.analysis.DataAnalysisOperation;
import org.h2.expression.analysis.Window;
import org.h2.expression.condition.Comparison;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.index.IndexType;
......@@ -389,7 +389,7 @@ public class Select extends Query {
private void queryWindow(int columnCount, LocalResult result, long offset, boolean quickOffset) {
initGroupData(columnCount);
try {
gatherGroup(columnCount, Aggregate.STAGE_WINDOW);
gatherGroup(columnCount, DataAnalysisOperation.STAGE_WINDOW);
processGroupResult(columnCount, result, offset, quickOffset);
} finally {
groupData.reset();
......@@ -399,12 +399,12 @@ public class Select extends Query {
private void queryGroupWindow(int columnCount, LocalResult result, long offset, boolean quickOffset) {
initGroupData(columnCount);
try {
gatherGroup(columnCount, Aggregate.STAGE_GROUP);
gatherGroup(columnCount, DataAnalysisOperation.STAGE_GROUP);
try {
isGroupWindowStage2 = true;
while (groupData.next() != null) {
if (havingIndex < 0 || expressions.get(havingIndex).getBooleanValue(session)) {
updateAgg(columnCount, Aggregate.STAGE_WINDOW);
updateAgg(columnCount, DataAnalysisOperation.STAGE_WINDOW);
} else {
groupData.remove();
}
......@@ -422,7 +422,7 @@ public class Select extends Query {
private void queryGroup(int columnCount, LocalResult result, long offset, boolean quickOffset) {
initGroupData(columnCount);
try {
gatherGroup(columnCount, Aggregate.STAGE_GROUP);
gatherGroup(columnCount, DataAnalysisOperation.STAGE_GROUP);
processGroupResult(columnCount, result, offset, quickOffset);
} finally {
groupData.reset();
......@@ -433,7 +433,7 @@ public class Select extends Query {
if (groupData == null) {
groupData = SelectGroups.getInstance(session, expressions, isGroupQuery, groupIndex);
} else {
updateAgg(columnCount, Aggregate.STAGE_RESET);
updateAgg(columnCount, DataAnalysisOperation.STAGE_RESET);
}
groupData.reset();
}
......@@ -1742,7 +1742,7 @@ public class Select extends Query {
groupData = SelectGroups.getInstance(getSession(), Select.this.expressions, isGroupQuery, groupIndex);
} else {
// TODO is this branch possible?
updateAgg(columnCount, Aggregate.STAGE_RESET);
updateAgg(columnCount, DataAnalysisOperation.STAGE_RESET);
groupData.resetLazy();
}
}
......@@ -1778,7 +1778,7 @@ public class Select extends Query {
groupData.nextLazyGroup();
}
groupData.nextLazyRow();
updateAgg(columnCount, Aggregate.STAGE_GROUP);
updateAgg(columnCount, DataAnalysisOperation.STAGE_GROUP);
if (row != null) {
return row;
}
......
......@@ -14,8 +14,8 @@ import java.util.Map.Entry;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.aggregate.DataAnalysisOperation;
import org.h2.expression.aggregate.PartitionData;
import org.h2.expression.analysis.DataAnalysisOperation;
import org.h2.expression.analysis.PartitionData;
import org.h2.util.ValueHashMap;
import org.h2.value.Value;
import org.h2.value.ValueArray;
......
......@@ -209,6 +209,12 @@ public class Mode {
*/
public boolean dateTimeValueWithinTransaction;
/**
* If {@code true}, ANY and SOME after comparison operators are parsed as
* array comparison operators.
*/
public boolean anyAndSomeAreComparisons;
/**
* An optional Set of hidden/disallowed column types.
* Certain DBMSs don't support all column types provided by H2, such as
......@@ -354,6 +360,7 @@ public class Mode {
dt.name = "MONEY";
mode.typeByNameMap.put("MONEY", dt);
mode.dateTimeValueWithinTransaction = true;
mode.anyAndSomeAreComparisons = true;
add(mode);
mode = new Mode(ModeEnum.Ignite);
......
......@@ -5,6 +5,8 @@
*/
package org.h2.engine;
import java.io.File;
import org.h2.util.MathUtils;
import org.h2.util.Utils;
......@@ -42,26 +44,16 @@ public class SysProperties {
public static final String H2_BROWSER = "h2.browser";
/**
* System property <code>file.encoding</code> (default: Cp1252).<br />
* It is usually set by the system and is the default encoding used for the
* RunScript and CSV tool.
*/
public static final String FILE_ENCODING =
Utils.getProperty("file.encoding", "Cp1252");
/**
* System property <code>file.separator</code> (default: /).<br />
* It is usually set by the system, and used to build absolute file names.
* System property <code>file.separator</code>.<br />
* It is set by the system, and used to build absolute file names.
*/
public static final String FILE_SEPARATOR =
Utils.getProperty("file.separator", "/");
public static final String FILE_SEPARATOR = File.separator;
/**
* System property <code>line.separator</code> (default: \n).<br />
* It is usually set by the system, and used by the script and trace tools.
* System property <code>line.separator</code>.<br />
* It is set by the system, and used by the script and trace tools.
*/
public static final String LINE_SEPARATOR =
Utils.getProperty("line.separator", "\n");
public static final String LINE_SEPARATOR = System.lineSeparator();
/**
* System property <code>user.home</code> (empty string if not set).<br />
......
......@@ -8,6 +8,7 @@ package org.h2.expression;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.expression.IntervalOperation.IntervalOpType;
import org.h2.expression.function.Function;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
......
......@@ -54,7 +54,12 @@ public abstract class Expression {
if (i > 0) {
builder.append(", ");
}
expressions[i].getSQL(builder);
Expression e = expressions[i];
if (e == null) {
builder.append("DEFAULT");
} else {
e.getSQL(builder);
}
}
}
......@@ -373,7 +378,7 @@ public abstract class Expression {
* @param value the value to extract columns from
* @return array of expression columns
*/
static Expression[] getExpressionColumns(Session session, ValueArray value) {
protected static Expression[] getExpressionColumns(Session session, ValueArray value) {
Value[] list = value.getList();
ExpressionColumn[] expr = new ExpressionColumn[list.length];
for (int i = 0, len = list.length; i < len; i++) {
......
......@@ -12,6 +12,7 @@ import org.h2.command.dml.SelectGroups;
import org.h2.command.dml.SelectListColumnResolver;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.condition.Comparison;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.schema.Constant;
......
......@@ -132,4 +132,24 @@ public class ExpressionList extends Expression {
return expr;
}
@Override
public boolean isConstant() {
for (Expression e : list) {
if (!e.isConstant()) {
return false;
}
}
return true;
}
@Override
public int getSubexpressionCount() {
return list.length;
}
@Override
public Expression getSubexpression(int index) {
return list[index];
}
}
......@@ -220,7 +220,7 @@ public class ExpressionVisitor {
* @param resolver the resolver
* @return the new visitor
*/
static ExpressionVisitor getNotFromResolverVisitor(ColumnResolver resolver) {
public static ExpressionVisitor getNotFromResolverVisitor(ColumnResolver resolver) {
return new ExpressionVisitor(NOT_FROM_RESOLVER, 0, null, null, null,
resolver, null, null);
}
......
......@@ -20,10 +20,10 @@ import java.math.BigInteger;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.engine.Session;
import org.h2.expression.function.DateTimeFunctions;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.DateTimeFunctions;
import org.h2.util.IntervalUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
......@@ -312,22 +312,21 @@ public class IntervalOperation extends Expression {
return left.getCost() + 1 + right.getCost();
}
/**
* Get the left sub-expression of this operation.
*
* @return the left sub-expression
*/
public Expression getLeftSubExpression() {
return left;
@Override
public int getSubexpressionCount() {
return 2;
}
/**
* Get the right sub-expression of this operation.
*
* @return the right sub-expression
*/
public Expression getRightSubExpression() {
return right;
@Override
public Expression getSubexpression(int index) {
switch (index) {
case 0:
return left;
case 1:
return right;
default:
throw new IndexOutOfBoundsException();
}
}
}
......@@ -7,6 +7,7 @@ package org.h2.expression;
import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.table.Column;
import org.h2.table.ColumnResolver;
......
......@@ -6,6 +6,7 @@
package org.h2.expression;
import org.h2.engine.Session;
import org.h2.expression.condition.Comparison;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
......
......@@ -14,6 +14,8 @@ import org.h2.command.dml.SelectGroups;
import org.h2.command.dml.SelectOrderBy;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.analysis.DataAnalysisOperation;
import org.h2.expression.analysis.WindowFrame;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......
......@@ -19,6 +19,7 @@ import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.analysis.Window;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.DbException;
......@@ -104,14 +105,14 @@ public class Aggregate extends AbstractAggregate {
VAR_SAMP,
/**
* The aggregate type for BOOL_OR(expression).
* The aggregate type for ANY(expression).
*/
BOOL_OR,
ANY,
/**
* The aggregate type for BOOL_AND(expression).
* The aggregate type for EVERY(expression).
*/
BOOL_AND,
EVERY,
/**
* The aggregate type for BOOL_OR(expression).
......@@ -154,21 +155,6 @@ public class Aggregate extends AbstractAggregate {
ENVELOPE,
}
/**
* Reset stage. Used to reset internal data to its initial state.
*/
public static final int STAGE_RESET = 0;
/**
* Group stage, used for explicit or implicit GROUP BY operation.
*/
public static final int STAGE_GROUP = 1;
/**
* Window processing stage.
*/
public static final int STAGE_WINDOW = 2;
private static final HashMap<String, AggregateType> AGGREGATES = new HashMap<>(64);
private final AggregateType type;
......@@ -223,12 +209,13 @@ public class Aggregate extends AbstractAggregate {
addAggregate("VAR_SAMP", AggregateType.VAR_SAMP);
addAggregate("VAR", AggregateType.VAR_SAMP);
addAggregate("VARIANCE", AggregateType.VAR_SAMP);
addAggregate("BOOL_OR", AggregateType.BOOL_OR);
// HSQLDB compatibility, but conflicts with x > EVERY(...)
addAggregate("SOME", AggregateType.BOOL_OR);
addAggregate("BOOL_AND", AggregateType.BOOL_AND);
// HSQLDB compatibility, but conflicts with x > SOME(...)
addAggregate("EVERY", AggregateType.BOOL_AND);
addAggregate("ANY", AggregateType.ANY);
addAggregate("SOME", AggregateType.ANY);
// PostgreSQL compatibility
addAggregate("BOOL_OR", AggregateType.ANY);
addAggregate("EVERY", AggregateType.EVERY);
// PostgreSQL compatibility
addAggregate("BOOL_AND", AggregateType.EVERY);
addAggregate("SELECTIVITY", AggregateType.SELECTIVITY);
addAggregate("HISTOGRAM", AggregateType.HISTOGRAM);
addAggregate("BIT_OR", AggregateType.BIT_OR);
......@@ -666,8 +653,8 @@ public class Aggregate extends AbstractAggregate {
displaySize = ValueDouble.DISPLAY_SIZE;
scale = 0;
break;
case BOOL_AND:
case BOOL_OR:
case EVERY:
case ANY:
dataType = Value.BOOLEAN;
precision = ValueBoolean.PRECISION;
displaySize = ValueBoolean.DISPLAY_SIZE;
......@@ -793,11 +780,11 @@ public class Aggregate extends AbstractAggregate {
case VAR_SAMP:
text = "VAR_SAMP";
break;
case BOOL_AND:
text = "BOOL_AND";
case EVERY:
text = "EVERY";
break;
case BOOL_OR:
text = "BOOL_OR";
case ANY:
text = "ANY";
break;
case BIT_AND:
text = "BIT_AND";
......
......@@ -40,8 +40,8 @@ abstract class AggregateData {
case MAX:
case BIT_OR:
case BIT_AND:
case BOOL_OR:
case BOOL_AND:
case ANY:
case EVERY:
return new AggregateDataDefault(aggregateType);
case SUM:
case AVG:
......
......@@ -83,7 +83,7 @@ class AggregateDataDefault extends AggregateData {
}
break;
}
case BOOL_AND:
case EVERY:
v = v.convertTo(Value.BOOLEAN);
if (value == null) {
value = v;
......@@ -91,7 +91,7 @@ class AggregateDataDefault extends AggregateData {
value = ValueBoolean.get(value.getBoolean() && v.getBoolean());
}
break;
case BOOL_OR:
case ANY:
v = v.convertTo(Value.BOOLEAN);
if (value == null) {
value = v;
......@@ -127,8 +127,8 @@ class AggregateDataDefault extends AggregateData {
case MAX:
case BIT_OR:
case BIT_AND:
case BOOL_OR:
case BOOL_AND:
case ANY:
case EVERY:
v = value;
break;
case AVG:
......
......@@ -9,6 +9,6 @@ Initial Developer: H2 Group
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Aggregate and window functions.
Aggregate functions.
</p></body></html>
\ No newline at end of file
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import java.util.ArrayList;
import java.util.Collections;
......@@ -29,6 +29,21 @@ import org.h2.value.ValueInt;
*/
public abstract class DataAnalysisOperation extends Expression {
/**
* Reset stage. Used to reset internal data to its initial state.
*/
public static final int STAGE_RESET = 0;
/**
* Group stage, used for explicit or implicit GROUP BY operation.
*/
public static final int STAGE_GROUP = 1;
/**
* Window processing stage.
*/
public static final int STAGE_WINDOW = 2;
protected final Select select;
protected Window over;
......@@ -49,7 +64,7 @@ public abstract class DataAnalysisOperation extends Expression {
return new SortOrder(session.getDatabase(), index, sortType, null);
}
DataAnalysisOperation(Select select) {
protected DataAnalysisOperation(Select select) {
this.select = select;
}
......@@ -76,7 +91,7 @@ public abstract class DataAnalysisOperation extends Expression {
*
* @return the sort order for OVER clause
*/
SortOrder getOverOrderBySort() {
protected SortOrder getOverOrderBySort() {
return overOrderBySort;
}
......@@ -125,12 +140,12 @@ public abstract class DataAnalysisOperation extends Expression {
@Override
public final void updateAggregate(Session session, int stage) {
if (stage == Aggregate.STAGE_RESET) {
updateGroupAggregates(session, Aggregate.STAGE_RESET);
if (stage == STAGE_RESET) {
updateGroupAggregates(session, STAGE_RESET);
lastGroupRowId = 0;
return;
}
boolean window = stage == Aggregate.STAGE_WINDOW;
boolean window = stage == STAGE_WINDOW;
if (window != (over != null)) {
if (!window && select.isWindowQuery()) {
updateGroupAggregates(session, stage);
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import java.util.HashMap;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import java.util.ArrayList;
......@@ -32,12 +32,14 @@ public final class Window {
private String parent;
/**
* Appends ORDER BY clause to the specified builder.
*
* @param builder
* string builder
* @param orderBy
* ORDER BY clause, or null
*/
static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) {
public static void appendOrderBy(StringBuilder builder, ArrayList<SelectOrderBy> orderBy) {
if (orderBy != null && !orderBy.isEmpty()) {
if (builder.charAt(builder.length() - 1) != '(') {
builder.append(' ');
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import java.util.ArrayList;
import java.util.Collections;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import org.h2.expression.Expression;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
/**
* Window frame bound type.
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
/**
* Window frame exclusion clause.
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
/**
* Window frame units.
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
import java.util.ArrayList;
import java.util.HashMap;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression.aggregate;
package org.h2.expression.analysis;
/**
* A type of a window function.
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, Version 1.0,
and under the Eclipse Public License, Version 1.0
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Base classes for data analysis operations and implementations of window functions.
</p></body></html>
\ No newline at end of file
......@@ -3,13 +3,17 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
......@@ -517,4 +521,26 @@ public class CompareLike extends Condition {
return left.getCost() + right.getCost() + 3;
}
@Override
public int getSubexpressionCount() {
return escape == null ? 2 : 3;
}
@Override
public Expression getSubexpression(int index) {
switch (index) {
case 0:
return left;
case 1:
return right;
case 2:
if (escape != null) {
return escape;
}
//$FALL-THROUGH$
default:
throw new IndexOutOfBoundsException();
}
}
}
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import java.util.ArrayList;
import java.util.Arrays;
......@@ -11,6 +11,11 @@ import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.engine.SysProperties;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.Column;
......
......@@ -3,8 +3,9 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import org.h2.expression.Expression;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
......
......@@ -3,10 +3,13 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import org.h2.engine.Session;
import org.h2.engine.SysProperties;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
......
......@@ -3,10 +3,12 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import org.h2.command.dml.Query;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
......
......@@ -3,12 +3,20 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import java.util.ArrayList;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.expression.function.Function;
import org.h2.expression.function.TableFunction;
import org.h2.index.IndexCondition;
import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......@@ -47,7 +55,15 @@ public class ConditionIn extends Condition {
}
boolean result = false;
boolean hasNull = false;
for (Expression e : valueList) {
int size = valueList.size();
if (size == 1) {
Expression e = valueList.get(0);
if (e instanceof TableFunction) {
return ConditionInParameter.getValue(database, l, e.getValue(session));
}
}
for (int i = 0; i < size; i++) {
Expression e = valueList.get(i);
Value r = e.getValue(session);
if (r == ValueNull.INSTANCE) {
hasNull = true;
......@@ -80,9 +96,38 @@ public class ConditionIn extends Condition {
if (constant && left == ValueExpression.getNull()) {
return left;
}
int size = valueList.size();
if (size == 1) {
Expression right = valueList.get(0);
if (right instanceof TableFunction) {
TableFunction tf = (TableFunction) right;
if (tf.getFunctionType() == Function.UNNEST) {
Expression[] args = tf.getArgs();
if (args.length == 1) {
Expression arg = args[0];
if (arg instanceof Parameter) {
return new ConditionInParameter(database, left, (Parameter) arg);
}
}
}
if (tf.isConstant()) {
boolean allValuesNull = true;
ResultInterface ri = right.getValue(session).getResult();
ArrayList<Expression> list = new ArrayList<>(ri.getRowCount());
while (ri.next()) {
Value v = ri.currentRow()[0];
if (v != ValueNull.INSTANCE) {
allValuesNull = false;
}
list.add(ValueExpression.get(v));
}
return optimize2(session, constant, true, allValuesNull, list);
}
return this;
}
}
boolean allValuesConstant = true;
boolean allValuesNull = true;
int size = valueList.size();
for (int i = 0; i < size; i++) {
Expression e = valueList.get(i);
e = e.optimize(session);
......@@ -98,14 +143,16 @@ public class ConditionIn extends Condition {
}
valueList.set(i, e);
}
return optimize2(session, constant, allValuesConstant, allValuesNull, valueList);
}
private Expression optimize2(Session session, boolean constant, boolean allValuesConstant, boolean allValuesNull,
ArrayList<Expression> values) {
if (constant && allValuesConstant) {
return ValueExpression.get(getValue(session));
}
if (size == 1) {
Expression right = valueList.get(0);
Expression expr = new Comparison(session, Comparison.EQUAL, left, right);
expr = expr.optimize(session);
return expr;
if (values.size() == 1) {
return new Comparison(session, Comparison.EQUAL, left, values.get(0)).optimize(session);
}
if (allValuesConstant && !allValuesNull) {
int leftType = left.getType();
......@@ -115,7 +162,7 @@ public class ConditionIn extends Condition {
if (leftType == Value.ENUM && !(left instanceof ExpressionColumn)) {
return this;
}
Expression expr = new ConditionInConstantSet(session, left, valueList);
Expression expr = new ConditionInConstantSet(session, left, values);
expr = expr.optimize(session);
return expr;
}
......@@ -207,4 +254,20 @@ public class ConditionIn extends Condition {
}
return null;
}
@Override
public int getSubexpressionCount() {
return 1 + valueList.size();
}
@Override
public Expression getSubexpression(int index) {
if (index == 0) {
return left;
} else if (index > 0 && index <= valueList.size()) {
return valueList.get(index - 1);
}
throw new IndexOutOfBoundsException();
}
}
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import java.util.ArrayList;
import java.util.TreeSet;
......@@ -11,6 +11,9 @@ import java.util.TreeSet;
import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
......@@ -176,4 +179,20 @@ public class ConditionInConstantSet extends Condition {
}
return null;
}
@Override
public int getSubexpressionCount() {
return 1 + valueList.size();
}
@Override
public Expression getSubexpression(int index) {
if (index == 0) {
return left;
} else if (index > 0 && index <= valueList.size()) {
return valueList.get(index - 1);
}
throw new IndexOutOfBoundsException();
}
}
......@@ -3,13 +3,19 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import java.util.AbstractList;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.Parameter;
import org.h2.expression.ValueExpression;
import org.h2.index.IndexCondition;
import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......@@ -18,7 +24,7 @@ import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
/**
* A condition with parameter as {@code = ANY(?)}.
* A condition with parameter as {@code IN(UNNEST(?))}.
*/
public class ConditionInParameter extends Condition {
private static final class ParameterList extends AbstractList<Expression> {
......@@ -59,13 +65,48 @@ public class ConditionInParameter extends Condition {
private final Parameter parameter;
static Value getValue(Database database, Value l, Value value) {
boolean result = false;
boolean hasNull = false;
if (value == ValueNull.INSTANCE) {
hasNull = true;
} else if (value.getType() == Value.RESULT_SET) {
for (ResultInterface ri = value.getResult(); ri.next();) {
Value r = ri.currentRow()[0];
if (r == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
}
}
} else {
for (Value r : ((ValueArray) value.convertTo(Value.ARRAY)).getList()) {
if (r == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
}
}
}
if (!result && hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
}
/**
* Create a new {@code = ANY(?)} condition.
* Create a new {@code IN(UNNEST(?))} condition.
*
* @param database
* the database
* @param left
* the expression before {@code = ANY(?)}
* the expression before {@code IN(UNNEST(?))}
* @param parameter
* parameter
*/
......@@ -81,31 +122,7 @@ public class ConditionInParameter extends Condition {
if (l == ValueNull.INSTANCE) {
return l;
}
boolean result = false;
boolean hasNull = false;
Value value = parameter.getValue(session);
if (value instanceof ValueArray) {
for (Value r : ((ValueArray) value).getList()) {
if (r == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
}
}
} else {
if (value == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, value, Comparison.EQUAL);
}
}
if (!result && hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
return getValue(database, l, parameter.getValue(session));
}
@Override
......@@ -142,8 +159,8 @@ public class ConditionInParameter extends Condition {
@Override
public StringBuilder getSQL(StringBuilder builder) {
builder.append('(');
left.getSQL(builder).append(" = ANY(");
return parameter.getSQL(builder).append("))");
left.getSQL(builder).append(" IN(UNNEST(");
return parameter.getSQL(builder).append(")))");
}
@Override
......
......@@ -3,12 +3,15 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import org.h2.api.ErrorCode;
import org.h2.command.dml.Query;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.result.LocalResult;
......
......@@ -3,9 +3,12 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.condition;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, Version 1.0,
and under the Eclipse Public License, Version 1.0
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Condition expressions.
</p></body></html>
\ No newline at end of file
......@@ -3,30 +3,30 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import static org.h2.expression.Function.CENTURY;
import static org.h2.expression.Function.DAY_OF_MONTH;
import static org.h2.expression.Function.DAY_OF_WEEK;
import static org.h2.expression.Function.DAY_OF_YEAR;
import static org.h2.expression.Function.DECADE;
import static org.h2.expression.Function.EPOCH;
import static org.h2.expression.Function.HOUR;
import static org.h2.expression.Function.ISO_DAY_OF_WEEK;
import static org.h2.expression.Function.ISO_WEEK;
import static org.h2.expression.Function.ISO_YEAR;
import static org.h2.expression.Function.MICROSECOND;
import static org.h2.expression.Function.MILLENNIUM;
import static org.h2.expression.Function.MILLISECOND;
import static org.h2.expression.Function.MINUTE;
import static org.h2.expression.Function.MONTH;
import static org.h2.expression.Function.NANOSECOND;
import static org.h2.expression.Function.QUARTER;
import static org.h2.expression.Function.SECOND;
import static org.h2.expression.Function.TIMEZONE_HOUR;
import static org.h2.expression.Function.TIMEZONE_MINUTE;
import static org.h2.expression.Function.WEEK;
import static org.h2.expression.Function.YEAR;
package org.h2.expression.function;
import static org.h2.expression.function.Function.CENTURY;
import static org.h2.expression.function.Function.DAY_OF_MONTH;
import static org.h2.expression.function.Function.DAY_OF_WEEK;
import static org.h2.expression.function.Function.DAY_OF_YEAR;
import static org.h2.expression.function.Function.DECADE;
import static org.h2.expression.function.Function.EPOCH;
import static org.h2.expression.function.Function.HOUR;
import static org.h2.expression.function.Function.ISO_DAY_OF_WEEK;
import static org.h2.expression.function.Function.ISO_WEEK;
import static org.h2.expression.function.Function.ISO_YEAR;
import static org.h2.expression.function.Function.MICROSECOND;
import static org.h2.expression.function.Function.MILLENNIUM;
import static org.h2.expression.function.Function.MILLISECOND;
import static org.h2.expression.function.Function.MINUTE;
import static org.h2.expression.function.Function.MONTH;
import static org.h2.expression.function.Function.NANOSECOND;
import static org.h2.expression.function.Function.QUARTER;
import static org.h2.expression.function.Function.SECOND;
import static org.h2.expression.function.Function.TIMEZONE_HOUR;
import static org.h2.expression.function.Function.TIMEZONE_MINUTE;
import static org.h2.expression.function.Function.WEEK;
import static org.h2.expression.function.Function.YEAR;
import static org.h2.util.DateTimeUtils.MILLIS_PER_DAY;
import static org.h2.util.DateTimeUtils.NANOS_PER_DAY;
import static org.h2.util.DateTimeUtils.NANOS_PER_HOUR;
......@@ -43,8 +43,10 @@ import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.api.IntervalQualifier;
import org.h2.expression.Function;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.IntervalUtils;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueDate;
import org.h2.value.ValueDecimal;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.function;
import java.io.FileOutputStream;
import java.io.IOException;
......@@ -17,6 +17,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
......@@ -27,6 +28,12 @@ import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.SequenceValue;
import org.h2.expression.ValueExpression;
import org.h2.expression.Variable;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.mode.FunctionsMSSQLServer;
......@@ -45,14 +52,11 @@ import org.h2.table.TableFilter;
import org.h2.tools.CompressTool;
import org.h2.tools.Csv;
import org.h2.util.Bits;
import org.h2.util.DateTimeFunctions;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
import org.h2.util.StringUtils;
import org.h2.util.ToChar;
import org.h2.util.ToDateParser;
import org.h2.util.Utils;
import org.h2.value.DataType;
import org.h2.value.ExtTypeInfo;
......@@ -130,7 +134,8 @@ public class Function extends Expression implements FunctionCall {
ARRAY_LENGTH = 217, LINK_SCHEMA = 218, GREATEST = 219, LEAST = 220,
CANCEL_SESSION = 221, SET = 222, TABLE = 223, TABLE_DISTINCT = 224,
FILE_READ = 225, TRANSACTION_ID = 226, TRUNCATE_VALUE = 227,
NVL2 = 228, DECODE = 229, ARRAY_CONTAINS = 230, FILE_WRITE = 232;
NVL2 = 228, DECODE = 229, ARRAY_CONTAINS = 230, FILE_WRITE = 232,
UNNEST = 233;
public static final int REGEXP_LIKE = 240;
......@@ -455,10 +460,9 @@ public class Function extends Expression implements FunctionCall {
addFunction("H2VERSION", H2VERSION, 0, Value.STRING);
// TableFunction
addFunctionWithNull("TABLE", TABLE,
VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT,
VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("TABLE", TABLE, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("TABLE_DISTINCT", TABLE_DISTINCT, VAR_ARGS, Value.RESULT_SET);
addFunctionWithNull("UNNEST", UNNEST, VAR_ARGS, Value.RESULT_SET);
// ON DUPLICATE KEY VALUES function
addFunction("VALUES", VALUES, 1, Value.NULL, false, true, false, true);
......@@ -534,6 +538,7 @@ public class Function extends Expression implements FunctionCall {
switch (info.type) {
case TABLE:
case TABLE_DISTINCT:
case UNNEST:
return new TableFunction(database, info, Long.MAX_VALUE);
default:
return new Function(database, info);
......@@ -1350,42 +1355,11 @@ public class Function extends Expression implements FunctionCall {
break;
}
case REGEXP_REPLACE: {
String input = v0.getString();
String regexp = v1.getString();
String replacement = v2.getString();
if (database.getMode().regexpReplaceBackslashReferences) {
if ((replacement.indexOf('\\') >= 0) || (replacement.indexOf('$') >= 0)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < replacement.length(); i++) {
char c = replacement.charAt(i);
if (c == '$') {
sb.append('\\');
} else if (c == '\\' && ++i < replacement.length()) {
c = replacement.charAt(i);
sb.append(c >= '0' && c <= '9' ? '$' : '\\');
}
sb.append(c);
}
replacement = sb.toString();
}
}
String regexpMode = v3 == null || v3.getString() == null ? "" :
v3.getString();
int flags = makeRegexpFlags(regexpMode);
try {
result = ValueString.get(
Pattern.compile(regexp, flags).matcher(v0.getString())
.replaceAll(replacement),
database.getMode().treatEmptyStringsAsNull);
} catch (StringIndexOutOfBoundsException e) {
throw DbException.get(
ErrorCode.LIKE_ESCAPE_ERROR_1, e, replacement);
} catch (PatternSyntaxException e) {
throw DbException.get(
ErrorCode.LIKE_ESCAPE_ERROR_1, e, regexp);
} catch (IllegalArgumentException e) {
throw DbException.get(
ErrorCode.LIKE_ESCAPE_ERROR_1, e, replacement);
}
String regexpMode = v3 != null ? v3.getString() : null;
result = regexpReplace(input, regexp, replacement, regexpMode);
break;
}
case RPAD:
......@@ -1648,9 +1622,8 @@ public class Function extends Expression implements FunctionCall {
break;
case REGEXP_LIKE: {
String regexp = v1.getString();
String regexpMode = v2 == null || v2.getString() == null ? "" :
v2.getString();
int flags = makeRegexpFlags(regexpMode);
String regexpMode = v2 != null ? v2.getString() : null;
int flags = makeRegexpFlags(regexpMode, false);
try {
result = ValueBoolean.get(Pattern.compile(regexp, flags)
.matcher(v0.getString()).find());
......@@ -2032,7 +2005,41 @@ public class Function extends Expression implements FunctionCall {
return md;
}
private static int makeRegexpFlags(String stringFlags) {
private Value regexpReplace(String input, String regexp, String replacement, String regexpMode) {
Mode mode = database.getMode();
if (mode.regexpReplaceBackslashReferences) {
if ((replacement.indexOf('\\') >= 0) || (replacement.indexOf('$') >= 0)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < replacement.length(); i++) {
char c = replacement.charAt(i);
if (c == '$') {
sb.append('\\');
} else if (c == '\\' && ++i < replacement.length()) {
c = replacement.charAt(i);
sb.append(c >= '0' && c <= '9' ? '$' : '\\');
}
sb.append(c);
}
replacement = sb.toString();
}
}
boolean isInPostgreSqlMode = Mode.ModeEnum.PostgreSQL.equals(mode.getEnum());
int flags = makeRegexpFlags(regexpMode, isInPostgreSqlMode);
try {
Matcher matcher = Pattern.compile(regexp, flags).matcher(input);
return ValueString.get(isInPostgreSqlMode && (regexpMode == null || regexpMode.indexOf('g') < 0) ?
matcher.replaceFirst(replacement) : matcher.replaceAll(replacement),
mode.treatEmptyStringsAsNull);
} catch (StringIndexOutOfBoundsException e) {
throw DbException.get(ErrorCode.LIKE_ESCAPE_ERROR_1, e, replacement);
} catch (PatternSyntaxException e) {
throw DbException.get(ErrorCode.LIKE_ESCAPE_ERROR_1, e, regexp);
} catch (IllegalArgumentException e) {
throw DbException.get(ErrorCode.LIKE_ESCAPE_ERROR_1, e, replacement);
}
}
private static int makeRegexpFlags(String stringFlags, boolean ignoreGlobalFlag) {
int flags = Pattern.UNICODE_CASE;
if (stringFlags != null) {
for (int i = 0; i < stringFlags.length(); ++i) {
......@@ -2049,6 +2056,11 @@ public class Function extends Expression implements FunctionCall {
case 'm':
flags |= Pattern.MULTILINE;
break;
case 'g':
if (ignoreGlobalFlag) {
break;
}
//$FALL-THROUGH$
default:
throw DbException.get(ErrorCode.INVALID_VALUE_2, stringFlags);
}
......
......@@ -3,9 +3,10 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.function;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.value.ValueResultSet;
/**
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.function;
/**
* This class contains information about a built-in function.
......
......@@ -3,12 +3,15 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.function;
import org.h2.command.Parser;
import org.h2.engine.Constants;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.ValueExpression;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.DataType;
......@@ -178,4 +181,14 @@ public class JavaFunction extends Expression implements FunctionCall {
return functionAlias.isBufferResultSetToLocalTemp();
}
@Override
public int getSubexpressionCount() {
return args.length;
}
@Override
public Expression getSubexpression(int index) {
return args[index];
}
}
......@@ -3,18 +3,21 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.expression;
package org.h2.expression.function;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.message.DbException;
import org.h2.result.LocalResult;
import org.h2.table.Column;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
import org.h2.value.ValueResultSet;
......@@ -22,19 +25,17 @@ import org.h2.value.ValueResultSet;
* Implementation of the functions TABLE(..) and TABLE_DISTINCT(..).
*/
public class TableFunction extends Function {
private final boolean distinct;
private final long rowCount;
private Column[] columnList;
private Column[] columns;
TableFunction(Database database, FunctionInfo info, long rowCount) {
super(database, info);
distinct = info.type == Function.TABLE_DISTINCT;
this.rowCount = rowCount;
}
@Override
public Value getValue(Session session) {
return getTable(session, args, false, distinct);
return getTable(session, false);
}
@Override
......@@ -46,52 +47,60 @@ public class TableFunction extends Function {
@Override
public StringBuilder getSQL(StringBuilder builder) {
if (info.type == UNNEST) {
super.getSQL(builder);
if (args.length < columns.length) {
builder.append(" WITH ORDINALITY");
}
return builder;
}
builder.append(getName()).append('(');
for (int i = 0; i < args.length; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(columnList[i].getCreateSQL()).append('=');
builder.append(columns[i].getCreateSQL()).append('=');
args[i].getSQL(builder);
}
return builder.append(')');
}
@Override
public String getName() {
return distinct ? "TABLE_DISTINCT" : "TABLE";
}
@Override
public ValueResultSet getValueForColumnList(Session session,
Expression[] nullArgs) {
return getTable(session, args, true, false);
return getTable(session, true);
}
public void setColumns(ArrayList<Column> columns) {
this.columnList = columns.toArray(new Column[0]);
this.columns = columns.toArray(new Column[0]);
}
private ValueResultSet getTable(Session session, Expression[] argList,
boolean onlyColumnList, boolean distinctRows) {
int len = columnList.length;
Expression[] header = new Expression[len];
private ValueResultSet getTable(Session session, boolean onlyColumnList) {
int totalColumns = columns.length;
Expression[] header = new Expression[totalColumns];
Database db = session.getDatabase();
for (int i = 0; i < len; i++) {
Column c = columnList[i];
for (int i = 0; i < totalColumns; i++) {
Column c = columns[i];
ExpressionColumn col = new ExpressionColumn(db, c);
header[i] = col;
}
LocalResult result = db.getResultFactory().create(session, header, len);
if (distinctRows) {
LocalResult result = db.getResultFactory().create(session, header, totalColumns);
if (!onlyColumnList && info.type == TABLE_DISTINCT) {
result.setDistinct();
}
if (!onlyColumnList) {
int len = totalColumns;
boolean unnest = info.type == UNNEST, addNumber = false;
if (unnest) {
len = args.length;
if (len < totalColumns) {
addNumber = true;
}
}
Value[][] list = new Value[len][];
int rows = 0;
for (int i = 0; i < len; i++) {
Value v = argList[i].getValue(session);
Value v = args[i].getValue(session);
if (v == ValueNull.INSTANCE) {
list[i] = new Value[0];
} else {
......@@ -102,21 +111,25 @@ public class TableFunction extends Function {
}
}
for (int row = 0; row < rows; row++) {
Value[] r = new Value[len];
Value[] r = new Value[totalColumns];
for (int j = 0; j < len; j++) {
Value[] l = list[j];
Value v;
if (l.length <= row) {
v = ValueNull.INSTANCE;
} else {
Column c = columnList[j];
Column c = columns[j];
v = l[row];
v = c.convert(v);
v = v.convertPrecision(c.getPrecision(), false);
v = v.convertScale(true, c.getScale());
if (!unnest) {
v = c.convert(v).convertPrecision(c.getPrecision(), false)
.convertScale(true, c.getScale());
}
}
r[j] = v;
}
if (addNumber) {
r[len] = ValueInt.get(row + 1);
}
result.addRow(r);
}
}
......@@ -130,7 +143,17 @@ public class TableFunction extends Function {
@Override
public Expression[] getExpressionColumns(Session session) {
return getExpressionColumns(session, getTable(session, getArgs(), true, false).getResult());
return getExpressionColumns(session, getValueForColumnList(session, null).getResult());
}
@Override
public boolean isConstant() {
for (Expression e : args) {
if (!e.isConstant()) {
return false;
}
}
return true;
}
}
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler
*/
package org.h2.util;
package org.h2.expression.function;
import java.math.BigDecimal;
import java.text.DateFormatSymbols;
......@@ -17,6 +17,8 @@ import java.util.TimeZone;
import org.h2.api.ErrorCode;
import org.h2.message.DbException;
import org.h2.util.DateTimeUtils;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueTimestampTimeZone;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler
*/
package org.h2.util;
package org.h2.expression.function;
import static java.lang.String.format;
......@@ -12,6 +12,7 @@ import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import org.h2.util.DateTimeUtils;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: Daniel Gredler
*/
package org.h2.util;
package org.h2.expression.function;
import static java.lang.String.format;
import java.util.ArrayList;
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<!--
Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0, Version 1.0,
and under the Eclipse Public License, Version 1.0
Initial Developer: H2 Group
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" /><title>
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Functions.
</p></body></html>
\ No newline at end of file
......@@ -9,6 +9,6 @@ Initial Developer: H2 Group
Javadoc package documentation
</title></head><body style="font: 9pt/130% Tahoma, Arial, Helvetica, sans-serif; font-weight: normal;"><p>
Expressions include mathematical operations, conditions, simple values, and functions.
Expressions include mathematical operations, simple values, and others.
</p></body></html>
\ No newline at end of file
......@@ -27,11 +27,11 @@ import java.util.UUID;
import org.h2.api.Trigger;
import org.h2.command.Parser;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ValueExpression;
import org.h2.expression.condition.Comparison;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
......
......@@ -11,10 +11,10 @@ import java.util.HashSet;
import java.util.List;
import org.h2.command.dml.Query;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.table.Column;
......
......@@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.HashSet;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
......
......@@ -16,8 +16,8 @@ import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants;
import org.h2.engine.Session;
import org.h2.expression.Comparison;
import org.h2.expression.Parameter;
import org.h2.expression.condition.Comparison;
import org.h2.message.DbException;
import org.h2.result.LocalResult;
import org.h2.result.ResultInterface;
......
......@@ -8,8 +8,8 @@ package org.h2.mode;
import java.util.HashMap;
import org.h2.engine.Database;
import org.h2.expression.Function;
import org.h2.expression.FunctionInfo;
import org.h2.expression.function.Function;
import org.h2.expression.function.FunctionInfo;
/**
* Base class for mode-specific functions.
......
......@@ -8,8 +8,8 @@ package org.h2.mode;
import java.util.HashMap;
import org.h2.engine.Database;
import org.h2.expression.Function;
import org.h2.expression.FunctionInfo;
import org.h2.expression.function.Function;
import org.h2.expression.function.FunctionInfo;
/**
* Functions for {@link org.h2.engine.Mode.ModeEnum#MSSQLServer} compatibility
......
......@@ -14,9 +14,9 @@ import org.h2.api.ErrorCode;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.Function;
import org.h2.expression.FunctionInfo;
import org.h2.expression.ValueExpression;
import org.h2.expression.function.Function;
import org.h2.expression.function.FunctionInfo;
import org.h2.message.DbException;
import org.h2.util.StringUtils;
import org.h2.value.DataType;
......
......@@ -1053,7 +1053,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return version
*/
public final long getVersion() {
RootReference rootReference = getRoot();
return getVersion(getRoot());
}
private long getVersion(RootReference rootReference) {
RootReference previous = rootReference.previous;
return previous == null || previous.root != rootReference.root ||
previous.appendCounter != rootReference.appendCounter ?
......@@ -1061,7 +1064,10 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
final boolean hasChangesSince(long version) {
return getVersion() > version;
RootReference rootReference = getRoot();
Page root = rootReference.root;
return !root.isSaved() && root.getTotalCount() > 0 ||
getVersion(rootReference) > version;
}
public boolean isSingleWriter() {
......@@ -1153,28 +1159,33 @@ public class MVMap<K, V> extends AbstractMap<K, V>
MVStore.TxCounter txCounter = store.registerVersionUsage();
try {
beforeWrite();
setRoot(copy(sourceMap.getRootPage()));
copy(sourceMap.getRootPage(), null, 0);
} finally {
store.deregisterVersionUsage(txCounter);
}
}
private Page copy(Page source) {
private Page copy(Page source, Page parent, int index) {
Page target = source.copy(this);
store.registerUnsavedPage(target.getMemory());
if (parent == null) {
setRoot(target);
} else {
parent.setChild(index, target);
}
if (!source.isLeaf()) {
for (int i = 0; i < getChildPageCount(target); i++) {
if (source.getChildPagePos(i) != 0) {
// position 0 means no child
// (for example the last entry of an r-tree node)
// (the MVMap is also used for r-trees for compacting)
Page child = copy(source.getChildPage(i));
target.setChild(i, child);
copy(source.getChildPage(i), target, i);
}
}
setRoot(target);
beforeWrite();
target.setComplete();
}
store.registerUnsavedPage(target.getMemory());
if (store.isSaveNeeded()) {
store.commit();
}
return target;
}
......@@ -1186,7 +1197,6 @@ public class MVMap<K, V> extends AbstractMap<K, V>
* @return potentially updated RootReference
*/
private RootReference flushAppendBuffer(RootReference rootReference) {
beforeWrite();
int attempt = 0;
int keyCount;
while((keyCount = rootReference.getAppendCounter()) > 0) {
......@@ -1276,6 +1286,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
RootReference rootReference = getRootInternal();
int appendCounter = rootReference.getAppendCounter();
if (appendCounter >= keysPerPage) {
beforeWrite();
rootReference = flushAppendBuffer(rootReference);
appendCounter = rootReference.getAppendCounter();
assert appendCounter < keysPerPage;
......
......@@ -130,7 +130,7 @@ MVStore:
/**
* A persistent storage for maps.
*/
public class MVStore {
public class MVStore implements AutoCloseable {
/**
* The block size (physical sector size) of the disk. The store header is
......@@ -1326,9 +1326,7 @@ public class MVStore {
shrinkFileIfPossible(1);
}
for (Page p : changed) {
if (p.getTotalCount() > 0) {
p.writeEnd();
}
p.writeEnd();
}
metaRoot.writeEnd();
......@@ -1346,7 +1344,7 @@ public class MVStore {
*/
private void freeUnusedIfNeeded(long time) {
int freeDelay = retentionTime / 5;
if (time >= lastFreeUnusedChunks + freeDelay) {
if (time - lastFreeUnusedChunks >= freeDelay) {
// set early in case it fails (out of memory or so)
lastFreeUnusedChunks = time;
freeUnusedChunks(true);
......@@ -1391,6 +1389,7 @@ public class MVStore {
* @param fast if true, simplified version is used, which assumes that recent chunks
* are still in-use and do not scan recent versions of the store.
* Also is this case only oldest available version of the store is scanned.
* @return set of chunk ids in-use, or null if all chunks should be considered in-use
*/
private Set<Integer> collectReferencedChunks(boolean fast) {
assert lastChunk != null;
......@@ -1428,6 +1427,15 @@ public class MVStore {
}
}
/**
* Scans all map of a particular store version and marks visited chunks as in-use.
* @param rootReference of the meta map of the version
* @param collector to report visited chunks to
* @param executorService to use for parallel processing
* @param executingThreadCounter counter for threads already in use
* @param inspectedRoots set of page positions for map's roots already inspected
* or null if not to be used
*/
private void inspectVersion(MVMap.RootReference rootReference, ChunkIdsCollector collector,
ThreadPoolExecutor executorService,
AtomicInteger executingThreadCounter,
......@@ -1443,17 +1451,17 @@ public class MVStore {
}
for (Cursor<String, String> c = new Cursor<>(rootPage, "root."); c.hasNext(); ) {
String key = c.next();
assert key != null;
if (!key.startsWith("root.")) {
break;
}
pos = DataUtils.parseHexLong(c.getValue());
assert DataUtils.isPageSaved(pos);
if (inspectedRoots == null || inspectedRoots.add(pos)) {
// to allow for something like "root.tmp.123" to be processed
int mapId = DataUtils.parseHexInt(key.substring(key.lastIndexOf('.') + 1));
collector.setMapId(mapId);
collector.visit(pos, executorService, executingThreadCounter);
if (DataUtils.isPageSaved(pos)) {
if (inspectedRoots == null || inspectedRoots.add(pos)) {
// to allow for something like "root.tmp.123" to be processed
int mapId = DataUtils.parseHexInt(key.substring(key.lastIndexOf('.') + 1));
collector.setMapId(mapId);
collector.visit(pos, executorService, executingThreadCounter);
}
}
}
}
......@@ -1538,7 +1546,7 @@ public class MVStore {
}
}
private void registerChunk(int chunkId) {
void registerChunk(int chunkId) {
if (referencedChunks.put(chunkId, 1) == null && parent != null) {
parent.registerChunk(chunkId);
}
......@@ -1641,12 +1649,12 @@ public class MVStore {
}
freedPageSpace.clear();
}
for (Chunk c : modified) {
meta.put(Chunk.getMetaKey(c.id), c.asString());
}
if (modified.isEmpty()) {
break;
}
for (Chunk c : modified) {
meta.put(Chunk.getMetaKey(c.id), c.asString());
}
markMetaChanged();
}
}
......
......@@ -481,30 +481,22 @@ public class MVStoreTool {
* @param compress whether to compress the data
*/
public static void compact(String sourceFileName, String targetFileName, boolean compress) {
MVStore source = new MVStore.Builder().
fileName(sourceFileName).
readOnly().
open();
// Bugfix - Add double "try-finally" statements to close source and target stores for
//releasing lock and file resources in these stores even if OOM occurs.
// Fix issues such as "Cannot delete file "/h2/data/test.mv.db.tempFile" [90025-197]"
//when client connects to this server and reopens this store database in this process.
// @since 2018-09-13 little-pan
try{
try (MVStore source = new MVStore.Builder().
fileName(sourceFileName).readOnly().open()) {
// Bugfix - Add double "try-finally" statements to close source and target stores for
//releasing lock and file resources in these stores even if OOM occurs.
// Fix issues such as "Cannot delete file "/h2/data/test.mv.db.tempFile" [90025-197]"
//when client connects to this server and reopens this store database in this process.
// @since 2018-09-13 little-pan
FileUtils.delete(targetFileName);
MVStore.Builder b = new MVStore.Builder().
fileName(targetFileName);
if (compress) {
b.compress();
}
MVStore target = b.open();
try{
try (MVStore target = b.open()) {
compact(source, target);
}finally{
target.close();
}
}finally{
source.close();
}
}
......@@ -515,6 +507,10 @@ public class MVStoreTool {
* @param target the target store
*/
public static void compact(MVStore source, MVStore target) {
int autoCommitDelay = target.getAutoCommitDelay();
int retentionTime = target.getRetentionTime();
target.setAutoCommitDelay(0);
target.setRetentionTime(Integer.MAX_VALUE); // disable unused chunks collection
MVMap<String, String> sourceMeta = source.getMetaMap();
MVMap<String, String> targetMeta = target.getMetaMap();
for (Entry<String, String> m : sourceMeta.entrySet()) {
......@@ -540,6 +536,8 @@ public class MVStoreTool {
MVMap<Object, Object> targetMap = target.openMap(mapName, mp);
targetMap.copyFrom(sourceMap);
}
target.setRetentionTime(retentionTime);
target.setAutoCommitDelay(autoCommitDelay);
}
/**
......
......@@ -813,6 +813,12 @@ public abstract class Page implements Cloneable
return mem;
}
public boolean isComplete() {
return true;
}
public void setComplete() {}
/**
* Remove the page.
*/
......@@ -883,14 +889,13 @@ public abstract class Page implements Cloneable
void clearPageReference() {
if (page != null) {
if (!page.isSaved()) {
throw DataUtils.newIllegalStateException(
DataUtils.ERROR_INTERNAL, "Page not written");
}
page.writeEnd();
assert pos == page.getPos();
assert count == page.getTotalCount();
page = null;
assert page.isSaved() || !page.isComplete();
if (page.isSaved()) {
assert pos == page.getPos();
assert count == page.getTotalCount() : count + " != " + page.getTotalCount();
page = null;
}
}
}
......@@ -900,7 +905,7 @@ public abstract class Page implements Cloneable
void resetPos() {
Page p = page;
if (p != null) {
if (p != null && p.isSaved()) {
pos = p.getPos();
assert count == p.getTotalCount();
}
......@@ -910,12 +915,12 @@ public abstract class Page implements Cloneable
public String toString() {
return "Cnt:" + count + ", pos:" + DataUtils.getPageChunkId(pos) +
"-" + DataUtils.getPageOffset(pos) + ":" + DataUtils.getPageMaxLength(pos) +
(DataUtils.getPageType(pos) == 0 ? " leaf" : " node") + ", " + page;
(page == null ? DataUtils.getPageType(pos) == 0 : page.isLeaf() ? " leaf" : " node") + ", " + page;
}
}
private static final class NonLeaf extends Page
private static class NonLeaf extends Page
{
/**
* The child page references.
......@@ -931,7 +936,7 @@ public abstract class Page implements Cloneable
super(map);
}
private NonLeaf(MVMap<?, ?> map, NonLeaf source, PageReference[] children, long totalCount) {
NonLeaf(MVMap<?, ?> map, NonLeaf source, PageReference[] children, long totalCount) {
super(map, source);
this.children = children;
this.totalCount = totalCount;
......@@ -950,10 +955,7 @@ public abstract class Page implements Cloneable
@Override
public Page copy(MVMap<?, ?> map) {
// replace child pages with empty pages
PageReference[] children = new PageReference[this.children.length];
Arrays.fill(children, PageReference.EMPTY);
return new NonLeaf(map, this, children, 0);
return new IncompleteNonLeaf(map, this);
}
@Override
......@@ -1012,7 +1014,7 @@ public abstract class Page implements Cloneable
@Override
public long getTotalCount() {
assert totalCount == calculateTotalCount() :
assert !isComplete() || totalCount == calculateTotalCount() :
"Total count: " + totalCount + " != " + calculateTotalCount();
return totalCount;
}
......@@ -1026,6 +1028,10 @@ public abstract class Page implements Cloneable
return check;
}
protected void recalculateTotalCount() {
totalCount = calculateTotalCount();
}
@Override
long getCounts(int index) {
return children[index].count;
......@@ -1150,15 +1156,7 @@ public abstract class Page implements Cloneable
void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff) {
if (!isSaved()) {
int patch = write(chunk, buff);
int len = getRawChildPageCount();
for (int i = 0; i < len; i++) {
PageReference ref = children[i];
Page p = ref.getPage();
if (p != null) {
p.writeUnsavedRecursive(chunk, buff);
ref.resetPos();
}
}
writeChildrenRecursive(chunk, buff);
int old = buff.position();
buff.position(patch);
writeChildren(buff, false);
......@@ -1166,6 +1164,18 @@ public abstract class Page implements Cloneable
}
}
void writeChildrenRecursive(Chunk chunk, WriteBuffer buff) {
int len = getRawChildPageCount();
for (int i = 0; i < len; i++) {
PageReference ref = children[i];
Page p = ref.getPage();
if (p != null) {
p.writeUnsavedRecursive(chunk, buff);
ref.resetPos();
}
}
}
@Override
void writeEnd() {
int len = getRawChildPageCount();
......@@ -1202,6 +1212,48 @@ public abstract class Page implements Cloneable
}
private static class IncompleteNonLeaf extends NonLeaf {
private boolean complete;
IncompleteNonLeaf(MVMap<?, ?> map, NonLeaf source) {
super(map, source, constructEmptyPageRefs(source.getRawChildPageCount()), source.getTotalCount());
}
private static PageReference[] constructEmptyPageRefs(int size) {
// replace child pages with empty pages
PageReference[] children = new PageReference[size];
Arrays.fill(children, PageReference.EMPTY);
return children;
}
@Override
void writeUnsavedRecursive(Chunk chunk, WriteBuffer buff) {
if (complete) {
super.writeUnsavedRecursive(chunk, buff);
} else if (!isSaved()) {
writeChildrenRecursive(chunk, buff);
}
}
public boolean isComplete() {
return complete;
}
public void setComplete() {
recalculateTotalCount();
complete = true;
}
@Override
public void dump(StringBuilder buff) {
super.dump(buff);
buff.append(", complete:").append(complete);
}
}
private static class Leaf extends Page
{
/**
......
......@@ -15,11 +15,11 @@ import org.h2.engine.Constants;
import org.h2.engine.Domain;
import org.h2.engine.Mode;
import org.h2.engine.Session;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor;
import org.h2.expression.SequenceValue;
import org.h2.expression.ValueExpression;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.schema.Schema;
......
......@@ -9,8 +9,8 @@ import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.FunctionCall;
import org.h2.expression.TableFunction;
import org.h2.expression.function.FunctionCall;
import org.h2.expression.function.TableFunction;
import org.h2.index.FunctionIndex;
import org.h2.index.Index;
import org.h2.index.IndexType;
......
......@@ -15,10 +15,10 @@ import org.h2.command.dml.Select;
import org.h2.engine.Right;
import org.h2.engine.Session;
import org.h2.engine.SysProperties;
import org.h2.expression.Comparison;
import org.h2.expression.ConditionAndOr;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.expression.condition.Comparison;
import org.h2.expression.condition.ConditionAndOr;
import org.h2.index.Index;
import org.h2.index.IndexCondition;
import org.h2.index.IndexCursor;
......
......@@ -44,7 +44,7 @@ public class Csv implements SimpleRowSource {
private String[] columnNames;
private String characterSet = SysProperties.FILE_ENCODING;
private String characterSet;
private char escapeCharacter = '\"';
private char fieldDelimiter = '\"';
private char fieldSeparatorRead = ',';
......@@ -136,7 +136,6 @@ public class Csv implements SimpleRowSource {
* @param rs the result set - the result set must be positioned before the
* first row.
* @param charset the charset or null to use the system default charset
* (see system property file.encoding)
* @return the number of rows written
*/
public int write(String outputFileName, ResultSet rs, String charset)
......@@ -184,7 +183,6 @@ public class Csv implements SimpleRowSource {
* @param colNames or null if the column names should be read from the CSV
* file
* @param charset the charset or null to use the system default charset
* (see system property file.encoding)
* @return the result set
*/
public ResultSet read(String inputFileName, String[] colNames,
......@@ -246,9 +244,7 @@ public class Csv implements SimpleRowSource {
private void init(String newFileName, String charset) {
this.fileName = newFileName;
if (charset != null) {
this.characterSet = charset;
}
this.characterSet = charset;
}
private void initWrite() throws IOException {
......@@ -256,7 +252,8 @@ public class Csv implements SimpleRowSource {
try {
OutputStream out = FileUtils.newOutputStream(fileName, false);
out = new BufferedOutputStream(out, Constants.IO_BUFFER_SIZE);
output = new BufferedWriter(new OutputStreamWriter(out, characterSet));
output = new BufferedWriter(characterSet != null ?
new OutputStreamWriter(out, characterSet) : new OutputStreamWriter(out));
} catch (Exception e) {
close();
throw DbException.convertToIOException(e);
......@@ -314,7 +311,7 @@ public class Csv implements SimpleRowSource {
try {
InputStream in = FileUtils.newInputStream(fileName);
in = new BufferedInputStream(in, Constants.IO_BUFFER_SIZE);
input = new InputStreamReader(in, characterSet);
input = characterSet != null ? new InputStreamReader(in, characterSet) : new InputStreamReader(in);
} catch (IOException e) {
close();
throw e;
......
......@@ -166,7 +166,7 @@ public class Profiler implements Runnable {
}
continue;
}
try (Reader reader = new InputStreamReader(new FileInputStream(arg), "CP1252")) {
try (Reader reader = new InputStreamReader(new FileInputStream(arg))) {
LineNumberReader r = new LineNumberReader(reader);
while (true) {
String line = r.readLine();
......@@ -177,7 +177,7 @@ public class Profiler implements Runnable {
}
}
}
try (Reader reader = new InputStreamReader(new FileInputStream(arg), "CP1252")) {
try (Reader reader = new InputStreamReader(new FileInputStream(arg))) {
LineNumberReader r = new LineNumberReader(reader);
processList(readStackTrace(r));
}
......
......@@ -133,6 +133,7 @@ import org.h2.test.store.TestCacheLIRS;
import org.h2.test.store.TestCacheLongKeyLIRS;
import org.h2.test.store.TestConcurrent;
import org.h2.test.store.TestDataUtils;
import org.h2.test.store.TestDefrag;
import org.h2.test.store.TestFreeSpace;
import org.h2.test.store.TestKillProcessWhileWriting;
import org.h2.test.store.TestMVRTree;
......@@ -922,6 +923,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
addTest(new TestFileLockSerialized());
addTest(new TestFileLockProcess());
addTest(new TestFileSystem());
addTest(new TestDefrag());
addTest(new TestTools());
addTest(new TestSampleApps());
......
......@@ -46,6 +46,8 @@ import org.h2.api.Aggregate;
import org.h2.api.AggregateFunction;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.expression.function.ToDateParser;
import org.h2.expression.function.ToChar.Capitalization;
import org.h2.message.DbException;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
......@@ -55,8 +57,6 @@ import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.StringUtils;
import org.h2.util.ToChar.Capitalization;
import org.h2.util.ToDateParser;
import org.h2.value.Value;
import org.h2.value.ValueTimestamp;
import org.h2.value.ValueTimestampTimeZone;
......
......@@ -1635,7 +1635,14 @@ public class TestPreparedStatement extends TestDb {
anyParameterCheck(ps, values, expected);
ps = conn.prepareStatement("SELECT ID FROM TEST INNER JOIN TABLE(X INT=?) T ON TEST.VALUE = T.X");
anyParameterCheck(ps, values, expected);
// Test expression IN(UNNEST(?))
ps = conn.prepareStatement("SELECT ID FROM TEST WHERE VALUE IN(UNNEST(?))");
assertThrows(ErrorCode.PARAMETER_NOT_SET_1, ps).executeQuery();
anyParameterCheck(ps, values, expected);
anyParameterCheck(ps, 300, new int[] {30});
anyParameterCheck(ps, -5, new int[0]);
// Test expression = ANY(?)
conn.createStatement().execute("SET MODE PostgreSQL");
ps = conn.prepareStatement("SELECT ID FROM TEST WHERE VALUE = ANY(?)");
assertThrows(ErrorCode.PARAMETER_NOT_SET_1, ps).executeQuery();
anyParameterCheck(ps, values, expected);
......
......@@ -161,8 +161,8 @@ public class TestScript extends TestDb {
for (String s : new String[] { "help" }) {
testScript("other/" + s + ".sql");
}
for (String s : new String[] { "array-agg", "avg", "bit-and", "bit-or", "count", "envelope",
"group-concat", "histogram", "max", "median", "min", "mode", "selectivity", "stddev-pop",
for (String s : new String[] { "any", "array-agg", "avg", "bit-and", "bit-or", "count", "envelope",
"every", "group-concat", "histogram", "max", "median", "min", "mode", "selectivity", "stddev-pop",
"stddev-samp", "sum", "var-pop", "var-samp" }) {
testScript("functions/aggregate/" + s + ".sql");
}
......@@ -194,7 +194,7 @@ public class TestScript extends TestDb {
"ifnull", "least", "link-schema", "lock-mode", "lock-timeout",
"memory-free", "memory-used", "nextval", "nullif", "nvl2",
"readonly", "rownum", "schema", "scope-identity", "session-id",
"set", "table", "transaction-id", "truncate-value", "user" }) {
"set", "table", "transaction-id", "truncate-value", "unnest", "user" }) {
testScript("functions/system/" + s + ".sql");
}
for (String s : new String[] { "add_months", "current_date", "current_timestamp",
......
......@@ -8,6 +8,7 @@ package org.h2.test.scripts;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
......@@ -46,8 +47,7 @@ public class TestScriptSimple extends TestDb {
reconnect();
String inFile = "org/h2/test/scripts/testSimple.in.txt";
InputStream is = getClass().getClassLoader().getResourceAsStream(inFile);
LineNumberReader lineReader = new LineNumberReader(
new InputStreamReader(is, "Cp1252"));
LineNumberReader lineReader = new LineNumberReader(new InputStreamReader(is, StandardCharsets.UTF_8));
try (ScriptReader reader = new ScriptReader(lineReader)) {
while (true) {
String sql = reader.readStatement();
......
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE TEST(A INT, B INT);
> ok
INSERT INTO TEST VALUES (1, 1), (1, 3), (2, 1), (2, 5), (3, 4);
> update count: 5
SELECT A, ANY(B < 2), SOME(B > 3), BOOL_OR(B = 1), ANY(B = 1) FILTER (WHERE A = 1) FROM TEST GROUP BY A;
> A ANY(B < 2) ANY(B > 3) ANY(B = 1) ANY(B = 1) FILTER (WHERE (A = 1))
> - ---------- ---------- ---------- ---------------------------------
> 1 TRUE FALSE TRUE TRUE
> 2 TRUE TRUE TRUE null
> 3 FALSE TRUE FALSE null
> rows: 3
DROP TABLE TEST;
> ok
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
CREATE TABLE TEST(A INT, B INT);
> ok
INSERT INTO TEST VALUES (1, 1), (1, 3), (2, 1), (2, 5), (3, 4);
> update count: 5
SELECT A, EVERY(B < 5), BOOL_AND(B > 1), EVERY(B >= 1) FILTER (WHERE A = 1) FROM TEST GROUP BY A;
> A EVERY(B < 5) EVERY(B > 1) EVERY(B >= 1) FILTER (WHERE (A = 1))
> - ------------ ------------ ------------------------------------
> 1 TRUE FALSE TRUE
> 2 FALSE FALSE null
> 3 TRUE TRUE null
> rows: 3
DROP TABLE TEST;
> ok
......@@ -35,3 +35,21 @@ select regexp_replace('first last', '(\w+) (\w+)', '\2 \1');
select regexp_replace('first last', '(\w+) (\w+)', '$2 $1');
>> last first
select regexp_replace('AbcDef', '[^a-z]', '', 'g');
> exception INVALID_VALUE_2
select regexp_replace('First and Second', '[A-Z]', '');
>> irst and econd
set mode PostgreSQL;
> ok
select regexp_replace('AbcDef', '[^a-z]', '', 'g');
>> bcef
select regexp_replace('AbcDef123', '[a-z]', '!', 'gi');
>> !!!!!!123
select regexp_replace('First Only', '[A-Z]', '');
>> irst Only
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
SELECT * FROM UNNEST();
> exception INVALID_PARAMETER_COUNT_2
SELECT * FROM UNNEST(ARRAY[]);
> C1
> --
> rows: 0
SELECT * FROM UNNEST(ARRAY[1, 2, 3]);
> C1
> --
> 1
> 2
> 3
> rows: 3
SELECT * FROM UNNEST(ARRAY[1], ARRAY[2, 3, 4], ARRAY[5, 6]);
> C1 C2 C3
> ---- -- ----
> 1 2 5
> null 3 6
> null 4 null
> rows: 3
SELECT * FROM UNNEST(ARRAY[1], ARRAY[2, 3, 4], ARRAY[5, 6]) WITH ORDINALITY;
> C1 C2 C3 NORD
> ---- -- ---- ----
> 1 2 5 1
> null 3 6 2
> null 4 null 3
> rows: 3
EXPLAIN SELECT * FROM UNNEST(ARRAY[1]);
>> SELECT UNNEST.C1 FROM UNNEST((1,)) /* function */
EXPLAIN SELECT * FROM UNNEST(ARRAY[1]) WITH ORDINALITY;
>> SELECT UNNEST.C1, UNNEST.NORD FROM UNNEST((1,)) WITH ORDINALITY /* function */
SELECT 1 IN(UNNEST(ARRAY[1, 2, 3]));
>> TRUE
SELECT 4 IN(UNNEST(ARRAY[1, 2, 3]));
>> FALSE
SELECT X, X IN(UNNEST(ARRAY[2, 4])) FROM SYSTEM_RANGE(1, 5);
> X X IN(2, 4)
> - ----------
> 1 FALSE
> 2 TRUE
> 3 FALSE
> 4 TRUE
> 5 FALSE
> rows: 5
SELECT X, X IN(UNNEST(?)) FROM SYSTEM_RANGE(1, 5);
{
2
> X X IN(UNNEST(?1))
> - ----------------
> 1 FALSE
> 2 TRUE
> 3 FALSE
> 4 FALSE
> 5 FALSE
> rows: 5
};
> update count: 0
CREATE TABLE TEST(A INT, B ARRAY);
> ok
INSERT INTO TEST VALUES (2, ARRAY[2, 4]), (3, ARRAY[2, 5]);
> update count: 2
SELECT A, B, A IN(UNNEST(B)) FROM TEST;
> A B A IN(UNNEST(B))
> - ------ ---------------
> 2 (2, 4) TRUE
> 3 (2, 5) FALSE
> rows: 2
DROP TABLE TEST;
> ok
......@@ -6513,6 +6513,9 @@ SELECT * FROM CUSTOMER WHERE NAME NOT IN(SELECT NAME FROM CUSTOMER);
> -- ----
> rows: 0
SET MODE PostgreSQL;
> ok
SELECT * FROM CUSTOMER WHERE NAME = ANY(SELECT NAME FROM CUSTOMER);
> ID NAME
> -- -------
......@@ -6545,6 +6548,9 @@ SELECT * FROM CUSTOMER WHERE NAME < ANY(SELECT NAME FROM CUSTOMER);
> 2 Meier
> rows: 2
SET MODE Regular;
> ok
DROP TABLE INVOICE;
> ok
......@@ -6576,9 +6582,9 @@ select * from s;
> rows: 1
select some(y>10), every(y>10), min(y), max(y) from t;
> BOOL_OR(Y > 10.0) BOOL_AND(Y > 10.0) MIN(Y) MAX(Y)
> ----------------- ------------------ ------ ------
> null null null null
> ANY(Y > 10.0) EVERY(Y > 10.0) MIN(Y) MAX(Y)
> ------------- --------------- ------ ------
> null null null null
> rows: 1
insert into t values(1000000004, 4);
......@@ -6634,9 +6640,9 @@ stddev_pop(distinct y) s_py, stddev_samp(distinct y) s_sy, var_pop(distinct y) v
> rows: 1
select some(y>10), every(y>10), min(y), max(y) from t;
> BOOL_OR(Y > 10.0) BOOL_AND(Y > 10.0) MIN(Y) MAX(Y)
> ----------------- ------------------ ------ ------
> TRUE FALSE 4.0 16.0
> ANY(Y > 10.0) EVERY(Y > 10.0) MIN(Y) MAX(Y)
> ------------- --------------- ------ ------
> TRUE FALSE 4.0 16.0
> rows: 1
drop view s;
......
/*
* Copyright 2004-2018 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.store;
import static org.h2.engine.Constants.SUFFIX_MV_FILE;
import org.h2.test.TestBase;
import org.h2.test.TestDb;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* Test off-line compaction procedure used by SHUTDOWN DEFRAG command
*
* @author <a href='mailto:andrei.tokar@gmail.com'>Andrei Tokar</a>
*/
public class TestDefrag extends TestDb
{
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public boolean isEnabled() {
return config.mvStore && !config.memory && config.big && !config.travis;
}
@Override
public void test() throws Exception {
String dbName = getTestName();
deleteDb(dbName);
File dbFile = new File(getBaseDir(), dbName + SUFFIX_MV_FILE);
try (Connection c = getConnection(dbName)) {
try (Statement st = c.createStatement()) {
st.execute("CREATE TABLE IF NOT EXISTS test (id INT PRIMARY KEY, txt varchar)" +
" AS SELECT x, x || SPACE(200) FROM SYSTEM_RANGE(1,10000000)");
}
long origSize = dbFile.length();
assertTrue(origSize > 4_000_000_000L);
try (Statement st = c.createStatement()) {
st.execute("shutdown defrag");
}
long compactedSize = dbFile.length();
assertTrue(compactedSize < 400_000_000);
}
try (Connection c = getConnection(dbName + ";LAZY_QUERY_EXECUTION=1")) {
try (Statement st = c.createStatement()) {
ResultSet rs = st.executeQuery("SELECT * FROM test");
int count = 0;
while (rs.next()) {
++count;
assertEquals(count, rs.getInt(1));
assertTrue(rs.getString(2).startsWith(count + " "));
}
assertEquals(10_000_000, count);
}
}
deleteDb(dbName);
}
}
......@@ -45,7 +45,7 @@ public class TestConcurrentUpdate extends TestDb {
@Override
public void test() throws Exception {
deleteDb("concurrent");
final String url = getURL("concurrent", true);
final String url = getURL("concurrent;LOCK_TIMEOUT=2000", true);
try (Connection conn = getConnection(url)) {
Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)");
......
......@@ -26,6 +26,8 @@ public class TestClearReferences extends TestBase {
"org.h2.compress.CompressLZF.cachedHashTable",
"org.h2.engine.DbSettings.defaultSettings",
"org.h2.engine.SessionRemote.sessionFactory",
"org.h2.expression.function.DateTimeFunctions.MONTHS_AND_WEEKS",
"org.h2.expression.function.ToChar.NAMES",
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.RecoverTester.instance",
"org.h2.store.fs.FilePath.defaultProvider",
......@@ -36,7 +38,6 @@ public class TestClearReferences extends TestBase {
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
"org.h2.util.DateTimeFunctions.MONTHS_AND_WEEKS",
"org.h2.util.DateTimeUtils.timeZone",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.util.NetUtils.cachedLocalAddress",
......@@ -45,7 +46,6 @@ public class TestClearReferences extends TestBase {
"org.h2.util.JdbcUtils.allowedClassNamePrefixes",
"org.h2.util.JdbcUtils.userClassFactories",
"org.h2.util.Task.counter",
"org.h2.util.ToChar.NAMES",
"org.h2.value.CompareMode.lastUsed",
"org.h2.value.Value.softCache",
};
......
......@@ -6,7 +6,7 @@
package org.h2.test.unit;
import java.text.Collator;
import org.h2.expression.CompareLike;
import org.h2.expression.condition.CompareLike;
import org.h2.test.TestBase;
import org.h2.value.CompareMode;
......
......@@ -10,10 +10,10 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Random;
import org.h2.expression.function.DateTimeFunctions;
import org.h2.message.DbException;
import org.h2.test.TestBase;
import org.h2.test.utils.AssertThrows;
import org.h2.util.DateTimeFunctions;
import org.h2.util.StringUtils;
/**
......
......@@ -802,3 +802,4 @@ partitioned tri partitions
discard enhancements nolock surefire logarithm
qualification opportunity jumping exploited unacceptable vrs duplicated
queryparser tokenized freeze factorings recompilation unenclosed rfe dsync
econd irst bcef ordinality nord unnest
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论