Unverified 提交 3d365e4c authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1567 from katzyn/any

Add ANY, UNNEST, and fix SOME parsing
......@@ -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,18 @@ 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
......
......@@ -952,13 +952,14 @@ or the SQL statement <code>SET MODE PostgreSQL</code>.
</li><li>LOG(x) is base 10 in this mode.
</li><li>REGEXP_REPLACE():
<ul>
<li>Uses \ for back-references</li>
<li>Will not throw an exception when the <code>flagsString</code> parameter contains a 'g'</li>
<li>In the absence of the 'g' flag in the <code>flagsString</code> parameter, only the first-matched substring will be replaced</li>
<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,14 +149,6 @@ 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;
......@@ -181,6 +173,14 @@ 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;
......@@ -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;
......
......@@ -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;
......
......@@ -18,8 +18,6 @@ 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;
......@@ -27,6 +25,8 @@ import org.h2.expression.Parameter;
import org.h2.expression.Wildcard;
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;
......
......@@ -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);
......
......@@ -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,6 +132,16 @@ 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;
......
......@@ -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);
}
......
......@@ -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;
......
......@@ -105,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).
......@@ -209,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);
......@@ -652,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;
......@@ -779,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:
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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
......@@ -134,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;
......@@ -459,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);
......@@ -538,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);
......
......@@ -17,6 +17,7 @@ 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;
......@@ -24,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
......@@ -48,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 {
......@@ -104,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);
}
}
......@@ -132,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;
}
}
......@@ -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 others.
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;
......
......@@ -936,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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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",
......
-- 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
-- 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;
......
......@@ -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;
......
......@@ -802,4 +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
econd irst bcef ordinality nord unnest
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论