This is an automated email from the ASF dual-hosted git repository. morrysnow pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new a0d3206d78 [fix](Nereids) support nested complex type literal (#25287) a0d3206d78 is described below commit a0d3206d78d94334728a6a547e7913e1f0ca1103 Author: morrySnow <101034200+morrys...@users.noreply.github.com> AuthorDate: Thu Oct 12 14:17:38 2023 +0800 [fix](Nereids) support nested complex type literal (#25287) --- .../org/apache/doris/analysis/ArrayLiteral.java | 2 +- .../doris/nereids/parser/LogicalPlanBuilder.java | 48 +++++++-- .../expression/rules/FoldConstantRuleOnFE.java | 3 +- .../rules/expression/rules/FunctionBinder.java | 2 +- .../trees/expressions/functions/scalar/Array.java | 5 +- .../trees/expressions/literal/ArrayLiteral.java | 66 ++++++------- .../nereids/trees/expressions/literal/Literal.java | 7 ++ .../trees/expressions/literal/MapLiteral.java | 24 ++--- .../trees/expressions/literal/StructLiteral.java | 107 +++++++++++++++++++++ .../expressions/visitor/ExpressionVisitor.java | 5 + .../trees/plans/logical/LogicalSetOperation.java | 2 +- .../org/apache/doris/nereids/types/JsonType.java | 8 +- .../apache/doris/nereids/types/StructField.java | 2 +- .../doris/nereids/util/TypeCoercionUtils.java | 61 +++++++++++- .../suites/nereids_syntax_p0/array_function.groovy | 39 ++++---- 15 files changed, 288 insertions(+), 93 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java index b7d95af1dd..37245ed524 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ArrayLiteral.java @@ -40,7 +40,7 @@ public class ArrayLiteral extends LiteralExpr { children = new ArrayList<>(); } - public ArrayLiteral(Type type, LiteralExpr... exprs) throws AnalysisException { + public ArrayLiteral(Type type, LiteralExpr... exprs) { this.type = type; children = new ArrayList<>(Arrays.asList(exprs)); analysisDone(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 2685d8471b..6867c9c465 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -224,8 +224,6 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.Array; import org.apache.doris.nereids.trees.expressions.functions.scalar.ArraySlice; import org.apache.doris.nereids.trees.expressions.functions.scalar.Char; import org.apache.doris.nereids.trees.expressions.functions.scalar.ConvertTo; -import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateMap; -import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateStruct; import org.apache.doris.nereids.trees.expressions.functions.scalar.DayCeil; import org.apache.doris.nereids.trees.expressions.functions.scalar.DayFloor; import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd; @@ -264,6 +262,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.YearFloor; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsAdd; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsDiff; import org.apache.doris.nereids.trees.expressions.functions.scalar.YearsSub; +import org.apache.doris.nereids.trees.expressions.literal.ArrayLiteral; import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; import org.apache.doris.nereids.trees.expressions.literal.DateLiteral; @@ -276,9 +275,11 @@ import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; import org.apache.doris.nereids.trees.expressions.literal.Interval; import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.MapLiteral; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StructLiteral; import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; import org.apache.doris.nereids.trees.plans.JoinHint; @@ -1759,22 +1760,49 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> { return sb.toString(); } + /** + * cast all items to same types. + * TODO remove this function after we refactor type coercion. + */ + private List<Literal> typeCoercionItems(List<Literal> items) { + DataType dataType = new Array(items.toArray(new Literal[0])).expectedInputTypes().get(0); + return items.stream() + .map(item -> item.checkedCastTo(dataType)) + .map(Literal.class::cast) + .collect(Collectors.toList()); + } + @Override - public Object visitArrayLiteral(ArrayLiteralContext ctx) { - Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new); - return new Array(items); + public ArrayLiteral visitArrayLiteral(ArrayLiteralContext ctx) { + List<Literal> items = ctx.items.stream().<Literal>map(this::typedVisit).collect(Collectors.toList()); + if (items.isEmpty()) { + return new ArrayLiteral(items); + } + return new ArrayLiteral(typeCoercionItems(items)); } @Override - public Object visitMapLiteral(MapLiteralContext ctx) { - Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new); - return new CreateMap(items); + public MapLiteral visitMapLiteral(MapLiteralContext ctx) { + List<Literal> items = ctx.items.stream().<Literal>map(this::typedVisit).collect(Collectors.toList()); + if (items.size() % 2 != 0) { + throw new ParseException("map can't be odd parameters, need even parameters", ctx); + } + List<Literal> keys = Lists.newArrayList(); + List<Literal> values = Lists.newArrayList(); + for (int i = 0; i < items.size(); i++) { + if (i % 2 == 0) { + keys.add(items.get(i)); + } else { + values.add(items.get(i)); + } + } + return new MapLiteral(typeCoercionItems(keys), typeCoercionItems(values)); } @Override public Object visitStructLiteral(StructLiteralContext ctx) { - Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new); - return new CreateStruct(items); + List<Literal> fields = ctx.items.stream().<Literal>map(this::typedVisit).collect(Collectors.toList()); + return new StructLiteral(fields); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java index 00230b1894..321465082d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java @@ -507,7 +507,8 @@ public class FoldConstantRuleOnFE extends AbstractExpressionRewriteRule { return checkedExpr.get(); } List<Literal> arguments = (List) array.getArguments(); - return new ArrayLiteral(arguments); + // we should pass dataType to constructor because arguments maybe empty + return new ArrayLiteral(arguments, array.getDataType()); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java index 63591bc3e0..4f5def6f1e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java @@ -336,7 +336,7 @@ public class FunctionBinder extends AbstractExpressionRewriteRule { public Expression visitCast(Cast cast, ExpressionRewriteContext context) { cast = (Cast) super.visitCast(cast, context); // NOTICE: just for compatibility with legacy planner. - if (cast.child().getDataType() instanceof ArrayType || cast.getDataType() instanceof ArrayType) { + if (cast.child().getDataType().isComplexType() || cast.getDataType().isComplexType()) { TypeCoercionUtils.checkCanCastTo(cast.child().getDataType(), cast.getDataType()); } return cast; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java index a377cec5f9..ea74eaefdf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java @@ -25,7 +25,6 @@ import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.ArrayType; import org.apache.doris.nereids.types.DataType; -import org.apache.doris.nereids.types.StringType; import org.apache.doris.nereids.util.TypeCoercionUtils; import com.google.common.collect.ImmutableList; @@ -76,7 +75,9 @@ public class Array extends ScalarFunction .collect(Collectors.partitioningBy(TypeCoercionUtils::hasCharacterType)); List<DataType> needTypeCoercion = Lists.newArrayList(Sets.newHashSet(partitioned.get(true))); if (needTypeCoercion.size() > 1 || !partitioned.get(false).isEmpty()) { - needTypeCoercion = Lists.newArrayList(StringType.INSTANCE); + needTypeCoercion = needTypeCoercion.stream() + .map(TypeCoercionUtils::replaceCharacterToString) + .collect(Collectors.toList()); } needTypeCoercion.addAll(partitioned.get(false)); return needTypeCoercion.stream() diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/ArrayLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/ArrayLiteral.java index daee005522..307e300930 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/ArrayLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/ArrayLiteral.java @@ -19,17 +19,22 @@ package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.ArrayType; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.NullType; import com.google.common.collect.ImmutableList; +import org.springframework.util.CollectionUtils; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; -/** ArrayLiteral */ +/** + * ArrayLiteral + */ public class ArrayLiteral extends Literal { private final List<Literal> items; @@ -38,15 +43,16 @@ public class ArrayLiteral extends Literal { * construct array literal */ public ArrayLiteral(List<Literal> items) { - super(computeDataType(items)); - this.items = items.stream() - .map(i -> { - if (i instanceof NullLiteral) { - DataType type = ((ArrayType) (this.getDataType())).getItemType(); - return new NullLiteral(type); - } - return i; - }).collect(ImmutableList.toImmutableList()); + super(ArrayType.of(CollectionUtils.isEmpty(items) ? NullType.INSTANCE : items.get(0).getDataType())); + this.items = ImmutableList.copyOf(Objects.requireNonNull(items, "items should not null")); + } + + /** + * when items is empty, we could not get dataType from items, so we need pass dataType explicitly. + */ + public ArrayLiteral(List<Literal> items, DataType dataType) { + super(dataType); + this.items = ImmutableList.copyOf(Objects.requireNonNull(items, "items should not null")); } @Override @@ -56,17 +62,24 @@ public class ArrayLiteral extends Literal { @Override public LiteralExpr toLegacyLiteral() { - if (items.isEmpty()) { - return new org.apache.doris.analysis.ArrayLiteral(); + LiteralExpr[] itemExprs = items.stream() + .map(Literal::toLegacyLiteral) + .toArray(LiteralExpr[]::new); + return new org.apache.doris.analysis.ArrayLiteral(getDataType().toCatalogDataType(), itemExprs); + } + + @Override + protected Expression uncheckedCastTo(DataType targetType) throws AnalysisException { + if (this.dataType.equals(targetType)) { + return this; + } else if (targetType instanceof ArrayType) { + // we should pass dataType to constructor because arguments maybe empty + return new ArrayLiteral(items.stream() + .map(i -> i.uncheckedCastTo(((ArrayType) targetType).getItemType())) + .map(Literal.class::cast) + .collect(ImmutableList.toImmutableList()), targetType); } else { - LiteralExpr[] itemExprs = items.stream() - .map(Literal::toLegacyLiteral) - .toArray(LiteralExpr[]::new); - try { - return new org.apache.doris.analysis.ArrayLiteral(getDataType().toCatalogDataType(), itemExprs); - } catch (Throwable t) { - throw new AnalysisException(t.getMessage(), t); - } + return super.uncheckedCastTo(targetType); } } @@ -90,17 +103,4 @@ public class ArrayLiteral extends Literal { public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { return visitor.visitArrayLiteral(this, context); } - - private static DataType computeDataType(List<Literal> items) { - if (items.isEmpty()) { - return ArrayType.SYSTEM_DEFAULT; - } - DataType dataType = NullType.INSTANCE; - for (Literal item : items) { - if (!item.dataType.isNullType()) { - dataType = item.dataType; - } - } - return ArrayType.of(dataType); - } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java index 3397d92f5e..1a28817ce8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java @@ -197,6 +197,13 @@ public abstract class Literal extends Expression implements LeafExpression, Comp @Override protected Expression uncheckedCastTo(DataType targetType) throws AnalysisException { + if (this.dataType.equals(targetType)) { + return this; + } + if (this instanceof NullLiteral) { + return new NullLiteral(targetType); + } + // TODO support string to complex String desc = getStringValue(); if (targetType.isBooleanType()) { if ("0".equals(desc) || "false".equals(desc.toLowerCase(Locale.ROOT))) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/MapLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/MapLiteral.java index 12b0cb8ca7..47b09de04d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/MapLiteral.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/MapLiteral.java @@ -18,7 +18,6 @@ package org.apache.doris.nereids.trees.expressions.literal; import org.apache.doris.analysis.LiteralExpr; -import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.types.MapType; @@ -58,22 +57,13 @@ public class MapLiteral extends Literal { @Override public LiteralExpr toLegacyLiteral() { - if (keys.isEmpty()) { - return new org.apache.doris.analysis.MapLiteral(); - } else { - List<LiteralExpr> keyExprs = keys.stream() - .map(Literal::toLegacyLiteral) - .collect(Collectors.toList()); - List<LiteralExpr> valueExprs = values.stream() - .map(Literal::toLegacyLiteral) - .collect(Collectors.toList()); - try { - return new org.apache.doris.analysis.MapLiteral( - getDataType().toCatalogDataType(), keyExprs, valueExprs); - } catch (Throwable t) { - throw new AnalysisException(t.getMessage(), t); - } - } + List<LiteralExpr> keyExprs = keys.stream() + .map(Literal::toLegacyLiteral) + .collect(Collectors.toList()); + List<LiteralExpr> valueExprs = values.stream() + .map(Literal::toLegacyLiteral) + .collect(Collectors.toList()); + return new org.apache.doris.analysis.MapLiteral(getDataType().toCatalogDataType(), keyExprs, valueExprs); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StructLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StructLiteral.java new file mode 100644 index 0000000000..0041673770 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/StructLiteral.java @@ -0,0 +1,107 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.literal; + +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.StructField; +import org.apache.doris.nereids.types.StructType; + +import com.google.common.collect.ImmutableList; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * struct literal + */ +public class StructLiteral extends Literal { + + private final List<Literal> fields; + + public StructLiteral() { + super(StructType.SYSTEM_DEFAULT); + this.fields = ImmutableList.of(); + } + + public StructLiteral(List<Literal> fields) { + super(computeDataType(fields)); + this.fields = ImmutableList.copyOf(fields); + } + + @Override + public List<Literal> getValue() { + return fields; + } + + @Override + public LiteralExpr toLegacyLiteral() { + try { + return new org.apache.doris.analysis.StructLiteral( + fields.stream().map(Literal::toLegacyLiteral).toArray(LiteralExpr[]::new) + ); + } catch (Exception e) { + throw new AnalysisException(e.getMessage(), e); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + StructLiteral that = (StructLiteral) o; + return Objects.equals(fields, that.fields); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), fields); + } + + @Override + public String toString() { + return toSql(); + } + + @Override + public String toSql() { + return "{" + fields.stream().map(Literal::toSql).collect(Collectors.joining(",")) + "}"; + } + + @Override + public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { + return visitor.visitStructLiteral(this, context); + } + + private static StructType computeDataType(List<Literal> fields) { + ImmutableList.Builder<StructField> structFields = ImmutableList.builder(); + for (int i = 0; i < fields.size(); i++) { + structFields.add(new StructField(String.valueOf(i + 1), fields.get(i).getDataType(), true, "")); + } + return new StructType(structFields.build()); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java index 0f3bbf3b31..a275cadd87 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitor.java @@ -107,6 +107,7 @@ import org.apache.doris.nereids.trees.expressions.literal.MapLiteral; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; +import org.apache.doris.nereids.trees.expressions.literal.StructLiteral; import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral; @@ -309,6 +310,10 @@ public abstract class ExpressionVisitor<R, C> return visitLiteral(mapLiteral, context); } + public R visitStructLiteral(StructLiteral structLiteral, C context) { + return visitLiteral(structLiteral, context); + } + public R visitCompoundPredicate(CompoundPredicate compoundPredicate, C context) { return visitBinaryOperator(compoundPredicate, context); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java index d19b939876..71c1f08745 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSetOperation.java @@ -245,7 +245,7 @@ public abstract class LogicalSetOperation extends AbstractLogicalPlan implements boolean nullable = leftFields.get(i).isNullable() || rightFields.get(i).isNullable(); DataType commonType = getAssignmentCompatibleType( leftFields.get(i).getDataType(), rightFields.get(i).getDataType()); - StructField commonField = leftFields.get(i).withDataTypeAndNulalble(commonType, nullable); + StructField commonField = leftFields.get(i).withDataTypeAndNullable(commonType, nullable); commonFields.add(commonField); } return new StructType(commonFields.build()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/JsonType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/JsonType.java index 8455daaac2..b958e9acf3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/JsonType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/JsonType.java @@ -19,12 +19,13 @@ package org.apache.doris.nereids.types; import org.apache.doris.catalog.Type; import org.apache.doris.nereids.annotation.Developing; +import org.apache.doris.nereids.types.coercion.PrimitiveType; /** * Json type in Nereids. */ @Developing -public class JsonType extends DataType { +public class JsonType extends PrimitiveType { public static final JsonType INSTANCE = new JsonType(); @@ -62,9 +63,4 @@ public class JsonType extends DataType { public int width() { return WIDTH; } - - @Override - public String toSql() { - return "JSON"; - } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructField.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructField.java index fed07259be..73533fc0a5 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructField.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructField.java @@ -71,7 +71,7 @@ public class StructField { return new StructField(name, dataType, nullable, comment); } - public StructField withDataTypeAndNulalble(DataType dataType, boolean nullable) { + public StructField withDataTypeAndNullable(DataType dataType, boolean nullable) { return new StructField(name, dataType, nullable, comment); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java index b8c4f1366b..3adda07c3f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java @@ -265,7 +265,6 @@ public class TypeCoercionUtils { /** * return ture if datatype has character type in it, cannot use instance of CharacterType because of complex type. */ - @Developing public static boolean hasCharacterType(DataType dataType) { if (dataType instanceof ArrayType) { return hasCharacterType(((ArrayType) dataType).getItemType()); @@ -278,6 +277,27 @@ public class TypeCoercionUtils { return dataType instanceof CharacterType; } + /** + * replace all character types to string for correct type coercion + */ + public static DataType replaceCharacterToString(DataType dataType) { + if (dataType instanceof ArrayType) { + return ArrayType.of(replaceCharacterToString(((ArrayType) dataType).getItemType())); + } else if (dataType instanceof MapType) { + return MapType.of(replaceCharacterToString(((MapType) dataType).getKeyType()), + replaceCharacterToString(((MapType) dataType).getValueType())); + } else if (dataType instanceof StructType) { + List<StructField> newFields = ((StructType) dataType).getFields().stream() + .map(f -> f.withDataType(replaceCharacterToString(f.getDataType()))) + .collect(ImmutableList.toImmutableList()); + return new StructType(newFields); + } else if (dataType instanceof CharacterType) { + return StringType.INSTANCE; + } else { + return dataType; + } + } + /** * The type used for arithmetic operations. */ @@ -825,6 +845,10 @@ public class TypeCoercionUtils { // same type if (left.getDataType().equals(right.getDataType())) { + if (!supportCompare(left.getDataType())) { + throw new AnalysisException("data type " + left.getDataType() + + " could not used in ComparisonPredicate " + comparisonPredicate.toSql()); + } return comparisonPredicate.withChildren(left, right); } @@ -837,6 +861,10 @@ public class TypeCoercionUtils { Optional<DataType> commonType = findWiderTypeForTwoForComparison( left.getDataType(), right.getDataType(), false); if (commonType.isPresent()) { + if (!supportCompare(commonType.get())) { + throw new AnalysisException("data type " + commonType.get() + + " could not used in ComparisonPredicate " + comparisonPredicate.toSql()); + } left = castIfNotSameType(left, commonType.get()); right = castIfNotSameType(right, commonType.get()); } @@ -852,6 +880,10 @@ public class TypeCoercionUtils { if (inPredicate.getOptions().stream().map(Expression::getDataType) .allMatch(dt -> dt.equals(inPredicate.getCompareExpr().getDataType()))) { + if (!supportCompare(inPredicate.getCompareExpr().getDataType())) { + throw new AnalysisException("data type " + inPredicate.getCompareExpr().getDataType() + + " could not used in InPredicate " + inPredicate.toSql()); + } return inPredicate; } Optional<DataType> optionalCommonType = TypeCoercionUtils.findWiderCommonTypeForComparison( @@ -859,6 +891,10 @@ public class TypeCoercionUtils { .stream() .map(Expression::getDataType).collect(Collectors.toList()), true); + if (optionalCommonType.isPresent() && !supportCompare(optionalCommonType.get())) { + throw new AnalysisException("data type " + optionalCommonType.get() + + " could not used in InPredicate " + inPredicate.toSql()); + } return optionalCommonType .map(commonType -> { @@ -969,6 +1005,11 @@ public class TypeCoercionUtils { Map<Boolean, List<DataType>> partitioned = dataTypes.stream() .collect(Collectors.partitioningBy(TypeCoercionUtils::hasCharacterType)); List<DataType> needTypeCoercion = Lists.newArrayList(Sets.newHashSet(partitioned.get(true))); + if (needTypeCoercion.size() > 1 || !partitioned.get(false).isEmpty()) { + needTypeCoercion = needTypeCoercion.stream() + .map(TypeCoercionUtils::replaceCharacterToString) + .collect(Collectors.toList()); + } needTypeCoercion.addAll(partitioned.get(false)); return needTypeCoercion.stream().map(Optional::of).reduce(Optional.of(NullType.INSTANCE), (r, c) -> { @@ -1175,6 +1216,11 @@ public class TypeCoercionUtils { Map<Boolean, List<DataType>> partitioned = dataTypes.stream() .collect(Collectors.partitioningBy(TypeCoercionUtils::hasCharacterType)); List<DataType> needTypeCoercion = Lists.newArrayList(Sets.newHashSet(partitioned.get(true))); + if (needTypeCoercion.size() > 1 || !partitioned.get(false).isEmpty()) { + needTypeCoercion = needTypeCoercion.stream() + .map(TypeCoercionUtils::replaceCharacterToString) + .collect(Collectors.toList()); + } needTypeCoercion.addAll(partitioned.get(false)); return needTypeCoercion.stream().map(Optional::of).reduce(Optional.of(NullType.INSTANCE), (r, c) -> { @@ -1493,4 +1539,17 @@ public class TypeCoercionUtils { return binaryArithmetic.withChildren(castIfNotSameType(left, dt1), castIfNotSameType(right, dt2)); } + + private static boolean supportCompare(DataType dataType) { + if (!(dataType instanceof PrimitiveType)) { + return false; + } + if (dataType.isObjectType()) { + return false; + } + if (dataType instanceof JsonType) { + return false; + } + return true; + } } diff --git a/regression-test/suites/nereids_syntax_p0/array_function.groovy b/regression-test/suites/nereids_syntax_p0/array_function.groovy index 8cc857f07d..e05dd92ec9 100644 --- a/regression-test/suites/nereids_syntax_p0/array_function.groovy +++ b/regression-test/suites/nereids_syntax_p0/array_function.groovy @@ -24,25 +24,26 @@ suite("array_function") { qt_3 "SELECT ARRAY_MAP((x,y)->x+y, ARRAY(-41, NULL, -18), ARRAY(98, 47, NULL))" qt_4 "SELECT ARRAY_MAP(x->x+1, ARRAY(-82.31, -72.18, 35.59, -67.13))" qt_5 "SELECT ARRAY_MAP((x,y)->x+y, ARRAY(-37.03, 81.89, 56.38, -36.76), ARRAY(1.56, -14.58, 42.22, -56.13))" - // test { - // sql "select array(), array(null), array(1), array('abc'), array(null, 1), array(1, null)" - // result([["[]", "[NULL]", "[1]", "['abc']", "[NULL, 1]", "[1, NULL]"]]) - // } + test { + sql "select array(), array(null), array(1), array('abc'), array(null, 1), array(1, null)" + result([["[]", "[NULL]", "[1]", "[\"abc\"]", "[NULL, 1]", "[1, NULL]"]]) + } - // test { - // sql "select array(), array('a'), array(number, 'a') from numbers('number'='3')" - // result([ - // ["[]", "['a']", "['0', 'a']"], - // ["[]", "['a']", "['1', 'a']"], - // ["[]", "['a']", "['2', 'a']"] - // ]) - // } + test { + sql "select array(), array('a'), array(number, 'a') from numbers('number'='3')" + result([ + ["[]", "[\"a\"]", "[\"0\", \"a\"]"], + ["[]", "[\"a\"]", "[\"1\", \"a\"]"], + ["[]", "[\"a\"]", "[\"2\", \"a\"]"] + ]) + } - // test { - // sql """select - // array_min(array(5, 4, 3, 2, 1, null)), - // array_join(array(5, 4, 3, 2, 1, null), ','), - // array_union(array(1, 2, 3), array(4.0, 5.0, 6.1))""" - // result([[1, "5,4,3,2,1", "[1, 2, 3, 4, 5, 6.1]"]]) - // } + test { + sql """ + SELECT [[[2]], [['aa'],[2,1.0]]] + """ + result([ + ["""[[["2"]], [["aa"], ["2.0", "1.0"]]]"""] + ]) + } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org