[feature](Nereids) support struct type (#23597)

1. support struct data type
2. add array / map / struct literal syntax
3. fix array union / intersect / except type coercion
4. fix explict cast data type check for array
5. fix bound function type coercion
This commit is contained in:
morrySnow
2023-08-29 20:41:24 +08:00
committed by GitHub
parent 4f7e7040ad
commit e02747e976
28 changed files with 743 additions and 74 deletions

View File

@ -82,6 +82,8 @@ COMMA: ',';
DOT: '.';
LEFT_BRACKET: '[';
RIGHT_BRACKET: ']';
LEFT_BRACE: '{';
RIGHT_BRACE: '}';
// TODO: add a doc to list reserved words
@ -232,7 +234,6 @@ LAST: 'LAST';
LATERAL: 'LATERAL';
LAZY: 'LAZY';
LEADING: 'LEADING';
LEFT_BRACE: '{';
LEFT: 'LEFT';
LIKE: 'LIKE';
ILIKE: 'ILIKE';
@ -319,7 +320,6 @@ RESTRICT: 'RESTRICT';
RESTRICTIVE: 'RESTRICTIVE';
REVOKE: 'REVOKE';
REWRITTEN: 'REWRITTEN';
RIGHT_BRACE: '}';
RIGHT: 'RIGHT';
// original optimizer only support REGEXP, the new optimizer should be consistent with it
RLIKE: 'RLIKE';

View File

@ -401,6 +401,7 @@ primaryExpression
| CASE value=expression whenClause+ (ELSE elseExpression=expression)? END #simpleCase
| name=CAST LEFT_PAREN expression AS dataType RIGHT_PAREN #cast
| constant #constantDefault
| interval #intervalLiteral
| ASTERISK #star
| qualifiedName DOT ASTERISK #star
| functionIdentifier LEFT_PAREN ((DISTINCT|ALL)? arguments+=expression
@ -468,11 +469,14 @@ specifiedPartition
constant
: NULL #nullLiteral
| interval #intervalLiteral
| type=(DATE | DATEV2 | TIMESTAMP) STRING_LITERAL #typeConstructor
| type=(DATE | DATEV2 | TIMESTAMP) STRING_LITERAL #typeConstructor
| number #numericLiteral
| booleanValue #booleanLiteral
| STRING_LITERAL #stringLiteral
| STRING_LITERAL #stringLiteral
| LEFT_BRACKET items+=constant (COMMA items+=constant)* RIGHT_BRACKET #arrayLiteral
| LEFT_BRACE items+=constant COLON items+=constant
(COMMA items+=constant COLON items+=constant)* RIGHT_BRACE #mapLiteral
| LEFT_BRACE items+=constant (COMMA items+=constant)* RIGHT_BRACE #structLiteral
;
comparisonOperator
@ -543,7 +547,7 @@ quotedIdentifier
;
number
: MINUS? INTEGER_VALUE #integerLiteral
: MINUS? INTEGER_VALUE #integerLiteral
| MINUS? (EXPONENT_VALUE | DECIMAL_VALUE) #decimalLiteral
;

View File

@ -93,6 +93,8 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.ConvertTz;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Cos;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CountEqual;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateMap;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateNamedStruct;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateStruct;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentCatalog;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
@ -305,6 +307,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrLeft;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrRight;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring;
@ -436,6 +439,8 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(Cos.class, "cos"),
scalar(CountEqual.class, "countequal"),
scalar(CreateMap.class, "map"),
scalar(CreateStruct.class, "struct"),
scalar(CreateNamedStruct.class, "named_struct"),
scalar(CurrentCatalog.class, "current_catalog"),
scalar(CurrentDate.class, "curdate", "current_date"),
scalar(CurrentTime.class, "curtime", "current_time"),
@ -610,6 +615,7 @@ public class BuiltinScalarFunctions implements FunctionHelper {
scalar(Sign.class, "sign"),
scalar(Sin.class, "sin"),
scalar(Sleep.class, "sleep"),
scalar(StructElement.class, "struct_element"),
scalar(Sm3.class, "sm3"),
scalar(Sm3sum.class, "sm3sum"),
scalar(Sm4Decrypt.class, "sm4_decrypt"),

View File

@ -30,6 +30,7 @@ import org.apache.doris.nereids.DorisParser.AliasQueryContext;
import org.apache.doris.nereids.DorisParser.AliasedQueryContext;
import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext;
import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext;
import org.apache.doris.nereids.DorisParser.ArrayLiteralContext;
import org.apache.doris.nereids.DorisParser.ArraySliceContext;
import org.apache.doris.nereids.DorisParser.BitOperationContext;
import org.apache.doris.nereids.DorisParser.BooleanExpressionContext;
@ -41,6 +42,8 @@ import org.apache.doris.nereids.DorisParser.ColumnReferenceContext;
import org.apache.doris.nereids.DorisParser.CommentJoinHintContext;
import org.apache.doris.nereids.DorisParser.CommentRelationHintContext;
import org.apache.doris.nereids.DorisParser.ComparisonContext;
import org.apache.doris.nereids.DorisParser.ComplexColTypeContext;
import org.apache.doris.nereids.DorisParser.ComplexColTypeListContext;
import org.apache.doris.nereids.DorisParser.ComplexDataTypeContext;
import org.apache.doris.nereids.DorisParser.ConstantContext;
import org.apache.doris.nereids.DorisParser.CreateRowPolicyContext;
@ -74,6 +77,7 @@ import org.apache.doris.nereids.DorisParser.LateralViewContext;
import org.apache.doris.nereids.DorisParser.LimitClauseContext;
import org.apache.doris.nereids.DorisParser.LogicalBinaryContext;
import org.apache.doris.nereids.DorisParser.LogicalNotContext;
import org.apache.doris.nereids.DorisParser.MapLiteralContext;
import org.apache.doris.nereids.DorisParser.MultiStatementsContext;
import org.apache.doris.nereids.DorisParser.MultipartIdentifierContext;
import org.apache.doris.nereids.DorisParser.NamedExpressionContext;
@ -105,6 +109,7 @@ import org.apache.doris.nereids.DorisParser.SortItemContext;
import org.apache.doris.nereids.DorisParser.StarContext;
import org.apache.doris.nereids.DorisParser.StatementDefaultContext;
import org.apache.doris.nereids.DorisParser.StringLiteralContext;
import org.apache.doris.nereids.DorisParser.StructLiteralContext;
import org.apache.doris.nereids.DorisParser.SubqueryContext;
import org.apache.doris.nereids.DorisParser.SubqueryExpressionContext;
import org.apache.doris.nereids.DorisParser.SystemVariableContext;
@ -187,7 +192,10 @@ import org.apache.doris.nereids.trees.expressions.WindowFrame;
import org.apache.doris.nereids.trees.expressions.functions.Function;
import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
import org.apache.doris.nereids.trees.expressions.functions.agg.GroupConcat;
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.CreateMap;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateStruct;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysAdd;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysDiff;
import org.apache.doris.nereids.trees.expressions.functions.scalar.DaysSub;
@ -264,6 +272,8 @@ import org.apache.doris.nereids.trees.plans.logical.UsingJoin;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.MapType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.coercion.CharacterType;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.policy.FilterType;
@ -1311,7 +1321,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
}
@Override
public Expression visitTypeConstructor(TypeConstructorContext ctx) {
public Literal visitTypeConstructor(TypeConstructorContext ctx) {
String value = ctx.STRING_LITERAL().getText();
value = value.substring(1, value.length() - 1);
String type = ctx.type.getText().toUpperCase();
@ -1367,7 +1377,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
* Create a NULL literal expression.
*/
@Override
public Expression visitNullLiteral(NullLiteralContext ctx) {
public Literal visitNullLiteral(NullLiteralContext ctx) {
return new NullLiteral();
}
@ -1444,6 +1454,24 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
return sb.toString();
}
@Override
public Object visitArrayLiteral(ArrayLiteralContext ctx) {
Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new);
return new Array(items);
}
@Override
public Object visitMapLiteral(MapLiteralContext ctx) {
Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new);
return new CreateMap(items);
}
@Override
public Object visitStructLiteral(StructLiteralContext ctx) {
Literal[] items = ctx.items.stream().<Literal>map(this::typedVisit).toArray(Literal[]::new);
return new CreateStruct(items);
}
@Override
public Expression visitParenthesizedExpression(ParenthesizedExpressionContext ctx) {
return getExpression(ctx.expression());
@ -2084,13 +2112,25 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
case DorisParser.MAP:
return MapType.of(typedVisit(ctx.dataType(0)), typedVisit(ctx.dataType(1)));
case DorisParser.STRUCT:
throw new AnalysisException("do not support STRUCT type for Nereids");
return new StructType(visitComplexColTypeList(ctx.complexColTypeList()));
default:
throw new AnalysisException("do not support " + ctx.complex.getText() + " type for Nereids");
}
});
}
@Override
public List<StructField> visitComplexColTypeList(ComplexColTypeListContext ctx) {
return ctx.complexColType().stream().map(this::visitComplexColType).collect(ImmutableList.toImmutableList());
}
@Override
public StructField visitComplexColType(ComplexColTypeContext ctx) {
String comment = ctx.commentSpec().STRING_LITERAL().getText();
comment = escapeBackSlash(comment.substring(1, comment.length() - 1));
return new StructField(ctx.identifier().getText(), typedVisit(ctx.dataType()), true, comment);
}
private Expression parseFunctionWithOrderKeys(String functionName, boolean isDistinct,
List<Expression> params, List<OrderKey> orderKeys, ParserRuleContext ctx) {
if (functionName.equalsIgnoreCase("group_concat")) {

View File

@ -25,9 +25,11 @@ import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.MapType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.coercion.CharacterType;
import java.util.Map;
import java.util.List;
/**
* check cast valid
@ -42,7 +44,7 @@ public class CheckCast extends AbstractExpressionRewriteRule {
DataType originalType = cast.child().getDataType();
DataType targetType = cast.getDataType();
if (!check(originalType, targetType)) {
throw new AnalysisException("cannot cast " + originalType + " to " + targetType);
throw new AnalysisException("cannot cast " + originalType.toSql() + " to " + targetType.toSql());
}
return cast;
}
@ -54,17 +56,19 @@ public class CheckCast extends AbstractExpressionRewriteRule {
return check(((MapType) originalType).getKeyType(), ((MapType) targetType).getKeyType())
&& check(((MapType) originalType).getValueType(), ((MapType) targetType).getValueType());
} else if (originalType instanceof StructType && targetType instanceof StructType) {
Map<String, DataType> targetItems = ((StructType) targetType).getItems();
for (Map.Entry<String, DataType> entry : ((StructType) originalType).getItems().entrySet()) {
if (targetItems.containsKey(entry.getKey())) {
if (!check(entry.getValue(), targetItems.get(entry.getKey()))) {
return false;
}
} else {
List<StructField> targetFields = ((StructType) targetType).getFields();
List<StructField> originalFields = ((StructType) originalType).getFields();
if (targetFields.size() != originalFields.size()) {
return false;
}
for (int i = 0; i < targetFields.size(); i++) {
if (!targetFields.get(i).equals(originalFields.get(i))) {
return false;
}
}
return true;
} else if (originalType instanceof CharacterType && targetType instanceof StructType) {
return true;
} else {
return checkPrimitiveType(originalType, targetType);
}

View File

@ -46,6 +46,7 @@ import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
import org.apache.doris.nereids.trees.expressions.functions.FunctionBuilder;
import org.apache.doris.nereids.trees.expressions.functions.udf.AliasUdfBuilder;
import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.BigIntType;
import org.apache.doris.nereids.types.BooleanType;
import org.apache.doris.nereids.types.DataType;
@ -116,6 +117,13 @@ public class FunctionBinder extends AbstractExpressionRewriteRule {
}
}
@Override
public Expression visitBoundFunction(BoundFunction boundFunction, ExpressionRewriteContext context) {
boundFunction = (BoundFunction) boundFunction.withChildren(boundFunction.children().stream()
.map(e -> e.accept(this, context)).collect(Collectors.toList()));
return TypeCoercionUtils.processBoundFunction(boundFunction);
}
/**
* gets the method for calculating the time.
* e.g. YEARS_ADD、YEARS_SUB、DAYS_ADD 、DAYS_SUB
@ -271,4 +279,14 @@ public class FunctionBinder extends AbstractExpressionRewriteRule {
}
return match.withChildren(left, right);
}
@Override
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) {
TypeCoercionUtils.checkCanCastTo(cast.child().getDataType(), cast.getDataType());
}
return cast;
}
}

View File

@ -40,8 +40,7 @@ import java.util.Set;
*/
public class CheckDataTypes implements CustomRewriter {
private static final Set<Class<? extends DataType>> UNSUPPORTED_TYPE = ImmutableSet.of(
StructType.class, JsonType.class);
private static final Set<Class<? extends DataType>> UNSUPPORTED_TYPE = ImmutableSet.of(JsonType.class);
@Override
public Plan rewriteRoot(Plan rootPlan, JobContext jobContext) {
@ -92,8 +91,9 @@ public class CheckDataTypes implements CustomRewriter {
} else if (dataType instanceof MapType) {
checkTypes(((MapType) dataType).getKeyType());
checkTypes(((MapType) dataType).getValueType());
}
if (UNSUPPORTED_TYPE.contains(dataType.getClass())) {
} else if (dataType instanceof StructType) {
((StructType) dataType).getFields().forEach(f -> this.checkTypes(f.getDataType()));
} else if (UNSUPPORTED_TYPE.contains(dataType.getClass())) {
throw new AnalysisException(String.format("type %s is unsupported for Nereids", dataType));
}
}

View File

@ -31,6 +31,7 @@ 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.MapType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.coercion.AnyDataType;
@ -129,7 +130,19 @@ public abstract class Expression extends AbstractTreeNode<Expression> implements
&& checkInputDataTypesWithExpectType(
((MapType) input).getValueType(), ((MapType) expected).getValueType());
} else if (input instanceof StructType && expected instanceof StructType) {
throw new AnalysisException("not support struct type now.");
List<StructField> inputFields = ((StructType) input).getFields();
List<StructField> expectedFields = ((StructType) expected).getFields();
if (inputFields.size() != expectedFields.size()) {
return false;
}
for (int i = 0; i < inputFields.size(); i++) {
if (!checkInputDataTypesWithExpectType(
inputFields.get(i).getDataType(),
expectedFields.get(i).getDataType())) {
return false;
}
}
return true;
} else {
return checkPrimitiveInputDataTypesWithExpectType(input, expected);
}

View File

@ -19,7 +19,6 @@ package org.apache.doris.nereids.trees.expressions.functions;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.annotation.Developing;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.functions.ComputeSignatureHelper.ComputeSignatureChain;
import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes;
import org.apache.doris.nereids.types.ArrayType;
@ -117,7 +116,7 @@ public interface ComputeSignature extends FunctionTrait, ImplicitCastInputTypes
.get();
}
/** default computeSignature */
/** use processor to process computeSignature */
static boolean processComplexType(DataType signatureType, DataType realType,
BiFunction<DataType, DataType, Boolean> processor) {
if (signatureType instanceof ArrayType && realType instanceof ArrayType) {
@ -127,7 +126,9 @@ public interface ComputeSignature extends FunctionTrait, ImplicitCastInputTypes
return processor.apply(((MapType) signatureType).getKeyType(), ((MapType) realType).getKeyType())
&& processor.apply(((MapType) signatureType).getValueType(), ((MapType) realType).getValueType());
} else if (signatureType instanceof StructType && realType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
return true;
} else {
return processor.apply(signatureType, realType);
}

View File

@ -73,7 +73,8 @@ public class ComputeSignatureHelper {
collectAnyDataType(((MapType) sigType).getKeyType(), NullType.INSTANCE, indexToArgumentTypes);
collectAnyDataType(((MapType) sigType).getValueType(), NullType.INSTANCE, indexToArgumentTypes);
} else if (sigType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
} else {
if (sigType instanceof AnyDataType && ((AnyDataType) sigType).getIndex() >= 0) {
List<DataType> dataTypes = indexToArgumentTypes.computeIfAbsent(
@ -90,7 +91,8 @@ public class ComputeSignatureHelper {
collectAnyDataType(((MapType) sigType).getValueType(),
((MapType) expressionType).getValueType(), indexToArgumentTypes);
} else if (sigType instanceof StructType && expressionType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
} else {
if (sigType instanceof AnyDataType && ((AnyDataType) sigType).getIndex() >= 0) {
List<DataType> dataTypes = indexToArgumentTypes.computeIfAbsent(
@ -112,7 +114,8 @@ public class ComputeSignatureHelper {
collectFollowToAnyDataType(((MapType) sigType).getValueType(),
NullType.INSTANCE, indexToArgumentTypes, allNullTypeIndex);
} else if (sigType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
} else {
if (sigType instanceof FollowToAnyDataType
&& allNullTypeIndex.contains(((FollowToAnyDataType) sigType).getIndex())) {
@ -130,7 +133,8 @@ public class ComputeSignatureHelper {
collectFollowToAnyDataType(((MapType) sigType).getValueType(),
((MapType) expressionType).getValueType(), indexToArgumentTypes, allNullTypeIndex);
} else if (sigType instanceof StructType && expressionType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
} else {
if (sigType instanceof FollowToAnyDataType
&& allNullTypeIndex.contains(((FollowToAnyDataType) sigType).getIndex())) {
@ -149,7 +153,9 @@ public class ComputeSignatureHelper {
return MapType.of(replaceAnyDataType(((MapType) dataType).getKeyType(), indexToCommonTypes),
replaceAnyDataType(((MapType) dataType).getValueType(), indexToCommonTypes));
} else if (dataType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
// TODO: do not support struct type now
// throw new AnalysisException("do not support struct type now");
return dataType;
} else {
if (dataType instanceof AnyDataType && ((AnyDataType) dataType).getIndex() >= 0) {
Optional<DataType> optionalDataType = indexToCommonTypes.get(((AnyDataType) dataType).getIndex());
@ -177,7 +183,7 @@ public class ComputeSignatureHelper {
DataType sigType;
if (i >= signature.argumentsTypes.size()) {
sigType = signature.getVarArgType().orElseThrow(
() -> new IllegalStateException("function arity not match with signature"));
() -> new AnalysisException("function arity not match with signature"));
} else {
sigType = signature.argumentsTypes.get(i);
}

View File

@ -25,7 +25,6 @@ import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.nereids.types.coercion.FollowToAnyDataType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@ -40,7 +39,7 @@ public class ArrayExcept extends ScalarFunction implements ExplicitlyCastableSig
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.retArgType(0)
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new FollowToAnyDataType(0)))
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new AnyDataType(0)))
);
/**

View File

@ -25,7 +25,6 @@ import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.nereids.types.coercion.FollowToAnyDataType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@ -40,7 +39,7 @@ public class ArrayIntersect extends ScalarFunction implements ExplicitlyCastable
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.retArgType(0)
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new FollowToAnyDataType(0)))
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new AnyDataType(0)))
);
/**

View File

@ -25,7 +25,6 @@ import org.apache.doris.nereids.trees.expressions.shape.BinaryExpression;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.ArrayType;
import org.apache.doris.nereids.types.coercion.AnyDataType;
import org.apache.doris.nereids.types.coercion.FollowToAnyDataType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@ -40,7 +39,7 @@ public class ArrayUnion extends ScalarFunction implements ExplicitlyCastableSign
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.retArgType(0)
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new FollowToAnyDataType(0)))
.args(ArrayType.of(new AnyDataType(0)), ArrayType.of(new AnyDataType(0)))
);
/**

View File

@ -32,7 +32,7 @@ import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* ScalarFunction 'array'. This class is generated by GenerateFunction.
* ScalarFunction 'map'.
*/
public class CreateMap extends ScalarFunction
implements ExplicitlyCastableSignature, AlwaysNotNullable {
@ -59,7 +59,7 @@ public class CreateMap extends ScalarFunction
@Override
public void checkLegalityBeforeTypeCoercion() {
if (arity() % 2 != 0) {
throw new AnalysisException("map can't be odd parameters, need even parameters " + this);
throw new AnalysisException("map can't be odd parameters, need even parameters " + this.toSql());
}
}

View File

@ -0,0 +1,105 @@
// 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.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Set;
/**
* ScalarFunction 'named_struct'.
*/
public class CreateNamedStruct extends ScalarFunction
implements ExplicitlyCastableSignature, AlwaysNotNullable {
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(StructType.SYSTEM_DEFAULT).args()
);
/**
* constructor with 0 or more arguments.
*/
public CreateNamedStruct(Expression... varArgs) {
super("named_struct", varArgs);
}
@Override
public void checkLegalityBeforeTypeCoercion() {
if (arity() % 2 != 0) {
throw new AnalysisException("named_struct can't be odd parameters, need even parameters " + this.toSql());
}
Set<String> names = Sets.newHashSet();
for (int i = 0; i < arity(); i = i + 2) {
if (!(child(i) instanceof StringLikeLiteral)) {
throw new AnalysisException("named_struct only allows"
+ " constant string parameter in odd position: " + this);
} else {
String name = ((StringLikeLiteral) child(i)).getStringValue();
if (names.contains(name)) {
throw new AnalysisException("The name of the struct field cannot be repeated."
+ " same name fields are " + name);
} else {
names.add(name);
}
}
}
}
/**
* withChildren.
*/
@Override
public CreateNamedStruct withChildren(List<Expression> children) {
return new CreateNamedStruct(children.toArray(new Expression[0]));
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitCreateNamedStruct(this, context);
}
@Override
public List<FunctionSignature> getSignatures() {
if (arity() == 0) {
return SIGNATURES;
} else {
ImmutableList.Builder<StructField> structFields = ImmutableList.builder();
for (int i = 0; i < arity(); i = i + 2) {
StringLikeLiteral nameLiteral = (StringLikeLiteral) child(i);
structFields.add(new StructField(nameLiteral.getStringValue(),
children.get(i + 1).getDataType(), true, ""));
}
return ImmutableList.of(FunctionSignature.ret(new StructType(structFields.build()))
.args(children.stream().map(ExpressionTrait::getDataType).toArray(DataType[]::new)));
}
}
}

View File

@ -0,0 +1,77 @@
// 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.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNotNullable;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* ScalarFunction 'struct'.
*/
public class CreateStruct extends ScalarFunction
implements ExplicitlyCastableSignature, AlwaysNotNullable {
public static final List<FunctionSignature> SIGNATURES = ImmutableList.of(
FunctionSignature.ret(StructType.SYSTEM_DEFAULT).args()
);
/**
* constructor with 0 or more arguments.
*/
public CreateStruct(Expression... varArgs) {
super("struct", varArgs);
}
/**
* withChildren.
*/
@Override
public CreateStruct withChildren(List<Expression> children) {
return new CreateStruct(children.toArray(new Expression[0]));
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitCreateStruct(this, context);
}
@Override
public List<FunctionSignature> getSignatures() {
if (arity() == 0) {
return SIGNATURES;
} else {
ImmutableList.Builder<StructField> structFields = ImmutableList.builder();
for (int i = 0; i < arity(); i++) {
structFields.add(new StructField(String.valueOf(i + 1), children.get(i).getDataType(), true, ""));
}
return ImmutableList.of(FunctionSignature.ret(new StructType(structFields.build()))
.args(children.stream().map(ExpressionTrait::getDataType).toArray(DataType[]::new)));
}
}
}

View File

@ -0,0 +1,99 @@
// 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.functions.scalar;
import org.apache.doris.catalog.FunctionSignature;
import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.functions.AlwaysNullable;
import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
import org.apache.doris.nereids.trees.expressions.functions.SearchSignature;
import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.StructType;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* ScalarFunction 'struct_element'.
*/
public class StructElement extends ScalarFunction
implements ExplicitlyCastableSignature, AlwaysNullable {
/**
* constructor with 0 or more arguments.
*/
public StructElement(Expression arg0, Expression arg1) {
super("struct_element", arg0, arg1);
}
@Override
public void checkLegalityBeforeTypeCoercion() {
if (!(child(0).getDataType() instanceof StructType)) {
SearchSignature.throwCanNotFoundFunctionException(this.getName(), this.getArguments());
}
if (!(child(1) instanceof StringLikeLiteral || child(1) instanceof IntegerLikeLiteral)) {
throw new AnalysisException("struct_element only allows"
+ " constant int or string second parameter: " + this.toSql());
}
}
/**
* withChildren.
*/
@Override
public StructElement withChildren(List<Expression> children) {
Preconditions.checkArgument(children.size() == 2, "children size should be 2");
return new StructElement(children.get(0), children.get(1));
}
@Override
public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
return visitor.visitStructElement(this, context);
}
@Override
public List<FunctionSignature> getSignatures() {
StructType structArgType = (StructType) child(0).getDataType();
DataType retType;
if (child(1) instanceof IntegerLikeLiteral) {
int offset = ((IntegerLikeLiteral) child(1)).getIntValue();
if (offset <= 0 || offset > structArgType.getFields().size()) {
throw new AnalysisException("the specified field index out of bound: " + this.toSql());
} else {
retType = structArgType.getFields().get(offset - 1).getDataType();
}
} else if (child(1) instanceof StringLikeLiteral) {
String name = ((StringLikeLiteral) child(1)).getStringValue();
if (!structArgType.getNameToFields().containsKey(name)) {
throw new AnalysisException("the specified field name " + name + " was not found: " + this.toSql());
} else {
retType = structArgType.getNameToFields().get(name).getDataType();
}
} else {
throw new AnalysisException("struct_element only allows"
+ " constant int or string second parameter: " + this.toSql());
}
return ImmutableList.of(FunctionSignature.ret(retType).args(structArgType, child(1).getDataType()));
}
}

View File

@ -43,4 +43,9 @@ public abstract class StringLikeLiteral extends Literal {
}
return (double) v;
}
@Override
public String toString() {
return "'" + value + "'";
}
}

View File

@ -52,9 +52,4 @@ public class StringLiteral extends StringLikeLiteral {
public LiteralExpr toLegacyLiteral() {
return new org.apache.doris.analysis.StringLiteral(value);
}
@Override
public String toString() {
return "'" + value + "'";
}
}

View File

@ -50,9 +50,4 @@ public class VarcharLiteral extends StringLikeLiteral {
public LiteralExpr toLegacyLiteral() {
return new StringLiteral(value);
}
@Override
public String toString() {
return "'" + value + "'";
}
}

View File

@ -97,6 +97,8 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.ConvertTz;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Cos;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CountEqual;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateMap;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateNamedStruct;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CreateStruct;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentCatalog;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentDate;
import org.apache.doris.nereids.trees.expressions.functions.scalar.CurrentTime;
@ -309,6 +311,7 @@ import org.apache.doris.nereids.trees.expressions.functions.scalar.StartsWith;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrLeft;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrRight;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StrToDate;
import org.apache.doris.nereids.trees.expressions.functions.scalar.StructElement;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SubBitmap;
import org.apache.doris.nereids.trees.expressions.functions.scalar.SubReplace;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring;
@ -1695,4 +1698,18 @@ public interface ScalarFunctionVisitor<R, C> {
default R visitMapValues(MapValues mapValues, C context) {
return visitScalarFunction(mapValues, context);
}
// struct function
default R visitCreateStruct(CreateStruct createStruct, C context) {
return visitScalarFunction(createStruct, context);
}
default R visitCreateNamedStruct(CreateNamedStruct createNamedStruct, C context) {
return visitScalarFunction(createNamedStruct, context);
}
default R visitStructElement(StructElement structElement, C context) {
return visitScalarFunction(structElement, context);
}
}

View File

@ -290,8 +290,11 @@ public abstract class DataType {
} else if (type.isJsonbType()) {
return JsonType.INSTANCE;
} else if (type.isStructType()) {
// TODO: support struct type really
return StructType.INSTANCE;
List<StructField> structFields = ((org.apache.doris.catalog.StructType) (type)).getFields().stream()
.map(cf -> new StructField(cf.getName(), fromCatalogType(cf.getType()),
cf.getContainsNull(), cf.getComment()))
.collect(ImmutableList.toImmutableList());
return new StructType(structFields);
} else if (type.isMapType()) {
org.apache.doris.catalog.MapType mapType = (org.apache.doris.catalog.MapType) type;
return MapType.of(fromCatalogType(mapType.getKeyType()), fromCatalogType(mapType.getValueType()));

View File

@ -0,0 +1,104 @@
// 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.types;
import org.apache.doris.nereids.util.Utils;
import java.util.Objects;
/**
* A field inside a StructType.
*/
public class StructField {
private final String name;
private final DataType dataType;
private final boolean nullable;
private final String comment;
/**
* StructField Constructor
* @param name The name of this field
* @param dataType The data type of this field
* @param nullable Indicates if values of this field can be `null` values
*/
public StructField(String name, DataType dataType, boolean nullable, String comment) {
this.name = Objects.requireNonNull(name, "name should not be null");
this.dataType = Objects.requireNonNull(dataType, "dataType should not be null");
this.nullable = nullable;
this.comment = Objects.requireNonNull(comment, "comment should not be null");
}
public String getName() {
return name;
}
public DataType getDataType() {
return dataType;
}
public boolean isNullable() {
return nullable;
}
public String getComment() {
return comment;
}
public StructField withDataType(DataType dataType) {
return new StructField(name, dataType, nullable, comment);
}
public org.apache.doris.catalog.StructField toCatalogDataType() {
return new org.apache.doris.catalog.StructField(
name, dataType.toCatalogDataType(), comment, nullable);
}
public String toSql() {
return name + ":" + dataType.toSql()
+ (nullable ? " " : " NOT NULL")
+ (comment.isEmpty() ? "" : " COMMENT " + comment);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
StructField that = (StructField) o;
return nullable == that.nullable && Objects.equals(name, that.name) && Objects.equals(dataType,
that.dataType);
}
@Override
public int hashCode() {
return Objects.hash(name, dataType, nullable);
}
@Override
public String toString() {
return Utils.toSqlString("StructField",
"name", name,
"dataType", dataType,
"nullable", nullable,
"comment", comment);
}
}

View File

@ -19,12 +19,18 @@ package org.apache.doris.nereids.types;
import org.apache.doris.catalog.Type;
import org.apache.doris.nereids.annotation.Developing;
import org.apache.doris.nereids.exceptions.AnalysisException;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Struct type in Nereids.
@ -32,28 +38,43 @@ import java.util.Objects;
@Developing
public class StructType extends DataType {
public static final StructType INSTANCE = new StructType();
public static final StructType SYSTEM_DEFAULT = new StructType();
public static final int WIDTH = 24;
private final Map<String, DataType> items;
private final List<StructField> fields;
private final Supplier<Map<String, StructField>> nameToFields;
private StructType() {
items = ImmutableMap.of();
nameToFields = Suppliers.memoize(ImmutableMap::of);
fields = ImmutableList.of();
}
public StructType(Map<String, DataType> items) {
this.items = ImmutableSortedMap.copyOf(Objects.requireNonNull(items, "items should not be null"),
String.CASE_INSENSITIVE_ORDER);
/**
* construct struct type.
*/
public StructType(List<StructField> fields) {
this.fields = ImmutableList.copyOf(Objects.requireNonNull(fields, "fields should not be null"));
this.nameToFields = Suppliers.memoize(() -> this.fields.stream().collect(ImmutableMap.toImmutableMap(
StructField::getName, f -> f, (f1, f2) -> {
throw new AnalysisException("The name of the struct field cannot be repeated."
+ " same name fields are " + f1 + " and " + f2);
})));
}
public Map<String, DataType> getItems() {
return items;
public List<StructField> getFields() {
return fields;
}
public Map<String, StructField> getNameToFields() {
return nameToFields.get();
}
@Override
public Type toCatalogDataType() {
return Type.STRUCT;
return new org.apache.doris.catalog.StructType(fields.stream()
.map(StructField::toCatalogDataType)
.collect(Collectors.toCollection(ArrayList::new)));
}
@Override
@ -68,7 +89,22 @@ public class StructType extends DataType {
@Override
public boolean equals(Object o) {
return o instanceof StructType;
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
StructType that = (StructType) o;
return Objects.equals(fields, that.fields);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), fields);
}
@Override
@ -78,6 +114,11 @@ public class StructType extends DataType {
@Override
public String toSql() {
return "STRUCT";
return "STRUCT<" + fields.stream().map(StructField::toSql).collect(Collectors.joining(", ")) + ">";
}
@Override
public String toString() {
return "STRUCT<" + fields.stream().map(StructField::toString).collect(Collectors.joining(", ")) + ">";
}
}

View File

@ -85,6 +85,7 @@ import org.apache.doris.nereids.types.MapType;
import org.apache.doris.nereids.types.NullType;
import org.apache.doris.nereids.types.SmallIntType;
import org.apache.doris.nereids.types.StringType;
import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.types.TimeType;
import org.apache.doris.nereids.types.TimeV2Type;
@ -153,7 +154,22 @@ public class TypeCoercionUtils {
}
return Optional.empty();
} else if (input instanceof StructType && expected instanceof StructType) {
throw new AnalysisException("not support struct type now.");
List<StructField> inputFields = ((StructType) input).getFields();
List<StructField> expectedFields = ((StructType) expected).getFields();
if (inputFields.size() != expectedFields.size()) {
return Optional.empty();
}
List<StructField> newFields = Lists.newArrayList();
for (int i = 0; i < inputFields.size(); i++) {
Optional<DataType> newDataType = implicitCast(inputFields.get(i).getDataType(),
expectedFields.get(i).getDataType());
if (newDataType.isPresent()) {
newFields.add(inputFields.get(i).withDataType(newDataType.get()));
} else {
return Optional.empty();
}
}
return Optional.of(new StructType(newFields));
} else {
return implicitCastPrimitive(input, expected);
}
@ -247,7 +263,7 @@ public class TypeCoercionUtils {
return hasCharacterType(((MapType) dataType).getKeyType())
|| hasCharacterType(((MapType) dataType).getValueType());
} else if (dataType instanceof StructType) {
throw new AnalysisException("do not support struct type now");
return ((StructType) dataType).getFields().stream().anyMatch(f -> hasCharacterType(f.getDataType()));
}
return dataType instanceof CharacterType;
}
@ -293,7 +309,17 @@ public class TypeCoercionUtils {
return matchesType(((MapType) input).getKeyType(), ((MapType) target).getKeyType())
&& matchesType(((MapType) input).getValueType(), ((MapType) target).getValueType());
} else if (input instanceof StructType && target instanceof StructType) {
throw new AnalysisException("do not support struct type now");
List<StructField> inputFields = ((StructType) input).getFields();
List<StructField> targetFields = ((StructType) target).getFields();
if (inputFields.size() != targetFields.size()) {
return false;
}
for (int i = 0; i < inputFields.size(); i++) {
if (!matchesType(inputFields.get(i).getDataType(), targetFields.get(i).getDataType())) {
return false;
}
}
return true;
} else {
if (input instanceof NullType) {
return false;
@ -994,7 +1020,22 @@ public class TypeCoercionUtils {
return Optional.of(MapType.of(keyType.get(), valueType.get()));
}
} else if (left instanceof StructType && right instanceof StructType) {
throw new AnalysisException("do not support struct type now");
List<StructField> leftFields = ((StructType) left).getFields();
List<StructField> rightFields = ((StructType) right).getFields();
if (leftFields.size() != rightFields.size()) {
return Optional.empty();
}
List<StructField> newFields = Lists.newArrayList();
for (int i = 0; i < leftFields.size(); i++) {
Optional<DataType> newDataType = findCommonComplexTypeForComparison(leftFields.get(i).getDataType(),
rightFields.get(i).getDataType(), intStringToString);
if (newDataType.isPresent()) {
newFields.add(leftFields.get(i).withDataType(newDataType.get()));
} else {
return Optional.empty();
}
}
return Optional.of(new StructType(newFields));
}
return Optional.empty();
}
@ -1192,7 +1233,22 @@ public class TypeCoercionUtils {
return Optional.of(MapType.of(keyType.get(), valueType.get()));
}
} else if (left instanceof StructType && right instanceof StructType) {
throw new AnalysisException("do not support struct type now");
List<StructField> leftFields = ((StructType) left).getFields();
List<StructField> rightFields = ((StructType) right).getFields();
if (leftFields.size() != rightFields.size()) {
return Optional.empty();
}
List<StructField> newFields = Lists.newArrayList();
for (int i = 0; i < leftFields.size(); i++) {
Optional<DataType> newDataType = findCommonComplexTypeForCaseWhen(leftFields.get(i).getDataType(),
rightFields.get(i).getDataType());
if (newDataType.isPresent()) {
newFields.add(leftFields.get(i).withDataType(newDataType.get()));
} else {
return Optional.empty();
}
}
return Optional.of(new StructType(newFields));
}
return Optional.empty();
}

View File

@ -63,23 +63,23 @@ public class UnsupportedTypeTest extends TestWithFeService {
"select karr from type_tb",
"select array_range(10)",
"select kmap from type_tb1",
"select * from type_tb1",
"select jsonb_parse('{\"k1\":\"v31\",\"k2\":300}')",
"select * from type_tb",
"select * from type_tb1",
};
Class[] exceptions = {
null,
null,
null,
null,
AnalysisException.class,
null,
AnalysisException.class,
AnalysisException.class
};
for (int i = 0; i < 3; ++i) {
for (int i = 0; i < 5; ++i) {
runPlanner(sqls[i]);
}
for (int i = 4; i < sqls.length; ++i) {
for (int i = 5; i < sqls.length; ++i) {
int iCopy = i;
Assertions.assertThrows(exceptions[i], () -> runPlanner(sqls[iCopy]));
}