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

Fix NPE with ENUM in SelectUnion

上级 0347313c
......@@ -21,6 +21,12 @@ Change Log
<h2>Next Version (unreleased)</h2>
<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>
<li>Issue #1604: TestCrashAPI: PreparedStatement.getGeneratedKeys() is already closed
......
......@@ -5841,37 +5841,22 @@ public class Parser {
Expression expr = readExpression();
expr = expr.optimize(session);
TypeInfo type = expr.getType();
int valueType = type.getValueType();
long prec;
int scale, displaySize;
Column column;
String columnName = "C" + (i + 1);
if (rows.isEmpty()) {
if (valueType == Value.UNKNOWN) {
valueType = Value.STRING;
if (type.getValueType() == Value.UNKNOWN) {
type = TypeInfo.TYPE_STRING_DEFAULT;
}
DataType dt = DataType.getDataType(valueType);
prec = dt.defaultPrecision;
scale = dt.defaultScale;
displaySize = dt.defaultDisplaySize;
column = new Column(columnName, valueType, prec, scale, displaySize);
column = new Column(columnName, type);
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);
i++;
} while (multiColumn && readIfMore(true));
......
......@@ -27,7 +27,6 @@ import org.h2.table.ColumnResolver;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ColumnNamer;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
......@@ -122,7 +121,7 @@ public class SelectUnion extends Query {
Mode mode = session.getDatabase().getMode();
for (int i = 0; i < columnCount; 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;
}
......@@ -329,13 +328,8 @@ public class SelectUnion extends Query {
for (int i = 0; i < len; i++) {
Expression l = le.get(i);
Expression r = re.get(i);
TypeInfo lType = l.getType(), rType = r.getType();
int type = Value.getHigherOrder(lType.getValueType(), rType.getValueType());
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);
String columnName = columnNamer.getColumnName(l, i, l.getAlias());
Column col = new Column(columnName, Value.getHigherType(l.getType(), r.getType()));
Expression e = new ExpressionColumn(session.getDatabase(), col);
expressions.add(e);
}
......
......@@ -202,7 +202,6 @@ public class BinaryOperation extends Expression {
int dataType = Value.getHigherOrder(l, r);
if (dataType == Value.ENUM) {
type = TypeInfo.TYPE_INT;
dataType = Value.INT;
} else {
type = TypeInfo.getTypeInfo(dataType);
if (DataType.isStringType(dataType) && session.getDatabase().getMode().allowPlusForStringConcat) {
......
......@@ -18,7 +18,7 @@ import org.h2.index.IndexCondition;
import org.h2.message.DbException;
import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter;
import org.h2.value.ExtTypeInfo;
import org.h2.value.TypeInfo;
import org.h2.value.Value;
import org.h2.value.ValueBoolean;
import org.h2.value.ValueNull;
......@@ -36,8 +36,7 @@ public class ConditionInConstantSet extends Condition {
private final ArrayList<Expression> valueList;
private final TreeSet<Value> valueSet;
private boolean hasNull;
private final int type;
private ExtTypeInfo extTypeInfo;
private final TypeInfo type;
/**
* Create a new IN(..) condition.
......@@ -54,17 +53,10 @@ public class ConditionInConstantSet extends Condition {
this.valueList = valueList;
Database database = session.getDatabase();
this.valueSet = new TreeSet<>(database.getCompareMode());
type = left.getType().getValueType();
type = left.getType();
Mode mode = database.getMode();
if (type == Value.ENUM) {
extTypeInfo = ((ExpressionColumn) left).getColumn().getType().getExtTypeInfo();
for (Expression expression : valueList) {
add(extTypeInfo.cast(expression.getValue(session)));
}
} else {
for (Expression expression : valueList) {
add(expression.getValue(session).convertTo(type, mode));
}
for (Expression expression : valueList) {
add(expression.getValue(session).convertTo(type, mode, null));
}
}
......@@ -173,11 +165,7 @@ public class ConditionInConstantSet extends Condition {
if (add != null) {
if (add.isConstant()) {
valueList.add(add);
if (type == Value.ENUM) {
add(add.getValue(session).convertToEnum(extTypeInfo));
} else {
add(add.getValue(session).convertTo(type, session.getDatabase().getMode()));
}
add(add.getValue(session).convertTo(type, session.getDatabase().getMode(), null));
return this;
}
}
......
......@@ -463,14 +463,11 @@ public abstract class Value extends VersionedValue {
public static int getHigherOrder(int t1, int t2) {
if (t1 == Value.UNKNOWN || t2 == Value.UNKNOWN) {
if (t1 == t2) {
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, ?");
} else if (t1 == Value.NULL) {
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "NULL, ?");
} else if (t2 == Value.NULL) {
throw DbException.get(
ErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
throw DbException.get(ErrorCode.UNKNOWN_DATA_TYPE_1, "?, NULL");
}
}
if (t1 == t2) {
......@@ -481,6 +478,34 @@ public abstract class Value extends VersionedValue {
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,
* 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 {
}
/**
* 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 mode
......@@ -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 precision the precision of the column to convert this value to.
......
......@@ -284,5 +284,20 @@ CREATE TABLE TEST(E ENUM('a', 'b'));
EXPLAIN SELECT * FROM TEST 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;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论