blob: 9459ab3556e44822c4348aaff6fa461b3fec848b [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.DEFAULT;
import java.util.ArrayList;
import java.util.List;
import org.apache.impala.authorization.PrivilegeRequest;
import org.apache.impala.catalog.FeView;
import org.apache.impala.catalog.View;
import org.apache.impala.common.AnalysisException;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
/**
* Representation of the WITH clause that may appear before a query statement or insert
* statement. A WITH clause contains a list of named view definitions that may be
* referenced in the query statement that follows it.
*
* Scoping rules:
* A WITH-clause view is visible inside the query statement that it belongs to.
* This includes inline views and nested WITH clauses inside the query statement.
*
* Each WITH clause establishes a new analysis scope. A WITH-clause view definition
* may refer to views from the same WITH-clause appearing to its left, and to all
* WITH-clause views from outer scopes.
*
* References to WITH-clause views are resolved inside out, i.e., a match is found by
* first looking in the current scope and then in the enclosing scope(s).
*
* Views defined within the same WITH-clause may not use the same alias.
*/
public class WithClause extends StmtNode {
/////////////////////////////////////////
// BEGIN: Members that need to be reset()
private final List<View> views_;
// END: Members that need to be reset()
/////////////////////////////////////////
public WithClause(List<View> views) {
Preconditions.checkNotNull(views);
Preconditions.checkState(!views.isEmpty());
views_ = views;
}
/**
* Analyzes all views and registers them with the analyzer. Enforces scoping rules.
* All local views registered with the analyzer are have QueryStmts with resolved
* TableRefs to simplify the analysis of view references.
*/
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
// Create a new analyzer for the WITH clause with a new global state (IMPALA-1357)
// but a child of 'analyzer' so that the global state for 'analyzer' is not polluted
// during analysis of the WITH clause. withClauseAnalyzer is a child of 'analyzer' so
// that local views registered in parent blocks are visible here.
Analyzer withClauseAnalyzer = Analyzer.createWithNewGlobalState(analyzer);
withClauseAnalyzer.setHasWithClause();
if (analyzer.isExplain()) withClauseAnalyzer.setIsExplain();
for (View view: views_) {
Analyzer viewAnalyzer = new Analyzer(withClauseAnalyzer);
view.getQueryStmt().analyze(viewAnalyzer);
// Register this view so that the next view can reference it.
withClauseAnalyzer.registerLocalView(view);
}
// Register all local views with the analyzer.
for (FeView localView: withClauseAnalyzer.getLocalViews().values()) {
analyzer.registerLocalView(localView);
}
// Record audit events because the resolved table references won't generate any
// when a view is referenced.
analyzer.getAccessEvents().addAll(withClauseAnalyzer.getAccessEvents());
// Register all privilege requests made from the root analyzer.
for (PrivilegeRequest req: withClauseAnalyzer.getPrivilegeReqs()) {
analyzer.registerPrivReq(req);
}
}
/**
* C'tor for cloning.
*/
private WithClause(WithClause other) {
Preconditions.checkNotNull(other);
views_ = new ArrayList<>();
for (View view: other.views_) {
views_.add(new View(view.getName(), view.getQueryStmt().clone(),
view.getOriginalColLabels()));
}
}
public void reset() {
for (View view: views_) view.getQueryStmt().reset();
}
@Override
public WithClause clone() { return new WithClause(this); }
@Override
public final String toSql() {
return toSql(DEFAULT);
}
@Override
public String toSql(ToSqlOptions options) {
List<String> viewStrings = new ArrayList<>();
for (View view: views_) {
// Enclose the view alias and explicit labels in quotes if Hive cannot parse it
// without quotes. This is needed for view compatibility between Impala and Hive.
String aliasSql = ToSqlUtils.getIdentSql(view.getName());
if (view.getColLabels() != null) {
aliasSql += "(" + Joiner.on(", ").join(
ToSqlUtils.getIdentSqlList(view.getOriginalColLabels())) + ")";
}
viewStrings.add(aliasSql + " AS (" + view.getQueryStmt().toSql(options) + ")");
}
return "WITH " + Joiner.on(",").join(viewStrings);
}
public List<View> getViews() { return views_; }
}