/* * 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 com.facebook.presto.metadata.QualifiedObjectName; import com.facebook.presto.metadata.Signature; import com.facebook.presto.metadata.TableHandle; import com.facebook.presto.spi.ColumnHandle; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.tree.ExistsPredicate; import com.facebook.presto.sql.tree.Expression; import com.facebook.presto.sql.tree.FunctionCall; import com.facebook.presto.sql.tree.Identifier; import com.facebook.presto.sql.tree.InPredicate; import com.facebook.presto.sql.tree.Join; import com.facebook.presto.sql.tree.LambdaArgumentDeclaration; import com.facebook.presto.sql.tree.Node; import com.facebook.presto.sql.tree.OrderBy; import com.facebook.presto.sql.tree.QuantifiedComparisonExpression; import com.facebook.presto.sql.tree.Query; import com.facebook.presto.sql.tree.QuerySpecification; import com.facebook.presto.sql.tree.Relation; import com.facebook.presto.sql.tree.SampledRelation; import com.facebook.presto.sql.tree.Statement; import com.facebook.presto.sql.tree.SubqueryExpression; import com.facebook.presto.sql.tree.Table; import com.facebook.presto.util.maps.IdentityLinkedHashMap; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ListMultimap; import javax.annotation.concurrent.Immutable; import java.util.ArrayDeque; import java.util.Collection; import java.util.Deque; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; import static java.util.Collections.newSetFromMap; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; import static java.util.Objects.requireNonNull; public class Analysis { private final Statement root; private final List<Expression> parameters; private String updateType; private final IdentityLinkedHashMap<Table, Query> namedQueries = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Node, Scope> scopes = new IdentityLinkedHashMap<>(); private final IdentityHashMap<Expression, FieldId> columnReferences = new IdentityHashMap<>(); private final IdentityLinkedHashMap<QuerySpecification, List<FunctionCall>> aggregates = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<OrderBy, List<Expression>> orderByAggregates = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<QuerySpecification, List<List<Expression>>> groupByExpressions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Node, Expression> where = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<QuerySpecification, Expression> having = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Node, List<Expression>> orderByExpressions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Node, List<Expression>> outputExpressions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<QuerySpecification, List<FunctionCall>> windowFunctions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<OrderBy, List<FunctionCall>> orderByWindowFunctions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Join, Expression> joins = new IdentityLinkedHashMap<>(); private final ListMultimap<Node, InPredicate> inPredicatesSubqueries = ArrayListMultimap.create(); private final ListMultimap<Node, SubqueryExpression> scalarSubqueries = ArrayListMultimap.create(); private final ListMultimap<Node, ExistsPredicate> existsSubqueries = ArrayListMultimap.create(); private final ListMultimap<Node, QuantifiedComparisonExpression> quantifiedComparisonSubqueries = ArrayListMultimap.create(); private final IdentityLinkedHashMap<Table, TableHandle> tables = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Expression, Type> types = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Expression, Type> coercions = new IdentityLinkedHashMap<>(); private final Set<Expression> typeOnlyCoercions = newSetFromMap(new IdentityLinkedHashMap<>()); private final IdentityLinkedHashMap<Relation, Type[]> relationCoercions = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<FunctionCall, Signature> functionSignature = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Identifier, LambdaArgumentDeclaration> lambdaArgumentReferences = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<Field, ColumnHandle> columns = new IdentityLinkedHashMap<>(); private final IdentityLinkedHashMap<SampledRelation, Double> sampleRatios = new IdentityLinkedHashMap<>(); // for create table private Optional<QualifiedObjectName> createTableDestination = Optional.empty(); private Map<String, Expression> createTableProperties = ImmutableMap.of(); private boolean createTableAsSelectWithData = true; private boolean createTableAsSelectNoOp = false; private Optional<String> createTableComment = Optional.empty(); private Optional<Insert> insert = Optional.empty(); // for describe input and describe output private final boolean isDescribe; // for recursive view detection private final Deque<Table> tablesForView = new ArrayDeque<>(); public Analysis(Statement root, List<Expression> parameters, boolean isDescribe) { requireNonNull(parameters); this.root = root; this.parameters = parameters; this.isDescribe = isDescribe; } public Statement getStatement() { return root; } public String getUpdateType() { return updateType; } public void setUpdateType(String updateType) { this.updateType = updateType; } public boolean isCreateTableAsSelectWithData() { return createTableAsSelectWithData; } public void setCreateTableAsSelectWithData(boolean createTableAsSelectWithData) { this.createTableAsSelectWithData = createTableAsSelectWithData; } public boolean isCreateTableAsSelectNoOp() { return createTableAsSelectNoOp; } public void setCreateTableAsSelectNoOp(boolean createTableAsSelectNoOp) { this.createTableAsSelectNoOp = createTableAsSelectNoOp; } public void setAggregates(QuerySpecification node, List<FunctionCall> aggregates) { this.aggregates.put(node, aggregates); } public List<FunctionCall> getAggregates(QuerySpecification query) { return aggregates.get(query); } public void setOrderByAggregates(OrderBy node, List<Expression> aggregates) { this.orderByAggregates.put(node, ImmutableList.copyOf(aggregates)); } public List<Expression> getOrderByAggregates(OrderBy query) { return orderByAggregates.get(query); } public IdentityLinkedHashMap<Expression, Type> getTypes() { return new IdentityLinkedHashMap<>(types); } public Type getType(Expression expression) { checkArgument(types.containsKey(expression), "Expression not analyzed: %s", expression); return types.get(expression); } public Type getTypeWithCoercions(Expression expression) { checkArgument(types.containsKey(expression), "Expression not analyzed: %s", expression); if (coercions.containsKey(expression)) { return coercions.get(expression); } return types.get(expression); } public Type[] getRelationCoercion(Relation relation) { return relationCoercions.get(relation); } public void addRelationCoercion(Relation relation, Type[] types) { relationCoercions.put(relation, types); } public IdentityLinkedHashMap<Expression, Type> getCoercions() { return coercions; } public Type getCoercion(Expression expression) { return coercions.get(expression); } public void addLambdaArgumentReferences(IdentityLinkedHashMap<Identifier, LambdaArgumentDeclaration> lambdaArgumentReferences) { this.lambdaArgumentReferences.putAll(lambdaArgumentReferences); } public LambdaArgumentDeclaration getLambdaArgumentReference(Identifier identifier) { return lambdaArgumentReferences.get(identifier); } public IdentityLinkedHashMap<Identifier, LambdaArgumentDeclaration> getLambdaArgumentReferences() { return lambdaArgumentReferences; } public void setGroupingSets(QuerySpecification node, List<List<Expression>> expressions) { groupByExpressions.put(node, expressions); } public boolean isTypeOnlyCoercion(Expression expression) { return typeOnlyCoercions.contains(expression); } public List<List<Expression>> getGroupingSets(QuerySpecification node) { return groupByExpressions.get(node); } public void setWhere(Node node, Expression expression) { where.put(node, expression); } public Expression getWhere(QuerySpecification node) { return where.get(node); } public void setOrderByExpressions(Node node, List<Expression> items) { orderByExpressions.put(node, items); } public List<Expression> getOrderByExpressions(Node node) { return orderByExpressions.get(node); } public void setOutputExpressions(Node node, List<Expression> expressions) { outputExpressions.put(node, expressions); } public List<Expression> getOutputExpressions(Node node) { return outputExpressions.get(node); } public void setHaving(QuerySpecification node, Expression expression) { having.put(node, expression); } public void setJoinCriteria(Join node, Expression criteria) { joins.put(node, criteria); } public Expression getJoinCriteria(Join join) { return joins.get(join); } public void recordSubqueries(Node node, ExpressionAnalysis expressionAnalysis) { this.inPredicatesSubqueries.putAll(node, expressionAnalysis.getSubqueryInPredicates()); this.scalarSubqueries.putAll(node, expressionAnalysis.getScalarSubqueries()); this.existsSubqueries.putAll(node, expressionAnalysis.getExistsSubqueries()); this.quantifiedComparisonSubqueries.putAll(node, expressionAnalysis.getQuantifiedComparisons()); } public List<InPredicate> getInPredicateSubqueries(Node node) { if (inPredicatesSubqueries.containsKey(node)) { return inPredicatesSubqueries.get(node); } return ImmutableList.of(); } public List<SubqueryExpression> getScalarSubqueries(Node node) { if (scalarSubqueries.containsKey(node)) { return scalarSubqueries.get(node); } return ImmutableList.of(); } public List<ExistsPredicate> getExistsSubqueries(Node node) { if (existsSubqueries.containsKey(node)) { return existsSubqueries.get(node); } return ImmutableList.of(); } public List<QuantifiedComparisonExpression> getQuantifiedComparisonSubqueries(Node node) { if (quantifiedComparisonSubqueries.containsKey(node)) { return quantifiedComparisonSubqueries.get(node); } return ImmutableList.of(); } public void setWindowFunctions(QuerySpecification node, List<FunctionCall> functions) { windowFunctions.put(node, functions); } public List<FunctionCall> getWindowFunctions(QuerySpecification query) { return windowFunctions.get(query); } public void setOrderByWindowFunctions(OrderBy node, List<FunctionCall> functions) { orderByWindowFunctions.put(node, ImmutableList.copyOf(functions)); } public List<FunctionCall> getOrderByWindowFunctions(OrderBy query) { return orderByWindowFunctions.get(query); } public void addColumnReferences(IdentityLinkedHashMap<Expression, FieldId> columnReferences) { this.columnReferences.putAll(columnReferences); } public Scope getScope(Node node) { return tryGetScope(node).orElseThrow(() -> new IllegalArgumentException(String.format("Analysis does not contain information for node: %s", node))); } public Optional<Scope> tryGetScope(Node node) { if (scopes.containsKey(node)) { return Optional.of(scopes.get(node)); } return Optional.empty(); } public Scope getRootScope() { return getScope(root); } public void setScope(Node node, Scope scope) { scopes.put(node, scope); } public RelationType getOutputDescriptor() { return getOutputDescriptor(root); } public RelationType getOutputDescriptor(Node node) { return getScope(node).getRelationType(); } public TableHandle getTableHandle(Table table) { return tables.get(table); } public Collection<TableHandle> getTables() { return tables.values(); } public void registerTable(Table table, TableHandle handle) { tables.put(table, handle); } public Signature getFunctionSignature(FunctionCall function) { return functionSignature.get(function); } public void addFunctionSignatures(IdentityLinkedHashMap<FunctionCall, Signature> infos) { functionSignature.putAll(infos); } public Set<Expression> getColumnReferences() { return unmodifiableSet(columnReferences.keySet()); } public Map<Expression, FieldId> getColumnReferenceFields() { return unmodifiableMap(columnReferences); } public void addTypes(IdentityLinkedHashMap<Expression, Type> types) { this.types.putAll(types); } public void addCoercion(Expression expression, Type type, boolean isTypeOnlyCoercion) { this.coercions.put(expression, type); if (isTypeOnlyCoercion) { this.typeOnlyCoercions.add(expression); } } public void addCoercions(IdentityLinkedHashMap<Expression, Type> coercions, Set<Expression> typeOnlyCoercions) { this.coercions.putAll(coercions); this.typeOnlyCoercions.addAll(typeOnlyCoercions); } public Expression getHaving(QuerySpecification query) { return having.get(query); } public void setColumn(Field field, ColumnHandle handle) { columns.put(field, handle); } public ColumnHandle getColumn(Field field) { return columns.get(field); } public void setCreateTableDestination(QualifiedObjectName destination) { this.createTableDestination = Optional.of(destination); } public Optional<QualifiedObjectName> getCreateTableDestination() { return createTableDestination; } public void setCreateTableProperties(Map<String, Expression> createTableProperties) { this.createTableProperties = createTableProperties; } public Map<String, Expression> getCreateTableProperties() { return createTableProperties; } public void setCreateTableComment(Optional<String> createTableComment) { this.createTableComment = requireNonNull(createTableComment); } public Optional<String> getCreateTableComment() { return createTableComment; } public void setInsert(Insert insert) { this.insert = Optional.of(insert); } public Optional<Insert> getInsert() { return insert; } public Query getNamedQuery(Table table) { return namedQueries.get(table); } public void registerNamedQuery(Table tableReference, Query query) { requireNonNull(tableReference, "tableReference is null"); requireNonNull(query, "query is null"); namedQueries.put(tableReference, query); } public void registerTableForView(Table tableReference) { tablesForView.push(requireNonNull(tableReference, "table is null")); } public void unregisterTableForView() { tablesForView.pop(); } public boolean hasTableInView(Table tableReference) { return tablesForView.contains(tableReference); } public void setSampleRatio(SampledRelation relation, double ratio) { sampleRatios.put(relation, ratio); } public double getSampleRatio(SampledRelation relation) { Preconditions.checkState(sampleRatios.containsKey(relation), "Sample ratio missing for %s. Broken analysis?", relation); return sampleRatios.get(relation); } public List<Expression> getParameters() { return parameters; } public boolean isDescribe() { return isDescribe; } @Immutable public static final class Insert { private final TableHandle target; private final List<ColumnHandle> columns; public Insert(TableHandle target, List<ColumnHandle> columns) { this.target = requireNonNull(target, "target is null"); this.columns = requireNonNull(columns, "columns is null"); checkArgument(columns.size() > 0, "No columns given to insert"); } public List<ColumnHandle> getColumns() { return columns; } public TableHandle getTarget() { return target; } } }