blob: b6cf29129c946b44aac63773b0adcf94b8230acb [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.impala.analysis;
import static org.apache.impala.analysis.ToSqlOptions.REWRITTEN;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.impala.analysis.StmtMetadataLoader.StmtTableCache;
import org.apache.impala.authorization.AuthorizationChecker;
import org.apache.impala.authorization.AuthorizationContext;
import org.apache.impala.authorization.AuthorizationFactory;
import org.apache.impala.authorization.PrivilegeRequest;
import org.apache.impala.authorization.AuthorizationException;
import org.apache.impala.catalog.FeCatalog;
import org.apache.impala.catalog.Type;
import org.apache.impala.common.AnalysisException;
import org.apache.impala.common.ImpalaException;
import org.apache.impala.common.RuntimeEnv;
import org.apache.impala.rewrite.ExprRewriter;
import org.apache.impala.thrift.TAccessEvent;
import org.apache.impala.thrift.TClientRequest;
import org.apache.impala.thrift.TLineageGraph;
import org.apache.impala.thrift.TQueryCtx;
import org.apache.impala.thrift.TQueryOptions;
import org.apache.impala.util.EventSequence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
/**
* Wrapper class for parsing, analyzing and rewriting a SQL stmt.
*/
public class AnalysisContext {
private final static Logger LOG = LoggerFactory.getLogger(AnalysisContext.class);
private final TQueryCtx queryCtx_;
private final AuthorizationFactory authzFactory_;
private final EventSequence timeline_;
// Set in analyzeAndAuthorize().
private FeCatalog catalog_;
private AnalysisResult analysisResult_;
// Use Hive's scheme for auto-generating column labels. Only used for testing.
private boolean useHiveColLabels_;
public AnalysisContext(TQueryCtx queryCtx, AuthorizationFactory authzFactory,
EventSequence timeline) {
queryCtx_ = queryCtx;
authzFactory_ = authzFactory;
timeline_ = timeline;
}
public FeCatalog getCatalog() { return catalog_; }
public TQueryCtx getQueryCtx() { return queryCtx_; }
public TQueryOptions getQueryOptions() {
return queryCtx_.client_request.query_options;
}
public String getUser() { return queryCtx_.session.connected_user; }
public void setUseHiveColLabels(boolean b) {
Preconditions.checkState(RuntimeEnv.INSTANCE.isTestEnv());
useHiveColLabels_ = b;
}
static public class AnalysisResult {
private StatementBase stmt_;
private Analyzer analyzer_;
private boolean userHasProfileAccess_ = true;
public boolean isAlterTableStmt() { return stmt_ instanceof AlterTableStmt; }
public boolean isAlterViewStmt() { return stmt_ instanceof AlterViewStmt; }
public boolean isComputeStatsStmt() { return stmt_ instanceof ComputeStatsStmt; }
public boolean isQueryStmt() { return stmt_ instanceof QueryStmt; }
public boolean isSetOperationStmt() { return stmt_ instanceof SetOperationStmt; }
public boolean isInsertStmt() { return stmt_ instanceof InsertStmt; }
public boolean isOptimizeStmt() { return stmt_ instanceof OptimizeStmt; }
public boolean isDropDbStmt() { return stmt_ instanceof DropDbStmt; }
public boolean isDropTableOrViewStmt() {
return stmt_ instanceof DropTableOrViewStmt;
}
public boolean isDropFunctionStmt() { return stmt_ instanceof DropFunctionStmt; }
public boolean isDropDataSrcStmt() { return stmt_ instanceof DropDataSrcStmt; }
public boolean isDropStatsStmt() { return stmt_ instanceof DropStatsStmt; }
public boolean isCreateTableLikeStmt() {
return stmt_ instanceof CreateTableLikeStmt;
}
public boolean isCreateViewStmt() { return stmt_ instanceof CreateViewStmt; }
public boolean isCreateTableAsSelectStmt() {
return stmt_ instanceof CreateTableAsSelectStmt;
}
public boolean isCreateTableStmt() { return stmt_ instanceof CreateTableStmt; }
public boolean isCreateDbStmt() { return stmt_ instanceof CreateDbStmt; }
public boolean isCreateUdfStmt() { return stmt_ instanceof CreateUdfStmt; }
public boolean isCreateUdaStmt() { return stmt_ instanceof CreateUdaStmt; }
public boolean isCreateDataSrcStmt() { return stmt_ instanceof CreateDataSrcStmt; }
public boolean isLoadDataStmt() { return stmt_ instanceof LoadDataStmt; }
public boolean isUseStmt() { return stmt_ instanceof UseStmt; }
public boolean isSetStmt() { return stmt_ instanceof SetStmt; }
public boolean isShowTablesStmt() { return stmt_ instanceof ShowTablesStmt; }
public boolean isShowViewsStmt() { return stmt_ instanceof ShowViewsStmt; }
public boolean isDescribeHistoryStmt() {
return stmt_ instanceof DescribeHistoryStmt;
}
public boolean isShowDbsStmt() { return stmt_ instanceof ShowDbsStmt; }
public boolean isShowDataSrcsStmt() { return stmt_ instanceof ShowDataSrcsStmt; }
public boolean isShowStatsStmt() { return stmt_ instanceof ShowStatsStmt; }
public boolean isShowFunctionsStmt() { return stmt_ instanceof ShowFunctionsStmt; }
public boolean isShowCreateTableStmt() {
return stmt_ instanceof ShowCreateTableStmt;
}
public boolean isShowCreateFunctionStmt() {
return stmt_ instanceof ShowCreateFunctionStmt;
}
public boolean isShowFilesStmt() { return stmt_ instanceof ShowFilesStmt; }
public boolean isAdminFnStmt() { return stmt_ instanceof AdminFnStmt; }
public boolean isDescribeDbStmt() { return stmt_ instanceof DescribeDbStmt; }
public boolean isDescribeTableStmt() { return stmt_ instanceof DescribeTableStmt; }
public boolean isResetMetadataStmt() { return stmt_ instanceof ResetMetadataStmt; }
public boolean isExplainStmt() { return stmt_.isExplain(); }
public boolean isShowRolesStmt() { return stmt_ instanceof ShowRolesStmt; }
public boolean isShowGrantPrincipalStmt() {
return stmt_ instanceof ShowGrantPrincipalStmt;
}
public boolean isCreateDropRoleStmt() { return stmt_ instanceof CreateDropRoleStmt; }
public boolean isGrantRevokeRoleStmt() {
return stmt_ instanceof GrantRevokeRoleStmt;
}
public boolean isGrantRevokePrivStmt() {
return stmt_ instanceof GrantRevokePrivStmt;
}
public boolean isTruncateStmt() { return stmt_ instanceof TruncateStmt; }
public boolean isUpdateStmt() { return stmt_ instanceof UpdateStmt; }
public UpdateStmt getUpdateStmt() { return (UpdateStmt) stmt_; }
public boolean isDeleteStmt() { return stmt_ instanceof DeleteStmt; }
public DeleteStmt getDeleteStmt() { return (DeleteStmt) stmt_; }
public boolean isCommentOnStmt() { return stmt_ instanceof CommentOnStmt; }
public boolean isAlterDbStmt() { return stmt_ instanceof AlterDbStmt; }
public boolean isCatalogOp() {
return isUseStmt() || isViewMetadataStmt() || isDdlStmt();
}
public boolean isTestCaseStmt() { return stmt_ instanceof CopyTestCaseStmt; }
private boolean isDdlStmt() {
return isCreateTableLikeStmt() || isCreateTableStmt() ||
isCreateViewStmt() || isCreateDbStmt() || isDropDbStmt() ||
isDropTableOrViewStmt() || isResetMetadataStmt() || isAlterTableStmt() ||
isAlterViewStmt() || isComputeStatsStmt() || isCreateUdfStmt() ||
isCreateUdaStmt() || isDropFunctionStmt() || isCreateTableAsSelectStmt() ||
isCreateDataSrcStmt() || isDropDataSrcStmt() || isDropStatsStmt() ||
isCreateDropRoleStmt() || isGrantRevokeStmt() || isTruncateStmt() ||
isCommentOnStmt() || isAlterDbStmt();
}
private boolean isViewMetadataStmt() {
return isShowFilesStmt() || isShowTablesStmt() || isShowViewsStmt() ||
isShowDbsStmt() || isShowFunctionsStmt() || isShowRolesStmt() ||
isShowGrantPrincipalStmt() || isShowCreateTableStmt() ||
isShowDataSrcsStmt() || isShowStatsStmt() || isDescribeTableStmt() ||
isDescribeDbStmt() || isShowCreateFunctionStmt() || isDescribeHistoryStmt();
}
private boolean isGrantRevokeStmt() {
return isGrantRevokeRoleStmt() || isGrantRevokePrivStmt();
}
public boolean isDmlStmt() {
return isInsertStmt() || isUpdateStmt() || isDeleteStmt() || isOptimizeStmt();
}
/**
* Returns true for statements that may produce several privilege requests of
* hierarchical nature, e.g., table/column.
*/
public boolean isHierarchicalAuthStmt() {
return isQueryStmt() || isInsertStmt() || isUpdateStmt() || isDeleteStmt()
|| isCreateTableAsSelectStmt() || isCreateViewStmt() || isAlterViewStmt()
|| isOptimizeStmt() || isTestCaseStmt();
}
/**
* Returns true for statements that may produce a single column-level privilege
* request without a request at the table level.
* Example: USE functional; ALTER TABLE allcomplextypes.int_array_col [...];
* The path 'allcomplextypes.int_array_col' table ref path resolves to
* a column, so a column-level privilege request is registered.
*/
public boolean isSingleColumnPrivStmt() {
return isDescribeTableStmt() || isResetMetadataStmt() || isUseStmt()
|| isShowTablesStmt() || isShowViewsStmt() || isAlterTableStmt()
|| isShowFunctionsStmt();
}
public boolean isConvertTableToIcebergStmt() {
return stmt_ instanceof ConvertTableToIcebergStmt;
}
public AlterTableStmt getAlterTableStmt() {
Preconditions.checkState(isAlterTableStmt());
return (AlterTableStmt) stmt_;
}
public AlterViewStmt getAlterViewStmt() {
Preconditions.checkState(isAlterViewStmt());
return (AlterViewStmt) stmt_;
}
public ComputeStatsStmt getComputeStatsStmt() {
Preconditions.checkState(isComputeStatsStmt());
return (ComputeStatsStmt) stmt_;
}
public CreateTableLikeStmt getCreateTableLikeStmt() {
Preconditions.checkState(isCreateTableLikeStmt());
return (CreateTableLikeStmt) stmt_;
}
public CreateViewStmt getCreateViewStmt() {
Preconditions.checkState(isCreateViewStmt());
return (CreateViewStmt) stmt_;
}
public CreateTableAsSelectStmt getCreateTableAsSelectStmt() {
Preconditions.checkState(isCreateTableAsSelectStmt());
return (CreateTableAsSelectStmt) stmt_;
}
public CreateTableStmt getCreateTableStmt() {
Preconditions.checkState(isCreateTableStmt());
return (CreateTableStmt) stmt_;
}
public CreateDbStmt getCreateDbStmt() {
Preconditions.checkState(isCreateDbStmt());
return (CreateDbStmt) stmt_;
}
public CreateUdfStmt getCreateUdfStmt() {
Preconditions.checkState(isCreateUdfStmt());
return (CreateUdfStmt) stmt_;
}
public CreateUdaStmt getCreateUdaStmt() {
Preconditions.checkState(isCreateUdfStmt());
return (CreateUdaStmt) stmt_;
}
public DropDbStmt getDropDbStmt() {
Preconditions.checkState(isDropDbStmt());
return (DropDbStmt) stmt_;
}
public DropTableOrViewStmt getDropTableOrViewStmt() {
Preconditions.checkState(isDropTableOrViewStmt());
return (DropTableOrViewStmt) stmt_;
}
public TruncateStmt getTruncateStmt() {
Preconditions.checkState(isTruncateStmt());
return (TruncateStmt) stmt_;
}
public DropFunctionStmt getDropFunctionStmt() {
Preconditions.checkState(isDropFunctionStmt());
return (DropFunctionStmt) stmt_;
}
public LoadDataStmt getLoadDataStmt() {
Preconditions.checkState(isLoadDataStmt());
return (LoadDataStmt) stmt_;
}
public QueryStmt getQueryStmt() {
Preconditions.checkState(isQueryStmt());
return (QueryStmt) stmt_;
}
public OptimizeStmt getOptimizeStmt() {
Preconditions.checkState(isOptimizeStmt());
return (OptimizeStmt) stmt_;
}
public InsertStmt getInsertStmt() {
if (isCreateTableAsSelectStmt()) {
return getCreateTableAsSelectStmt().getInsertStmt();
} else {
Preconditions.checkState(isInsertStmt());
return (InsertStmt) stmt_;
}
}
public UseStmt getUseStmt() {
Preconditions.checkState(isUseStmt());
return (UseStmt) stmt_;
}
public SetStmt getSetStmt() {
Preconditions.checkState(isSetStmt());
return (SetStmt) stmt_;
}
public ShowTablesStmt getShowTablesStmt() {
Preconditions.checkState(isShowTablesStmt());
return (ShowTablesStmt) stmt_;
}
public ShowViewsStmt getShowViewsStmt() {
Preconditions.checkState(isShowViewsStmt());
return (ShowViewsStmt) stmt_;
}
public ShowDbsStmt getShowDbsStmt() {
Preconditions.checkState(isShowDbsStmt());
return (ShowDbsStmt) stmt_;
}
public ShowDataSrcsStmt getShowDataSrcsStmt() {
Preconditions.checkState(isShowDataSrcsStmt());
return (ShowDataSrcsStmt) stmt_;
}
public ShowStatsStmt getShowStatsStmt() {
Preconditions.checkState(isShowStatsStmt());
return (ShowStatsStmt) stmt_;
}
public ShowFunctionsStmt getShowFunctionsStmt() {
Preconditions.checkState(isShowFunctionsStmt());
return (ShowFunctionsStmt) stmt_;
}
public ShowFilesStmt getShowFilesStmt() {
Preconditions.checkState(isShowFilesStmt());
return (ShowFilesStmt) stmt_;
}
public DescribeHistoryStmt getDescribeHistoryStmt() {
Preconditions.checkState(isDescribeHistoryStmt());
return (DescribeHistoryStmt) stmt_;
}
public DescribeDbStmt getDescribeDbStmt() {
Preconditions.checkState(isDescribeDbStmt());
return (DescribeDbStmt) stmt_;
}
public DescribeTableStmt getDescribeTableStmt() {
Preconditions.checkState(isDescribeTableStmt());
return (DescribeTableStmt) stmt_;
}
public ShowCreateTableStmt getShowCreateTableStmt() {
Preconditions.checkState(isShowCreateTableStmt());
return (ShowCreateTableStmt) stmt_;
}
public ShowCreateFunctionStmt getShowCreateFunctionStmt() {
Preconditions.checkState(isShowCreateFunctionStmt());
return (ShowCreateFunctionStmt) stmt_;
}
public CommentOnStmt getCommentOnStmt() {
Preconditions.checkState(isCommentOnStmt());
return (CommentOnStmt) stmt_;
}
public AlterDbStmt getAlterDbStmt() {
Preconditions.checkState(isAlterDbStmt());
return (AlterDbStmt) stmt_;
}
public AdminFnStmt getAdminFnStmt() {
Preconditions.checkState(isAdminFnStmt());
return (AdminFnStmt) stmt_;
}
public ConvertTableToIcebergStmt getConvertTableToIcebergStmt() {
Preconditions.checkState(isConvertTableToIcebergStmt());
return (ConvertTableToIcebergStmt) stmt_;
}
public StatementBase getStmt() { return stmt_; }
public Analyzer getAnalyzer() { return analyzer_; }
public Set<TAccessEvent> getAccessEvents() { return analyzer_.getAccessEvents(); }
public boolean canRewriteStatement() {
return !isCreateViewStmt() && !isAlterViewStmt() && !isShowCreateTableStmt();
}
public boolean requiresSubqueryRewrite() {
return canRewriteStatement() && analyzer_.containsSubquery();
}
public boolean requiresAcidComplexScanRewrite() {
return canRewriteStatement() && analyzer_.hasTopLevelAcidCollectionTableRef();
}
public boolean requiresZippingUnnestRewrite() {
return canRewriteStatement() && isZippingUnnestInSelectList(stmt_);
}
public boolean requiresExprRewrite() {
return isQueryStmt() || isInsertStmt() || isCreateTableAsSelectStmt()
|| isUpdateStmt() || isDeleteStmt() || isOptimizeStmt();
}
public boolean requiresSetOperationRewrite() {
return analyzer_.containsSetOperation() && !isCreateViewStmt() && !isAlterViewStmt()
&& !isShowCreateTableStmt();
}
public TLineageGraph getThriftLineageGraph() {
return analyzer_.getThriftSerializedLineageGraph();
}
public void setUserHasProfileAccess(boolean value) { userHasProfileAccess_ = value; }
public boolean userHasProfileAccess() { return userHasProfileAccess_; }
private boolean isZippingUnnestInSelectList(StatementBase stmt) {
if (!(stmt instanceof SelectStmt)) return false;
if (!stmt.analyzer_.getTableRefsFromUnnestExpr().isEmpty()) return true;
SelectStmt selectStmt = (SelectStmt)stmt;
for (TableRef tblRef : selectStmt.fromClause_.getTableRefs()) {
if (tblRef instanceof InlineViewRef &&
isZippingUnnestInSelectList(((InlineViewRef)tblRef).getViewStmt())) {
return true;
}
}
return false;
}
}
public Analyzer createAnalyzer(StmtTableCache stmtTableCache) {
return createAnalyzer(stmtTableCache, null);
}
public Analyzer createAnalyzer(StmtTableCache stmtTableCache,
AuthorizationContext authzCtx) {
Analyzer result = new Analyzer(stmtTableCache, queryCtx_, authzFactory_, authzCtx);
result.setUseHiveColLabels(useHiveColLabels_);
return result;
}
public AnalysisResult analyzeAndAuthorize(StatementBase stmt,
StmtTableCache stmtTableCache, AuthorizationChecker authzChecker)
throws ImpalaException {
return analyzeAndAuthorize(
stmt, stmtTableCache, authzChecker, false /*disableAuthorization*/);
}
/**
* Analyzes and authorizes the given statement using the provided table cache and
* authorization checker.
* AuthorizationExceptions take precedence over AnalysisExceptions so as not to
* reveal the existence/absence of objects the user is not authorized to see.
*/
public AnalysisResult analyzeAndAuthorize(StatementBase stmt,
StmtTableCache stmtTableCache, AuthorizationChecker authzChecker,
boolean disableAuthorization) throws ImpalaException {
// TODO: Clean up the creation/setting of the analysis result.
analysisResult_ = new AnalysisResult();
analysisResult_.stmt_ = stmt;
catalog_ = stmtTableCache.catalog;
// Analyze statement and record exception.
AnalysisException analysisException = null;
TClientRequest clientRequest = queryCtx_.getClient_request();
AuthorizationContext authzCtx = authzChecker.createAuthorizationContext(true,
clientRequest.isSetRedacted_stmt() ?
clientRequest.getRedacted_stmt() : clientRequest.getStmt(),
queryCtx_.getSession(), Optional.of(timeline_));
Preconditions.checkState(authzCtx != null);
try {
analyze(stmtTableCache, authzCtx);
} catch (AnalysisException e) {
analysisException = e;
} finally {
authzChecker.postAnalyze(authzCtx);
}
// A statement that returns at most one row does not need to spool query results.
if (analysisException == null && analysisResult_.stmt_ instanceof SelectStmt &&
((SelectStmt)analysisResult_.stmt_).returnsAtMostOneRow()) {
clientRequest.query_options.setSpool_query_results(false);
if (LOG.isTraceEnabled()) {
LOG.trace("Result spooling is disabled due to the statement returning at most "
+ "one row.");
}
}
long durationMs = timeline_.markEvent("Analysis finished") / 1000000;
LOG.info("Analysis took {} ms", durationMs);
// Authorize statement and record exception. Authorization relies on information
// collected during analysis.
AuthorizationException authException = null;
if (!disableAuthorization) {
try {
if (analysisResult_.getAnalyzer().encounteredMVAuthException()) {
throw new AuthorizationException(
analysisResult_.getAnalyzer().getMVAuthExceptionMsg());
}
authzChecker.authorize(authzCtx, analysisResult_, catalog_);
} catch (AuthorizationException e) {
authException = e;
} finally {
authzChecker.postAuthorize(authzCtx, authException == null,
analysisException == null);
}
}
// AuthorizationExceptions take precedence over AnalysisExceptions so as not
// to reveal the existence/absence of objects the user is not authorized to see.
if (authException != null) throw authException;
if (analysisException != null) throw analysisException;
return analysisResult_;
}
/**
* Analyzes the statement set in 'analysisResult_' with a new Analyzer based on the
* given loaded tables. Performs expr and subquery rewrites which require re-analyzing
* the transformed statement.
*/
private void analyze(StmtTableCache stmtTableCache, AuthorizationContext authzCtx)
throws AnalysisException {
Preconditions.checkNotNull(analysisResult_);
Preconditions.checkNotNull(analysisResult_.stmt_);
analysisResult_.analyzer_ = createAnalyzer(stmtTableCache, authzCtx);
analysisResult_.stmt_.analyze(analysisResult_.analyzer_);
// Enforce the statement expression limit at the end of analysis so that there is an
// accurate count of the total number of expressions. The first analyze() call is not
// very expensive (~seconds) even for large statements. The limit on the total length
// of the SQL statement (max_statement_length_bytes) provides an upper bound.
// It is important to enforce this before expression rewrites, because rewrites are
// expensive with large expression trees. For example, a SQL that takes a few seconds
// to analyze the first time may take 10 minutes for rewrites.
analysisResult_.analyzer_.checkStmtExprLimit();
// The rewrites should have no user-visible effect on query results, including types
// and labels. Remember the original result types and column labels to restore them
// after the rewritten stmt has been reset() and re-analyzed. For a CTAS statement,
// the types represent column types of the table that will be created, including the
// partition columns, if any.
List<Type> origResultTypes = new ArrayList<>();
for (Expr e : analysisResult_.stmt_.getResultExprs()) {
origResultTypes.add(e.getType());
}
List<String> origColLabels =
Lists.newArrayList(analysisResult_.stmt_.getColLabels());
// Apply column/row masking, expr, setop, and subquery rewrites.
boolean shouldReAnalyze = false;
if (authzFactory_.getAuthorizationConfig().isEnabled()) {
shouldReAnalyze = analysisResult_.stmt_.resolveTableMask(analysisResult_.analyzer_);
// If any catalog table/view is replaced by table masking views, we need to
// resolve them. Also re-analyze the SlotRefs to reference the output exprs of
// the table masking views.
if (shouldReAnalyze) {
// To be consistent with Hive, privilege requests introduced by the masking views
// should also be collected (IMPALA-10728).
reAnalyze(stmtTableCache, authzCtx, origResultTypes, origColLabels,
/*collectPrivileges*/ true);
}
shouldReAnalyze = false;
}
ExprRewriter rewriter = analysisResult_.analyzer_.getExprRewriter();
if (analysisResult_.requiresExprRewrite()) {
rewriter.reset();
analysisResult_.stmt_.rewriteExprs(rewriter);
shouldReAnalyze = rewriter.changed();
}
if (analysisResult_.requiresSubqueryRewrite()) {
new StmtRewriter.SubqueryRewriter().rewrite(analysisResult_);
shouldReAnalyze = true;
}
if (analysisResult_.requiresSetOperationRewrite()) {
new StmtRewriter().rewrite(analysisResult_);
shouldReAnalyze = true;
}
if (analysisResult_.requiresAcidComplexScanRewrite()) {
new StmtRewriter.AcidRewriter().rewrite(analysisResult_);
shouldReAnalyze = true;
}
if (analysisResult_.requiresZippingUnnestRewrite()) {
new StmtRewriter.ZippingUnnestRewriter().rewrite(analysisResult_);
shouldReAnalyze = true;
}
if (!shouldReAnalyze) return;
// For SetOperationStmt we must replace the query statement with the rewritten version
// before re-analysis and set the explain flag of the rewritten version if the
// original is explain statement.
if (analysisResult_.requiresSetOperationRewrite()) {
if (analysisResult_.isSetOperationStmt()) {
if (((SetOperationStmt) analysisResult_.getStmt()).hasRewrittenStmt()) {
boolean isExplain = analysisResult_.isExplainStmt();
analysisResult_.stmt_ =
((SetOperationStmt) analysisResult_.getStmt()).getRewrittenStmt();
if (isExplain) analysisResult_.stmt_.setIsExplain();
}
}
}
reAnalyze(stmtTableCache, authzCtx, origResultTypes, origColLabels,
/*collectPrivileges*/ false);
Preconditions.checkState(!analysisResult_.requiresSubqueryRewrite());
}
private void reAnalyze(StmtTableCache stmtTableCache, AuthorizationContext authzCtx,
List<Type> origResultTypes, List<String> origColLabels, boolean collectPrivileges)
throws AnalysisException {
boolean isExplain = analysisResult_.isExplainStmt();
// Some expressions, such as function calls with constant arguments, can get
// folded into literals. Since literals do not require privilege requests, we
// must save the original privileges in order to not lose them during
// re-analysis.
ImmutableList<PrivilegeRequest> origPrivReqs =
analysisResult_.analyzer_.getPrivilegeReqs();
// Re-analyze the stmt with a new analyzer.
analysisResult_.analyzer_ = createAnalyzer(stmtTableCache, authzCtx);
// Restore privilege requests found during the previous pass
for (PrivilegeRequest req : origPrivReqs) {
analysisResult_.analyzer_.registerPrivReq(req);
}
// Only collect privilege requests in need.
analysisResult_.analyzer_.setEnablePrivChecks(collectPrivileges);
analysisResult_.stmt_.reset();
try {
analysisResult_.stmt_.analyze(analysisResult_.analyzer_);
analysisResult_.analyzer_.setEnablePrivChecks(true); // Always restore
} catch (AnalysisException e) {
logRewriteErrorNoThrow(analysisResult_.stmt_, e);
throw new AnalysisException("An error occurred after query rewrite: " +
e.getMessage(), e);
}
// Restore the original result types and column labels.
analysisResult_.stmt_.castResultExprs(origResultTypes);
analysisResult_.stmt_.setColLabels(origColLabels);
if (LOG.isTraceEnabled()) {
LOG.trace("Rewritten SQL: " + analysisResult_.stmt_.toSql(REWRITTEN));
}
if (isExplain) analysisResult_.stmt_.setIsExplain();
}
private void logRewriteErrorNoThrow(StatementBase stmt,
AnalysisException analysisException) {
try {
LOG.error(String.format("Error analyzing the rewritten query.\n" +
"Original SQL: %s\nRewritten SQL: %s", stmt.toSql(),
stmt.toSql(REWRITTEN)), analysisException);
} catch (Exception e) {
LOG.error("An exception occurred during printing out " +
"the rewritten SQL statement.", e);
}
}
public Analyzer getAnalyzer() { return analysisResult_.getAnalyzer(); }
public EventSequence getTimeline() { return timeline_; }
// This should only be called after analyzeAndAuthorize().
public AnalysisResult getAnalysisResult() {
Preconditions.checkNotNull(analysisResult_);
Preconditions.checkNotNull(analysisResult_.stmt_);
return analysisResult_;
}
}