[refactor](complex-type) refactor array/map/struct literal to not invoke execute() function in prepare state (#19068)

This commit is contained in:
xy720
2023-05-11 18:44:37 +08:00
committed by GitHub
parent 99cef84acf
commit 39ec8aa64c
23 changed files with 278 additions and 119 deletions

View File

@ -23,6 +23,7 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.thrift.TExprNode;
import org.apache.doris.thrift.TExprNodeType;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import java.io.DataInput;
@ -60,19 +61,26 @@ public class ArrayLiteral extends LiteralExpr {
type = new ArrayType(itemType, containsNull);
children = new ArrayList<>();
for (LiteralExpr expr : exprs) {
if (expr.getType().equals(itemType)) {
children.add(expr);
} else {
children.add(expr.castTo(itemType));
try {
for (LiteralExpr expr : exprs) {
if (expr.getType().equals(itemType)) {
children.add(expr);
} else {
children.add(expr.convertTo(itemType));
}
}
} catch (AnalysisException e) {
String s = "[" + StringUtils.join(exprs, ',') + "]";
throw new AnalysisException("Invalid ARRAY " + s + " literal: " + e.getMessage());
}
analysisDone();
}
protected ArrayLiteral(ArrayLiteral other) {
super(other);
}
@Override
public boolean isMinValue() {
return false;
@ -103,7 +111,7 @@ public class ArrayLiteral extends LiteralExpr {
public String getStringValue() {
List<String> list = new ArrayList<>(children.size());
children.forEach(v -> list.add(v.getStringValue()));
return "ARRAY[" + StringUtils.join(list, ", ") + "]";
return "[" + StringUtils.join(list, ", ") + "]";
}
@Override
@ -149,15 +157,32 @@ public class ArrayLiteral extends LiteralExpr {
return new ArrayLiteral(this);
}
@Override
public LiteralExpr convertTo(Type targetType) throws AnalysisException {
Preconditions.checkState(targetType instanceof ArrayType);
Type itemType = ((ArrayType) targetType).getItemType();
LiteralExpr[] literals = new LiteralExpr[children.size()];
for (int i = 0; i < children.size(); i++) {
literals[i] = (LiteralExpr) (Expr.convertLiteral(children.get(i), itemType));
}
return new ArrayLiteral(literals);
}
@Override
public Expr uncheckedCastTo(Type targetType) throws AnalysisException {
if (!targetType.isArrayType()) {
return super.uncheckedCastTo(targetType);
}
Type itemType = ((ArrayType) targetType).getItemType();
ArrayLiteral literal = new ArrayLiteral(this);
for (int i = 0; i < children.size(); ++ i) {
Expr child = children.get(i);
literal.children.set(i, child.uncheckedCastTo(((ArrayType) targetType).getItemType()));
Expr child = Expr.convertLiteral(children.get(i), itemType);
// all children should be literal or else it will make be core
if (!child.isLiteral()) {
throw new AnalysisException("Unexpected array literal cast failed. from type: "
+ this.type + ", to type: " + targetType);
}
literal.children.set(i, child);
}
literal.setType(targetType);
return literal;

View File

@ -453,6 +453,11 @@ public class CastExpr extends Expr {
Expr targetExpr;
try {
targetExpr = castTo((LiteralExpr) value);
if (targetTypeDef != null) {
targetExpr.setType(targetTypeDef.getType());
} else {
targetExpr.setType(type);
}
} catch (AnalysisException ae) {
targetExpr = this;
} catch (NumberFormatException nfe) {

View File

@ -1485,6 +1485,21 @@ public abstract class Expr extends TreeNode<Expr> implements ParseNode, Cloneabl
setChild(childIndex, newChild);
}
/**
* Assuming it can cast to the targetType, use for convert literal value to
* target type without a cast node.
*/
public static Expr convertLiteral(Expr expr, Type targetType) throws AnalysisException {
if (!(expr instanceof LiteralExpr)) {
return expr;
}
Expr newExpr = expr.uncheckedCastTo(targetType);
if (newExpr instanceof CastExpr) {
return ((LiteralExpr) newExpr.getChild(0)).convertTo(targetType);
}
return newExpr;
}
/**
* Add a cast expression above child.
* If child is a literal expression, we attempt to

View File

@ -1575,7 +1575,9 @@ public class FunctionCallExpr extends Expr {
|| fnName.getFunction().equalsIgnoreCase("array_concat")
|| fnName.getFunction().equalsIgnoreCase("array_shuffle")
|| fnName.getFunction().equalsIgnoreCase("shuffle")
|| fnName.getFunction().equalsIgnoreCase("array_except"))
|| fnName.getFunction().equalsIgnoreCase("array_except")
|| fnName.getFunction().equalsIgnoreCase("array_contains")
|| fnName.getFunction().equalsIgnoreCase("array_position"))
&& ((args[ix].isDecimalV3())
|| (children.get(0).getType().isArrayType()
&& (((ArrayType) children.get(0).getType()).getItemType().isDecimalV3())

View File

@ -21,6 +21,7 @@
package org.apache.doris.analysis;
import org.apache.doris.catalog.PrimitiveType;
import org.apache.doris.catalog.ScalarType;
import org.apache.doris.catalog.Type;
import org.apache.doris.common.AnalysisException;
import org.apache.doris.common.NotImplementedException;
@ -137,6 +138,39 @@ public abstract class LiteralExpr extends Expr implements Comparable<LiteralExpr
return literalExpr;
}
public Expr convertTo(Type targetType) throws AnalysisException {
Preconditions.checkArgument(!targetType.equals(Type.INVALID));
if (this instanceof NullLiteral) {
return NullLiteral.create(targetType);
} else if (targetType.isBoolean()) {
if (this instanceof StringLiteral || this instanceof JsonLiteral) {
return new BoolLiteral(getStringValue());
} else {
if (getLongValue() != 0) {
return new BoolLiteral(true);
} else {
return new BoolLiteral(false);
}
}
} else if (targetType.isIntegerType()) {
return new IntLiteral(getLongValue(), targetType);
} else if (targetType.isLargeIntType()) {
return new LargeIntLiteral(getStringValue());
} else if (targetType.isFloatingPointType()) {
return new FloatLiteral(getDoubleValue(), targetType);
} else if (targetType.isDecimalV2() || targetType.isDecimalV3()) {
DecimalLiteral literal = new DecimalLiteral(getStringValue(),
((ScalarType) targetType).getScalarScale());
literal.setType(targetType);
return literal;
} else if (targetType.isStringType()) {
return new StringLiteral(getStringValue());
} else if (targetType.isDateType()) {
return new StringLiteral(getStringValue()).convertToDate(targetType);
}
return this;
}
public static LiteralExpr createInfinity(Type type, boolean isMax) throws AnalysisException {
Preconditions.checkArgument(!type.equals(Type.INVALID));
if (isMax) {

View File

@ -25,6 +25,7 @@ import org.apache.doris.thrift.TExprNodeType;
import org.apache.doris.thrift.TTypeDesc;
import org.apache.doris.thrift.TTypeNode;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import java.io.DataInput;
@ -65,17 +66,22 @@ public class MapLiteral extends LiteralExpr {
throw new AnalysisException("Invalid value type in Map.");
}
for (int idx = 0; idx < exprs.length && idx + 1 < exprs.length; idx += 2) {
if (exprs[idx].getType().equals(keyType)) {
children.add(exprs[idx]);
} else {
children.add(exprs[idx].castTo(keyType));
}
if (exprs[idx + 1].getType().equals(valueType)) {
children.add(exprs[idx + 1]);
} else {
children.add(exprs[idx + 1].castTo(valueType));
try {
for (int idx = 0; idx < exprs.length && idx + 1 < exprs.length; idx += 2) {
if (exprs[idx].getType().equals(keyType)) {
children.add(exprs[idx]);
} else {
children.add(exprs[idx].convertTo(keyType));
}
if (exprs[idx + 1].getType().equals(valueType)) {
children.add(exprs[idx + 1]);
} else {
children.add(exprs[idx + 1].convertTo(valueType));
}
}
} catch (AnalysisException e) {
String s = "{" + StringUtils.join(exprs, ',') + "}";
throw new AnalysisException("Invalid Map " + s + " literal: " + e.getMessage());
}
type = new MapType(keyType, valueType);
@ -85,6 +91,19 @@ public class MapLiteral extends LiteralExpr {
super(other);
}
@Override
public LiteralExpr convertTo(Type targetType) throws AnalysisException {
Preconditions.checkState(targetType instanceof MapType);
Type keyType = ((MapType) targetType).getKeyType();
Type valType = ((MapType) targetType).getValueType();
LiteralExpr[] literals = new LiteralExpr[children.size()];
for (int i = 0; i < children.size(); i += 2) {
literals[i] = (LiteralExpr) Expr.convertLiteral(children.get(i), keyType);
literals[i + 1] = (LiteralExpr) Expr.convertLiteral(children.get(i + 1), valType);
}
return new MapLiteral(literals);
}
@Override
public Expr uncheckedCastTo(Type targetType) throws AnalysisException {
if (!targetType.isMapType()) {
@ -95,8 +114,15 @@ public class MapLiteral extends LiteralExpr {
Type valueType = ((MapType) targetType).getValueType();
for (int i = 0; i < children.size() && i + 1 < children.size(); i += 2) {
literal.children.set(i, children.get(i).uncheckedCastTo(keyType));
literal.children.set(i + 1, children.get(i + 1).uncheckedCastTo(valueType));
Expr key = Expr.convertLiteral(children.get(i), keyType);
Expr value = Expr.convertLiteral(children.get(i + 1), valueType);
// all children should be literal or else it will make be core
if ((!key.isLiteral()) || (!value.isLiteral())) {
throw new AnalysisException("Unexpected map literal cast failed. from type: "
+ this.type + ", to type: " + targetType);
}
literal.children.set(i, key);
literal.children.set(i + 1, value);
}
literal.setType(targetType);
return literal;
@ -109,6 +135,20 @@ public class MapLiteral extends LiteralExpr {
}
}
@Override
public String getStringValue() {
List<String> list = new ArrayList<>(children.size());
for (int i = 0; i < children.size() && i + 1 < children.size(); i += 2) {
list.add(children.get(i).getStringValue() + ":" + children.get(i + 1).getStringValue());
}
return "{" + StringUtils.join(list, ", ") + "}";
}
@Override
public String getStringValueForArray() {
return null;
}
@Override
protected String toSqlImpl() {
List<String> list = new ArrayList<>(children.size());
@ -166,14 +206,4 @@ public class MapLiteral extends LiteralExpr {
Expr.writeTo(e, out);
}
}
@Override
public String getStringValue() {
return toSqlImpl();
}
@Override
public String getStringValueForArray() {
return null;
}
}

View File

@ -24,6 +24,7 @@ import org.apache.doris.common.AnalysisException;
import org.apache.doris.thrift.TExprNode;
import org.apache.doris.thrift.TExprNodeType;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import java.io.DataInput;
@ -126,6 +127,17 @@ public class StructLiteral extends LiteralExpr {
return 0;
}
@Override
public LiteralExpr convertTo(Type targetType) throws AnalysisException {
Preconditions.checkState(targetType instanceof StructType);
List<StructField> fields = ((StructType) targetType).getFields();
LiteralExpr[] literals = new LiteralExpr[children.size()];
for (int i = 0; i < children.size(); i++) {
literals[i] = (LiteralExpr) Expr.convertLiteral(children.get(i), fields.get(i).getType());
}
return new StructLiteral(literals);
}
@Override
public Expr uncheckedCastTo(Type targetType) throws AnalysisException {
if (!targetType.isStructType()) {
@ -134,8 +146,13 @@ public class StructLiteral extends LiteralExpr {
ArrayList<StructField> fields = ((StructType) targetType).getFields();
StructLiteral literal = new StructLiteral(this);
for (int i = 0; i < children.size(); ++ i) {
Expr child = children.get(i);
literal.children.set(i, child.uncheckedCastTo((fields.get(i).getType())));
Expr child = Expr.convertLiteral(children.get(i), fields.get(i).getType());
// all children should be literal or else it will make be core
if (!child.isLiteral()) {
throw new AnalysisException("Unexpected struct literal cast failed. from type: "
+ this.type + ", to type: " + targetType);
}
literal.children.set(i, child);
}
literal.setType(targetType);
return literal;