Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Licensed 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 com.facebook.presto.sql.analyzer;
  
  
  
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import static com.facebook.presto.metadata.FunctionRegistry.canCoerce;
  import static com.facebook.presto.metadata.FunctionRegistry.getCommonSuperType;
  import static com.facebook.presto.metadata.OperatorType.SUBSCRIPT;
  import static com.facebook.presto.spi.type.BigintType.BIGINT;
  import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
  import static com.facebook.presto.spi.type.DateType.DATE;
  import static com.facebook.presto.spi.type.DoubleType.DOUBLE;
  import static com.facebook.presto.spi.type.IntervalDayTimeType.INTERVAL_DAY_TIME;
  import static com.facebook.presto.spi.type.IntervalYearMonthType.INTERVAL_YEAR_MONTH;
  import static com.facebook.presto.spi.type.TimeType.TIME;
  import static com.facebook.presto.spi.type.TimeWithTimeZoneType.TIME_WITH_TIME_ZONE;
  import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP;
  import static com.facebook.presto.spi.type.TimestampWithTimeZoneType.TIMESTAMP_WITH_TIME_ZONE;
  import static com.facebook.presto.spi.type.TypeSignature.parseTypeSignature;
  import static com.facebook.presto.spi.type.VarcharType.VARCHAR;
  import static com.facebook.presto.sql.analyzer.SemanticErrorCode.AMBIGUOUS_ATTRIBUTE;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_ATTRIBUTE;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MULTIPLE_FIELDS_FROM_SCALAR_SUBQUERY;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.TYPE_MISMATCH;
 import static com.facebook.presto.sql.tree.Extract.Field.TIMEZONE_HOUR;
 import static com.facebook.presto.sql.tree.Extract.Field.TIMEZONE_MINUTE;
 import static com.facebook.presto.type.ArrayParametricType.ARRAY;
 import static com.facebook.presto.type.RowType.RowField;
 import static com.facebook.presto.type.UnknownType.UNKNOWN;
 import static com.facebook.presto.util.DateTimeUtils.timeHasTimeZone;
 import static com.facebook.presto.util.DateTimeUtils.timestampHasTimeZone;
 import static com.facebook.presto.util.ImmutableCollectors.toImmutableList;
 import static com.facebook.presto.util.Types.checkType;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.collect.Sets.newIdentityHashSet;
 
 public class ExpressionAnalyzer
 {
     private final Analysis analysis;
     private final Metadata metadata;
     private final SqlParser sqlParser;
     private final boolean experimentalSyntaxEnabled;
     private final Session session;
     private final Map<QualifiedNameIntegerresolvedNames = new HashMap<>();
     private final IdentityHashMap<ExpressionTypeexpressionTypes = new IdentityHashMap<>();
     private final IdentityHashMap<ExpressionTypeexpressionCoercions = new IdentityHashMap<>();
     private final IdentityHashMap<ExpressionBooleanrowFieldAccessors = new IdentityHashMap<>();
     private final Set<InPredicatesubqueryInPredicates = newIdentityHashSet();
 
     public ExpressionAnalyzer(Analysis analysisSession sessionMetadata metadataSqlParser sqlParserboolean experimentalSyntaxEnabled)
     {
         this. = checkNotNull(analysis"analysis is null");
         this. = checkNotNull(session"session is null");
         this. = checkNotNull(metadata"metadata is null");
         this. = checkNotNull(sqlParser"sqlParser is null");
         this. = experimentalSyntaxEnabled;
     }
 
     {
         return ;
     }
 
     {
         return ;
     }
 
     {
         return ;
     }
 
     {
         return ;
     }
 
     {
         return ;
     }
 
     {
         return ;
     }

    

Parameters:
tupleDescriptor the tuple descriptor to use to resolve QualifiedNames
context the namespace context of the surrounding query
 
     public Type analyze(Expression expressionTupleDescriptor tupleDescriptorAnalysisContext context)
     {
         Visitor visitor = new Visitor(tupleDescriptor);
 
         return expression.accept(visitorcontext);
     }
 
     private class Visitor
             extends AstVisitor<TypeAnalysisContext>
     {
         private final TupleDescriptor tupleDescriptor;
 
         private Visitor(TupleDescriptor tupleDescriptor)
         {
             this. = checkNotNull(tupleDescriptor"tupleDescriptor is null");
         }
 
         @Override
         public Type process(Node node, @Nullable AnalysisContext context)
         {
             // don't double processs a node
             Type type = .get(node);
             if (type != null) {
                 return type;
             }
             return super.process(nodecontext);
         }
 
         @Override
         protected Type visitRow(Row nodeAnalysisContext context)
         {
             List<Typetypes = node.getItems().stream()
                     .map((child) -> process(childcontext))
                     .collect(toImmutableList());
 
             Type type = new RowType(types, Optional.empty());
             .put(nodetype);
 
             return type;
         }
 
         @Override
         protected Type visitCurrentTime(CurrentTime nodeAnalysisContext context)
         {
             if (node.getPrecision() != null) {
                 throw new SemanticException(node"non-default precision not yet supported");
             }
 
             Type type;
             switch (node.getType()) {
                 case :
                     type = ;
                     break;
                 case :
                     type = ;
                     break;
                 case :
                     type = ;
                     break;
                 case :
                     type = ;
                     break;
                 case :
                     type = ;
                     break;
                 default:
                     throw new SemanticException(node"%s not yet supported"node.getType().getName());
             }
 
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitQualifiedNameReference(QualifiedNameReference nodeAnalysisContext context)
         {
             List<Fieldmatches = .resolveFields(node.getName());
             if (matches.isEmpty()) {
                 // TODO This is kind of hacky, instead we should change the way QualifiedNameReferences are parsed
                 return tryVisitRowFieldAccessor(nodecontext);
             }
             else if (matches.size() > 1) {
                 throw new SemanticException(node"Column '%s' is ambiguous"node.getName());
             }
 
             Field field = Iterables.getOnlyElement(matches);
             int fieldIndex = .indexOf(field);
             .put(node.getName(), fieldIndex);
             .put(nodefield.getType());
 
             return field.getType();
         }
 
         private Type tryVisitRowFieldAccessor(QualifiedNameReference nodeAnalysisContext context)
         {
             if (node.getName().getParts().size() < 2) {
                 throw createMissingAttributeException(node);
             }
             QualifiedName base = new QualifiedName(node.getName().getParts().subList(0, node.getName().getParts().size() - 1));
             List<Fieldmatches = .resolveFields(base);
             if (matches.isEmpty()) {
                 throw createMissingAttributeException(node);
             }
             else if (matches.size() > 1) {
                 throw new SemanticException(node"Column '%s' is ambiguous"node.getName());
             }
 
             Field field = Iterables.getOnlyElement(matches);
             if (field.getType() instanceof RowType) {
                 RowType rowType = checkType(field.getType(), RowType.class"field.getType()");
                 Type rowFieldType = null;
                 for (RowField rowField : rowType.getFields()) {
                     if (rowField.getName().equals(Optional.of(node.getName().getSuffix()))) {
                         rowFieldType = rowField.getType();
                         break;
                     }
                 }
                 if (rowFieldType == null) {
                     throw createMissingAttributeException(node);
                 }
                 int fieldIndex = .indexOf(field);
                 .put(node.getName(), fieldIndex);
                 .put(noderowFieldType);
                 .put(nodetrue);
 
                 return rowFieldType;
             }
             throw createMissingAttributeException(node);
         }
 
         {
             return new SemanticException(node"Column '%s' cannot be resolved"node.getName());
         }
 
         @Override
         protected Type visitNotExpression(NotExpression nodeAnalysisContext context)
         {
             coerceType(contextnode.getValue(), "Value of logical NOT expression");
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitLogicalBinaryExpression(LogicalBinaryExpression nodeAnalysisContext context)
         {
             coerceType(contextnode.getLeft(), "Left side of logical expression");
             coerceType(contextnode.getRight(), "Right side of logical expression");
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitComparisonExpression(ComparisonExpression nodeAnalysisContext context)
         {
             OperatorType operatorType;
             if (node.getType() == ..) {
                 operatorType = .;
             }
             else {
                 operatorType = OperatorType.valueOf(node.getType().name());
             }
             return getOperator(contextnodeoperatorTypenode.getLeft(), node.getRight());
         }
 
         @Override
         protected Type visitIsNullPredicate(IsNullPredicate nodeAnalysisContext context)
         {
             process(node.getValue(), context);
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitIsNotNullPredicate(IsNotNullPredicate nodeAnalysisContext context)
         {
             process(node.getValue(), context);
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitNullIfExpression(NullIfExpression nodeAnalysisContext context)
         {
             Type firstType = process(node.getFirst(), context);
             Type secondType = process(node.getSecond(), context);
 
             if (!FunctionRegistry.getCommonSuperType(firstTypesecondType).isPresent()) {
                 throw new SemanticException(node"Types are not comparable with NULLIF: %s vs %s"firstTypesecondType);
             }
 
             .put(nodefirstType);
             return firstType;
         }
 
         @Override
         protected Type visitIfExpression(IfExpression nodeAnalysisContext context)
         {
             coerceType(contextnode.getCondition(), "IF condition");
 
             Type type;
             if (node.getFalseValue().isPresent()) {
                 type = coerceToSingleType(contextnode"Result types for IF must be the same: %s vs %s"node.getTrueValue(), node.getFalseValue().get());
             }
             else {
                 type = process(node.getTrueValue(), context);
             }
 
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitSearchedCaseExpression(SearchedCaseExpression nodeAnalysisContext context)
         {
             for (WhenClause whenClause : node.getWhenClauses()) {
                 coerceType(contextwhenClause.getOperand(), "CASE WHEN clause");
             }
 
             Type type = coerceToSingleType(context,
                     "All CASE results must be the same type: %s",
                     getCaseResultExpressions(node.getWhenClauses(), node.getDefaultValue()));
             .put(nodetype);
 
             for (WhenClause whenClause : node.getWhenClauses()) {
                 Type whenClauseType = process(whenClause.getResult(), context);
                 checkNotNull(whenClauseType"Expression types does not contain an entry for %s"whenClause);
                 .put(whenClausewhenClauseType);
             }
 
             return type;
         }
 
         @Override
         protected Type visitSimpleCaseExpression(SimpleCaseExpression nodeAnalysisContext context)
         {
             for (WhenClause whenClause : node.getWhenClauses()) {
                 coerceToSingleType(contextnode"CASE operand type does not match WHEN clause operand type: %s vs %s"node.getOperand(), whenClause.getOperand());
             }
 
             Type type = coerceToSingleType(context,
                     "All CASE results must be the same type: %s",
                     getCaseResultExpressions(node.getWhenClauses(), node.getDefaultValue()));
             .put(nodetype);
 
             for (WhenClause whenClause : node.getWhenClauses()) {
                 Type whenClauseType = process(whenClause.getResult(), context);
                 checkNotNull(whenClauseType"Expression types does not contain an entry for %s"whenClause);
                 .put(whenClausewhenClauseType);
             }
 
             return type;
         }
 
         private List<ExpressiongetCaseResultExpressions(List<WhenClausewhenClausesOptional<ExpressiondefaultValue)
         {
             List<ExpressionresultExpressions = new ArrayList<>();
             for (WhenClause whenClause : whenClauses) {
                 resultExpressions.add(whenClause.getResult());
             }
             defaultValue.ifPresent(resultExpressions::add);
             return resultExpressions;
         }
 
         @Override
         protected Type visitCoalesceExpression(CoalesceExpression nodeAnalysisContext context)
         {
             Type type = coerceToSingleType(context"All COALESCE operands must be the same type: %s"node.getOperands());
 
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitArithmeticUnary(ArithmeticUnaryExpression nodeAnalysisContext context)
         {
             switch (node.getSign()) {
                 case :
                     Type type = process(node.getValue(), context);
 
                     if (!type.equals() && !type.equals()) {
                         // TODO: figure out a type-agnostic way of dealing with this. Maybe add a special unary operator
                         // that types can chose to implement, or piggyback on the existence of the negation operator
                         throw new SemanticException(node"Unary '+' operator cannot by applied to %s type"type);
                     }
                     .put(nodetype);
                     break;
                 case :
                     return getOperator(contextnode.node.getValue());
             }
 
             throw new UnsupportedOperationException("Unsupported unary operator: " + node.getSign());
         }
 
         @Override
         protected Type visitArithmeticBinary(ArithmeticBinaryExpression nodeAnalysisContext context)
         {
             return getOperator(contextnode, OperatorType.valueOf(node.getType().name()), node.getLeft(), node.getRight());
         }
 
         @Override
         protected Type visitLikePredicate(LikePredicate nodeAnalysisContext context)
         {
             coerceType(contextnode.getValue(), "Left side of LIKE expression");
             coerceType(contextnode.getPattern(), "Pattern for LIKE expression");
             if (node.getEscape() != null) {
                 coerceType(contextnode.getEscape(), "Escape for LIKE expression");
             }
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitSubscriptExpression(SubscriptExpression nodeAnalysisContext context)
         {
             return getOperator(contextnodenode.getBase(), node.getIndex());
         }
 
         @Override
         protected Type visitArrayConstructor(ArrayConstructor nodeAnalysisContext context)
         {
             Type type = coerceToSingleType(context"All ARRAY elements must be the same type: %s"node.getValues());
             Type arrayType = .getTypeManager().getParameterizedType(.getName(), ImmutableList.of(type.getTypeSignature()), ImmutableList.of());
             .put(nodearrayType);
             return arrayType;
         }
 
         @Override
         protected Type visitStringLiteral(StringLiteral nodeAnalysisContext context)
         {
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitLongLiteral(LongLiteral nodeAnalysisContext context)
         {
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitDoubleLiteral(DoubleLiteral nodeAnalysisContext context)
         {
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitBooleanLiteral(BooleanLiteral nodeAnalysisContext context)
         {
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitGenericLiteral(GenericLiteral nodeAnalysisContext context)
         {
             Type type = .getType(parseTypeSignature(node.getType()));
             if (type == null) {
                 throw new SemanticException(node"Unknown type: " + node.getType());
             }
 
             try {
                 .getFunctionRegistry().getCoercion(type);
             }
             catch (IllegalArgumentException e) {
                 throw new SemanticException(node"No literal form for type %s"type);
             }
 
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitTimeLiteral(TimeLiteral nodeAnalysisContext context)
         {
             Type type;
             if (timeHasTimeZone(node.getValue())) {
                 type = ;
             }
             else {
                 type = ;
             }
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitTimestampLiteral(TimestampLiteral nodeAnalysisContext context)
         {
             Type type;
             if (timestampHasTimeZone(node.getValue())) {
                 type = ;
             }
             else {
                 type = ;
             }
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitIntervalLiteral(IntervalLiteral nodeAnalysisContext context)
         {
             Type type;
             if (node.isYearToMonth()) {
                 type = ;
             }
             else {
                 type = ;
             }
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitNullLiteral(NullLiteral nodeAnalysisContext context)
         {
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitFunctionCall(FunctionCall nodeAnalysisContext context)
         {
             if (node.getWindow().isPresent()) {
                 for (Expression expression : node.getWindow().get().getPartitionBy()) {
                     process(expressioncontext);
                     Type type = .get(expression);
                     if (!type.isComparable()) {
                         throw new SemanticException(node"%s is not comparable, and therefore cannot be used in window function PARTITION BY"type);
                     }
                 }
 
                 for (SortItem sortItem : node.getWindow().get().getOrderBy()) {
                     process(sortItem.getSortKey(), context);
                     Type type = .get(sortItem.getSortKey());
                     if (!type.isComparable()) {
                         throw new SemanticException(node"%s is not comparable, and therefore cannot be used in window function ORDER BY"type);
                     }
                 }
 
                 if (node.getWindow().get().getFrame().isPresent()) {
                     WindowFrame frame = node.getWindow().get().getFrame().get();
 
                     if (frame.getStart().getValue().isPresent()) {
                         Type type = process(frame.getStart().getValue().get(), context);
                         if (!type.equals()) {
                             throw new SemanticException(node"Window frame start value type must be BIGINT (actual %s)"type);
                         }
                     }
 
                     if (frame.getEnd().isPresent() && frame.getEnd().get().getValue().isPresent()) {
                         Type type = process(frame.getEnd().get().getValue().get(), context);
                         if (!type.equals()) {
                             throw new SemanticException(node"Window frame end value type must be BIGINT (actual %s)"type);
                         }
                     }
                 }
             }
 
             ImmutableList.Builder<TypeSignatureargumentTypes = ImmutableList.builder();
             for (Expression expression : node.getArguments()) {
                 argumentTypes.add(process(expressioncontext).getTypeSignature());
             }
 
             FunctionInfo function = .resolveFunction(node.getName(), argumentTypes.build(), context.isApproximate());
             for (int i = 0; i < node.getArguments().size(); i++) {
                 Expression expression = node.getArguments().get(i);
                 Type type = .getType(function.getArgumentTypes().get(i));
                 checkNotNull(type"Type %s not found"function.getArgumentTypes().get(i));
                 if (node.isDistinct() && !type.isComparable()) {
                     throw new SemanticException(node"DISTINCT can only be applied to comparable types (actual: %s)"type);
                 }
                 coerceType(contextexpressiontype, String.format("Function %s argument %d"function.getSignature(), i));
             }
             .put(nodefunction);
 
             Type type = .getType(function.getReturnType());
             .put(nodetype);
 
             return type;
         }
 
         @Override
         protected Type visitExtract(Extract nodeAnalysisContext context)
         {
             Type type = process(node.getExpression(), context);
             if (!isDateTimeType(type)) {
                 throw new SemanticException(node.getExpression(), "Type of argument to extract must be DATE, TIME, TIMESTAMP, or INTERVAL (actual %s)"type);
             }
             Extract.Field field = node.getField();
             if ((field ==  || field == ) && !(type ==  || type == )) {
                 throw new SemanticException(node.getExpression(), "Type of argument to extract time zone field must have a time zone (actual %s)"type);
             }
 
             .put(node);
             return ;
         }
 
         private boolean isDateTimeType(Type type)
         {
             return type ==  ||
                     type ==  ||
                     type ==  ||
                     type ==  ||
                     type ==  ||
                     type ==  ||
                     type == ;
         }
 
         @Override
         protected Type visitBetweenPredicate(BetweenPredicate nodeAnalysisContext context)
         {
             return getOperator(contextnode.node.getValue(), node.getMin(), node.getMax());
         }
 
         @Override
         public Type visitCast(Cast nodeAnalysisContext context)
         {
             Type type = .getType(parseTypeSignature(node.getType()));
             if (type == null) {
                 throw new SemanticException(node"Unknown type: " + node.getType());
             }
 
             if (type == ) {
                 throw new SemanticException(node"UNKNOWN is not a valid type");
             }
 
             Type value = process(node.getExpression(), context);
             if (value != ) {
                 try {
                     .getFunctionRegistry().getCoercion(valuetype);
                 }
                 catch (OperatorNotFoundException e) {
                     throw new SemanticException(node"Cannot cast %s to %s"valuetype);
                 }
             }
 
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitInPredicate(InPredicate nodeAnalysisContext context)
         {
             Expression value = node.getValue();
             process(valuecontext);
 
             Expression valueList = node.getValueList();
             process(valueListcontext);
 
             if (valueList instanceof InListExpression) {
                 InListExpression inListExpression = (InListExpressionvalueList;
 
                 coerceToSingleType(context,
                         "IN value and list items must be the same type: %s",
                         ImmutableList.<Expression>builder().add(value).addAll(inListExpression.getValues()).build());
             }
             else if (valueList instanceof SubqueryExpression) {
                 coerceToSingleType(contextnode"value and result of subquery must be of the same type for IN expression: %s vs %s"valuevalueList);
                 .add(node);
             }
 
             .put(node);
             return ;
         }
 
         @Override
         protected Type visitInListExpression(InListExpression nodeAnalysisContext context)
         {
             Type type = coerceToSingleType(context"All IN list values must be the same type: %s"node.getValues());
 
             .put(nodetype);
             return type// TODO: this really should a be relation type
         }
 
         @Override
         protected Type visitSubqueryExpression(SubqueryExpression nodeAnalysisContext context)
         {
             StatementAnalyzer analyzer = new StatementAnalyzer(, Optional.empty());
             TupleDescriptor descriptor = analyzer.process(node.getQuery(), context);
 
             // Scalar subqueries should only produce one column
             if (descriptor.getVisibleFieldCount() != 1) {
                 throw new SemanticException(,
                         node,
                         "Subquery expression must produce only one field. Found %s",
                         descriptor.getVisibleFieldCount());
             }
 
             Type type = Iterables.getOnlyElement(descriptor.getVisibleFields()).getType();
 
             .put(nodetype);
             return type;
         }
 
         @Override
         public Type visitInputReference(InputReference nodeAnalysisContext context)
         {
             Type type = .getFieldByIndex(node.getChannel()).getType();
             .put(nodetype);
             return type;
         }
 
         @Override
         protected Type visitExpression(Expression nodeAnalysisContext context)
         {
             throw new UnsupportedOperationException("not yet implemented: " + node.getClass().getName());
         }
 
         private Type getOperator(AnalysisContext contextExpression nodeOperatorType operatorTypeExpression... arguments)
         {
             ImmutableList.Builder<TypeargumentTypes = ImmutableList.builder();
             for (Expression expression : arguments) {
                 argumentTypes.add(process(expressioncontext));
             }
 
             FunctionInfo operatorInfo;
             try {
                 operatorInfo = .resolveOperator(operatorTypeargumentTypes.build());
             }
             catch (OperatorNotFoundException e) {
                 throw new SemanticException(nodee.getMessage());
             }
 
             for (int i = 0; i < arguments.lengthi++) {
                 Expression expression = arguments[i];
                 Type type = .getType(operatorInfo.getArgumentTypes().get(i));
                 coerceType(contextexpressiontype, String.format("Operator %s argument %d"operatorInfoi));
             }
 
             Type type = .getType(operatorInfo.getReturnType());
             .put(nodetype);
 
             return type;
         }
 
         private void coerceType(AnalysisContext contextExpression expressionType expectedTypeString message)
         {
             Type actualType = process(expressioncontext);
             if (!actualType.equals(expectedType)) {
                 if (!canCoerce(actualTypeexpectedType)) {
                     throw new SemanticException(expressionmessage + " must evaluate to a %s (actual: %s)"expectedTypeactualType);
                 }
                 .put(expressionexpectedType);
             }
         }
 
         private Type coerceToSingleType(AnalysisContext contextNode nodeString messageExpression firstExpression second)
         {
             Type firstType = null;
             if (first != null) {
                 firstType = process(firstcontext);
             }
             Type secondType = null;
             if (second != null) {
                 secondType = process(secondcontext);
             }
 
             if (firstType == null) {
                 return secondType;
             }
             if (secondType == null) {
                 return firstType;
             }
             if (firstType.equals(secondType)) {
                 return firstType;
             }
 
             // coerce types if possible
             if (canCoerce(firstTypesecondType)) {
                 .put(firstsecondType);
                 return secondType;
             }
             if (canCoerce(secondTypefirstType)) {
                 .put(secondfirstType);
                 return firstType;
             }
             throw new SemanticException(nodemessagefirstTypesecondType);
         }
 
         private Type coerceToSingleType(AnalysisContext contextString messageList<Expressionexpressions)
         {
             // determine super type
             Type superType = ;
             for (Expression expression : expressions) {
                 Optional<TypenewSuperType = getCommonSuperType(superTypeprocess(expressioncontext));
                 if (!newSuperType.isPresent()) {
                     throw new SemanticException(expressionmessagesuperType);
                 }
                 superType = newSuperType.get();
             }
 
             // verify all expressions can be coerced to the superType
             for (Expression expression : expressions) {
                 Type type = process(expressioncontext);
                 if (!type.equals(superType)) {
                     if (!canCoerce(typesuperType)) {
                         throw new SemanticException(expressionmessagesuperType);
                     }
                     .put(expressionsuperType);
                 }
             }
 
             return superType;
         }
     }
 
     public static IdentityHashMap<ExpressionTypegetExpressionTypes(
             Session session,
             Metadata metadata,
             SqlParser sqlParser,
             Map<SymbolTypetypes,
             Expression expression)
     {
         return getExpressionTypes(sessionmetadatasqlParsertypes, ImmutableList.of(expression));
     }
 
     public static IdentityHashMap<ExpressionTypegetExpressionTypes(
             Session session,
             Metadata metadata,
             SqlParser sqlParser,
             Map<SymbolTypetypes,
             Iterable<? extends Expressionexpressions)
     {
         return