Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.trino.sql.analyzer.Analysis Maven / Gradle / Ivy
/*
* 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 io.trino.sql.analyzer;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.collect.Streams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.Immutable;
import io.trino.metadata.AnalyzeMetadata;
import io.trino.metadata.QualifiedObjectName;
import io.trino.metadata.ResolvedFunction;
import io.trino.metadata.TableExecuteHandle;
import io.trino.metadata.TableHandle;
import io.trino.metadata.TableLayout;
import io.trino.security.AccessControl;
import io.trino.security.SecurityContext;
import io.trino.spi.QueryId;
import io.trino.spi.connector.CatalogHandle;
import io.trino.spi.connector.CatalogHandle.CatalogVersion;
import io.trino.spi.connector.ColumnHandle;
import io.trino.spi.connector.ColumnSchema;
import io.trino.spi.connector.ConnectorTableMetadata;
import io.trino.spi.connector.ConnectorTransactionHandle;
import io.trino.spi.eventlistener.ColumnDetail;
import io.trino.spi.eventlistener.ColumnInfo;
import io.trino.spi.eventlistener.RoutineInfo;
import io.trino.spi.eventlistener.TableInfo;
import io.trino.spi.function.table.Argument;
import io.trino.spi.function.table.ConnectorTableFunctionHandle;
import io.trino.spi.security.Identity;
import io.trino.spi.type.RowType;
import io.trino.spi.type.Type;
import io.trino.sql.analyzer.ExpressionAnalyzer.LabelPrefixedReference;
import io.trino.sql.analyzer.JsonPathAnalyzer.JsonPathAnalysis;
import io.trino.sql.planner.PartitioningHandle;
import io.trino.sql.tree.AllColumns;
import io.trino.sql.tree.DereferenceExpression;
import io.trino.sql.tree.ExistsPredicate;
import io.trino.sql.tree.Expression;
import io.trino.sql.tree.FieldReference;
import io.trino.sql.tree.FunctionCall;
import io.trino.sql.tree.GroupingOperation;
import io.trino.sql.tree.Identifier;
import io.trino.sql.tree.InPredicate;
import io.trino.sql.tree.Join;
import io.trino.sql.tree.LambdaArgumentDeclaration;
import io.trino.sql.tree.MeasureDefinition;
import io.trino.sql.tree.Node;
import io.trino.sql.tree.NodeRef;
import io.trino.sql.tree.Offset;
import io.trino.sql.tree.OrderBy;
import io.trino.sql.tree.Parameter;
import io.trino.sql.tree.QualifiedName;
import io.trino.sql.tree.QuantifiedComparisonExpression;
import io.trino.sql.tree.Query;
import io.trino.sql.tree.QuerySpecification;
import io.trino.sql.tree.RangeQuantifier;
import io.trino.sql.tree.Relation;
import io.trino.sql.tree.RowPattern;
import io.trino.sql.tree.SampledRelation;
import io.trino.sql.tree.Statement;
import io.trino.sql.tree.SubqueryExpression;
import io.trino.sql.tree.Table;
import io.trino.sql.tree.TableFunctionInvocation;
import io.trino.sql.tree.Unnest;
import io.trino.sql.tree.WindowFrame;
import io.trino.sql.tree.WindowOperation;
import io.trino.transaction.TransactionId;
import jakarta.annotation.Nullable;
import java.time.Instant;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.OptionalLong;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static io.trino.sql.analyzer.QueryType.DESCRIBE;
import static io.trino.sql.analyzer.QueryType.EXPLAIN;
import static java.lang.Boolean.FALSE;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static java.util.Objects.requireNonNull;
public class Analysis
{
@Nullable
private final Statement root;
private final Map, Expression> parameters;
private String updateType;
private Optional target = Optional.empty();
private boolean skipMaterializedViewRefresh;
private Optional tableExecuteReadsData;
private final Map, Query> namedQueries = new LinkedHashMap<>();
// map expandable query to the node being the inner recursive reference
private final Map, Node> expandableNamedQueries = new LinkedHashMap<>();
// map inner recursive reference in the expandable query to the recursion base scope
private final Map, Scope> expandableBaseScopes = new LinkedHashMap<>();
// Synthetic scope when a query does not have a FROM clause
// We need to track this separately because there's no node we can attach it to.
private final Map, Scope> implicitFromScopes = new LinkedHashMap<>();
private final Map, Scope> scopes = new LinkedHashMap<>();
private final Map, ResolvedField> columnReferences = new LinkedHashMap<>();
// a map of users to the columns per table that they access
private final Map>> tableColumnReferences = new LinkedHashMap<>();
// Record fields prefixed with labels in row pattern recognition context
private final Map, LabelPrefixedReference> labelDereferences = new LinkedHashMap<>();
private final Set> patternRecognitionFunctions = new LinkedHashSet<>();
private final Map, Range> ranges = new LinkedHashMap<>();
private final Map, Set> undefinedLabels = new LinkedHashMap<>();
private final Map, MeasureDefinition> measureDefinitions = new LinkedHashMap<>();
private final Set> patternAggregations = new LinkedHashSet<>();
// for JSON features
private final Map, JsonPathAnalysis> jsonPathAnalyses = new LinkedHashMap<>();
private final Map, ResolvedFunction> jsonInputFunctions = new LinkedHashMap<>();
private final Map, ResolvedFunction> jsonOutputFunctions = new LinkedHashMap<>();
private final Map, List> aggregates = new LinkedHashMap<>();
private final Map, List> orderByAggregates = new LinkedHashMap<>();
private final Map, GroupingSetAnalysis> groupingSets = new LinkedHashMap<>();
private final Map, Expression> where = new LinkedHashMap<>();
private final Map, Expression> having = new LinkedHashMap<>();
private final Map, List> orderByExpressions = new LinkedHashMap<>();
private final Set> redundantOrderBy = new HashSet<>();
private final Map, List> selectExpressions = new LinkedHashMap<>();
// Store resolved window specifications defined in WINDOW clause
private final Map, Map, ResolvedWindow>> windowDefinitions = new LinkedHashMap<>();
// Store resolved window specifications for window functions and row pattern measures
private final Map, ResolvedWindow> windows = new LinkedHashMap<>();
private final Map, List> windowFunctions = new LinkedHashMap<>();
private final Map, List> orderByWindowFunctions = new LinkedHashMap<>();
private final Map, List> windowMeasures = new LinkedHashMap<>();
private final Map, List> orderByWindowMeasures = new LinkedHashMap<>();
private final Map, Long> offset = new LinkedHashMap<>();
private final Map, OptionalLong> limit = new LinkedHashMap<>();
private final Map, List> selectAllResultFields = new LinkedHashMap<>();
private final Map, Expression> joins = new LinkedHashMap<>();
private final Map, JoinUsingAnalysis> joinUsing = new LinkedHashMap<>();
private final Map, SubqueryAnalysis> subqueries = new LinkedHashMap<>();
private final Map, PredicateCoercions> predicateCoercions = new LinkedHashMap<>();
private final Map, TableEntry> tables = new LinkedHashMap<>();
private final Map, Type> types = new LinkedHashMap<>();
private final Map, Type> coercions = new LinkedHashMap<>();
private final Set> typeOnlyCoercions = new LinkedHashSet<>();
private final Map, Type> sortKeyCoercionsForFrameBoundCalculation = new LinkedHashMap<>();
private final Map, Type> sortKeyCoercionsForFrameBoundComparison = new LinkedHashMap<>();
private final Map, ResolvedFunction> frameBoundCalculations = new LinkedHashMap<>();
private final Map, List> relationCoercions = new LinkedHashMap<>();
private final Map, RoutineEntry> resolvedFunctions = new LinkedHashMap<>();
private final Map, LambdaArgumentDeclaration> lambdaArgumentReferences = new LinkedHashMap<>();
private final Map columns = new LinkedHashMap<>();
private final Map, Double> sampleRatios = new LinkedHashMap<>();
private final Map, List> groupingOperations = new LinkedHashMap<>();
private final Multiset rowFilterScopes = HashMultiset.create();
private final Map, List> rowFilters = new LinkedHashMap<>();
private final Map, List> checkConstraints = new LinkedHashMap<>();
private final Multiset columnMaskScopes = HashMultiset.create();
private final Map, Map> columnMasks = new LinkedHashMap<>();
private final Map, UnnestAnalysis> unnestAnalysis = new LinkedHashMap<>();
private Optional create = Optional.empty();
private Optional insert = Optional.empty();
private Optional refreshMaterializedView = Optional.empty();
private Optional delegatedRefreshMaterializedView = Optional.empty();
private Optional analyzeMetadata = Optional.empty();
private Optional> updatedColumns = Optional.empty();
private Optional mergeAnalysis = Optional.empty();
private final QueryType queryType;
// for recursive view detection
private final Deque tablesForView = new ArrayDeque<>();
// row id field for update/delete queries
private final Map, FieldReference> rowIdField = new LinkedHashMap<>();
private final Multimap originColumnDetails = ArrayListMultimap.create();
private final Multimap, Field> fieldLineage = ArrayListMultimap.create();
private Optional tableExecuteHandle = Optional.empty();
// names of tables and aliased relations. All names are resolved case-insensitive.
private final Map, QualifiedName> relationNames = new LinkedHashMap<>();
private final Map, TableFunctionInvocationAnalysis> tableFunctionAnalyses = new LinkedHashMap<>();
private final Set> aliasedRelations = new LinkedHashSet<>();
private final Set> polymorphicTableFunctions = new LinkedHashSet<>();
public Analysis(@Nullable Statement root, Map, Expression> parameters, QueryType queryType)
{
this.root = root;
this.parameters = ImmutableMap.copyOf(requireNonNull(parameters, "parameters is null"));
this.queryType = requireNonNull(queryType, "queryType is null");
}
public Statement getStatement()
{
return root;
}
public String getUpdateType()
{
return updateType;
}
public Optional getTarget()
{
return target.map(target -> {
QualifiedObjectName name = target.getName();
return new Output(name.getCatalogName(), target.getCatalogVersion(), name.getSchemaName(), name.getObjectName(), target.getColumns());
});
}
public void setUpdateType(String updateType)
{
if (queryType != EXPLAIN) {
this.updateType = updateType;
}
}
public void setUpdateTarget(CatalogVersion catalogVersion, QualifiedObjectName targetName, Optional targetTable, Optional> targetColumns)
{
this.target = Optional.of(new UpdateTarget(catalogVersion, targetName, targetTable, targetColumns));
}
public boolean isUpdateTarget(Table table)
{
requireNonNull(table, "table is null");
return target
.flatMap(UpdateTarget::getTable)
.map(tableReference -> tableReference == table) // intentional comparison by reference
.orElse(FALSE);
}
public boolean isSkipMaterializedViewRefresh()
{
return skipMaterializedViewRefresh;
}
public void setSkipMaterializedViewRefresh(boolean skipMaterializedViewRefresh)
{
this.skipMaterializedViewRefresh = skipMaterializedViewRefresh;
}
public boolean isTableExecuteReadsData()
{
return tableExecuteReadsData.orElseThrow(() -> new IllegalStateException("tableExecuteReadsData not set"));
}
public void setTableExecuteReadsData(boolean readsData)
{
this.tableExecuteReadsData = Optional.of(readsData);
}
public void setAggregates(QuerySpecification node, List aggregates)
{
this.aggregates.put(NodeRef.of(node), ImmutableList.copyOf(aggregates));
}
public List getAggregates(QuerySpecification query)
{
return aggregates.get(NodeRef.of(query));
}
public void setOrderByAggregates(OrderBy node, List aggregates)
{
this.orderByAggregates.put(NodeRef.of(node), ImmutableList.copyOf(aggregates));
}
public List getOrderByAggregates(OrderBy node)
{
return orderByAggregates.get(NodeRef.of(node));
}
public Map, Type> getTypes()
{
return unmodifiableMap(types);
}
public Type getType(Expression expression)
{
Type type = types.get(NodeRef.of(expression));
checkArgument(type != null, "Expression not analyzed: %s", expression);
return type;
}
public List getRelationCoercion(Relation relation)
{
return relationCoercions.get(NodeRef.of(relation));
}
public void addRelationCoercion(Relation relation, Type[] types)
{
relationCoercions.put(NodeRef.of(relation), ImmutableList.copyOf(types));
}
public Map, Type> getCoercions()
{
return unmodifiableMap(coercions);
}
public Set> getTypeOnlyCoercions()
{
return unmodifiableSet(typeOnlyCoercions);
}
public Type getCoercion(Expression expression)
{
return coercions.get(NodeRef.of(expression));
}
public void addLambdaArgumentReferences(Map, LambdaArgumentDeclaration> lambdaArgumentReferences)
{
this.lambdaArgumentReferences.putAll(lambdaArgumentReferences);
}
public LambdaArgumentDeclaration getLambdaArgumentReference(Identifier identifier)
{
return lambdaArgumentReferences.get(NodeRef.of(identifier));
}
public Map, LambdaArgumentDeclaration> getLambdaArgumentReferences()
{
return unmodifiableMap(lambdaArgumentReferences);
}
public void setGroupingSets(QuerySpecification node, GroupingSetAnalysis groupingSets)
{
this.groupingSets.put(NodeRef.of(node), groupingSets);
}
public boolean isAggregation(QuerySpecification node)
{
return groupingSets.containsKey(NodeRef.of(node));
}
public boolean isTypeOnlyCoercion(Expression expression)
{
return typeOnlyCoercions.contains(NodeRef.of(expression));
}
public GroupingSetAnalysis getGroupingSets(QuerySpecification node)
{
return groupingSets.get(NodeRef.of(node));
}
public void setWhere(Node node, Expression expression)
{
where.put(NodeRef.of(node), expression);
}
public Expression getWhere(QuerySpecification node)
{
return where.get(NodeRef.of(node));
}
public void setOrderByExpressions(Node node, List items)
{
orderByExpressions.put(NodeRef.of(node), ImmutableList.copyOf(items));
}
public List getOrderByExpressions(Node node)
{
return orderByExpressions.get(NodeRef.of(node));
}
public void setOffset(Offset node, long rowCount)
{
offset.put(NodeRef.of(node), rowCount);
}
public long getOffset(Offset node)
{
checkState(offset.containsKey(NodeRef.of(node)), "missing OFFSET value for node %s", node);
return offset.get(NodeRef.of(node));
}
public void setLimit(Node node, OptionalLong rowCount)
{
limit.put(NodeRef.of(node), rowCount);
}
public void setLimit(Node node, long rowCount)
{
limit.put(NodeRef.of(node), OptionalLong.of(rowCount));
}
public OptionalLong getLimit(Node node)
{
checkState(limit.containsKey(NodeRef.of(node)), "missing LIMIT value for node %s", node);
return limit.get(NodeRef.of(node));
}
public void setSelectAllResultFields(AllColumns node, List expressions)
{
selectAllResultFields.put(NodeRef.of(node), ImmutableList.copyOf(expressions));
}
public List getSelectAllResultFields(AllColumns node)
{
return selectAllResultFields.get(NodeRef.of(node));
}
public void setSelectExpressions(Node node, List expressions)
{
selectExpressions.put(NodeRef.of(node), ImmutableList.copyOf(expressions));
}
public List getSelectExpressions(Node node)
{
return selectExpressions.get(NodeRef.of(node));
}
public void setHaving(QuerySpecification node, Expression expression)
{
having.put(NodeRef.of(node), expression);
}
public void setJoinCriteria(Join node, Expression criteria)
{
joins.put(NodeRef.of(node), criteria);
}
public Expression getJoinCriteria(Join join)
{
return joins.get(NodeRef.of(join));
}
public void recordSubqueries(Node node, ExpressionAnalysis expressionAnalysis)
{
SubqueryAnalysis subqueries = this.subqueries.computeIfAbsent(NodeRef.of(node), key -> new SubqueryAnalysis());
subqueries.addInPredicates(dereference(expressionAnalysis.getSubqueryInPredicates()));
subqueries.addSubqueries(dereference(expressionAnalysis.getSubqueries()));
subqueries.addExistsSubqueries(dereference(expressionAnalysis.getExistsSubqueries()));
subqueries.addQuantifiedComparisons(dereference(expressionAnalysis.getQuantifiedComparisons()));
}
private List dereference(Collection> nodeRefs)
{
return nodeRefs.stream()
.map(NodeRef::getNode)
.collect(toImmutableList());
}
public SubqueryAnalysis getSubqueries(Node node)
{
return subqueries.computeIfAbsent(NodeRef.of(node), key -> new SubqueryAnalysis());
}
public void addWindowDefinition(QuerySpecification query, CanonicalizationAware name, ResolvedWindow window)
{
windowDefinitions.computeIfAbsent(NodeRef.of(query), key -> new LinkedHashMap<>())
.put(name, window);
}
public ResolvedWindow getWindowDefinition(QuerySpecification query, CanonicalizationAware name)
{
Map, ResolvedWindow> windows = windowDefinitions.get(NodeRef.of(query));
if (windows != null) {
return windows.get(name);
}
return null;
}
public void setWindow(Node node, ResolvedWindow window)
{
windows.put(NodeRef.of(node), window);
}
public ResolvedWindow getWindow(Node node)
{
return windows.get(NodeRef.of(node));
}
public void setWindowFunctions(QuerySpecification node, List functions)
{
windowFunctions.put(NodeRef.of(node), ImmutableList.copyOf(functions));
}
public List getWindowFunctions(QuerySpecification query)
{
return windowFunctions.get(NodeRef.of(query));
}
public void setOrderByWindowFunctions(OrderBy node, List functions)
{
orderByWindowFunctions.put(NodeRef.of(node), ImmutableList.copyOf(functions));
}
public List getOrderByWindowFunctions(OrderBy query)
{
return orderByWindowFunctions.get(NodeRef.of(query));
}
public void setWindowMeasures(QuerySpecification node, List measures)
{
windowMeasures.put(NodeRef.of(node), ImmutableList.copyOf(measures));
}
public List getWindowMeasures(QuerySpecification node)
{
return windowMeasures.get(NodeRef.of(node));
}
public void setOrderByWindowMeasures(OrderBy node, List measures)
{
orderByWindowMeasures.put(NodeRef.of(node), ImmutableList.copyOf(measures));
}
public List getOrderByWindowMeasures(OrderBy node)
{
return orderByWindowMeasures.get(NodeRef.of(node));
}
public void addColumnReferences(Map, ResolvedField> columnReferences)
{
this.columnReferences.putAll(columnReferences);
}
public Scope getScope(Node node)
{
return tryGetScope(node).orElseThrow(() -> new IllegalArgumentException(format("Analysis does not contain information for node: %s", node)));
}
public Optional tryGetScope(Node node)
{
NodeRef key = NodeRef.of(node);
if (scopes.containsKey(key)) {
return Optional.of(scopes.get(key));
}
return Optional.empty();
}
public Scope getRootScope()
{
return getScope(root);
}
public void setScope(Node node, Scope scope)
{
scopes.put(NodeRef.of(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(NodeRef.of(table))
.getHandle()
.orElseThrow(() -> new IllegalArgumentException(format("%s is not a table reference", table)));
}
public Collection getTables()
{
return tables.values().stream()
.map(TableEntry::getHandle)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toImmutableList());
}
public void registerTable(
Table table,
Optional handle,
QualifiedObjectName name,
String authorization,
Scope accessControlScope)
{
tables.put(
NodeRef.of(table),
new TableEntry(
handle,
name,
authorization,
accessControlScope,
tablesForView.isEmpty() &&
rowFilterScopes.isEmpty() &&
columnMaskScopes.isEmpty()));
}
public Set getResolvedFunctions()
{
return resolvedFunctions.values().stream()
.map(RoutineEntry::getFunction)
.collect(toImmutableSet());
}
public ResolvedFunction getResolvedFunction(Expression node)
{
return resolvedFunctions.get(NodeRef.of(node)).getFunction();
}
public void addResolvedFunction(Expression node, ResolvedFunction function, String authorization)
{
resolvedFunctions.put(NodeRef.of(node), new RoutineEntry(function, authorization));
}
public Set> getColumnReferences()
{
return unmodifiableSet(columnReferences.keySet());
}
public Map, ResolvedField> getColumnReferenceFields()
{
return unmodifiableMap(columnReferences);
}
public ResolvedField getResolvedField(Expression expression)
{
checkArgument(isColumnReference(expression), "Expression is not a column reference: %s", expression);
return columnReferences.get(NodeRef.of(expression));
}
public boolean isColumnReference(Expression expression)
{
requireNonNull(expression, "expression is null");
return columnReferences.containsKey(NodeRef.of(expression));
}
public void addType(Expression expression, Type type)
{
this.types.put(NodeRef.of(expression), type);
}
public void addTypes(Map, Type> types)
{
this.types.putAll(types);
}
public void addCoercion(Expression expression, Type type, boolean isTypeOnlyCoercion)
{
this.coercions.put(NodeRef.of(expression), type);
if (isTypeOnlyCoercion) {
this.typeOnlyCoercions.add(NodeRef.of(expression));
}
}
public void addCoercions(
Map, Type> coercions,
Set> typeOnlyCoercions,
Map, Type> sortKeyCoercionsForFrameBoundCalculation,
Map, Type> sortKeyCoercionsForFrameBoundComparison)
{
this.coercions.putAll(coercions);
this.typeOnlyCoercions.addAll(typeOnlyCoercions);
this.sortKeyCoercionsForFrameBoundCalculation.putAll(sortKeyCoercionsForFrameBoundCalculation);
this.sortKeyCoercionsForFrameBoundComparison.putAll(sortKeyCoercionsForFrameBoundComparison);
}
public Type getSortKeyCoercionForFrameBoundCalculation(Expression frameOffset)
{
return sortKeyCoercionsForFrameBoundCalculation.get(NodeRef.of(frameOffset));
}
public Type getSortKeyCoercionForFrameBoundComparison(Expression frameOffset)
{
return sortKeyCoercionsForFrameBoundComparison.get(NodeRef.of(frameOffset));
}
public void addFrameBoundCalculations(Map, ResolvedFunction> frameBoundCalculations)
{
this.frameBoundCalculations.putAll(frameBoundCalculations);
}
public ResolvedFunction getFrameBoundCalculation(Expression frameOffset)
{
return frameBoundCalculations.get(NodeRef.of(frameOffset));
}
public Expression getHaving(QuerySpecification query)
{
return having.get(NodeRef.of(query));
}
public void setColumn(Field field, ColumnHandle handle)
{
columns.put(field, handle);
}
public ColumnHandle getColumn(Field field)
{
return columns.get(field);
}
public Optional getAnalyzeMetadata()
{
return analyzeMetadata;
}
public void setAnalyzeMetadata(AnalyzeMetadata analyzeMetadata)
{
this.analyzeMetadata = Optional.of(analyzeMetadata);
}
public void setCreate(Create create)
{
this.create = Optional.of(create);
}
public Optional getCreate()
{
return create;
}
public void setInsert(Insert insert)
{
this.insert = Optional.of(insert);
}
public Optional getInsert()
{
return insert;
}
public void setUpdatedColumns(List updatedColumns)
{
this.updatedColumns = Optional.of(updatedColumns);
}
public Optional> getUpdatedColumns()
{
return updatedColumns;
}
public Optional getMergeAnalysis()
{
return mergeAnalysis;
}
public void setMergeAnalysis(MergeAnalysis mergeAnalysis)
{
this.mergeAnalysis = Optional.of(mergeAnalysis);
}
public void setRefreshMaterializedView(RefreshMaterializedViewAnalysis refreshMaterializedView)
{
this.refreshMaterializedView = Optional.of(refreshMaterializedView);
}
public Optional getRefreshMaterializedView()
{
return refreshMaterializedView;
}
public void setDelegatedRefreshMaterializedView(QualifiedObjectName viewName)
{
this.delegatedRefreshMaterializedView = Optional.of(viewName);
}
public Optional getDelegatedRefreshMaterializedView()
{
return delegatedRefreshMaterializedView;
}
public Query getNamedQuery(Table table)
{
return namedQueries.get(NodeRef.of(table));
}
public void registerNamedQuery(Table tableReference, Query query)
{
requireNonNull(tableReference, "tableReference is null");
requireNonNull(query, "query is null");
namedQueries.put(NodeRef.of(tableReference), query);
}
public void registerExpandableQuery(Query query, Node recursiveReference)
{
requireNonNull(query, "query is null");
requireNonNull(recursiveReference, "recursiveReference is null");
expandableNamedQueries.put(NodeRef.of(query), recursiveReference);
}
public boolean isExpandableQuery(Query query)
{
return expandableNamedQueries.containsKey(NodeRef.of(query));
}
public Node getRecursiveReference(Query query)
{
checkArgument(isExpandableQuery(query), "query is not registered as expandable");
return expandableNamedQueries.get(NodeRef.of(query));
}
public void setExpandableBaseScope(Node node, Scope scope)
{
expandableBaseScopes.put(NodeRef.of(node), scope);
}
public Optional getExpandableBaseScope(Node node)
{
return Optional.ofNullable(expandableBaseScopes.get(NodeRef.of(node)));
}
public void registerTableForView(Table tableReference)
{
tablesForView.push(requireNonNull(tableReference, "tableReference 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(NodeRef.of(relation), ratio);
}
public double getSampleRatio(SampledRelation relation)
{
NodeRef key = NodeRef.of(relation);
checkState(sampleRatios.containsKey(key), "Sample ratio missing for %s. Broken analysis?", relation);
return sampleRatios.get(key);
}
public void setGroupingOperations(QuerySpecification querySpecification, List groupingOperations)
{
this.groupingOperations.put(NodeRef.of(querySpecification), ImmutableList.copyOf(groupingOperations));
}
public List getGroupingOperations(QuerySpecification querySpecification)
{
return Optional.ofNullable(groupingOperations.get(NodeRef.of(querySpecification)))
.orElse(emptyList());
}
public Map, Expression> getParameters()
{
return parameters;
}
public QueryType getQueryType()
{
return queryType;
}
public boolean isDescribe()
{
return queryType == DESCRIBE;
}
public void setJoinUsing(Join node, JoinUsingAnalysis analysis)
{
joinUsing.put(NodeRef.of(node), analysis);
}
public JoinUsingAnalysis getJoinUsing(Join node)
{
return joinUsing.get(NodeRef.of(node));
}
public void setUnnest(Unnest node, UnnestAnalysis analysis)
{
unnestAnalysis.put(NodeRef.of(node), analysis);
}
public UnnestAnalysis getUnnest(Unnest node)
{
return unnestAnalysis.get(NodeRef.of(node));
}
public void addTableColumnReferences(AccessControl accessControl, Identity identity, Multimap tableColumnMap)
{
AccessControlInfo accessControlInfo = new AccessControlInfo(accessControl, identity);
Map> references = tableColumnReferences.computeIfAbsent(accessControlInfo, k -> new LinkedHashMap<>());
tableColumnMap.asMap()
.forEach((key, value) -> references.computeIfAbsent(key, k -> new HashSet<>()).addAll(value));
}
public void addEmptyColumnReferencesForTable(AccessControl accessControl, Identity identity, QualifiedObjectName table)
{
AccessControlInfo accessControlInfo = new AccessControlInfo(accessControl, identity);
tableColumnReferences.computeIfAbsent(accessControlInfo, k -> new LinkedHashMap<>()).computeIfAbsent(table, k -> new HashSet<>());
}
public void addLabelDereferences(Map, LabelPrefixedReference> dereferences)
{
labelDereferences.putAll(dereferences);
}
public LabelPrefixedReference getLabelDereference(DereferenceExpression expression)
{
return labelDereferences.get(NodeRef.of(expression));
}
public void addPatternRecognitionFunctions(Set> functions)
{
patternRecognitionFunctions.addAll(functions);
}
public boolean isPatternRecognitionFunction(FunctionCall functionCall)
{
return patternRecognitionFunctions.contains(NodeRef.of(functionCall));
}
public void setRanges(Map, Range> quantifierRanges)
{
ranges.putAll(quantifierRanges);
}
public Range getRange(RangeQuantifier quantifier)
{
Range range = ranges.get(NodeRef.of(quantifier));
checkNotNull(range, "missing range for quantifier %s", quantifier);
return range;
}
public void setUndefinedLabels(RowPattern pattern, Set labels)
{
undefinedLabels.put(NodeRef.of(pattern), labels);
}
public void setUndefinedLabels(Map, Set> labels)
{
undefinedLabels.putAll(labels);
}
public Set getUndefinedLabels(RowPattern pattern)
{
Set labels = undefinedLabels.get(NodeRef.of(pattern));
checkNotNull(labels, "missing undefined labels for %s", pattern);
return labels;
}
public void setMeasureDefinitions(Map, MeasureDefinition> definitions)
{
measureDefinitions.putAll(definitions);
}
public MeasureDefinition getMeasureDefinition(WindowOperation measure)
{
return measureDefinitions.get(NodeRef.of(measure));
}
public void setPatternAggregations(Set> aggregations)
{
patternAggregations.addAll(aggregations);
}
public boolean isPatternAggregation(FunctionCall function)
{
return patternAggregations.contains(NodeRef.of(function));
}
public void setJsonPathAnalyses(Map, JsonPathAnalysis> pathAnalyses)
{
jsonPathAnalyses.putAll(pathAnalyses);
}
public JsonPathAnalysis getJsonPathAnalysis(Expression expression)
{
return jsonPathAnalyses.get(NodeRef.of(expression));
}
public void setJsonInputFunctions(Map, ResolvedFunction> functions)
{
jsonInputFunctions.putAll(functions);
}
public ResolvedFunction getJsonInputFunction(Expression expression)
{
return jsonInputFunctions.get(NodeRef.of(expression));
}
public void setJsonOutputFunctions(Map, ResolvedFunction> functions)
{
jsonOutputFunctions.putAll(functions);
}
public ResolvedFunction getJsonOutputFunction(Expression expression)
{
return jsonOutputFunctions.get(NodeRef.of(expression));
}
public Map>> getTableColumnReferences()
{
return tableColumnReferences;
}
public void markRedundantOrderBy(OrderBy orderBy)
{
redundantOrderBy.add(NodeRef.of(orderBy));
}
public boolean isOrderByRedundant(OrderBy orderBy)
{
return redundantOrderBy.contains(NodeRef.of(orderBy));
}
public boolean hasRowFilter(QualifiedObjectName table, String identity)
{
return rowFilterScopes.contains(new RowFilterScopeEntry(table, identity));
}
public void registerTableForRowFiltering(QualifiedObjectName table, String identity)
{
rowFilterScopes.add(new RowFilterScopeEntry(table, identity));
}
public void unregisterTableForRowFiltering(QualifiedObjectName table, String identity)
{
rowFilterScopes.remove(new RowFilterScopeEntry(table, identity));
}
public void addRowFilter(Table table, Expression filter)
{
rowFilters.computeIfAbsent(NodeRef.of(table), node -> new ArrayList<>())
.add(filter);
}
public void addCheckConstraints(Table table, Expression constraint)
{
checkConstraints.computeIfAbsent(NodeRef.of(table), node -> new ArrayList<>())
.add(constraint);
}
public List getRowFilters(Table node)
{
return unmodifiableList(rowFilters.getOrDefault(NodeRef.of(node), ImmutableList.of()));
}
public List getCheckConstraints(Table node)
{
return unmodifiableList(checkConstraints.getOrDefault(NodeRef.of(node), ImmutableList.of()));
}
public boolean hasColumnMask(QualifiedObjectName table, String column, String identity)
{
return columnMaskScopes.contains(new ColumnMaskScopeEntry(table, column, identity));
}
public void registerTableForColumnMasking(QualifiedObjectName table, String column, String identity)
{
columnMaskScopes.add(new ColumnMaskScopeEntry(table, column, identity));
}
public void unregisterTableForColumnMasking(QualifiedObjectName table, String column, String identity)
{
columnMaskScopes.remove(new ColumnMaskScopeEntry(table, column, identity));
}
public void addColumnMask(Table table, String column, Expression mask)
{
Map masks = columnMasks.computeIfAbsent(NodeRef.of(table), node -> new LinkedHashMap<>());
checkArgument(!masks.containsKey(column), "Mask already exists for column %s", column);
masks.put(column, mask);
}
public Map getColumnMasks(Table table)
{
return unmodifiableMap(columnMasks.getOrDefault(NodeRef.of(table), ImmutableMap.of()));
}
public List getReferencedTables()
{
return tables.entrySet().stream()
.filter(entry -> isInputTable(entry.getKey().getNode()))
.map(entry -> {
NodeRef table = entry.getKey();
QualifiedObjectName tableName = entry.getValue().getName();
List columns = tableColumnReferences.values().stream()
.map(tablesToColumns -> tablesToColumns.get(tableName))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.map(fieldName -> new ColumnInfo(
fieldName,
Optional.ofNullable(columnMasks.getOrDefault(table, ImmutableMap.of()).get(fieldName))
.map(Expression::toString)))
.collect(toImmutableList());
TableEntry info = entry.getValue();
return new TableInfo(
info.getName().getCatalogName(),
info.getName().getSchemaName(),
info.getName().getObjectName(),
info.getAuthorization(),
rowFilters.getOrDefault(table, ImmutableList.of()).stream()
.map(Expression::toString)
.collect(toImmutableList()),
columns,
info.isDirectlyReferenced());
})
.collect(toImmutableList());
}
public List getRoutines()
{
return resolvedFunctions.values().stream()
.map(value -> new RoutineInfo(value.function.getSignature().getName().getFunctionName(), value.getAuthorization()))
.collect(toImmutableList());
}
public void addSourceColumns(Field field, Set sourceColumn)
{
originColumnDetails.putAll(field, sourceColumn);
}
public Set getSourceColumns(Field field)
{
return ImmutableSet.copyOf(originColumnDetails.get(field));
}
public void addExpressionFields(Expression expression, Collection fields)
{
fieldLineage.putAll(NodeRef.of(expression), fields);
}
public Set getExpressionSourceColumns(Expression expression)
{
return fieldLineage.get(NodeRef.of(expression)).stream()
.flatMap(field -> getSourceColumns(field).stream())
.collect(toImmutableSet());
}
public void setRowIdField(Table table, FieldReference field)
{
rowIdField.put(NodeRef.of(table), field);
}
public FieldReference getRowIdField(Table table)
{
return rowIdField.get(NodeRef.of(table));
}
public Scope getAccessControlScope(Table node)
{
return tables.get(NodeRef.of(node)).getAccessControlScope();
}
public void setImplicitFromScope(QuerySpecification node, Scope scope)
{
implicitFromScopes.put(NodeRef.of(node), scope);
}
public Scope getImplicitFromScope(QuerySpecification node)
{
return implicitFromScopes.get(NodeRef.of(node));
}
public void addPredicateCoercions(Map, PredicateCoercions> coercions)
{
predicateCoercions.putAll(coercions);
}
public PredicateCoercions getPredicateCoercions(Expression expression)
{
return predicateCoercions.get(NodeRef.of(expression));
}
public void setTableExecuteHandle(TableExecuteHandle tableExecuteHandle)
{
requireNonNull(tableExecuteHandle, "tableExecuteHandle is null");
checkState(this.tableExecuteHandle.isEmpty(), "tableExecuteHandle already set");
this.tableExecuteHandle = Optional.of(tableExecuteHandle);
}
public Optional getTableExecuteHandle()
{
return tableExecuteHandle;
}
public void setTableFunctionAnalysis(TableFunctionInvocation node, TableFunctionInvocationAnalysis analysis)
{
tableFunctionAnalyses.put(NodeRef.of(node), analysis);
}
public TableFunctionInvocationAnalysis getTableFunctionAnalysis(TableFunctionInvocation node)
{
return tableFunctionAnalyses.get(NodeRef.of(node));
}
public void setRelationName(Relation relation, QualifiedName name)
{
relationNames.put(NodeRef.of(relation), name);
}
public QualifiedName getRelationName(Relation relation)
{
return relationNames.get(NodeRef.of(relation));
}
public void addAliased(Relation relation)
{
aliasedRelations.add(NodeRef.of(relation));
}
public boolean isAliased(Relation relation)
{
return aliasedRelations.contains(NodeRef.of(relation));
}
public void addPolymorphicTableFunction(TableFunctionInvocation invocation)
{
polymorphicTableFunctions.add(NodeRef.of(invocation));
}
public boolean isPolymorphicTableFunction(TableFunctionInvocation invocation)
{
return polymorphicTableFunctions.contains(NodeRef.of(invocation));
}
private boolean isInputTable(Table table)
{
return !(isUpdateTarget(table) || isInsertTarget(table));
}
private boolean isInsertTarget(Table table)
{
requireNonNull(table, "table is null");
return insert
.map(Insert::getTable)
.map(tableReference -> tableReference == table) // intentional comparison by reference
.orElse(FALSE);
}
@Immutable
public static final class SelectExpression
{
// expression refers to a select item, either to be returned directly, or unfolded by all-fields reference
// unfoldedExpressions applies to the latter case, and is a list of subscript expressions
// referencing each field of the row.
private final Expression expression;
private final Optional> unfoldedExpressions;
public SelectExpression(Expression expression, Optional> unfoldedExpressions)
{
this.expression = requireNonNull(expression, "expression is null");
this.unfoldedExpressions = requireNonNull(unfoldedExpressions);
}
public Expression getExpression()
{
return expression;
}
public Optional> getUnfoldedExpressions()
{
return unfoldedExpressions;
}
}
@Immutable
public static final class Create
{
private final Optional destination;
private final Optional metadata;
private final Optional layout;
private final boolean createTableAsSelectWithData;
private final boolean createTableAsSelectNoOp;
private final boolean replace;
public Create(
Optional destination,
Optional metadata,
Optional layout,
boolean createTableAsSelectWithData,
boolean createTableAsSelectNoOp,
boolean replace)
{
this.destination = requireNonNull(destination, "destination is null");
this.metadata = requireNonNull(metadata, "metadata is null");
this.layout = requireNonNull(layout, "layout is null");
this.createTableAsSelectWithData = createTableAsSelectWithData;
this.createTableAsSelectNoOp = createTableAsSelectNoOp;
this.replace = replace;
}
public Optional getDestination()
{
return destination;
}
public Optional getMetadata()
{
return metadata;
}
public Optional getLayout()
{
return layout;
}
public boolean isCreateTableAsSelectWithData()
{
return createTableAsSelectWithData;
}
public boolean isCreateTableAsSelectNoOp()
{
return createTableAsSelectNoOp;
}
public boolean isReplace()
{
return replace;
}
}
@Immutable
public static final class Insert
{
private final Table table;
private final TableHandle target;
private final List columns;
private final Optional newTableLayout;
public Insert(Table table, TableHandle target, List columns, Optional newTableLayout)
{
this.table = requireNonNull(table, "table is null");
this.target = requireNonNull(target, "target is null");
this.columns = requireNonNull(columns, "columns is null");
checkArgument(columns.size() > 0, "No columns given to insert");
this.newTableLayout = requireNonNull(newTableLayout, "newTableLayout is null");
}
public Table getTable()
{
return table;
}
public List getColumns()
{
return columns;
}
public TableHandle getTarget()
{
return target;
}
public Optional getNewTableLayout()
{
return newTableLayout;
}
}
@Immutable
public static final class RefreshMaterializedViewAnalysis
{
private final Table table;
private final TableHandle target;
private final Query query;
private final List columns;
public RefreshMaterializedViewAnalysis(Table table, TableHandle target, Query query, List columns)
{
this.table = requireNonNull(table, "table is null");
this.target = requireNonNull(target, "target is null");
this.query = query;
this.columns = requireNonNull(columns, "columns is null");
checkArgument(columns.size() > 0, "No columns given to refresh materialized view");
}
public Query getQuery()
{
return query;
}
public List getColumns()
{
return columns;
}
public TableHandle getTarget()
{
return target;
}
public Table getTable()
{
return table;
}
}
public static final class JoinUsingAnalysis
{
private final List leftJoinFields;
private final List rightJoinFields;
private final List otherLeftFields;
private final List otherRightFields;
JoinUsingAnalysis(List leftJoinFields, List rightJoinFields, List otherLeftFields, List otherRightFields)
{
this.leftJoinFields = ImmutableList.copyOf(leftJoinFields);
this.rightJoinFields = ImmutableList.copyOf(rightJoinFields);
this.otherLeftFields = ImmutableList.copyOf(otherLeftFields);
this.otherRightFields = ImmutableList.copyOf(otherRightFields);
checkArgument(leftJoinFields.size() == rightJoinFields.size(), "Expected join fields for left and right to have the same size");
}
public List getLeftJoinFields()
{
return leftJoinFields;
}
public List getRightJoinFields()
{
return rightJoinFields;
}
public List getOtherLeftFields()
{
return otherLeftFields;
}
public List getOtherRightFields()
{
return otherRightFields;
}
}
public static class GroupingSetAnalysis
{
private final List originalExpressions;
private final List>> cubes;
private final List>> rollups;
private final List>> ordinarySets;
private final List complexExpressions;
public GroupingSetAnalysis(
List originalExpressions,
List>> cubes,
List>> rollups,
List>> ordinarySets,
List complexExpressions)
{
this.originalExpressions = ImmutableList.copyOf(originalExpressions);
this.cubes = ImmutableList.copyOf(cubes);
this.rollups = ImmutableList.copyOf(rollups);
this.ordinarySets = ImmutableList.copyOf(ordinarySets);
this.complexExpressions = ImmutableList.copyOf(complexExpressions);
}
public List getOriginalExpressions()
{
return originalExpressions;
}
public List>> getCubes()
{
return cubes;
}
public List>> getRollups()
{
return rollups;
}
public List>> getOrdinarySets()
{
return ordinarySets;
}
public List getComplexExpressions()
{
return complexExpressions;
}
public Set getAllFields()
{
return Streams.concat(
cubes.stream()
.flatMap(Collection::stream)
.flatMap(Collection::stream),
rollups.stream()
.flatMap(Collection::stream)
.flatMap(Collection::stream),
ordinarySets.stream()
.flatMap(Collection::stream)
.flatMap(Collection::stream))
.collect(toImmutableSet());
}
}
public static class UnnestAnalysis
{
private final Map, List> mappings;
private final Optional ordinalityField;
public UnnestAnalysis(Map, List> mappings, Optional ordinalityField)
{
requireNonNull(mappings, "mappings is null");
this.mappings = mappings.entrySet().stream()
.collect(toImmutableMap(Map.Entry::getKey, entry -> ImmutableList.copyOf(entry.getValue())));
this.ordinalityField = requireNonNull(ordinalityField, "ordinalityField is null");
}
public Map, List> getMappings()
{
return mappings;
}
public Optional getOrdinalityField()
{
return ordinalityField;
}
}
public static class SubqueryAnalysis
{
private final List inPredicatesSubqueries = new ArrayList<>();
private final List subqueries = new ArrayList<>();
private final List existsSubqueries = new ArrayList<>();
private final List quantifiedComparisonSubqueries = new ArrayList<>();
public void addInPredicates(List expressions)
{
inPredicatesSubqueries.addAll(expressions);
}
public void addSubqueries(List expressions)
{
subqueries.addAll(expressions);
}
public void addExistsSubqueries(List expressions)
{
existsSubqueries.addAll(expressions);
}
public void addQuantifiedComparisons(List expressions)
{
quantifiedComparisonSubqueries.addAll(expressions);
}
public List getInPredicatesSubqueries()
{
return unmodifiableList(inPredicatesSubqueries);
}
public List getSubqueries()
{
return unmodifiableList(subqueries);
}
public List