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.List;
  import java.util.Map;
  import java.util.Set;
  
  import static com.facebook.presto.metadata.FunctionRegistry.getCommonSuperType;
  import static com.facebook.presto.metadata.ViewDefinition.ViewColumn;
  import static com.facebook.presto.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT;
  import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
  import static com.facebook.presto.sql.analyzer.ExpressionAnalyzer.getExpressionTypes;
  import static com.facebook.presto.sql.analyzer.SemanticErrorCode.AMBIGUOUS_ATTRIBUTE;
  import static com.facebook.presto.sql.analyzer.SemanticErrorCode.DUPLICATE_RELATION;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_ORDINAL;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.INVALID_WINDOW_FRAME;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISMATCHED_COLUMN_ALIASES;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISMATCHED_SET_COLUMN_TYPES;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_CATALOG;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_SCHEMA;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MISSING_TABLE;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MUST_BE_AGGREGATE_OR_GROUP_BY;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.MUST_BE_WINDOW_FUNCTION;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NESTED_WINDOW;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NON_NUMERIC_SAMPLE_PERCENTAGE;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.NOT_SUPPORTED;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.ORDER_BY_MUST_BE_IN_SELECT;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.TYPE_MISMATCH;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_ANALYSIS_ERROR;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_IS_STALE;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.VIEW_PARSE_ERROR;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.WILDCARD_WITHOUT_FROM;
 import static com.facebook.presto.sql.analyzer.SemanticErrorCode.WINDOW_REQUIRES_OVER;
 import static com.facebook.presto.sql.planner.ExpressionInterpreter.expressionOptimizer;
 import static com.facebook.presto.sql.tree.ComparisonExpression.Type.EQUAL;
 import static com.facebook.presto.sql.tree.FrameBound.Type.CURRENT_ROW;
 import static com.facebook.presto.sql.tree.FrameBound.Type.FOLLOWING;
 import static com.facebook.presto.sql.tree.FrameBound.Type.PRECEDING;
 import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_FOLLOWING;
 import static com.facebook.presto.sql.tree.FrameBound.Type.UNBOUNDED_PRECEDING;
 import static com.facebook.presto.sql.tree.WindowFrame.Type.RANGE;
 import static com.facebook.presto.type.UnknownType.UNKNOWN;
 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.base.Preconditions.checkState;
 
 public class TupleAnalyzer
 {
     private final Analysis analysis;
     private final Session session;
     private final Metadata metadata;
     private final SqlParser sqlParser;
     private final boolean experimentalSyntaxEnabled;
 
     public TupleAnalyzer(Analysis analysisSession sessionMetadata metadataSqlParser sqlParserboolean experimentalSyntaxEnabled)
     {
         checkNotNull(analysis"analysis is null");
         checkNotNull(session"session is null");
         checkNotNull(metadata"metadata is null");
 
         this. = analysis;
         this. = session;
         this. = metadata;
         this. = sqlParser;
         this. = experimentalSyntaxEnabled;
     }
 
     @Override
     protected TupleDescriptor visitUnnest(Unnest nodeAnalysisContext context)
     {
         ImmutableList.Builder<FieldoutputFields = ImmutableList.builder();
         for (Expression expression : node.getExpressions()) {
             ExpressionAnalysis expressionAnalysis = analyzeExpression(expressioncontext.getLateralTupleDescriptor(), context);
             Type expressionType = expressionAnalysis.getType(expression);
             if (expressionType instanceof ArrayType) {
                 outputFields.add(Field.newUnqualified(Optional.empty(), ((ArrayTypeexpressionType).getElementType()));
             }
             else if (expressionType instanceof MapType) {
                 outputFields.add(Field.newUnqualified(Optional.empty(), ((MapTypeexpressionType).getKeyType()));
                 outputFields.add(Field.newUnqualified(Optional.empty(), ((MapTypeexpressionType).getValueType()));
             }
             else {
                 throw new PrestoException("Cannot unnest type: " + expressionType);
             }
         }
         if (node.isWithOrdinality()) {
             outputFields.add(Field.newUnqualified(Optional.empty(), .));
         }
         TupleDescriptor descriptor = new TupleDescriptor(outputFields.build());
         .setOutputDescriptor(nodedescriptor);
         return descriptor;
     }
 
     @Override
     protected TupleDescriptor visitTable(Table tableAnalysisContext context)
     {
         if (!table.getName().getPrefix().isPresent()) {
             // is this a reference to a WITH query?
             String name = table.getName().getSuffix();
 
             Query query = context.getNamedQuery(name);
             if (query != null) {
                 .registerNamedQuery(tablequery);
 
                 // re-alias the fields with the name assigned to the query in the WITH declaration
                 TupleDescriptor queryDescriptor = .getOutputDescriptor(query);
                 ImmutableList.Builder<Fieldfields = ImmutableList.builder();
                 for (Field field : queryDescriptor.getAllFields()) {
                     fields.add(Field.newQualified(QualifiedName.of(name), field.getName(), field.getType(), false));
                 }
 
                 TupleDescriptor descriptor = new TupleDescriptor(fields.build());
                 .setOutputDescriptor(tabledescriptor);
                 return descriptor;
             }
         }
 
         QualifiedTableName name = MetadataUtil.createQualifiedTableName(table.getName());
 
         Optional<ViewDefinitionoptionalView = .getView(name);
         if (optionalView.isPresent()) {
             ViewDefinition view = optionalView.get();
 
             Query query = parseView(view.getOriginalSql(), nametable);
 
             .registerNamedQuery(tablequery);
 
             TupleDescriptor descriptor = analyzeView(querynameview.getCatalog(), view.getSchema(), table);
 
             if (isViewStale(view.getColumns(), descriptor.getVisibleFields())) {
                 throw new SemanticException(table"View '%s' is stale; it must be re-created"name);
             }
 
             .setOutputDescriptor(tabledescriptor);
             return descriptor;
         }
 
         Optional<TableHandletableHandle = .getTableHandle(name);
         if (!tableHandle.isPresent()) {
             if (!.getCatalogNames().containsKey(name.getCatalogName())) {
                 throw new SemanticException(table"Catalog %s does not exist"name.getCatalogName());
             }
             if (!.listSchemaNames(name.getCatalogName()).contains(name.getSchemaName())) {
                 throw new SemanticException(table"Schema %s does not exist"name.getSchemaName());
             }
 
             if (table.getName().getSuffix().equalsIgnoreCase("DUAL")) {
                 // TODO: remove this in a few releases
                 throw new SemanticException(table"DUAL table is no longer supported. Please use VALUES or FROM-less queries instead");
             }
 
             throw new SemanticException(table"Table %s does not exist"name);
         }
         TableMetadata tableMetadata = .getTableMetadata(tableHandle.get());
         Map<StringColumnHandlecolumnHandles = .getColumnHandles(tableHandle.get());
 
         // TODO: discover columns lazily based on where they are needed (to support datasources that can't enumerate all tables)
         ImmutableList.Builder<Fieldfields = ImmutableList.builder();
         for (ColumnMetadata column : tableMetadata.getColumns()) {
             Field field = Field.newQualified(table.getName(), Optional.of(column.getName()), column.getType(), column.isHidden());
             fields.add(field);
             ColumnHandle columnHandle = columnHandles.get(column.getName());
             checkArgument(columnHandle != null"Unknown field %s"field);
             .setColumn(fieldcolumnHandle);
         }
 
         .registerTable(tabletableHandle.get());
 
         TupleDescriptor descriptor = new TupleDescriptor(fields.build());
         .setOutputDescriptor(tabledescriptor);
         return descriptor;
     }
 
     @Override
     protected TupleDescriptor visitAliasedRelation(AliasedRelation relationAnalysisContext context)
     {
         TupleDescriptor child = process(relation.getRelation(), context);
 
         // todo this check should be inside of TupleDescriptor.withAlias, but the exception needs the node object
         if (relation.getColumnNames() != null) {
             int totalColumns = child.getVisibleFieldCount();
             if (totalColumns != relation.getColumnNames().size()) {
                 throw new SemanticException(relation"Column alias list has %s entries but '%s' has %s columns available"relation.getColumnNames().size(), relation.getAlias(), totalColumns);
             }
         }
 
         TupleDescriptor descriptor = child.withAlias(relation.getAlias(), relation.getColumnNames());
 
         .setOutputDescriptor(relationdescriptor);
         return descriptor;
     }
 
     @Override
     protected TupleDescriptor visitSampledRelation(final SampledRelation relationAnalysisContext context)
     {
         if (relation.getColumnsToStratifyOn().isPresent()) {
             throw new SemanticException(relation"STRATIFY ON is not yet implemented");
         }
 
         if (!DependencyExtractor.extract(relation.getSamplePercentage()).isEmpty()) {
             throw new SemanticException(relation.getSamplePercentage(), "Sample percentage cannot contain column references");
         }
 
         IdentityHashMap<ExpressionTypeexpressionTypes = getExpressionTypes(, ImmutableMap.<SymbolType>of(), relation.getSamplePercentage());
         ExpressionInterpreter samplePercentageEval = expressionOptimizer(relation.getSamplePercentage(), expressionTypes);
 
         Object samplePercentageObject = samplePercentageEval.optimize(new SymbolResolver()
         {
             @Override
             public Object getValue(Symbol symbol)
             {
                 throw new SemanticException(relation.getSamplePercentage(), "Sample percentage cannot contain column references");
             }
         });
 
         if (!(samplePercentageObject instanceof Number)) {
             throw new SemanticException(relation.getSamplePercentage(), "Sample percentage should evaluate to a numeric expression");
         }
 
         double samplePercentageValue = ((NumbersamplePercentageObject).doubleValue();
 
         if (samplePercentageValue < 0.0) {
             throw new SemanticException(.relation.getSamplePercentage(), "Sample percentage must be greater than or equal to 0");
         }
         else if ((samplePercentageValue > 100.0) && (relation.getType() != .. || relation.isRescaled())) {
             throw new SemanticException(.relation.getSamplePercentage(), "Sample percentage must be less than or equal to 100");
         }
 
         if (relation.isRescaled() && !) {
             throw new SemanticException(relation"Rescaling is not enabled");
         }
 
         TupleDescriptor descriptor = process(relation.getRelation(), context);
 
         .setOutputDescriptor(relationdescriptor);
         .setSampleRatio(relationsamplePercentageValue / 100);
 
         return descriptor;
     }
 
     @Override
     protected TupleDescriptor visitTableSubquery(TableSubquery nodeAnalysisContext context)
     {
         StatementAnalyzer analyzer = new StatementAnalyzer(, Optional.empty());
         TupleDescriptor descriptor = analyzer.process(node.getQuery(), context);
 
         .setOutputDescriptor(nodedescriptor);
 
         return descriptor;
     }
 
     @Override
     {
         // TODO: extract candidate names from SELECT, WHERE, HAVING, GROUP BY and ORDER BY expressions
         // to pass down to analyzeFrom
 
         AnalysisContext context = new AnalysisContext(parentContext);
 
         TupleDescriptor tupleDescriptor = analyzeFrom(nodecontext);
 
         analyzeWhere(nodetupleDescriptorcontext);
 
         List<FieldOrExpressionoutputExpressions = analyzeSelect(nodetupleDescriptorcontext);
         List<FieldOrExpressiongroupByExpressions = analyzeGroupBy(nodetupleDescriptorcontextoutputExpressions);
         List<FieldOrExpressionorderByExpressions = analyzeOrderBy(nodetupleDescriptorcontextoutputExpressions);
         analyzeHaving(nodetupleDescriptorcontext);
 
         analyzeAggregations(nodetupleDescriptorgroupByExpressionsoutputExpressionsorderByExpressionscontext);
         analyzeWindowFunctions(nodeoutputExpressionsorderByExpressions);
 
         TupleDescriptor descriptor = computeOutputDescriptor(nodetupleDescriptor);
         .setOutputDescriptor(nodedescriptor);
 
         return descriptor;
     }
 
     @Override
     protected TupleDescriptor visitUnion(Union nodeAnalysisContext context)
     {
         checkState(node.getRelations().size() >= 2);
 
 
         // Use the first descriptor as the output descriptor for the UNION
         TupleDescriptor outputDescriptor = analyzer.process(node.getRelations().get(0), context).withOnlyVisibleFields();
 
         for (Relation relation : Iterables.skip(node.getRelations(), 1)) {
             TupleDescriptor descriptor = analyzer.process(relationcontext).withOnlyVisibleFields();
             int outputFieldSize = outputDescriptor.getVisibleFields().size();
             int descFieldSize = descriptor.getVisibleFields().size();
             if (outputFieldSize != descFieldSize) {
                 throw new SemanticException(,
                                             node,
                                             "union query has different number of fields: %d, %d",
                                             outputFieldSizedescFieldSize);
             }
             for (int i = 0; i < descriptor.getVisibleFields().size(); i++) {
                 Type outputFieldType = outputDescriptor.getFieldByIndex(i).getType();
                 Type descFieldType = descriptor.getFieldByIndex(i).getType();
                 if (!outputFieldType.equals(descFieldType)) {
                     throw new SemanticException(,
                                                 node,
                                                 "column %d in union query has incompatible types: %s, %s",
                                                 ioutputFieldType.getDisplayName(), descFieldType.getDisplayName());
                 }
             }
         }
 
         .setOutputDescriptor(nodeoutputDescriptor);
         return outputDescriptor;
     }
 
     @Override
     protected TupleDescriptor visitIntersect(Intersect nodeAnalysisContext context)
     {
         throw new SemanticException(node"INTERSECT not yet implemented");
     }
 
     @Override
     protected TupleDescriptor visitExcept(Except nodeAnalysisContext context)
     {
         throw new SemanticException(node"EXCEPT not yet implemented");
     }
 
     @Override
     protected TupleDescriptor visitJoin(Join nodeAnalysisContext context)
     {
         JoinCriteria criteria = node.getCriteria().orElse(null);
         if (criteria instanceof NaturalJoin) {
             throw new SemanticException(node"Natural join not supported");
         }
 
         AnalysisContext leftContext = new AnalysisContext(context);
         TupleDescriptor left = process(node.getLeft(), context);
         leftContext.setLateralTupleDescriptor(left);
         TupleDescriptor right = process(node.getRight(), leftContext);
 
         // todo this check should be inside of TupleDescriptor.join and then remove the public getRelationAlias method, but the exception needs the node object
         Sets.SetView<QualifiedNameduplicateAliases = Sets.intersection(left.getRelationAliases(), right.getRelationAliases());
         if (!duplicateAliases.isEmpty()) {
             throw new SemanticException(node"Relations appear more than once: %s"duplicateAliases);
         }
 
         TupleDescriptor output = left.joinWith(right);
 
         if (node.getType() == .. || node.getType() == ..) {
             .setOutputDescriptor(nodeoutput);
             return output;
         }
 
         if (criteria instanceof JoinUsing) {
             // TODO: implement proper "using" semantics with respect to output columns
             List<Stringcolumns = ((JoinUsingcriteria).getColumns();
 
             List<Expressionexpressions = new ArrayList<>();
             for (String column : columns) {
                 Expression leftExpression = new QualifiedNameReference(QualifiedName.of(column));
                 Expression rightExpression = new QualifiedNameReference(QualifiedName.of(column));
 
                 ExpressionAnalysis leftExpressionAnalysis = analyzeExpression(leftExpressionleftcontext);
                 ExpressionAnalysis rightExpressionAnalysis = analyzeExpression(rightExpressionrightcontext);
                 checkState(leftExpressionAnalysis.getSubqueryInPredicates().isEmpty(), "INVARIANT");
                 checkState(rightExpressionAnalysis.getSubqueryInPredicates().isEmpty(), "INVARIANT");
 
                 expressions.add(new ComparisonExpression(leftExpressionrightExpression));
             }
 
             .setJoinCriteria(node, ExpressionUtils.and(expressions));
         }
         else if (criteria instanceof JoinOn) {
             Expression expression = ((JoinOncriteria).getExpression();
 
             // ensure all names can be resolved, types match, etc (we don't need to record resolved names, subexpression types, etc. because
             // we do it further down when after we determine which subexpressions apply to left vs right tuple)
             ExpressionAnalyzer analyzer = ExpressionAnalyzer.create();
             analyzer.analyze(expressionoutputcontext);
 
             Analyzer.verifyNoAggregatesOrWindowFunctions(expression"JOIN");
 
             // expressionInterpreter/optimizer only understands a subset of expression types
             // TODO: remove this when the new expression tree is implemented
             Expression canonicalized = CanonicalizeExpressions.canonicalizeExpression(expression);
 
             Object optimizedExpression = expressionOptimizer(canonicalizedanalyzer.getExpressionTypes()).optimize(.);
 
             if (!(optimizedExpression instanceof Expression) && optimizedExpression instanceof Boolean) {
                 // If the JoinOn clause evaluates to a boolean expression, simulate a cross join by adding the relevant redundant expression
                 if (optimizedExpression.equals(.)) {
                     optimizedExpression = new ComparisonExpression(new LongLiteral("0"), new LongLiteral("0"));
                 }
                 else {
                     optimizedExpression = new ComparisonExpression(new LongLiteral("0"), new LongLiteral("1"));
                 }
             }
 
             if (!(optimizedExpression instanceof Expression)) {
                 throw new SemanticException(node"Join clause must be a boolean expression");
             }
             // The optimization above may have rewritten the expression tree which breaks all the identity maps, so redo the analysis
             // to re-analyze coercions that might be necessary
             analyzer = ExpressionAnalyzer.create();
             analyzer.analyze((ExpressionoptimizedExpressionoutputcontext);
             .addCoercions(analyzer.getExpressionCoercions());
 
             for (Expression conjunct : ExpressionUtils.extractConjuncts((ExpressionoptimizedExpression)) {
                 if (!(conjunct instanceof ComparisonExpression)) {
                     throw new SemanticException(node"Non-equi joins not supported: %s"conjunct);
                 }
 
                 ComparisonExpression comparison = (ComparisonExpressionconjunct;
                 Set<QualifiedNamefirstDependencies = DependencyExtractor.extract(comparison.getLeft());
                 Set<QualifiedNamesecondDependencies = DependencyExtractor.extract(comparison.getRight());
 
                 Expression leftExpression;
                 Expression rightExpression;
                 if (Iterables.all(firstDependenciesleft.canResolvePredicate()) && Iterables.all(secondDependenciesright.canResolvePredicate())) {
                     leftExpression = comparison.getLeft();
                     rightExpression = comparison.getRight();
                 }
                 else if (Iterables.all(firstDependenciesright.canResolvePredicate()) && Iterables.all(secondDependenciesleft.canResolvePredicate())) {
                     leftExpression = comparison.getRight();
                     rightExpression = comparison.getLeft();
                 }
                 else {
                     // must have a complex expression that involves both tuples on one side of the comparison expression (e.g., coalesce(left.x, right.x) = 1)
                     throw new SemanticException(node"Non-equi joins not supported: %s"conjunct);
                 }
 
                 // analyze the clauses to record the types of all subexpressions and resolve names against the left/right underlying tuples
                 ExpressionAnalysis leftExpressionAnalysis = analyzeExpression(leftExpressionleftcontext);
                 ExpressionAnalysis rightExpressionAnalysis = analyzeExpression(rightExpressionrightcontext);
                 .addJoinInPredicates(nodenew Analysis.JoinInPredicates(leftExpressionAnalysis.getSubqueryInPredicates(), rightExpressionAnalysis.getSubqueryInPredicates()));
             }
 
             .setJoinCriteria(node, (ExpressionoptimizedExpression);
         }
         else {
             throw new UnsupportedOperationException("unsupported join criteria: " + criteria.getClass().getName());
         }
 
         .setOutputDescriptor(nodeoutput);
         return output;
     }
 
     @Override
     protected TupleDescriptor visitValues(Values nodeAnalysisContext context)
     {
         checkState(node.getRows().size() >= 1);
 
         // get unique row types
         Set<List<Type>> rowTypes = node.getRows().stream()
                 .map(row -> analyzeExpression(rownew TupleDescriptor(), context).getType(row))
                 .map(type -> {
                     if (type instanceof RowType) {
                         return type.getTypeParameters();
                     }
                     return ImmutableList.of(type);
                 })
                 .collect(ImmutableCollectors.toImmutableSet());
 
         // determine common super type of the rows
         List<TypefieldTypes = new ArrayList<>(rowTypes.iterator().next());
         for (List<TyperowType : rowTypes) {
             for (int i = 0; i < rowType.size(); i++) {
                 Type fieldType = rowType.get(i);
                 Type superType = fieldTypes.get(i);
 
                 Optional<TypecommonSuperType = getCommonSuperType(fieldTypesuperType);
                 if (!commonSuperType.isPresent()) {
                     throw new SemanticException(,
                             node,
                             "Values rows have mismatched types: %s vs %s",
                             Iterables.get(rowTypes, 0),
                             Iterables.get(rowTypes, 1));
                 }
                 fieldTypes.set(icommonSuperType.get());
             }
         }
 
         // add coercions for the rows
         for (Expression row : node.getRows()) {
             if (row instanceof Row) {
                 List<Expressionitems = ((Rowrow).getItems();
                 for (int i = 0; i < items.size(); i++) {
                     Type expectedType = fieldTypes.get(i);
                     Expression item = items.get(i);
                     if (!.getType(item).equals(expectedType)) {
                         .addCoercion(itemexpectedType);
                     }
                 }
             }
             else {
                 Type expectedType = fieldTypes.get(0);
                 if (!.getType(row).equals(expectedType)) {
                     .addCoercion(rowexpectedType);
                 }
             }
         }
 
         TupleDescriptor descriptor = new TupleDescriptor(fieldTypes.stream()
                 .map(valueType -> Field.newUnqualified(Optional.empty(), valueType))
                 .collect(toImmutableList()));
 
         .setOutputDescriptor(nodedescriptor);
         return descriptor;
     }
 
     private void analyzeWindowFunctions(QuerySpecification nodeList<FieldOrExpressionoutputExpressionsList<FieldOrExpressionorderByExpressions)
     {
         WindowFunctionExtractor extractor = new WindowFunctionExtractor();
 
         for (FieldOrExpression fieldOrExpression : Iterables.concat(outputExpressionsorderByExpressions)) {
             if (fieldOrExpression.isExpression()) {
                 extractor.process(fieldOrExpression.getExpression(), null);
                 if (fieldOrExpression.getExpression() instanceof FunctionCall) {
                     FunctionCall functionCall = (FunctionCallfieldOrExpression.getExpression();
                     FunctionInfo functionInfo = .getFunctionInfo(functionCall);
                     checkState(functionInfo != null"functionInfo is null");
                     if (functionInfo.isWindow() && !functionInfo.isAggregate() && !functionCall.getWindow().isPresent()) {
                         throw new SemanticException(node"Window function %s requires an OVER clause"functionInfo.getName());
                     }
                 }
             }
         }
 
         List<FunctionCallwindowFunctions = extractor.getWindowFunctions();
 
         for (FunctionCall windowFunction : windowFunctions) {
             Window window = windowFunction.getWindow().get();
 
             WindowFunctionExtractor nestedExtractor = new WindowFunctionExtractor();
             for (Expression argument : windowFunction.getArguments()) {
                 nestedExtractor.process(argumentnull);
             }
 
             for (Expression expression : window.getPartitionBy()) {
                 nestedExtractor.process(expressionnull);
             }
 
             for (SortItem sortItem : window.getOrderBy()) {
                 nestedExtractor.process(sortItem.getSortKey(), null);
             }
 
             if (window.getFrame().isPresent()) {
                 nestedExtractor.process(window.getFrame().get(), null);
             }
 
             if (!nestedExtractor.getWindowFunctions().isEmpty()) {
                 throw new SemanticException(node"Cannot nest window functions inside window function '%s': %s",
                         windowFunction,
                         extractor.getWindowFunctions());
             }
 
             if (windowFunction.isDistinct()) {
                 throw new SemanticException(node"DISTINCT in window function parameters not yet supported: %s"windowFunction);
             }
 
             if (window.getFrame().isPresent()) {
                 analyzeWindowFrame(window.getFrame().get());
             }
 
             List<TypeSignatureargumentTypes = Lists.transform(windowFunction.getArguments(), expression -> .getType(expression).getTypeSignature());
 
             FunctionInfo info = .resolveFunction(windowFunction.getName(), argumentTypesfalse);
             if (!info.isWindow()) {
                 throw new SemanticException(node"Not a window function: %s"windowFunction.getName());
             }
         }
 
         .setWindowFunctions(nodewindowFunctions);
     }
 
     private static void analyzeWindowFrame(WindowFrame frame)
     {
         FrameBound.Type startType = frame.getStart().getType();
         FrameBound.Type endType = frame.getEnd().orElse(new FrameBound()).getType();
 
         if (startType == ) {
             throw new SemanticException(frame"Window frame start cannot be UNBOUNDED FOLLOWING");
         }
         if (endType == ) {
             throw new SemanticException(frame"Window frame end cannot be UNBOUNDED PRECEDING");
         }
         if ((startType == ) && (endType == )) {
             throw new SemanticException(frame"Window frame starting from CURRENT ROW cannot end with PRECEDING");
         }
         if ((startType == ) && (endType == )) {
             throw new SemanticException(frame"Window frame starting from FOLLOWING cannot end with PRECEDING");
         }
         if ((startType == ) && (endType == )) {
             throw new SemanticException(frame"Window frame starting from FOLLOWING cannot end with CURRENT ROW");
         }
         if ((frame.getType() == ) && ((startType == ) || (endType == ))) {
             throw new SemanticException(frame"Window frame RANGE PRECEDING is only supported with UNBOUNDED");
         }
         if ((frame.getType() == ) && ((startType == ) || (endType == ))) {
             throw new SemanticException(frame"Window frame RANGE FOLLOWING is only supported with UNBOUNDED");
         }
     }
 
     private void analyzeHaving(QuerySpecification nodeTupleDescriptor tupleDescriptorAnalysisContext context)
     {
         if (node.getHaving().isPresent()) {
             Expression predicate = node.getHaving().get();
 
             ExpressionAnalysis expressionAnalysis = analyzeExpression(predicatetupleDescriptorcontext);
             .addInPredicates(nodeexpressionAnalysis.getSubqueryInPredicates());
 
             Type predicateType = expressionAnalysis.getType(predicate);
             if (!predicateType.equals() && !predicateType.equals()) {
                 throw new SemanticException(predicate"HAVING clause must evaluate to a boolean: actual type %s"predicateType);
             }
 
             .setHaving(nodepredicate);
         }
     }
 
     private List<FieldOrExpressionanalyzeOrderBy(QuerySpecification nodeTupleDescriptor tupleDescriptorAnalysisContext contextList<FieldOrExpressionoutputExpressions)
     {
         List<SortItemitems = node.getOrderBy();
 
         ImmutableList.Builder<FieldOrExpressionorderByExpressionsBuilder = ImmutableList.builder();
 
         if (!items.isEmpty()) {
             // Compute aliased output terms so we can resolve order by expressions against them first
             ImmutableMultimap.Builder<QualifiedNameExpressionbyAliasBuilder = ImmutableMultimap.builder();
             for (SelectItem item : node.getSelect().getSelectItems()) {
                 if (item instanceof SingleColumn) {
                     Optional<Stringalias = ((SingleColumnitem).getAlias();
                     if (alias.isPresent()) {
                         byAliasBuilder.put(QualifiedName.of(alias.get()), ((SingleColumnitem).getExpression()); // TODO: need to know if alias was quoted
                     }
                 }
             }
             Multimap<QualifiedNameExpressionbyAlias = byAliasBuilder.build();
 
             for (SortItem item : items) {
                 Expression expression = item.getSortKey();
 
                 FieldOrExpression orderByExpression = null;
                 if (expression instanceof QualifiedNameReference && !((QualifiedNameReferenceexpression).getName().getPrefix().isPresent()) {
                     // if this is a simple name reference, try to resolve against output columns
 
                     QualifiedName name = ((QualifiedNameReferenceexpression).getName();
                     Collection<Expressionexpressions = byAlias.get(name);
                     if (expressions.size() > 1) {
                         throw new SemanticException(expression"'%s' in ORDER BY is ambiguous"name.getSuffix());
                     }
                     else if (expressions.size() == 1) {
                         orderByExpression = new FieldOrExpression(Iterables.getOnlyElement(expressions));
                     }
 
                     // otherwise, couldn't resolve name against output aliases, so fall through...
                 }
                 else if (expression instanceof LongLiteral) {
                     // this is an ordinal in the output tuple
 
                     long ordinal = ((LongLiteralexpression).getValue();
                     if (ordinal < 1 || ordinal > outputExpressions.size()) {
                         throw new SemanticException(expression"ORDER BY position %s is not in select list"ordinal);
                     }
 
                     orderByExpression = outputExpressions.get((int) (ordinal - 1));
 
                     if (orderByExpression.isExpression()) {
                         Type type = .getType(orderByExpression.getExpression());
                         if (!type.isOrderable()) {
                             throw new SemanticException(node"The type of expression in position %s is not orderable (actual: %s), and therefore cannot be used in ORDER BY: %s"ordinaltypeorderByExpression);
                         }
                     }
                     else {
                         Type type = tupleDescriptor.getFieldByIndex(orderByExpression.getFieldIndex()).getType();
                         if (!type.isOrderable()) {
                             throw new SemanticException(node"The type of expression in position %s is not orderable (actual: %s), and therefore cannot be used in ORDER BY"ordinaltype);
                         }
                     }
                 }
 
                 // otherwise, just use the expression as is
                 if (orderByExpression == null) {
                     orderByExpression = new FieldOrExpression(expression);
                 }
 
                 if (orderByExpression.isExpression()) {
                     ExpressionAnalysis expressionAnalysis = analyzeExpression(orderByExpression.getExpression(), tupleDescriptorcontext);
                     .addInPredicates(nodeexpressionAnalysis.getSubqueryInPredicates());
 
                     Type type = expressionAnalysis.getType(orderByExpression.getExpression());
                     if (!type.isOrderable()) {
                         throw new SemanticException(node"Type %s is not orderable, and therefore cannot be used in ORDER BY: %s"typeexpression);
                     }
                 }
 
                 orderByExpressionsBuilder.add(orderByExpression);
             }
         }
 
         List<FieldOrExpressionorderByExpressions = orderByExpressionsBuilder.build();
         .setOrderByExpressions(nodeorderByExpressions);
 
         if (node.getSelect().isDistinct() && !outputExpressions.containsAll(orderByExpressions)) {
             throw new SemanticException(node.getSelect(), "For SELECT DISTINCT, ORDER BY expressions must appear in select list");
         }
         return orderByExpressions;
     }
 
     private List<FieldOrExpressionanalyzeGroupBy(QuerySpecification nodeTupleDescriptor tupleDescriptorAnalysisContext contextList<FieldOrExpressionoutputExpressions)
     {
         ImmutableList.Builder<FieldOrExpressiongroupByExpressionsBuilder = ImmutableList.builder();
         if (!node.getGroupBy().isEmpty()) {
             // Translate group by expressions that reference ordinals
             for (Expression expression : node.getGroupBy()) {
                 // first, see if this is an ordinal
                 FieldOrExpression groupByExpression;
 
                 if (expression instanceof LongLiteral) {
                     long ordinal = ((LongLiteralexpression).getValue();
                     if (ordinal < 1 || ordinal > outputExpressions.size()) {
                         throw new SemanticException(expression"GROUP BY position %s is not in select list"ordinal);
                     }
 
                     groupByExpression = outputExpressions.get((int) (ordinal - 1));
                 }
                 else {
                     ExpressionAnalysis expressionAnalysis = analyzeExpression(expressiontupleDescriptorcontext);
                     .addInPredicates(nodeexpressionAnalysis.getSubqueryInPredicates());
                     groupByExpression = new FieldOrExpression(expression);
                 }
 
                 Type type;
                 if (groupByExpression.isExpression()) {
                     Analyzer.verifyNoAggregatesOrWindowFunctions(groupByExpression.getExpression(), "GROUP BY");
                     type = .getType(groupByExpression.getExpression());
                 }
                 else {
                     type = tupleDescriptor.getFieldByIndex(groupByExpression.getFieldIndex()).getType();
                 }
                 if (!type.isComparable()) {
                     throw new SemanticException(node"%s is not comparable, and therefore cannot be used in GROUP BY"type);
                 }
 
                 groupByExpressionsBuilder.add(groupByExpression);
             }
         }
 
         List<FieldOrExpressiongroupByExpressions = groupByExpressionsBuilder.build();
         .setGroupByExpressions(nodegroupByExpressions);
         return groupByExpressions;
     }
 
     private TupleDescriptor computeOutputDescriptor(QuerySpecification nodeTupleDescriptor inputTupleDescriptor)
     {
         ImmutableList.Builder<FieldoutputFields = ImmutableList.builder();
 
         for (SelectItem item : node.getSelect().getSelectItems()) {
             if (item instanceof AllColumns) {
                 // expand * and T.*
                 Optional<QualifiedNamestarPrefix = ((AllColumnsitem).getPrefix();
 
                 for (Field field : inputTupleDescriptor.resolveFieldsWithPrefix(starPrefix)) {
                     outputFields.add(Field.newUnqualified(field.getName(), field.getType()));
                 }
             }
             else if (item instanceof SingleColumn) {
                 SingleColumn column = (SingleColumnitem;
 
                 Optional<Stringalias = column.getAlias();
                 if (!alias.isPresent() && column.getExpression() instanceof QualifiedNameReference) {
                     alias = Optional.of(((QualifiedNameReferencecolumn.getExpression()).getName().getSuffix());
                 }
 
                 outputFields.add(Field.newUnqualified(alias.getType(column.getExpression()))); // TODO don't use analysis as a side-channel. Use outputExpressions to look up the type
             }
             else {
                 throw new IllegalArgumentException("Unsupported SelectItem type: " + item.getClass().getName());
             }
         }
 
         return new TupleDescriptor(outputFields.build());
     }
 
     private List<FieldOrExpressionanalyzeSelect(QuerySpecification nodeTupleDescriptor tupleDescriptorAnalysisContext context)
     {
         ImmutableList.Builder<FieldOrExpressionoutputExpressionBuilder = ImmutableList.builder();
 
         for (SelectItem item : node.getSelect().getSelectItems()) {
             if (item instanceof AllColumns) {
                 // expand * and T.*
                 Optional<QualifiedNamestarPrefix = ((AllColumnsitem).getPrefix();
 
                 List<Fieldfields = tupleDescriptor.resolveFieldsWithPrefix(starPrefix);
                 if (fields.isEmpty()) {
                     if (starPrefix.isPresent()) {
                         throw new SemanticException(item"Table '%s' not found"starPrefix.get());
                     }
                     else {
                         throw new SemanticException(item"SELECT * not allowed in queries without FROM clause");
                     }
                 }
 
                 for (Field field : fields) {
                     int fieldIndex = tupleDescriptor.indexOf(field);
                     outputExpressionBuilder.add(new FieldOrExpression(fieldIndex));
 
                     if (node.getSelect().isDistinct() && !field.getType().isComparable()) {
                         throw new SemanticException(node.getSelect(), "DISTINCT can only be applied to comparable types (actual: %s)"field.getType());
                     }
                 }
             }