提交 ea64e353 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Fix NPE with ENUM in SelectUnion

上级 0347313c
...@@ -21,6 +21,12 @@ Change Log ...@@ -21,6 +21,12 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1665: TestCrashAPI: NPE with ENUM in MINUS operator
</li>
<li>Issue #1602: Combine type, precision, scale, display size and extTypeInfo into one object
</li>
<li>PR #1671: Assorted changes
</li>
<li>Issue #1668: MySQL compatibility DATE() function should return NULL on error <li>Issue #1668: MySQL compatibility DATE() function should return NULL on error
</li> </li>
<li>Issue #1604: TestCrashAPI: PreparedStatement.getGeneratedKeys() is already closed <li>Issue #1604: TestCrashAPI: PreparedStatement.getGeneratedKeys() is already closed
......
...@@ -5841,37 +5841,22 @@ public class Parser { ...@@ -5841,37 +5841,22 @@ public class Parser {
Expression expr = readExpression(); Expression expr = readExpression();
expr = expr.optimize(session); expr = expr.optimize(session);
TypeInfo type = expr.getType(); TypeInfo type = expr.getType();
int valueType = type.getValueType();
long prec;
int scale, displaySize;
Column column; Column column;
String columnName = "C" + (i + 1); String columnName = "C" + (i + 1);
if (rows.isEmpty()) { if (rows.isEmpty()) {
if (valueType == Value.UNKNOWN) { if (type.getValueType() == Value.UNKNOWN) {
valueType = Value.STRING; type = TypeInfo.TYPE_STRING_DEFAULT;
} }
DataType dt = DataType.getDataType(valueType); column = new Column(columnName, type);
prec = dt.defaultPrecision;
scale = dt.defaultScale;
displaySize = dt.defaultDisplaySize;
column = new Column(columnName, valueType, prec, scale, displaySize);
columns.add(column); columns.add(column);
} else {
if (i >= columns.size()) {
throw DbException.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
type = Value.getHigherType(columns.get(i).getType(), type);
column = new Column(columnName, type);
columns.set(i, column);
} }
prec = type.getPrecision();
scale = type.getScale();
displaySize = type.getDisplaySize();
if (i >= columns.size()) {
throw DbException
.get(ErrorCode.COLUMN_COUNT_DOES_NOT_MATCH);
}
Column c = columns.get(i);
TypeInfo t = c.getType();
valueType = Value.getHigherOrder(t.getValueType(), valueType);
prec = Math.max(t.getPrecision(), prec);
scale = Math.max(t.getScale(), scale);
displaySize = Math.max(t.getDisplaySize(), displaySize);
column = new Column(columnName, valueType, prec, scale, displaySize);
columns.set(i, column);
row.add(expr); row.add(expr);
i++; i++;
} while (multiColumn && readIfMore(true)); } while (multiColumn && readIfMore(true));
......
...@@ -27,7 +27,6 @@ import org.h2.table.ColumnResolver; ...@@ -27,7 +27,6 @@ import org.h2.table.ColumnResolver;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ColumnNamer; import org.h2.util.ColumnNamer;
import org.h2.value.TypeInfo;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -122,7 +121,7 @@ public class SelectUnion extends Query { ...@@ -122,7 +121,7 @@ public class SelectUnion extends Query {
Mode mode = session.getDatabase().getMode(); Mode mode = session.getDatabase().getMode();
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
Expression e = expressions.get(i); Expression e = expressions.get(i);
newValues[i] = values[i].convertTo(e.getType().getValueType(), mode); newValues[i] = values[i].convertTo(e.getType(), mode, null);
} }
return newValues; return newValues;
} }
...@@ -329,13 +328,8 @@ public class SelectUnion extends Query { ...@@ -329,13 +328,8 @@ public class SelectUnion extends Query {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
Expression l = le.get(i); Expression l = le.get(i);
Expression r = re.get(i); Expression r = re.get(i);
TypeInfo lType = l.getType(), rType = r.getType(); String columnName = columnNamer.getColumnName(l, i, l.getAlias());
int type = Value.getHigherOrder(lType.getValueType(), rType.getValueType()); Column col = new Column(columnName, Value.getHigherType(l.getType(), r.getType()));
long prec = Math.max(lType.getPrecision(), rType.getPrecision());
int scale = Math.max(lType.getScale(), rType.getScale());
int displaySize = Math.max(lType.getDisplaySize(), rType.getDisplaySize());
String columnName = columnNamer.getColumnName(l,i,l.getAlias());
Column col = new Column(columnName, type, prec, scale, displaySize);
Expression e = new ExpressionColumn(session.getDatabase(), col); Expression e = new ExpressionColumn(session.getDatabase(), col);
expressions.add(e); expressions.add(e);
} }
......
...@@ -202,7 +202,6 @@ public class BinaryOperation extends Expression { ...@@ -202,7 +202,6 @@ public class BinaryOperation extends Expression {
int dataType = Value.getHigherOrder(l, r); int dataType = Value.getHigherOrder(l, r);
if (dataType == Value.ENUM) { if (dataType == Value.ENUM) {
type = TypeInfo.TYPE_INT; type = TypeInfo.TYPE_INT;
dataType = Value.INT;
} else { } else {
type = TypeInfo.getTypeInfo(dataType); type = TypeInfo.getTypeInfo(dataType);
if (DataType.isStringType(dataType) && session.getDatabase().getMode().allowPlusForStringConcat) { if (DataType.isStringType(dataType) && session.getDatabase().getMode().allowPlusForStringConcat) {
......
...@@ -18,7 +18,7 @@ import org.h2.index.IndexCondition; ...@@ -18,7 +18,7 @@ import org.h2.index.IndexCondition;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.value.ExtTypeInfo; import org.h2.value.TypeInfo;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -36,8 +36,7 @@ public class ConditionInConstantSet extends Condition { ...@@ -36,8 +36,7 @@ public class ConditionInConstantSet extends Condition {
private final ArrayList<Expression> valueList; private final ArrayList<Expression> valueList;
private final TreeSet<Value> valueSet; private final TreeSet<Value> valueSet;
private boolean hasNull; private boolean hasNull;
private final int type; private final TypeInfo type;
private ExtTypeInfo extTypeInfo;
/** /**
* Create a new IN(..) condition. * Create a new IN(..) condition.
...@@ -54,17 +53,10 @@ public class ConditionInConstantSet extends Condition { ...@@ -54,17 +53,10 @@ public class ConditionInConstantSet extends Condition {
this.valueList = valueList; this.valueList = valueList;
Database database = session.getDatabase(); Database database = session.getDatabase();
this.valueSet = new TreeSet<>(database.getCompareMode()); this.valueSet = new TreeSet<>(database.getCompareMode());
type = left.getType().getValueType(); type = left.getType();
Mode mode = database.getMode(); Mode mode = database.getMode();
if (type == Value.ENUM) { for (Expression expression : valueList) {
extTypeInfo = ((ExpressionColumn) left).getColumn().getType().getExtTypeInfo(); add(expression.getValue(session).convertTo(type, mode, null));
for (Expression expression : valueList) {
add(extTypeInfo.cast(expression.getValue(session)));
}
} else {
for (Expression expression : valueList) {
add(expression.getValue(session).convertTo(type, mode));
}
} }
} }
...@@ -173,11 +165,7 @@ public class ConditionInConstantSet extends Condition { ...@@ -173,11 +165,7 @@ public class ConditionInConstantSet extends Condition {
if (add != null) { if (add != null) {
if (add.isConstant()) { if (add.isConstant()) {
valueList.add(add); valueList.add(add);
if (type == Value.ENUM) { add(add.getValue(session).convertTo(type, session.getDatabase().getMode(), null));
add(add.getValue(session).convertToEnum(extTypeInfo));
} else {
add(add.getValue(session).convertTo(type, session.getDatabase().getMode()));
}
return this; return this;
} }
} }
......
...@@ -463,14 +463,11 @@ public abstract class Value extends VersionedValue { ...@@ -463,14 +463,11 @@ public abstract class Value extends VersionedValue {
public static int getHigherOrder(int t1, int t2) { public static int getHigherOrder(int t1, int t2) {
if (t1 == Value.UNKNOWN || t2 == Value.UNKNOWN) { if (t1 == Value.UNKNOWN || t2 == Value.UNKNOWN) {
if (t1 == t2) { if (t1 == t2) {
throw DbException.get( throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
} else if (t1 == Value.NULL) { } else if (t1 == Value.NULL) {
throw DbException.get( throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
ErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
} else if (t2 == Value.NULL) { } else if (t2 == Value.NULL) {
throw DbException.get( throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
ErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
} }
} }
if (t1 == t2) { if (t1 == t2) {
...@@ -481,6 +478,34 @@ public abstract class Value extends VersionedValue { ...@@ -481,6 +478,34 @@ public abstract class Value extends VersionedValue {
return o1 > o2 ? t1 : t2; return o1 > o2 ? t1 : t2;
} }
/**
* Get the higher data type of two data types. If values need to be
* converted to match the other operands data type, the value with the
* lower order is converted to the value with the higher order.
*
* @param type1 the first data type
* @param type2 the second data type
* @return the higher data type of the two
*/
public static TypeInfo getHigherType(TypeInfo type1, TypeInfo type2) {
int t1 = type1.getValueType(), t2 = type2.getValueType();
if (t1 == Value.UNKNOWN || t2 == Value.UNKNOWN) {
if (t1 == t2) {
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
} else if (t1 == Value.NULL) {
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
} else if (t2 == Value.NULL) {
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
}
}
int dataType = getOrder(t1) > getOrder(t2) ? t1 : t2;
long precision = Math.max(type1.getPrecision(), type2.getPrecision());
int scale = Math.max(type1.getScale(), type2.getScale());
ExtTypeInfo ext1 = type1.getExtTypeInfo();
ExtTypeInfo ext = dataType == t1 && ext1 != null ? ext1 : dataType == t2 ? type2.getExtTypeInfo() : null;
return TypeInfo.getTypeInfo(dataType, precision, scale, ext);
}
/** /**
* Check if a value is in the cache that is equal to this value. If yes, * Check if a value is in the cache that is equal to this value. If yes,
* this value should be used to save memory. If the value is not in the * this value should be used to save memory. If the value is not in the
...@@ -697,7 +722,7 @@ public abstract class Value extends VersionedValue { ...@@ -697,7 +722,7 @@ public abstract class Value extends VersionedValue {
} }
/** /**
* Compare a value to the specified type. * Convert a value to the specified type.
* *
* @param targetType the type of the returned value * @param targetType the type of the returned value
* @param mode the mode * @param mode the mode
...@@ -708,7 +733,19 @@ public abstract class Value extends VersionedValue { ...@@ -708,7 +733,19 @@ public abstract class Value extends VersionedValue {
} }
/** /**
* Compare a value to the specified type. * Convert a value to the specified type.
*
* @param targetType the type of the returned value
* @param mode the conversion mode
* @param column the column (if any), used for to improve the error message if conversion fails
* @return the converted value
*/
public Value convertTo(TypeInfo targetType, Mode mode, Object column) {
return convertTo(targetType.getValueType(), -1, mode, column, targetType.getExtTypeInfo());
}
/**
* Convert a value to the specified type.
* *
* @param targetType the type of the returned value * @param targetType the type of the returned value
* @param precision the precision of the column to convert this value to. * @param precision the precision of the column to convert this value to.
......
...@@ -284,5 +284,20 @@ CREATE TABLE TEST(E ENUM('a', 'b')); ...@@ -284,5 +284,20 @@ CREATE TABLE TEST(E ENUM('a', 'b'));
EXPLAIN SELECT * FROM TEST WHERE E = 'a'; EXPLAIN SELECT * FROM TEST WHERE E = 'a';
>> SELECT TEST.E FROM PUBLIC.TEST /* PUBLIC.TEST.tableScan */ WHERE E = 'a' >> SELECT TEST.E FROM PUBLIC.TEST /* PUBLIC.TEST.tableScan */ WHERE E = 'a'
INSERT INTO TEST VALUES ('a');
> update count: 1
(SELECT * FROM TEST A) UNION ALL (SELECT * FROM TEST A);
> E
> -
> a
> a
> rows: 2
(SELECT * FROM TEST A) MINUS (SELECT * FROM TEST A);
> E
> -
> rows: 0
DROP TABLE TEST; DROP TABLE TEST;
> ok > ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论