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

Merge pull request #1611 from katzyn/row

Handle null elements in array and row values in comparison operations properly
......@@ -412,8 +412,8 @@ public class Database implements DataHandler {
}
/**
* Compare two values with the current comparison mode. The values may not
* be of the same type.
* Compare two values with the current comparison mode. The values may have
* different data types including NULL.
*
* @param a the first value
* @param b the second value
......@@ -424,6 +424,24 @@ public class Database implements DataHandler {
return a.compareTo(b, mode, compareMode);
}
/**
* Compare two values with the current comparison mode. The values may have
* different data types including NULL.
*
* @param v the other value
* @param databaseMode the database mode
* @param compareMode the compare mode
* @param a the first value
* @param b the second value
* @param forEquality perform only check for equality (= or <>)
* @return 0 if both values are equal, -1 if the first value is smaller, 1
* if the second value is larger, {@link Integer#MIN_VALUE} if order
* is not defined due to NULL comparison
*/
public int compareWithNull(Value a, Value b, boolean forEquality) {
return a.compareWithNull(b, forEquality, mode, compareMode);
}
/**
* Compare two values with the current comparison mode. The values must be
* of the same type.
......
......@@ -283,58 +283,101 @@ public class Comparison extends Condition {
}
return ValueBoolean.get(result);
}
if (l == ValueNull.INSTANCE) {
if ((compareType & NULL_SAFE) == 0) {
return ValueNull.INSTANCE;
}
}
Value r = right.getValue(session);
if (r == ValueNull.INSTANCE) {
if ((compareType & NULL_SAFE) == 0) {
return ValueNull.INSTANCE;
}
// Optimization: do not evaluate right if not necessary
if (l == ValueNull.INSTANCE && (compareType & NULL_SAFE) == 0) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(compareNotNull(database, l, r, compareType));
return compare(database, l, right.getValue(session), compareType);
}
/**
* Compare two values, given the values are not NULL.
* Compare two values.
*
* @param database the database
* @param l the first value
* @param r the second value
* @param compareType the compare type
* @return true if the comparison indicated by the comparison type evaluates
* to true
* @return result of comparison, either TRUE, FALSE, or NULL
*/
static boolean compareNotNull(Database database, Value l, Value r,
int compareType) {
boolean result;
static Value compare(Database database, Value l, Value r, int compareType) {
Value result;
switch (compareType) {
case EQUAL:
case EQUAL: {
int cmp = database.compareWithNull(l, r, true);
if (cmp == 0) {
result = ValueBoolean.TRUE;
} else if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.FALSE;
}
break;
}
case EQUAL_NULL_SAFE:
result = database.areEqual(l, r);
result = ValueBoolean.get(database.areEqual(l, r));
break;
case NOT_EQUAL:
case NOT_EQUAL: {
int cmp = database.compareWithNull(l, r, true);
if (cmp == 0) {
result = ValueBoolean.FALSE;
} else if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.TRUE;
}
break;
}
case NOT_EQUAL_NULL_SAFE:
result = !database.areEqual(l, r);
result = ValueBoolean.get(!database.areEqual(l, r));
break;
case BIGGER_EQUAL:
result = database.compare(l, r) >= 0;
case BIGGER_EQUAL: {
int cmp = database.compareWithNull(l, r, false);
if (cmp >= 0) {
result = ValueBoolean.TRUE;
} else if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.FALSE;
}
break;
case BIGGER:
result = database.compare(l, r) > 0;
}
case BIGGER: {
int cmp = database.compareWithNull(l, r, false);
if (cmp > 0) {
result = ValueBoolean.TRUE;
} else if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.FALSE;
}
break;
case SMALLER_EQUAL:
result = database.compare(l, r) <= 0;
}
case SMALLER_EQUAL: {
int cmp = database.compareWithNull(l, r, false);
if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.get(cmp <= 0);
}
break;
case SMALLER:
result = database.compare(l, r) < 0;
}
case SMALLER: {
int cmp = database.compareWithNull(l, r, false);
if (cmp == Integer.MIN_VALUE) {
result = ValueNull.INSTANCE;
} else {
result = ValueBoolean.get(cmp < 0);
}
break;
}
case SPATIAL_INTERSECTS: {
ValueGeometry lg = (ValueGeometry) l.convertTo(Value.GEOMETRY);
ValueGeometry rg = (ValueGeometry) r.convertTo(Value.GEOMETRY);
result = lg.intersectsBoundingBox(rg);
if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
result = ValueNull.INSTANCE;
} else {
ValueGeometry lg = (ValueGeometry) l.convertTo(Value.GEOMETRY);
ValueGeometry rg = (ValueGeometry) r.convertTo(Value.GEOMETRY);
result = ValueBoolean.get(lg.intersectsBoundingBox(rg));
}
break;
}
default:
......
......@@ -49,8 +49,8 @@ public class ConditionIn extends Condition {
@Override
public Value getValue(Session session) {
Value l = left.getValue(session);
if (l == ValueNull.INSTANCE) {
return l;
if (l.containsNull()) {
return ValueNull.INSTANCE;
}
int size = valueList.size();
if (size == 1) {
......@@ -59,24 +59,21 @@ public class ConditionIn extends Condition {
return ConditionInParameter.getValue(database, l, e.getValue(session));
}
}
boolean result = false;
boolean hasNull = false;
for (int i = 0; i < size; i++) {
Expression e = valueList.get(i);
Value r = e.getValue(session);
if (r == ValueNull.INSTANCE) {
Value cmp = Comparison.compare(database, l, r, Comparison.EQUAL);
if (cmp == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
} else if (cmp == ValueBoolean.TRUE) {
return cmp;
}
}
if (!result && hasNull) {
if (hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
return ValueBoolean.FALSE;
}
@Override
......@@ -114,7 +111,7 @@ public class ConditionIn extends Condition {
ArrayList<Expression> list = new ArrayList<>(ri.getRowCount());
while (ri.next()) {
Value v = ri.currentRow()[0];
if (v != ValueNull.INSTANCE) {
if (!v.containsNull()) {
allValuesNull = false;
}
list.add(ValueExpression.get(v));
......@@ -129,7 +126,7 @@ public class ConditionIn extends Condition {
for (int i = 0; i < size; i++) {
Expression e = valueList.get(i);
e = e.optimize(session);
if (e.isConstant() && e.getValue(session) != ValueNull.INSTANCE) {
if (e.isConstant() && !e.getValue(session).containsNull()) {
allValuesNull = false;
}
if (allValuesConstant && !e.isConstant()) {
......
......@@ -35,6 +35,7 @@ public class ConditionInConstantSet extends Condition {
private Expression left;
private final ArrayList<Expression> valueList;
private final TreeSet<Value> valueSet;
private boolean hasNull;
private final int type;
private ExtTypeInfo extTypeInfo;
......@@ -58,27 +59,32 @@ public class ConditionInConstantSet extends Condition {
if (type == Value.ENUM) {
extTypeInfo = ((ExpressionColumn) left).getColumn().getExtTypeInfo();
for (Expression expression : valueList) {
valueSet.add(extTypeInfo.cast(expression.getValue(session)));
add(extTypeInfo.cast(expression.getValue(session)));
}
} else {
for (Expression expression : valueList) {
valueSet.add(expression.getValue(session).convertTo(type, mode));
add(expression.getValue(session).convertTo(type, mode));
}
}
}
private void add(Value v) {
if (v.containsNull()) {
hasNull = true;
} else {
valueSet.add(v);
}
}
@Override
public Value getValue(Session session) {
Value x = left.getValue(session);
if (x == ValueNull.INSTANCE) {
if (x.containsNull()) {
return x;
}
boolean result = valueSet.contains(x);
if (!result) {
boolean setHasNull = valueSet.contains(ValueNull.INSTANCE);
if (setHasNull) {
return ValueNull.INSTANCE;
}
if (!result && hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
}
......@@ -168,9 +174,9 @@ public class ConditionInConstantSet extends Condition {
if (add.isConstant()) {
valueList.add(add);
if (type == Value.ENUM) {
valueSet.add(add.getValue(session).convertToEnum(extTypeInfo));
add(add.getValue(session).convertToEnum(extTypeInfo));
} else {
valueSet.add(add.getValue(session).convertTo(type, session.getDatabase().getMode()));
add(add.getValue(session).convertTo(type, session.getDatabase().getMode()));
}
return this;
}
......
......@@ -66,38 +66,33 @@ 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) {
if (value.containsNull()) {
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) {
Value cmp = Comparison.compare(database, l, r, Comparison.EQUAL);
if (cmp == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
} else if (cmp == ValueBoolean.TRUE) {
return cmp;
}
}
} else {
for (Value r : ((ValueArray) value.convertTo(Value.ARRAY)).getList()) {
if (r == ValueNull.INSTANCE) {
Value cmp = Comparison.compare(database, l, r, Comparison.EQUAL);
if (cmp == ValueNull.INSTANCE) {
hasNull = true;
} else {
result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL);
if (result) {
break;
}
} else if (cmp == ValueBoolean.TRUE) {
return cmp;
}
}
}
if (!result && hasNull) {
if (hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
return ValueBoolean.FALSE;
}
/**
......
......@@ -5,6 +5,7 @@
*/
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;
......@@ -12,15 +13,16 @@ 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;
import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueArray;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
import org.h2.value.ValueRow;
/**
* An 'in' condition with a subquery, as in WHERE ID IN(SELECT ...)
......@@ -52,8 +54,8 @@ public class ConditionInSelect extends Condition {
Value l = left.getValue(session);
if (!rows.hasNext()) {
return ValueBoolean.get(all);
} else if (l == ValueNull.INSTANCE) {
return l;
} else if (l.containsNull()) {
return ValueNull.INSTANCE;
}
if (!database.getSettings().optimizeInSelect) {
return getValueSlow(rows, l);
......@@ -64,8 +66,8 @@ public class ConditionInSelect extends Condition {
}
int columnCount = query.getColumnCount();
if (columnCount != 1) {
l = l.convertTo(Value.ARRAY);
Value[] leftValue = ((ValueArray) l).getList();
l = l.convertTo(Value.ROW);
Value[] leftValue = ((ValueRow) l).getList();
if (columnCount == leftValue.length && rows.containsDistinct(leftValue)) {
return ValueBoolean.TRUE;
}
......@@ -74,13 +76,20 @@ public class ConditionInSelect extends Condition {
if (dataType == Value.NULL) {
return ValueBoolean.FALSE;
}
if (l.getType() == Value.ROW) {
Value[] leftList = ((ValueRow) l).getList();
if (leftList.length != 1) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
l = leftList[0];
}
l = l.convertTo(dataType, database.getMode());
if (rows.containsDistinct(new Value[] { l })) {
return ValueBoolean.TRUE;
}
if (rows.containsDistinct(new Value[] { ValueNull.INSTANCE })) {
return ValueNull.INSTANCE;
}
}
if (rows.containsNull()) {
return ValueNull.INSTANCE;
}
return ValueBoolean.FALSE;
}
......@@ -89,29 +98,35 @@ public class ConditionInSelect extends Condition {
// this only returns the correct result if the result has at least one
// row, and if l is not null
boolean hasNull = false;
boolean result = all;
while (rows.next()) {
boolean value;
Value[] currentRow = rows.currentRow();
Value r = query.getColumnCount() == 1 ? currentRow[0] : org.h2.value.ValueArray.get(currentRow);
if (r == ValueNull.INSTANCE) {
value = false;
hasNull = true;
} else {
value = Comparison.compareNotNull(database, l, r, compareType);
if (all) {
while (rows.next()) {
Value cmp = compare(l, rows);
if (cmp == ValueNull.INSTANCE) {
hasNull = true;
} else if (cmp == ValueBoolean.FALSE) {
return cmp;
}
}
if (!value && all) {
result = false;
break;
} else if (value && !all) {
result = true;
break;
} else {
while (rows.next()) {
Value cmp = compare(l, rows);
if (cmp == ValueNull.INSTANCE) {
hasNull = true;
} else if (cmp == ValueBoolean.TRUE) {
return cmp;
}
}
}
if (!result && hasNull) {
if (hasNull) {
return ValueNull.INSTANCE;
}
return ValueBoolean.get(result);
return ValueBoolean.get(all);
}
private Value compare(Value l, ResultInterface rows) {
Value[] currentRow = rows.currentRow();
Value r = l.getType() != Value.ROW && query.getColumnCount() == 1 ? currentRow[0] : ValueRow.get(currentRow);
return Comparison.compare(database, l, r, compareType);
}
@Override
......
......@@ -49,6 +49,14 @@ public interface LocalResult extends ResultInterface, ResultTarget {
*/
boolean containsDistinct(Value[] values);
/**
* Check if this result set contains a NULL value. This method may reset
* this result.
*
* @return true if there is a NULL value
*/
boolean containsNull();
/**
* Remove the row from the result set if it exists.
*
......
......@@ -45,6 +45,7 @@ public class LocalResultImpl implements LocalResult {
private int[] distinctIndexes;
private boolean closed;
private boolean containsLobs;
private Boolean containsNull;
/**
* Construct a local result object.
......@@ -127,6 +128,7 @@ public class LocalResultImpl implements LocalResult {
copy.offset = 0;
copy.limit = -1;
copy.external = e2;
copy.containsNull = containsNull;
return copy;
}
......@@ -212,6 +214,27 @@ public class LocalResultImpl implements LocalResult {
return distinctRows.get(array) != null;
}
@Override
public boolean containsNull() {
Boolean r = containsNull;
if (r == null) {
r = false;
reset();
loop: while (next()) {
Value[] row = currentRow;
for (int i = 0; i < visibleColumnCount; i++) {
if (row[i].containsNull()) {
r = true;
break loop;
}
}
}
reset();
containsNull = r;
}
return r;
}
@Override
public void reset() {
rowId = -1;
......
......@@ -1347,7 +1347,7 @@ public abstract class Value {
return ValueResultSet.get(result);
}
private DbException getDataConversionError(int targetType) {
DbException getDataConversionError(int targetType) {
DataType from = DataType.getDataType(getType());
DataType to = DataType.getDataType(targetType);
throw DbException.get(ErrorCode.DATA_CONVERSION_ERROR_1, (from != null ? from.name : "type=" + getType())
......@@ -1372,7 +1372,7 @@ public abstract class Value {
* @param v the other value
* @param databaseMode the database mode
* @param compareMode the compare mode
* @return 0 if both values are equal, -1 if the other value is smaller, and
* @return 0 if both values are equal, -1 if this value is smaller, and
* 1 otherwise
*/
public final int compareTo(Value v, Mode databaseMode, CompareMode compareMode) {
......@@ -1401,6 +1401,48 @@ public abstract class Value {
return l.compareTypeSafe(v, compareMode);
}
/**
* Compare this value against another value using the specified compare
* mode.
*
* @param v the other value
* @param forEquality perform only check for equality
* @param databaseMode the database mode
* @param compareMode the compare mode
* @return 0 if both values are equal, -1 if this value is smaller, 1
* if other value is larger, {@link Integer#MIN_VALUE} if order is
* not defined due to NULL comparison
*/
public int compareWithNull(Value v, boolean forEquality, Mode databaseMode, CompareMode compareMode) {
if (this == ValueNull.INSTANCE || v == ValueNull.INSTANCE) {
return Integer.MIN_VALUE;
}
Value l = this;
int leftType = l.getType();
int rightType = v.getType();
if (leftType != rightType || leftType == Value.ENUM) {
int dataType = Value.getHigherOrder(leftType, rightType);
if (dataType == Value.ENUM) {
ExtTypeInfoEnum enumerators = ExtTypeInfoEnum.getEnumeratorsForBinaryOperation(l, v);
l = l.convertToEnum(enumerators);
v = v.convertToEnum(enumerators);
} else {
l = l.convertTo(dataType, databaseMode);
v = v.convertTo(dataType, databaseMode);
}
}
return l.compareTypeSafe(v, compareMode);
}
/**
* Returns true if this value is NULL or contains NULL value.
*
* @return true if this value is NULL or contains NULL value
*/
public boolean containsNull() {
return false;
}
public int getScale() {
return 0;
}
......
......@@ -10,14 +10,12 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.util.MathUtils;
/**
* Implementation of the ARRAY data type.
*/
public class ValueArray extends Value {
public class ValueArray extends ValueCollectionBase {
/**
* Empty array.
......@@ -25,12 +23,10 @@ public class ValueArray extends Value {
private static final Object EMPTY = get(new Value[0]);
private final Class<?> componentType;
private final Value[] values;
private int hash;
private ValueArray(Class<?> componentType, Value[] list) {
super(list);
this.componentType = componentType;
this.values = list;
}
/**
......@@ -65,19 +61,6 @@ public class ValueArray extends Value {
return (ValueArray) EMPTY;
}
@Override
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = 1;
for (Value v : values) {
h = h * 31 + v.hashCode();
}
hash = h;
return h;
}
public Value[] getList() {
return values;
}
......@@ -91,15 +74,6 @@ public class ValueArray extends Value {
return componentType;
}
@Override
public long getPrecision() {
long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
}
@Override
public String getString() {
StringBuilder builder = new StringBuilder().append('[');
......@@ -181,15 +155,6 @@ public class ValueArray extends Value {
return builder.append(']').toString();
}
@Override
public int getDisplaySize() {
long size = 0;
for (Value v : values) {
size += v.getDisplaySize();
}
return MathUtils.convertLongToInt(size);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ValueArray)) {
......@@ -211,15 +176,6 @@ public class ValueArray extends Value {
return true;
}
@Override
public int getMemory() {
int memory = 32;
for (Value v : values) {
memory += v.getMemory() + Constants.MEMORY_POINTER;
}
return memory;
}
@Override
public Value convertPrecision(long precision, boolean force) {
if (!force) {
......
/*
* 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.value;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Mode;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Base class for ARRAY and ROW values.
*/
abstract class ValueCollectionBase extends Value {
final Value[] values;
private int hash;
ValueCollectionBase(Value[] values) {
this.values = values;
}
@Override
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = getType();
for (Value v : values) {
h = h * 31 + v.hashCode();
}
hash = h;
return h;
}
@Override
public long getPrecision() {
long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
}
@Override
public int getDisplaySize() {
long size = 0;
for (Value v : values) {
size += v.getDisplaySize();
}
return MathUtils.convertLongToInt(size);
}
@Override
public int compareWithNull(Value v, boolean forEquality, Mode databaseMode, CompareMode compareMode) {
if (v == ValueNull.INSTANCE) {
return Integer.MIN_VALUE;
}
ValueCollectionBase l = this;
int leftType = l.getType();
int rightType = v.getType();
if (rightType != ARRAY && rightType != ROW) {
throw v.getDataConversionError(leftType);
}
ValueCollectionBase r = (ValueCollectionBase) v;
Value[] leftArray = l.values, rightArray = r.values;
int leftLength = leftArray.length, rightLength = rightArray.length;
if (leftLength != rightLength) {
if (leftType == ROW || rightType == ROW) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
if (forEquality) {
return 1;
}
}
if (forEquality) {
boolean hasNull = false;
for (int i = 0; i < leftLength; i++) {
Value v1 = leftArray[i];
Value v2 = rightArray[i];
int comp = v1.compareWithNull(v2, forEquality, databaseMode, compareMode);
if (comp != 0) {
if (comp != Integer.MIN_VALUE) {
return comp;
}
hasNull = true;
}
}
return hasNull ? Integer.MIN_VALUE : 0;
}
int len = Math.min(leftLength, rightLength);
for (int i = 0; i < len; i++) {
Value v1 = leftArray[i];
Value v2 = rightArray[i];
int comp = v1.compareWithNull(v2, forEquality, databaseMode, compareMode);
if (comp != 0) {
return comp;
}
}
return Integer.compare(leftLength, rightLength);
}
@Override
public boolean containsNull() {
for (Value v : values) {
if (v.containsNull()) {
return true;
}
}
return false;
}
@Override
public int getMemory() {
int memory = 32;
for (Value v : values) {
memory += v.getMemory() + Constants.MEMORY_POINTER;
}
return memory;
}
}
......@@ -143,6 +143,11 @@ public class ValueNull extends Value {
throw DbException.throwInternalError("compare null");
}
@Override
public boolean containsNull() {
return true;
}
@Override
public long getPrecision() {
return PRECISION;
......
......@@ -10,26 +10,21 @@ import java.sql.SQLException;
import java.util.Arrays;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.message.DbException;
import org.h2.util.MathUtils;
/**
* Row value.
*/
public class ValueRow extends Value {
public class ValueRow extends ValueCollectionBase {
/**
* Empty row.
*/
private static final Object EMPTY = get(new Value[0]);
private final Value[] values;
private int hash;
private ValueRow(Value[] list) {
this.values = list;
super(list);
}
/**
......@@ -52,19 +47,6 @@ public class ValueRow extends Value {
return (ValueRow) EMPTY;
}
@Override
public int hashCode() {
if (hash != 0) {
return hash;
}
int h = 1;
for (Value v : values) {
h = h * 31 + v.hashCode();
}
hash = h;
return h;
}
public Value[] getList() {
return values;
}
......@@ -74,15 +56,6 @@ public class ValueRow extends Value {
return Value.ROW;
}
@Override
public long getPrecision() {
long p = 0;
for (Value v : values) {
p += v.getPrecision();
}
return p;
}
@Override
public String getString() {
StringBuilder builder = new StringBuilder("ROW (");
......@@ -165,15 +138,6 @@ public class ValueRow extends Value {
return builder.append(')').toString();
}
@Override
public int getDisplaySize() {
long size = 0;
for (Value v : values) {
size += v.getDisplaySize();
}
return MathUtils.convertLongToInt(size);
}
@Override
public boolean equals(Object other) {
if (!(other instanceof ValueRow)) {
......@@ -195,15 +159,6 @@ public class ValueRow extends Value {
return true;
}
@Override
public int getMemory() {
int memory = 32;
for (Value v : values) {
memory += v.getMemory() + Constants.MEMORY_POINTER;
}
return memory;
}
@Override
public Value convertPrecision(long precision, boolean force) {
if (!force) {
......
......@@ -74,6 +74,7 @@ import org.h2.test.db.TestSetCollation;
import org.h2.test.db.TestSpaceReuse;
import org.h2.test.db.TestSpatial;
import org.h2.test.db.TestSpeed;
import org.h2.test.db.TestSubqueryPerformanceOnLazyExecutionMode;
import org.h2.test.db.TestSynonymForTable;
import org.h2.test.db.TestTableEngines;
import org.h2.test.db.TestTempTables;
......@@ -223,7 +224,6 @@ import org.h2.test.unit.TestSort;
import org.h2.test.unit.TestStreams;
import org.h2.test.unit.TestStringCache;
import org.h2.test.unit.TestStringUtils;
import org.h2.test.unit.TestSubqueryPerformanceOnLazyExecutionMode;
import org.h2.test.unit.TestTimeStampWithTimeZone;
import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestTraceSystem;
......
......@@ -3,7 +3,7 @@
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.unit;
package org.h2.test.db;
import org.h2.command.dml.SetTypes;
import org.h2.test.TestBase;
......@@ -33,6 +33,11 @@ public class TestSubqueryPerformanceOnLazyExecutionMode extends TestDb {
TestBase.createCaller().init().test();
}
@Override
public boolean isEnabled() {
return !config.travis;
}
@Override
public void test() throws Exception {
deleteDb("lazySubq");
......
......@@ -23,3 +23,66 @@ SELECT ARRAY[10];
SELECT ARRAY[10, 20, 30];
>> [10, 20, 30]
SELECT ARRAY[1, NULL] IS NOT DISTINCT FROM ARRAY[1, NULL];
>> TRUE
SELECT ARRAY[1, NULL] IS DISTINCT FROM ARRAY[1, NULL];
>> FALSE
SELECT ARRAY[1, NULL] = ARRAY[1, NULL];
>> null
SELECT ARRAY[1, NULL] <> ARRAY[1, NULL];
>> null
SELECT ARRAY[NULL] = ARRAY[NULL, NULL];
>> FALSE
select ARRAY[1, NULL, 2] = ARRAY[1, NULL, 1];
>> FALSE
select ARRAY[1, NULL, 2] <> ARRAY[1, NULL, 1];
>> TRUE
SELECT ARRAY[1, NULL] > ARRAY[1, NULL];
>> null
SELECT ARRAY[1, 2] > ARRAY[1, NULL];
>> null
SELECT ARRAY[1, 2, NULL] > ARRAY[1, 1, NULL];
>> TRUE
SELECT ARRAY[1, 1, NULL] > ARRAY[1, 2, NULL];
>> FALSE
SELECT ARRAY[1, 2, NULL] < ARRAY[1, 1, NULL];
>> FALSE
SELECT ARRAY[1, 1, NULL] <= ARRAY[1, 1, NULL];
>> null
SELECT ARRAY[1, NULL] IN (ARRAY[1, NULL]);
>> null
CREATE TABLE TEST(A ARRAY);
> ok
INSERT INTO TEST VALUES (ARRAY[1, NULL]), (ARRAY[1, 2]);
> update count: 2
SELECT ARRAY[1, 2] IN (SELECT A FROM TEST);
>> TRUE
SELECT ROW (ARRAY[1, 2]) IN (SELECT A FROM TEST);
>> TRUE
SELECT ARRAY[1, NULL] IN (SELECT A FROM TEST);
>> null
SELECT ROW (ARRAY[1, NULL]) IN (SELECT A FROM TEST);
>> null
DROP TABLE TEST;
> ok
......@@ -8,3 +8,66 @@ SELECT ROW (10);
SELECT (10, 20, 30);
>> ROW (10, 20, 30)
SELECT (1, NULL) IS NOT DISTINCT FROM (1, NULL);
>> TRUE
SELECT (1, NULL) IS DISTINCT FROM ROW (1, NULL);
>> FALSE
SELECT (1, NULL) = (1, NULL);
>> null
SELECT (1, NULL) <> (1, NULL);
>> null
SELECT ROW (NULL) = (NULL, NULL);
> exception COLUMN_COUNT_DOES_NOT_MATCH
select (1, NULL, 2) = (1, NULL, 1);
>> FALSE
select (1, NULL, 2) <> (1, NULL, 1);
>> TRUE
SELECT (1, NULL) > (1, NULL);
>> null
SELECT (1, 2) > (1, NULL);
>> null
SELECT (1, 2, NULL) > (1, 1, NULL);
>> TRUE
SELECT (1, 1, NULL) > (1, 2, NULL);
>> FALSE
SELECT (1, 2, NULL) < (1, 1, NULL);
>> FALSE
SELECT (1, 1, NULL) <= (1, 1, NULL);
>> null
SELECT (1, 2) IN (SELECT 1, 2);
>> TRUE
SELECT (1, 2) IN (SELECT * FROM VALUES (1, 2), (1, NULL));
>> TRUE
SELECT (1, 2) IN (SELECT * FROM VALUES (1, 1), (1, NULL));
>> null
SELECT (1, 2) IN (SELECT * FROM VALUES (1, 1), (1, 3));
>> FALSE
SELECT (1, NULL) IN (SELECT 1, NULL);
>> null
SELECT (1, ARRAY[1]) IN (SELECT 1, ARRAY[1]);
>> TRUE
SELECT (1, ARRAY[1]) IN (SELECT 1, ARRAY[2]);
>> FALSE
SELECT (1, ARRAY[NULL]) IN (SELECT 1, ARRAY[NULL]);
>> null
......@@ -244,11 +244,11 @@ CREATE TABLE TEST(A INT, B INT);
INSERT INTO TEST VALUES (1, 1), (1, 2), (2, 1), (2, 2), (2, 3);
> update count: 5
SELECT A, COUNT(B) FROM TEST GROUP BY A OFFSET 1;
SELECT A, COUNT(B) FROM TEST GROUP BY A ORDER BY A OFFSET 1;
> A COUNT(B)
> - --------
> 2 3
> rows: 1
> rows (ordered): 1
DROP TABLE TEST;
> ok
......@@ -463,3 +463,6 @@ SELECT (3, 4) > ALL (SELECT ID, V FROM TEST);
DROP TABLE TEST;
> ok
SELECT 1 = ALL (SELECT * FROM VALUES (NULL), (1), (2), (NULL) ORDER BY 1);
>> FALSE
......@@ -386,15 +386,17 @@ public class TestNestedJoins extends TestDb {
rs = stat.executeQuery("select distinct t1.a, t2.a, t3.a from t1 " +
"right outer join t3 on t1.b=t3.a " +
"right outer join t2 on t2.b=t1.a");
// expected: 1 1 1; null 2 null
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("1", rs.getString(2));
assertEquals("1", rs.getString(3));
// expected:
// null 2 null
// 1 1 1
assertTrue(rs.next());
assertEquals(null, rs.getString(1));
assertEquals("2", rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("1", rs.getString(2));
assertEquals("1", rs.getString(3));
assertFalse(rs.next());
stat.execute("drop table t1, t2, t3, t4");
......
......@@ -339,15 +339,17 @@ public class TestOuterJoins extends TestDb {
"ON T2.B = T1.A", sql);
rs = stat.executeQuery("select distinct t1.a, t2.a, t3.a from t1 " +
"right outer join t3 on t1.b=t3.a right outer join t2 on t2.b=t1.a");
// expected: 1 1 1; null 2 null
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("1", rs.getString(2));
assertEquals("1", rs.getString(3));
// expected:
// null 2 null
// 1 1 1
assertTrue(rs.next());
assertEquals(null, rs.getString(1));
assertEquals("2", rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("1", rs.getString(2));
assertEquals("1", rs.getString(3));
assertFalse(rs.next());
stat.execute("drop table t1, t2, t3, t4");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论