http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/ModifyStmt.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/ModifyStmt.java b/fe/src/main/java/com/cloudera/impala/analysis/ModifyStmt.java deleted file mode 100644 index 66f97f5..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/ModifyStmt.java +++ /dev/null @@ -1,292 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import com.cloudera.impala.authorization.Privilege; -import com.cloudera.impala.authorization.PrivilegeRequestBuilder; -import com.cloudera.impala.catalog.Column; -import com.cloudera.impala.catalog.KuduTable; -import com.cloudera.impala.catalog.Table; -import com.cloudera.impala.common.AnalysisException; -import com.cloudera.impala.common.Pair; -import com.cloudera.impala.planner.DataSink; -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import org.slf4j.LoggerFactory; - -import static java.lang.String.format; - -/** - * Abstract super class for statements that modify existing data like - * UPDATE and DELETE. - * - * The ModifyStmt has four major parts: - * - targetTablePath (not null) - * - fromClause (not null) - * - assignmentExprs (not null, can be empty) - * - wherePredicate (nullable) - * - * In the analysis phase, a SelectStmt is created with the result expressions set to - * match the right-hand side of the assignments in addition to projecting the key columns - * of the underlying table. During query execution, the plan that - * is generated from this SelectStmt produces all rows that need to be modified. - * - * Currently, only Kudu tables can be modified. - */ -public abstract class ModifyStmt extends StatementBase { - - private final static org.slf4j.Logger LOG = LoggerFactory.getLogger(ModifyStmt.class); - - // List of explicitly mentioned assignment expressions in the UPDATE's SET clause - protected final List<Pair<SlotRef, Expr>> assignments_; - - // Optional WHERE clause of the statement - protected final Expr wherePredicate_; - - // Path identifying the target table. - protected final List<String> targetTablePath_; - - // TableRef identifying the target table, set during analysis. - protected TableRef targetTableRef_; - - protected FromClause fromClause_; - - // Result of the analysis of the internal SelectStmt that produces the rows that - // will be modified. - protected SelectStmt sourceStmt_; - - // Target Kudu table. Since currently only Kudu tables are supported, we use a - // concrete table class. Result of analysis. - protected KuduTable table_; - - // END: Members that need to be reset() - ///////////////////////////////////////// - - // Position mapping of output expressions of the sourceStmt_ to column indices in the - // target table. The i'th position in this list maps to the referencedColumns_[i]'th - // position in the target table. Set in createSourceStmt() during analysis. - protected ArrayList<Integer> referencedColumns_; - - // On tables with a primary key, ignore key not found errors. - protected final boolean ignoreNotFound_; - - public ModifyStmt(List<String> targetTablePath, FromClause fromClause, - List<Pair<SlotRef, Expr>> assignmentExprs, - Expr wherePredicate, boolean ignoreNotFound) { - targetTablePath_ = Preconditions.checkNotNull(targetTablePath); - fromClause_ = Preconditions.checkNotNull(fromClause); - assignments_ = Preconditions.checkNotNull(assignmentExprs); - wherePredicate_ = wherePredicate; - ignoreNotFound_ = ignoreNotFound; - } - - /** - * The analysis of the ModifyStmt proceeds as follows: First, the FROM clause is - * analyzed and the targetTablePath is verified to be a valid alias into the FROM - * clause. When the target table is identified, the assignment expressions are - * validated and as a last step the internal SelectStmt is produced and analyzed. - * Potential query rewrites for the select statement are implemented here and are not - * triggered externally by the statement rewriter. - */ - @Override - public void analyze(Analyzer analyzer) throws AnalysisException { - super.analyze(analyzer); - fromClause_.analyze(analyzer); - - List<Path> candidates = analyzer.getTupleDescPaths(targetTablePath_); - if (candidates.isEmpty()) { - throw new AnalysisException(format("'%s' is not a valid table alias or reference.", - Joiner.on(".").join(targetTablePath_))); - } - - Preconditions.checkState(candidates.size() == 1); - Path path = candidates.get(0); - path.resolve(); - - if (path.destTupleDesc() == null) { - throw new AnalysisException(format( - "'%s' is not a table alias. Using the FROM clause requires the target table " + - "to be a table alias.", - Joiner.on(".").join(targetTablePath_))); - } - - targetTableRef_ = analyzer.getTableRef(path.getRootDesc().getId()); - if (targetTableRef_ instanceof InlineViewRef) { - throw new AnalysisException(format("Cannot modify view: '%s'", - targetTableRef_.toSql())); - } - - Preconditions.checkNotNull(targetTableRef_); - Table dstTbl = targetTableRef_.getTable(); - // Only Kudu tables can be updated - if (!(dstTbl instanceof KuduTable)) { - throw new AnalysisException( - format("Impala does not support modifying a non-Kudu table: %s", - dstTbl.getFullName())); - } - table_ = (KuduTable) dstTbl; - - // Make sure that the user is allowed to modify the target table, since no - // UPDATE / DELETE privilege exists, we reuse the INSERT one. - analyzer.registerPrivReq(new PrivilegeRequestBuilder() - .onTable(table_.getDb().getName(), table_.getName()) - .allOf(Privilege.INSERT).toRequest()); - - // Validates the assignments_ and creates the sourceStmt_. - if (sourceStmt_ == null) createSourceStmt(analyzer); - sourceStmt_.analyze(analyzer); - } - - @Override - public void reset() { - super.reset(); - fromClause_.reset(); - if (sourceStmt_ != null) sourceStmt_.reset(); - table_ = null; - } - - /** - * Builds and validates the sourceStmt_. The select list of the sourceStmt_ contains - * first the SlotRefs for the key Columns, followed by the expressions representing the - * assignments. This method sets the member variables for the sourceStmt_ and the - * referencedColumns_. - * - * This is only run once, on the first analysis. Following analysis will reset() and - * reuse previously created statements. - */ - private void createSourceStmt(Analyzer analyzer) - throws AnalysisException { - // Builds the select list and column position mapping for the target table. - ArrayList<SelectListItem> selectList = Lists.newArrayList(); - referencedColumns_ = Lists.newArrayList(); - buildAndValidateAssignmentExprs(analyzer, selectList, referencedColumns_); - - // Analyze the generated select statement. - sourceStmt_ = new SelectStmt(new SelectList(selectList), fromClause_, wherePredicate_, - null, null, null, null); - - // cast result expressions to the correct type of the referenced slot of the - // target table - int keyColumnsOffset = table_.getKuduKeyColumnNames().size(); - for (int i = keyColumnsOffset; i < sourceStmt_.resultExprs_.size(); ++i) { - sourceStmt_.resultExprs_.set(i, sourceStmt_.resultExprs_.get(i).castTo( - assignments_.get(i - keyColumnsOffset).first.getType())); - } - } - - /** - * Validates the list of value assignments that should be used to modify the target - * table. It verifies that only those columns are referenced that belong to the target - * table, no key columns are modified, and that a single column is not modified multiple - * times. Analyzes the Exprs and SlotRefs of assignments_ and writes a list of - * SelectListItems to the out parameter selectList that is used to build the select list - * for sourceStmt_. A list of integers indicating the column position of an entry in the - * select list in the target table is written to the out parameter referencedColumns. - * - * In addition to the expressions that are generated for each assignment, the - * expression list contains an expression for each key column. The key columns - * are always prepended to the list of expression representing the assignments. - */ - private void buildAndValidateAssignmentExprs(Analyzer analyzer, - ArrayList<SelectListItem> selectList, ArrayList<Integer> referencedColumns) - throws AnalysisException { - // The order of the referenced columns equals the order of the result expressions - HashSet<SlotId> uniqueSlots = Sets.newHashSet(); - HashSet<SlotId> keySlots = Sets.newHashSet(); - - // Mapping from column name to index - ArrayList<Column> cols = table_.getColumnsInHiveOrder(); - HashMap<String, Integer> colIndexMap = Maps.newHashMap(); - for (int i = 0; i < cols.size(); i++) { - colIndexMap.put(cols.get(i).getName(), i); - } - - // Add the key columns as slot refs - for (String k : table_.getKuduKeyColumnNames()) { - ArrayList<String> path = Path.createRawPath(targetTableRef_.getUniqueAlias(), k); - SlotRef ref = new SlotRef(path); - ref.analyze(analyzer); - selectList.add(new SelectListItem(ref, null)); - uniqueSlots.add(ref.getSlotId()); - keySlots.add(ref.getSlotId()); - referencedColumns.add(colIndexMap.get(k)); - } - - // Assignments are only used in the context of updates. - for (Pair<SlotRef, Expr> valueAssignment : assignments_) { - Expr rhsExpr = valueAssignment.second; - rhsExpr.analyze(analyzer); - - SlotRef lhsSlotRef = valueAssignment.first; - lhsSlotRef.analyze(analyzer); - - // Correct target table - if (!lhsSlotRef.isBoundByTupleIds(targetTableRef_.getId().asList())) { - throw new AnalysisException( - format("Left-hand side column '%s' in assignment expression '%s=%s' does not " - + "belong to target table '%s'", lhsSlotRef.toSql(), lhsSlotRef.toSql(), - rhsExpr.toSql(), targetTableRef_.getDesc().getTable().getFullName())); - } - - // No subqueries for rhs expression - if (rhsExpr.contains(Subquery.class)) { - throw new AnalysisException( - format("Subqueries are not supported as update expressions for column '%s'", - lhsSlotRef.toSql())); - } - - Column c = lhsSlotRef.getResolvedPath().destColumn(); - // TODO(Kudu) Add test for this code-path when Kudu supports nested types - if (c == null) { - throw new AnalysisException( - format("Left-hand side in assignment expression '%s=%s' must be a column " + - "reference", lhsSlotRef.toSql(), rhsExpr.toSql())); - } - - if (keySlots.contains(lhsSlotRef.getSlotId())) { - throw new AnalysisException(format("Key column '%s' cannot be updated.", - lhsSlotRef.toSql())); - } - - if (uniqueSlots.contains(lhsSlotRef.getSlotId())) { - throw new AnalysisException( - format("Duplicate value assignment to column: '%s'", lhsSlotRef.toSql())); - } - - rhsExpr = checkTypeCompatibility( - targetTableRef_.getDesc().getTable().getFullName(), c, rhsExpr); - uniqueSlots.add(lhsSlotRef.getSlotId()); - selectList.add(new SelectListItem(rhsExpr, null)); - referencedColumns.add(colIndexMap.get(c.getName())); - } - } - - public QueryStmt getQueryStmt() { return sourceStmt_; } - public abstract DataSink createDataSink(); - public abstract String toSql(); - - -}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/NullLiteral.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/NullLiteral.java b/fe/src/main/java/com/cloudera/impala/analysis/NullLiteral.java deleted file mode 100644 index 212d601..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/NullLiteral.java +++ /dev/null @@ -1,90 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import com.cloudera.impala.catalog.Type; -import com.cloudera.impala.thrift.TExprNode; -import com.cloudera.impala.thrift.TExprNodeType; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; - -public class NullLiteral extends LiteralExpr { - - public NullLiteral() { - type_ = Type.NULL; - evalCost_ = LITERAL_COST; - } - - /** - * Copy c'tor used in clone(). - */ - protected NullLiteral(NullLiteral other) { - super(other); - } - - /** - * Returns an analyzed NullLiteral of the specified type. - */ - public static NullLiteral create(Type type) { - NullLiteral l = new NullLiteral(); - l.analyzeNoThrow(null); - l.uncheckedCastTo(type); - return l; - } - - @Override - public boolean equals(Object obj) { - if (!super.equals(obj)) return false; - return obj instanceof NullLiteral; - } - - @Override - public int hashCode() { return 0; } - - @Override - public String toSqlImpl() { return getStringValue(); } - - @Override - public String debugString() { - return Objects.toStringHelper(this).addValue(super.debugString()).toString(); - } - - @Override - public String getStringValue() { return "NULL"; } - - @Override - protected Expr uncheckedCastTo(Type targetType) { - Preconditions.checkState(targetType.isValid()); - type_ = targetType; - return this; - } - - @Override - protected void toThrift(TExprNode msg) { - msg.node_type = TExprNodeType.NULL_LITERAL; - } - - @Override - public Expr clone() { return new NullLiteral(this); } - - @Override - protected void resetAnalysisState() { - super.resetAnalysisState(); - type_ = Type.NULL; - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/NumericLiteral.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/NumericLiteral.java b/fe/src/main/java/com/cloudera/impala/analysis/NumericLiteral.java deleted file mode 100644 index f3bc9da..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/NumericLiteral.java +++ /dev/null @@ -1,317 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.math.BigDecimal; -import java.math.BigInteger; - -import com.cloudera.impala.catalog.ScalarType; -import com.cloudera.impala.catalog.Type; -import com.cloudera.impala.common.AnalysisException; -import com.cloudera.impala.common.NotImplementedException; -import com.cloudera.impala.thrift.TDecimalLiteral; -import com.cloudera.impala.thrift.TExprNode; -import com.cloudera.impala.thrift.TExprNodeType; -import com.cloudera.impala.thrift.TFloatLiteral; -import com.cloudera.impala.thrift.TIntLiteral; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; - -/** - * Literal for all numeric values, including integer, floating-point and decimal types. - * Analysis of this expr determines the smallest type that can hold this value. - */ -public class NumericLiteral extends LiteralExpr { - // Use the java BigDecimal (arbitrary scale/precision) to represent the value. - // This object has notions of precision and scale but they do *not* match what - // we need. BigDecimal's precision is similar to significant figures and scale - // is the exponent. - // ".1" could be represented with an unscaled value = 1 and scale = 1 or - // unscaled value = 100 and scale = 3. Manipulating the value_ (e.g. multiplying - // it by 10) does not unnecessarily change the unscaled value. Special care - // needs to be taken when converting between the big decimals unscaled value - // and ours. (See getUnscaledValue()). - private BigDecimal value_; - - // If true, this literal has been explicitly cast to a type and should not - // be analyzed (which infers the type from value_). - private boolean explicitlyCast_; - - public NumericLiteral(BigDecimal value) { - init(value); - } - - public NumericLiteral(String value, Type t) throws AnalysisException { - BigDecimal val = null; - try { - val = new BigDecimal(value); - } catch (NumberFormatException e) { - throw new AnalysisException("invalid numeric literal: " + value, e); - } - init(val); - this.analyze(null); - if (type_.isDecimal() && t.isDecimal()) { - // Verify that the input decimal value is consistent with the specified - // column type. - ScalarType scalarType = (ScalarType) t; - if (!scalarType.isSupertypeOf((ScalarType) type_)) { - StringBuilder errMsg = new StringBuilder(); - errMsg.append("invalid ").append(t); - errMsg.append(" value: " + value); - throw new AnalysisException(errMsg.toString()); - } - } - if (t.isFloatingPointType()) explicitlyCastToFloat(t); - } - - /** - * The versions of the ctor that take types assume the type is correct - * and the NumericLiteral is created as analyzed with that type. The specified - * type is preserved across substitutions and re-analysis. - */ - public NumericLiteral(BigInteger value, Type type) { - isAnalyzed_ = true; - value_ = new BigDecimal(value); - type_ = type; - evalCost_ = LITERAL_COST; - explicitlyCast_ = true; - } - - public NumericLiteral(BigDecimal value, Type type) { - isAnalyzed_ = true; - value_ = value; - type_ = type; - evalCost_ = LITERAL_COST; - explicitlyCast_ = true; - } - - /** - * Copy c'tor used in clone(). - */ - protected NumericLiteral(NumericLiteral other) { - super(other); - value_ = other.value_; - explicitlyCast_ = other.explicitlyCast_; - } - - @Override - public String debugString() { - return Objects.toStringHelper(this) - .add("value", value_) - .add("type", type_) - .toString(); - } - - @Override - public boolean equals(Object obj) { - if (!super.equals(obj)) return false; - return ((NumericLiteral) obj).value_.equals(value_); - } - - @Override - public int hashCode() { return value_.hashCode(); } - - @Override - public String toSqlImpl() { return getStringValue(); } - @Override - public String getStringValue() { return value_.toString(); } - public double getDoubleValue() { return value_.doubleValue(); } - public long getLongValue() { return value_.longValue(); } - public long getIntValue() { return value_.intValue(); } - - @Override - protected void toThrift(TExprNode msg) { - switch (type_.getPrimitiveType()) { - case TINYINT: - case SMALLINT: - case INT: - case BIGINT: - msg.node_type = TExprNodeType.INT_LITERAL; - msg.int_literal = new TIntLiteral(value_.longValue()); - break; - case FLOAT: - case DOUBLE: - msg.node_type = TExprNodeType.FLOAT_LITERAL; - msg.float_literal = new TFloatLiteral(value_.doubleValue()); - break; - case DECIMAL: - msg.node_type = TExprNodeType.DECIMAL_LITERAL; - TDecimalLiteral literal = new TDecimalLiteral(); - literal.setValue(getUnscaledValue().toByteArray()); - msg.decimal_literal = literal; - break; - default: - Preconditions.checkState(false); - } - } - - public BigDecimal getValue() { return value_; } - - @Override - public void analyze(Analyzer analyzer) throws AnalysisException { - if (isAnalyzed_) return; - super.analyze(analyzer); - if (!explicitlyCast_) { - // Compute the precision and scale from the BigDecimal. - type_ = TypesUtil.computeDecimalType(value_); - if (type_ == null) { - Double d = new Double(value_.doubleValue()); - if (d.isInfinite()) { - throw new AnalysisException("Numeric literal '" + toSql() + - "' exceeds maximum range of doubles."); - } else if (d.doubleValue() == 0 && value_ != BigDecimal.ZERO) { - throw new AnalysisException("Numeric literal '" + toSql() + - "' underflows minimum resolution of doubles."); - } - - // Literal could not be stored in any of the supported decimal precisions and - // scale. Store it as a float/double instead. - float fvalue; - fvalue = value_.floatValue(); - if (fvalue == value_.doubleValue()) { - type_ = Type.FLOAT; - } else { - type_ = Type.DOUBLE; - } - } else { - // Check for integer types. - Preconditions.checkState(type_.isScalarType()); - ScalarType scalarType = (ScalarType) type_; - if (scalarType.decimalScale() == 0) { - if (value_.compareTo(BigDecimal.valueOf(Byte.MAX_VALUE)) <= 0 && - value_.compareTo(BigDecimal.valueOf(Byte.MIN_VALUE)) >= 0) { - type_ = Type.TINYINT; - } else if (value_.compareTo(BigDecimal.valueOf(Short.MAX_VALUE)) <= 0 && - value_.compareTo(BigDecimal.valueOf(Short.MIN_VALUE)) >= 0) { - type_ = Type.SMALLINT; - } else if (value_.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE)) <= 0 && - value_.compareTo(BigDecimal.valueOf(Integer.MIN_VALUE)) >= 0) { - type_ = Type.INT; - } else if (value_.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) <= 0 && - value_.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) >= 0) { - type_ = Type.BIGINT; - } - } - } - } - evalCost_ = LITERAL_COST; - isAnalyzed_ = true; - } - - /** - * Explicitly cast this literal to 'targetType'. The targetType must be a - * float point type. - */ - protected void explicitlyCastToFloat(Type targetType) { - Preconditions.checkState(targetType.isFloatingPointType()); - type_ = targetType; - explicitlyCast_ = true; - } - - @Override - protected Expr uncheckedCastTo(Type targetType) throws AnalysisException { - Preconditions.checkState(targetType.isNumericType()); - // Implicit casting to decimals allows truncating digits from the left of the - // decimal point (see TypesUtil). A literal that is implicitly cast to a decimal - // with truncation is wrapped into a CastExpr so the BE can evaluate it and report - // a warning. This behavior is consistent with casting/overflow of non-constant - // exprs that return decimal. - // IMPALA-1837: Without the CastExpr wrapping, such literals can exceed the max - // expected byte size sent to the BE in toThrift(). - if (targetType.isDecimal()) { - ScalarType decimalType = (ScalarType) targetType; - // analyze() ensures that value_ never exceeds the maximum scale and precision. - Preconditions.checkState(isAnalyzed_); - // Sanity check that our implicit casting does not allow a reduced precision or - // truncating values from the right of the decimal point. - Preconditions.checkState(value_.precision() <= decimalType.decimalPrecision()); - Preconditions.checkState(value_.scale() <= decimalType.decimalScale()); - int valLeftDigits = value_.precision() - value_.scale(); - int typeLeftDigits = decimalType.decimalPrecision() - decimalType.decimalScale(); - if (typeLeftDigits < valLeftDigits) return new CastExpr(targetType, this); - } - type_ = targetType; - return this; - } - - @Override - public void swapSign() throws NotImplementedException { - // swapping sign does not change the type - value_ = value_.negate(); - } - - @Override - public int compareTo(LiteralExpr o) { - int ret = super.compareTo(o); - if (ret != 0) return ret; - NumericLiteral other = (NumericLiteral) o; - return value_.compareTo(other.value_); - } - - private void init(BigDecimal value) { - isAnalyzed_ = false; - value_ = value; - } - - // Returns the unscaled value of this literal. BigDecimal doesn't treat scale - // the way we do. We need to pad it out with zeros or truncate as necessary. - private BigInteger getUnscaledValue() { - Preconditions.checkState(type_.isDecimal()); - BigInteger result = value_.unscaledValue(); - int valueScale = value_.scale(); - // If valueScale is less than 0, it indicates the power of 10 to multiply the - // unscaled value. This path also handles this case by padding with zeros. - // e.g. unscaled value = 123, value scale = -2 means 12300. - ScalarType decimalType = (ScalarType) type_; - return result.multiply(BigInteger.TEN.pow(decimalType.decimalScale() - valueScale)); - } - - @Override - public Expr clone() { return new NumericLiteral(this); } - - /** - * Check overflow. - */ - public static boolean isOverflow(BigDecimal value, Type type) - throws AnalysisException { - switch (type.getPrimitiveType()) { - case TINYINT: - return (value.compareTo(BigDecimal.valueOf(Byte.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Byte.MIN_VALUE)) < 0); - case SMALLINT: - return (value.compareTo(BigDecimal.valueOf(Short.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Short.MIN_VALUE)) < 0); - case INT: - return (value.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Integer.MIN_VALUE)) < 0); - case BIGINT: - return (value.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) < 0); - case FLOAT: - return (value.compareTo(BigDecimal.valueOf(Float.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Float.MIN_VALUE)) < 0); - case DOUBLE: - return (value.compareTo(BigDecimal.valueOf(Double.MAX_VALUE)) > 0 || - value.compareTo(BigDecimal.valueOf(Double.MIN_VALUE)) < 0); - case DECIMAL: - return (TypesUtil.computeDecimalType(value) == null); - default: - throw new AnalysisException("Overflow check on " + type + " isn't supported."); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/OrderByElement.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/OrderByElement.java b/fe/src/main/java/com/cloudera/impala/analysis/OrderByElement.java deleted file mode 100644 index d430152..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/OrderByElement.java +++ /dev/null @@ -1,156 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.util.List; - -import com.google.common.collect.Lists; - - -/** - * Combination of expr, ASC/DESC, and nulls ordering. - */ -public class OrderByElement { - private Expr expr_; - private final boolean isAsc_; - // Represents the NULLs ordering specified: true when "NULLS FIRST", false when - // "NULLS LAST", and null if not specified. - private final Boolean nullsFirstParam_; - - /** - * Constructs the OrderByElement. - * - * 'nullsFirstParam' should be true if "NULLS FIRST", false if "NULLS LAST", or null if - * the NULLs order was not specified. - */ - public OrderByElement(Expr expr, boolean isAsc, Boolean nullsFirstParam) { - super(); - expr_ = expr; - isAsc_ = isAsc; - nullsFirstParam_ = nullsFirstParam; - } - - /** - * C'tor for cloning. - */ - private OrderByElement(OrderByElement other) { - expr_ = other.expr_.clone(); - isAsc_ = other.isAsc_; - if (other.nullsFirstParam_ != null) { - nullsFirstParam_ = new Boolean(other.nullsFirstParam_.booleanValue()); - } else { - nullsFirstParam_ = null; - } - } - - public Expr getExpr() { return expr_; } - public void setExpr(Expr e) { expr_ = e; } - public boolean isAsc() { return isAsc_; } - public Boolean getNullsFirstParam() { return nullsFirstParam_; } - public boolean nullsFirst() { return nullsFirst(nullsFirstParam_, isAsc_); } - - public String toSql() { - StringBuilder strBuilder = new StringBuilder(); - strBuilder.append(expr_.toSql()); - strBuilder.append(isAsc_ ? " ASC" : " DESC"); - // When ASC and NULLS LAST or DESC and NULLS FIRST, we do not print NULLS FIRST/LAST - // because it is the default behavior and we want to avoid printing NULLS FIRST/LAST - // whenever possible as it is incompatible with Hive (SQL compatibility with Hive is - // important for views). - if (nullsFirstParam_ != null) { - if (isAsc_ && nullsFirstParam_) { - // If ascending, nulls are last by default, so only add if nulls first. - strBuilder.append(" NULLS FIRST"); - } else if (!isAsc_ && !nullsFirstParam_) { - // If descending, nulls are first by default, so only add if nulls last. - strBuilder.append(" NULLS LAST"); - } - } - return strBuilder.toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (obj.getClass() != this.getClass()) return false; - OrderByElement o = (OrderByElement)obj; - boolean nullsFirstEqual = - (nullsFirstParam_ == null) == (o.nullsFirstParam_ == null); - if (nullsFirstParam_ != null && nullsFirstEqual) { - nullsFirstEqual = nullsFirstParam_.equals(o.nullsFirstParam_); - } - return expr_.equals(o.expr_) && isAsc_ == o.isAsc_ && nullsFirstEqual; - } - - @Override - public OrderByElement clone() { return new OrderByElement(this); } - - /** - * Compute nullsFirst. - * - * @param nullsFirstParam True if "NULLS FIRST", false if "NULLS LAST", or null if - * the NULLs order was not specified. - * @param isAsc - * @return Returns true if nulls are ordered first or false if nulls are ordered last. - * Independent of isAsc. - */ - public static boolean nullsFirst(Boolean nullsFirstParam, boolean isAsc) { - return nullsFirstParam == null ? !isAsc : nullsFirstParam; - } - - /** - * Returns a new list of order-by elements with the order by exprs of src substituted - * according to smap. Preserves the other sort params from src. - */ - public static List<OrderByElement> substitute(List<OrderByElement> src, - ExprSubstitutionMap smap, Analyzer analyzer) { - List<OrderByElement> result = Lists.newArrayListWithCapacity(src.size()); - for (OrderByElement element: src) { - result.add(new OrderByElement(element.getExpr().substitute(smap, analyzer, false), - element.isAsc_, element.nullsFirstParam_)); - } - return result; - } - - /** - * Extracts the order-by exprs from the list of order-by elements and returns them. - */ - public static List<Expr> getOrderByExprs(List<OrderByElement> src) { - List<Expr> result = Lists.newArrayListWithCapacity(src.size()); - for (OrderByElement element: src) { - result.add(element.getExpr()); - } - return result; - } - - /** - * Returns a new list of OrderByElements with the same (cloned) expressions but the - * ordering direction reversed (asc becomes desc, nulls first becomes nulls last, etc.) - */ - public static List<OrderByElement> reverse(List<OrderByElement> src) { - List<OrderByElement> result = Lists.newArrayListWithCapacity(src.size()); - for (int i = 0; i < src.size(); ++i) { - OrderByElement element = src.get(i); - OrderByElement reverseElement = - new OrderByElement(element.getExpr().clone(), !element.isAsc_, - Boolean.valueOf(!nullsFirst(element.nullsFirstParam_, element.isAsc_))); - result.add(reverseElement); - } - return result; - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/ParseNode.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/ParseNode.java b/fe/src/main/java/com/cloudera/impala/analysis/ParseNode.java deleted file mode 100644 index 6f54e26..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/ParseNode.java +++ /dev/null @@ -1,34 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import com.cloudera.impala.common.AnalysisException; - -public interface ParseNode { - - /** - * Perform semantic analysis of node and all of its children. - * Throws exception if any semantic errors were found. - */ - public void analyze(Analyzer analyzer) throws AnalysisException; - - /** - * Returns the SQL string corresponding to this node. - */ - public String toSql(); -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/PartitionKeyValue.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/PartitionKeyValue.java b/fe/src/main/java/com/cloudera/impala/analysis/PartitionKeyValue.java deleted file mode 100644 index 90c44f2..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/PartitionKeyValue.java +++ /dev/null @@ -1,88 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import com.cloudera.impala.common.AnalysisException; -import com.google.common.base.Preconditions; - -/** - * Representation of a single column:value element in the PARTITION (...) clause of an - * insert or alter table statement. - */ -public class PartitionKeyValue { - // Name of partitioning column. - private final String colName_; - // Value of partitioning column. Set to null for dynamic inserts. - private final Expr value_; - // Evaluation of value for static partition keys, null otherwise. Set in analyze(). - private LiteralExpr literalValue_; - - public PartitionKeyValue(String colName, Expr value) { - this.colName_ = colName.toLowerCase(); - this.value_ = value; - } - - public void analyze(Analyzer analyzer) throws AnalysisException { - if (isStatic() && !value_.isConstant()) { - throw new AnalysisException( - String.format("Non-constant expressions are not supported " + - "as static partition-key values in '%s'.", toString())); - } - if (value_ == null) return; - value_.analyze(analyzer); - literalValue_ = LiteralExpr.create(value_, analyzer.getQueryCtx()); - } - - public String getColName() { return colName_; } - public Expr getValue() { return value_; } - public LiteralExpr getLiteralValue() { return literalValue_; } - public boolean isDynamic() { return value_ == null; } - public boolean isStatic() { return !isDynamic(); } - - @Override - public String toString() { - return isStatic() ? colName_ + "=" + value_.toSql() : colName_; - } - - /** - * Returns a binary predicate as a SQL string which matches the column and value of this - * PartitionKeyValue. If the value is null, correctly substitutes 'IS' as the operator. - */ - public String toPredicateSql() { - String ident = ToSqlUtils.getIdentSql(colName_); - if (literalValue_ instanceof NullLiteral || - literalValue_.getStringValue().isEmpty()) { - return ident + " IS NULL"; - } - return isStatic() ? ident + "=" + value_.toSql() : ident; - } - - /** - * Utility method that returns the string value for the given partition key. For - * NULL values (a NullLiteral type) or empty literal values this will return the - * given null partition key value. - */ - public static String getPartitionKeyValueString(LiteralExpr literalValue, - String nullPartitionKeyValue) { - Preconditions.checkNotNull(literalValue); - if (literalValue instanceof NullLiteral || literalValue.getStringValue().isEmpty()) { - return nullPartitionKeyValue; - } - return literalValue.getStringValue(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/PartitionListItem.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/PartitionListItem.java b/fe/src/main/java/com/cloudera/impala/analysis/PartitionListItem.java deleted file mode 100644 index 1ffc51e..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/PartitionListItem.java +++ /dev/null @@ -1,37 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -/** - * Representation of a single column:value element in the PARTITION (...) clause of an insert - * statement. - */ -public class PartitionListItem { - // Name of partitioning column. - private final String colName_; - // Value of partitioning column. Set to null for dynamic inserts. - private final LiteralExpr value_; - - public PartitionListItem(String colName, LiteralExpr value) { - this.colName_ = colName; - this.value_ = value; - } - - public String getColName() { return colName_; } - public LiteralExpr getValue() { return value_; } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/PartitionSpec.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/PartitionSpec.java b/fe/src/main/java/com/cloudera/impala/analysis/PartitionSpec.java deleted file mode 100644 index 92bf0ae..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/PartitionSpec.java +++ /dev/null @@ -1,201 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.util.List; -import java.util.Set; - -import org.apache.hadoop.hive.metastore.api.FieldSchema; - -import com.cloudera.impala.authorization.Privilege; -import com.cloudera.impala.catalog.Column; -import com.cloudera.impala.catalog.HdfsTable; -import com.cloudera.impala.catalog.Table; -import com.cloudera.impala.catalog.Type; -import com.cloudera.impala.common.AnalysisException; -import com.cloudera.impala.thrift.TPartitionKeyValue; -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - -/* - * Represents a partition spec - a collection of partition key/values. - */ -public class PartitionSpec implements ParseNode { - private final ImmutableList<PartitionKeyValue> partitionSpec_; - private TableName tableName_; - private Boolean partitionShouldExist_; - private Privilege privilegeRequirement_; - - // Flag to determine if the partition already exists in the target table. - // Set during analysis. - private Boolean partitionExists_; - - // The value Hive is configured to use for NULL partition key values. - // Set during analysis. - private String nullPartitionKeyValue_; - - public PartitionSpec(List<PartitionKeyValue> partitionSpec) { - this.partitionSpec_ = ImmutableList.copyOf(partitionSpec); - } - - public List<PartitionKeyValue> getPartitionSpecKeyValues() { - return partitionSpec_; - } - - public String getTbl() { return tableName_.getTbl(); } - public void setTableName(TableName tableName) { this.tableName_ = tableName; } - public boolean partitionExists() { - Preconditions.checkNotNull(partitionExists_); - return partitionExists_; - } - - // The value Hive is configured to use for NULL partition key values. - // Set during analysis. - public String getNullPartitionKeyValue() { - Preconditions.checkNotNull(nullPartitionKeyValue_); - return nullPartitionKeyValue_; - } - - // If set, an additional analysis check will be performed to validate the target table - // contains the given partition spec. - public void setPartitionShouldExist() { partitionShouldExist_ = Boolean.TRUE; } - - // If set, an additional analysis check will be performed to validate the target table - // does not contain the given partition spec. - public void setPartitionShouldNotExist() { partitionShouldExist_ = Boolean.FALSE; } - - // Set the privilege requirement for this partition spec. Must be set prior to - // analysis. - public void setPrivilegeRequirement(Privilege privilege) { - privilegeRequirement_ = privilege; - } - - @Override - public void analyze(Analyzer analyzer) throws AnalysisException { - Preconditions.checkNotNull(tableName_); - Preconditions.checkNotNull(privilegeRequirement_); - - // Skip adding an audit event when analyzing partitions. The parent table should - // be audited outside of the PartitionSpec. - Table table = analyzer.getTable(tableName_, privilegeRequirement_, false); - String tableName = table.getDb().getName() + "." + getTbl(); - - // Make sure the target table is partitioned. - if (table.getMetaStoreTable().getPartitionKeysSize() == 0) { - throw new AnalysisException("Table is not partitioned: " + tableName); - } - - // Make sure static partition key values only contain constant exprs. - for (PartitionKeyValue kv: partitionSpec_) { - kv.analyze(analyzer); - } - - // Get all keys in the target table. - Set<String> targetPartitionKeys = Sets.newHashSet(); - for (FieldSchema fs: table.getMetaStoreTable().getPartitionKeys()) { - targetPartitionKeys.add(fs.getName().toLowerCase()); - } - - // All partition keys need to be specified. - if (targetPartitionKeys.size() != partitionSpec_.size()) { - throw new AnalysisException(String.format("Items in partition spec must exactly " + - "match the partition columns in the table definition: %s (%d vs %d)", - tableName, partitionSpec_.size(), targetPartitionKeys.size())); - } - - Set<String> keyNames = Sets.newHashSet(); - // Validate each partition key/value specified, ensuring a matching partition column - // exists in the target table, no duplicate keys were specified, and that all the - // column types are compatible. - for (PartitionKeyValue pk: partitionSpec_) { - if (!keyNames.add(pk.getColName().toLowerCase())) { - throw new AnalysisException("Duplicate partition key name: " + pk.getColName()); - } - - Column c = table.getColumn(pk.getColName()); - if (c == null) { - throw new AnalysisException(String.format( - "Partition column '%s' not found in table: %s", pk.getColName(), tableName)); - } else if (!targetPartitionKeys.contains(pk.getColName().toLowerCase())) { - throw new AnalysisException(String.format( - "Column '%s' is not a partition column in table: %s", - pk.getColName(), tableName)); - } else if (pk.getValue() instanceof NullLiteral) { - // No need for further analysis checks of this partition key value. - continue; - } - - Type colType = c.getType(); - Type literalType = pk.getValue().getType(); - Type compatibleType = - Type.getAssignmentCompatibleType(colType, literalType, false); - if (!compatibleType.isValid()) { - throw new AnalysisException(String.format("Value of partition spec (column=%s) " - + "has incompatible type: '%s'. Expected type: '%s'.", - pk.getColName(), literalType, colType)); - } - // Check for loss of precision with the partition value - if (!compatibleType.equals(colType)) { - throw new AnalysisException( - String.format("Partition key value may result in loss of precision.\n" + - "Would need to cast '%s' to '%s' for partition column: %s", - pk.getValue().toSql(), colType.toString(), pk.getColName())); - } - } - // Only HDFS tables are partitioned. - Preconditions.checkState(table instanceof HdfsTable); - HdfsTable hdfsTable = (HdfsTable) table; - nullPartitionKeyValue_ = hdfsTable.getNullPartitionKeyValue(); - - partitionExists_ = hdfsTable.getPartition(partitionSpec_) != null; - if (partitionShouldExist_ != null) { - if (partitionShouldExist_ && !partitionExists_) { - throw new AnalysisException("Partition spec does not exist: (" + - Joiner.on(", ").join(partitionSpec_) + ")."); - } else if (!partitionShouldExist_ && partitionExists_) { - throw new AnalysisException("Partition spec already exists: (" + - Joiner.on(", ").join(partitionSpec_) + ")."); - } - } - } - - /* - * Returns the Thrift representation of this PartitionSpec. - */ - public List<TPartitionKeyValue> toThrift() { - List<TPartitionKeyValue> thriftPartitionSpec = Lists.newArrayList(); - for (PartitionKeyValue kv: partitionSpec_) { - String value = PartitionKeyValue.getPartitionKeyValueString( - kv.getLiteralValue(), getNullPartitionKeyValue()); - thriftPartitionSpec.add(new TPartitionKeyValue(kv.getColName(), value)); - } - return thriftPartitionSpec; - } - - @Override - public String toSql() { - List<String> partitionSpecStr = Lists.newArrayList(); - for (PartitionKeyValue kv: partitionSpec_) { - partitionSpecStr.add(kv.getColName() + "=" + kv.getValue().toSql()); - } - return String.format("PARTITION (%s)", Joiner.on(", ").join(partitionSpecStr)); - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/Path.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/Path.java b/fe/src/main/java/com/cloudera/impala/analysis/Path.java deleted file mode 100644 index 03c601c..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/Path.java +++ /dev/null @@ -1,448 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.util.ArrayList; -import java.util.List; - -import com.cloudera.impala.catalog.ArrayType; -import com.cloudera.impala.catalog.Column; -import com.cloudera.impala.catalog.MapType; -import com.cloudera.impala.catalog.StructField; -import com.cloudera.impala.catalog.StructType; -import com.cloudera.impala.catalog.Table; -import com.cloudera.impala.catalog.Type; -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.collect.Lists; - -/** - * Represents a resolved or unresolved dot-separated path that is rooted at a registered - * tuple descriptor, catalog table/view, or an existing resolved path. - * - * This class implements the resolution logic for mapping an implicit or explicit - * raw path to the corresponding physical types/positions in the schema tree. - * - * Implicit vs. Explicit Paths - * The item of an array and the key/value of maps are accessed via their implicit field - * names. However, if the type of an array item or a map value is a struct, then we allow - * omitting the explicit reference to the struct type in paths for accessing fields - * within that struct as a shorthand for user convenience. An explicit reference to the - * struct type is always legal. Paths that explicitly reference such a struct are - * "physical" because they typically map exactly to the schema representation in the - * underlying storage format (e.g. Parquet/Avro). Paths that omit the struct reference - * are called "implicit". During resolution, explicit paths are always preferred over - * implicit paths for resolving ambiguities. - * - * Example - * create table d.t ( - * c array<struct<f:int,item:int,pos:int>> - * ); - * - * select ... from d.t.c - * d.t.c <-- resolves to type array<struct<f:int,item:int,pos:int>> - * c alias <-- type struct<item:struct<f:int,item:int,pos:int>,pos:bigint>> - * - * select c.item.f, c.f from d.t.c - * c.item.f <-- explicit path to "f" - * c.f <-- implicit path to "f", skips "item" reference - * (same for the unqualified versions item.f and f) - * - * select c.item, c.item.item from d.t.c - * c.item <-- explicit path to "item" struct of type struct<f:int,item:string> - * c.item.item <-- explicit path to string "item"; there is no logical path to the - * string "item" due to the "item" name conflict - * c.pos <-- explicit path to "pos" of type bigint - * c.item.pos <-- explicit path to "pos" of type int; there is no logical path to the - * int "pos" due to the "pos" name conflict - * (same for unqualified versions item, item.item, pos, item.pos) - * - * Please refer to TestImplicitAndExplicitPaths() for analogous examples for maps. - * - * Illegal Implicit Paths - * The intention of implicit paths is to allow users to skip a *single* trivial level of - * indirection in common cases. In particular, it is illegal to implicitly skip multiple - * levels in a path, illustrated as follows. - * - * Example - * create table d.t ( - * c array<array<struct<e:int,f:string>>> - * ); - * - * select c.f from d.t.c - * select 1 from d.t.c, c.f - * c.f <-- illegal path because it would have to implicitly skip two 'item' fields - * - * - * Uses of Paths and Terminology - * - * Uncorrelated References: Star exprs, SlotRefs and TableRefs that are rooted at a - * catalog Table or a registered TupleDescriptor in the same query block. - * - * Relative References: TableRefs that are rooted at a TupleDescriptor. - * - * Correlated References: SlotRefs and TableRefs that are rooted at a TupleDescriptor - * registered in an ancestor query block are called 'correlated'. All correlated - * references are relative, but not all relative references are correlated. - * - * A Path itself is never said to be un/correlated because it is intentionally unaware - * of the query block that it is used in. - */ -public class Path { - // Implicit field names of collections. - public static final String ARRAY_ITEM_FIELD_NAME = "item"; - public static final String ARRAY_POS_FIELD_NAME = "pos"; - public static final String MAP_KEY_FIELD_NAME = "key"; - public static final String MAP_VALUE_FIELD_NAME = "value"; - - public static enum PathType { - SLOT_REF, - TABLE_REF, - STAR, - ANY, // Reference to any field or table in schema. - } - - // Implicit or explicit raw path to be resolved relative to rootDesc_ or rootTable_. - // Every raw-path element is mapped to zero, one or two types/positions in resolution. - private final List<String> rawPath_; - - // Registered table alias that this path is rooted at, if any. - // Null if the path is rooted at a catalog table/view. - private final TupleDescriptor rootDesc_; - - // Catalog table that this resolved path is rooted at, if any. - // Null if the path is rooted at a registered tuple that does not - // belong to a catalog table/view. - private final Table rootTable_; - - // Root path that a relative path was created from. - private final Path rootPath_; - - // List of matched types and field positions set during resolution. The matched - // types/positions describe the physical path through the schema tree. - private final List<Type> matchedTypes_ = Lists.newArrayList(); - private final List<Integer> matchedPositions_ = Lists.newArrayList(); - - // Remembers the indices into rawPath_ and matchedTypes_ of the first collection - // matched during resolution. - private int firstCollectionPathIdx_ = -1; - private int firstCollectionTypeIdx_ = -1; - - // Indicates whether this path has been resolved. Set in resolve(). - private boolean isResolved_ = false; - - // Caches the result of getAbsolutePath() to avoid re-computing it. - private List<Integer> absolutePath_ = null; - - /** - * Constructs a Path rooted at the given rootDesc. - */ - public Path(TupleDescriptor rootDesc, List<String> rawPath) { - Preconditions.checkNotNull(rootDesc); - Preconditions.checkNotNull(rawPath); - rootTable_ = rootDesc.getTable(); - rootDesc_ = rootDesc; - rootPath_ = null; - rawPath_ = rawPath; - } - - /** - * Constructs a Path rooted at the given rootTable. - */ - public Path(Table rootTable, List<String> rawPath) { - Preconditions.checkNotNull(rootTable); - Preconditions.checkNotNull(rawPath); - rootTable_ = rootTable; - rootDesc_ = null; - rootPath_ = null; - rawPath_ = rawPath; - } - - /** - * Constructs a new unresolved path relative to an existing resolved path. - */ - public Path(Path rootPath, List<String> relRawPath) { - Preconditions.checkNotNull(rootPath); - Preconditions.checkState(rootPath.isResolved()); - Preconditions.checkNotNull(relRawPath); - rootTable_ = rootPath.rootTable_; - rootDesc_ = rootPath.rootDesc_; - rootPath_ = rootPath; - rawPath_ = Lists.newArrayListWithCapacity( - rootPath.getRawPath().size() + relRawPath.size()); - rawPath_.addAll(rootPath.getRawPath()); - rawPath_.addAll(relRawPath); - matchedTypes_.addAll(rootPath.matchedTypes_); - matchedPositions_.addAll(rootPath.matchedPositions_); - firstCollectionPathIdx_ = rootPath.firstCollectionPathIdx_; - firstCollectionTypeIdx_ = rootPath.firstCollectionTypeIdx_; - } - - /** - * Resolves this path in the context of the root tuple descriptor / root table - * or continues resolving this relative path from an existing root path. - * Returns true if the path could be fully resolved, false otherwise. - * A failed resolution leaves this Path in a partially resolved state. - */ - public boolean resolve() { - if (isResolved_) return true; - Preconditions.checkState(rootDesc_ != null || rootTable_ != null); - Type currentType = null; - int rawPathIdx = 0; - if (rootPath_ != null) { - // Continue resolving this path relative to the rootPath_. - currentType = rootPath_.destType(); - rawPathIdx = rootPath_.getRawPath().size(); - } else if (rootDesc_ != null) { - currentType = rootDesc_.getType(); - } else { - // Directly start from the item type because only implicit paths are allowed. - currentType = rootTable_.getType().getItemType(); - } - - // Map all remaining raw-path elements to field types and positions. - while (rawPathIdx < rawPath_.size()) { - if (!currentType.isComplexType()) return false; - StructType structType = getTypeAsStruct(currentType); - // Resolve explicit path. - StructField field = structType.getField(rawPath_.get(rawPathIdx)); - if (field == null) { - // Resolve implicit path. - if (structType instanceof CollectionStructType) { - field = ((CollectionStructType) structType).getOptionalField(); - // Collections must be matched explicitly. - if (field.getType().isCollectionType()) return false; - } else { - // Failed to resolve implicit or explicit path. - return false; - } - // Update the physical types/positions. - matchedTypes_.add(field.getType()); - matchedPositions_.add(field.getPosition()); - currentType = field.getType(); - // Do not consume a raw-path element. - continue; - } - matchedTypes_.add(field.getType()); - matchedPositions_.add(field.getPosition()); - if (field.getType().isCollectionType() && firstCollectionPathIdx_ == -1) { - Preconditions.checkState(firstCollectionTypeIdx_ == -1); - firstCollectionPathIdx_ = rawPathIdx; - firstCollectionTypeIdx_ = matchedTypes_.size() - 1; - } - currentType = field.getType(); - ++rawPathIdx; - } - Preconditions.checkState(matchedTypes_.size() == matchedPositions_.size()); - Preconditions.checkState(matchedTypes_.size() >= rawPath_.size()); - isResolved_ = true; - return true; - } - - /** - * If the given type is a collection, returns a collection struct type representing - * named fields of its explicit path. Returns the given type itself if it is already - * a struct. Requires that the given type is a complex type. - */ - public static StructType getTypeAsStruct(Type t) { - Preconditions.checkState(t.isComplexType()); - if (t.isStructType()) return (StructType) t; - if (t.isArrayType()) { - return CollectionStructType.createArrayStructType((ArrayType) t); - } else { - Preconditions.checkState(t.isMapType()); - return CollectionStructType.createMapStructType((MapType) t); - } - } - - public Table getRootTable() { return rootTable_; } - public TupleDescriptor getRootDesc() { return rootDesc_; } - public boolean isRootedAtTable() { return rootTable_ != null; } - public boolean isRootedAtTuple() { return rootDesc_ != null; } - public List<String> getRawPath() { return rawPath_; } - public boolean isResolved() { return isResolved_; } - - public List<Type> getMatchedTypes() { - Preconditions.checkState(isResolved_); - return matchedTypes_; - } - - public List<Integer> getMatchedPositions() { - Preconditions.checkState(isResolved_); - return matchedPositions_; - } - - public boolean hasNonDestCollection() { - Preconditions.checkState(isResolved_); - return firstCollectionPathIdx_ != -1 && - firstCollectionPathIdx_ != rawPath_.size() - 1; - } - - public String getFirstCollectionName() { - Preconditions.checkState(isResolved_); - if (firstCollectionPathIdx_ == -1) return null; - return rawPath_.get(firstCollectionPathIdx_); - } - - public Type getFirstCollectionType() { - Preconditions.checkState(isResolved_); - if (firstCollectionTypeIdx_ == -1) return null; - return matchedTypes_.get(firstCollectionTypeIdx_); - } - - public int getFirstCollectionIndex() { - Preconditions.checkState(isResolved_); - return firstCollectionTypeIdx_; - } - - public Type destType() { - Preconditions.checkState(isResolved_); - if (!matchedTypes_.isEmpty()) return matchedTypes_.get(matchedTypes_.size() - 1); - if (rootDesc_ != null) return rootDesc_.getType(); - if (rootTable_ != null) return rootTable_.getType(); - return null; - } - - public Table destTable() { - Preconditions.checkState(isResolved_); - if (rootTable_ != null && rootDesc_ == null && matchedTypes_.isEmpty()) { - return rootTable_; - } - return null; - } - - /** - * Returns the destination Column of this path, or null if the destination of this - * path is not a Column. This path must be rooted at a table or a tuple descriptor - * corresponding to a table for the destination to be a Column. - */ - public Column destColumn() { - Preconditions.checkState(isResolved_); - if (rootTable_ == null || rawPath_.size() != 1) return null; - return rootTable_.getColumn(rawPath_.get(rawPath_.size() - 1)); - } - - /** - * Returns the destination tuple descriptor of this path, or null - * if the destination of this path is not a registered alias. - */ - public TupleDescriptor destTupleDesc() { - Preconditions.checkState(isResolved_); - if (rootDesc_ != null && matchedTypes_.isEmpty()) return rootDesc_; - return null; - } - - public List<String> getFullyQualifiedRawPath() { - Preconditions.checkState(rootTable_ != null || rootDesc_ != null); - List<String> result = Lists.newArrayListWithCapacity(rawPath_.size() + 2); - if (rootDesc_ != null) { - result.addAll(Lists.newArrayList(rootDesc_.getAlias().split("\\."))); - } else { - result.add(rootTable_.getDb().getName()); - result.add(rootTable_.getName()); - } - result.addAll(rawPath_); - return result; - } - - /** - * Returns the absolute explicit path starting from the fully-qualified table name. - * The goal is produce a canonical non-ambiguous path that can be used as an - * identifier for table and slot references. - * - * Example: - * create table mydb.test (a array<struct<f1:int,f2:string>>); - * use mydb; - * select f1 from test t, t.a; - * - * This function should return the following for the path of the 'f1' SlotRef: - * mydb.test.a.item.f1 - */ - public List<String> getCanonicalPath() { - List<String> result = Lists.newArrayList(); - getCanonicalPath(result); - return result; - } - - /** - * Recursive helper for getCanonicalPath(). - */ - private void getCanonicalPath(List<String> result) { - Type currentType = null; - if (isRootedAtTuple()) { - rootDesc_.getPath().getCanonicalPath(result); - currentType = rootDesc_.getType(); - } else { - Preconditions.checkNotNull(isRootedAtTable()); - result.add(rootTable_.getTableName().getDb()); - result.add(rootTable_.getTableName().getTbl()); - currentType = rootTable_.getType().getItemType(); - } - // Compute the explicit path from the matched positions. Note that rawPath_ is - // not sufficient because it could contain implicit matches. - for (int i = 0; i < matchedPositions_.size(); ++i) { - StructType structType = getTypeAsStruct(currentType); - int matchPos = matchedPositions_.get(i); - Preconditions.checkState(matchPos < structType.getFields().size()); - StructField match = structType.getFields().get(matchPos); - result.add(match.getName()); - currentType = match.getType(); - } - } - - /** - * Returns the absolute physical path in positions starting from the schema root to the - * destination of this path. - */ - public List<Integer> getAbsolutePath() { - if (absolutePath_ != null) return absolutePath_; - Preconditions.checkState(isResolved_); - absolutePath_ = Lists.newArrayList(); - if (rootDesc_ != null) absolutePath_.addAll(rootDesc_.getPath().getAbsolutePath()); - absolutePath_.addAll(matchedPositions_); - return absolutePath_; - } - - @Override - public String toString() { - Preconditions.checkState(rootTable_ != null || rootDesc_ != null); - String pathRoot = null; - if (rootDesc_ != null) { - pathRoot = rootDesc_.getAlias(); - } else { - pathRoot = rootTable_.getFullName(); - } - if (rawPath_.isEmpty()) return pathRoot; - return pathRoot + "." + Joiner.on(".").join(rawPath_); - } - - /** - * Returns a raw path from a known root alias and field name. - */ - public static ArrayList<String> createRawPath(String rootAlias, String fieldName) { - ArrayList<String> result = Lists.newArrayList(rootAlias.split("\\.")); - result.add(fieldName); - return result; - } - - public static Path createRelPath(Path rootPath, String... fieldNames) { - Preconditions.checkState(rootPath.isResolved()); - Path result = new Path(rootPath, Lists.newArrayList(fieldNames)); - return result; - } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/Predicate.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/Predicate.java b/fe/src/main/java/com/cloudera/impala/analysis/Predicate.java deleted file mode 100644 index 4fadbce..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/Predicate.java +++ /dev/null @@ -1,97 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import com.cloudera.impala.catalog.Type; -import com.cloudera.impala.common.AnalysisException; -import com.cloudera.impala.common.Pair; -import com.cloudera.impala.common.Reference; - -public abstract class Predicate extends Expr { - protected boolean isEqJoinConjunct_; - - public Predicate() { - super(); - isEqJoinConjunct_ = false; - } - - /** - * Copy c'tor used in clone(). - */ - protected Predicate(Predicate other) { - super(other); - isEqJoinConjunct_ = other.isEqJoinConjunct_; - } - - public boolean isEqJoinConjunct() { return isEqJoinConjunct_; } - public void setIsEqJoinConjunct(boolean v) { isEqJoinConjunct_ = v; } - - @Override - public void analyze(Analyzer analyzer) throws AnalysisException { - if (isAnalyzed_) return; - super.analyze(analyzer); - type_ = Type.BOOLEAN; - // values: true/false/null - numDistinctValues_ = 3; - } - - /** - * Returns true if one of the children is a slotref (possibly wrapped in a cast) - * and the other children are all constant. Returns the slotref in 'slotRef' and - * its child index in 'idx'. - * This will pick up something like "col = 5", but not "2 * col = 10", which is - * what we want. - */ - public boolean isSingleColumnPredicate( - Reference<SlotRef> slotRefRef, Reference<Integer> idxRef) { - // find slotref - SlotRef slotRef = null; - int i = 0; - for (; i < children_.size(); ++i) { - slotRef = getChild(i).unwrapSlotRef(false); - if (slotRef != null) break; - } - if (slotRef == null) return false; - - // make sure everything else is constant - for (int j = 0; j < children_.size(); ++j) { - if (i == j) continue; - if (!getChild(j).isConstant()) return false; - } - - if (slotRefRef != null) slotRefRef.setRef(slotRef); - if (idxRef != null) idxRef.setRef(Integer.valueOf(i)); - return true; - } - - public static boolean isEquivalencePredicate(Expr expr) { - return (expr instanceof BinaryPredicate) - && ((BinaryPredicate) expr).getOp().isEquivalence(); - } - - /** - * If predicate is of the form "<slotref> = <slotref>", returns both SlotRefs, - * otherwise returns null. - */ - public Pair<SlotId, SlotId> getEqSlots() { return null; } - - /** - * Returns the SlotRef bound by this Predicate. - */ - public SlotRef getBoundSlot() { return null; } -} http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/b544f019/fe/src/main/java/com/cloudera/impala/analysis/PrivilegeSpec.java ---------------------------------------------------------------------- diff --git a/fe/src/main/java/com/cloudera/impala/analysis/PrivilegeSpec.java b/fe/src/main/java/com/cloudera/impala/analysis/PrivilegeSpec.java deleted file mode 100644 index 2948e58..0000000 --- a/fe/src/main/java/com/cloudera/impala/analysis/PrivilegeSpec.java +++ /dev/null @@ -1,287 +0,0 @@ -// 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 com.cloudera.impala.analysis; - -import java.util.List; - -import com.cloudera.impala.authorization.Privilege; -import com.cloudera.impala.catalog.DataSourceTable; -import com.cloudera.impala.catalog.RolePrivilege; -import com.cloudera.impala.catalog.Table; -import com.cloudera.impala.catalog.TableLoadingException; -import com.cloudera.impala.catalog.View; -import com.cloudera.impala.common.AnalysisException; -import com.cloudera.impala.thrift.TPrivilege; -import com.cloudera.impala.thrift.TPrivilegeLevel; -import com.cloudera.impala.thrift.TPrivilegeScope; -import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; - -/** - * Represents a privilege spec from a GRANT/REVOKE statement. - * A privilege spec may correspond to one or more privileges. Currently, a privilege spec - * can represent multiple privileges only at the COLUMN scope. - */ -public class PrivilegeSpec implements ParseNode { - private final TPrivilegeScope scope_; - private final TPrivilegeLevel privilegeLevel_; - private final TableName tableName_; - private final HdfsUri uri_; - private final List<String> columnNames_; - - // Set/modified during analysis - private String dbName_; - private String serverName_; - - private PrivilegeSpec(TPrivilegeLevel privilegeLevel, TPrivilegeScope scope, - String serverName, String dbName, TableName tableName, HdfsUri uri, - List<String> columnNames) { - Preconditions.checkNotNull(scope); - Preconditions.checkNotNull(privilegeLevel); - privilegeLevel_ = privilegeLevel; - scope_ = scope; - serverName_ = serverName; - tableName_ = tableName; - dbName_ = (tableName_ != null ? tableName_.getDb() : dbName); - uri_ = uri; - columnNames_ = columnNames; - } - - public static PrivilegeSpec createServerScopedPriv(TPrivilegeLevel privilegeLevel) { - return createServerScopedPriv(privilegeLevel, null); - } - - public static PrivilegeSpec createServerScopedPriv(TPrivilegeLevel privilegeLevel, - String serverName) { - return new PrivilegeSpec(privilegeLevel, TPrivilegeScope.SERVER, serverName, null, - null, null, null); - } - - public static PrivilegeSpec createDbScopedPriv(TPrivilegeLevel privilegeLevel, - String dbName) { - Preconditions.checkNotNull(dbName); - return new PrivilegeSpec(privilegeLevel, TPrivilegeScope.DATABASE, null, dbName, - null, null, null); - } - - public static PrivilegeSpec createTableScopedPriv(TPrivilegeLevel privilegeLevel, - TableName tableName) { - Preconditions.checkNotNull(tableName); - return new PrivilegeSpec(privilegeLevel, TPrivilegeScope.TABLE, null, null, tableName, - null, null); - } - - public static PrivilegeSpec createColumnScopedPriv(TPrivilegeLevel privilegeLevel, - TableName tableName, List<String> columnNames) { - Preconditions.checkNotNull(tableName); - Preconditions.checkNotNull(columnNames); - return new PrivilegeSpec(privilegeLevel, TPrivilegeScope.COLUMN, null, null, - tableName, null, columnNames); - } - - public static PrivilegeSpec createUriScopedPriv(TPrivilegeLevel privilegeLevel, - HdfsUri uri) { - Preconditions.checkNotNull(uri); - return new PrivilegeSpec(privilegeLevel, TPrivilegeScope.URI, null, null, null, uri, - null); - } - - public List<TPrivilege> toThrift() { - List<TPrivilege> privileges = Lists.newArrayList(); - if (scope_ == TPrivilegeScope.COLUMN) { - // Create a TPrivilege for every referenced column - for (String column: columnNames_) { - privileges.add(createTPrivilege(column)); - } - } else { - privileges.add(createTPrivilege(null)); - } - return privileges; - } - - /** - * Helper function to construct a TPrivilege from this privilege spec. If the scope is - * COLUMN, 'columnName' must be a non-null column name. Otherwise, 'columnName' is - * null. - */ - private TPrivilege createTPrivilege(String columnName) { - Preconditions.checkState(columnName == null ^ scope_ == TPrivilegeScope.COLUMN); - TPrivilege privilege = new TPrivilege(); - privilege.setScope(scope_); - privilege.setServer_name(serverName_); - // We don't currently filter on privilege level, so set it to an arbitrary value. - privilege.setPrivilege_level(privilegeLevel_); - if (dbName_ != null) privilege.setDb_name(dbName_); - if (tableName_ != null) privilege.setTable_name(tableName_.getTbl()); - if (uri_ != null) privilege.setUri(uri_.toString()); - if (columnName != null) privilege.setColumn_name(columnName); - privilege.setCreate_time_ms(-1); - privilege.setPrivilege_name(RolePrivilege.buildRolePrivilegeName(privilege)); - return privilege; - } - - /** - * Return the table path of a COLUMN level privilege. The table path consists - * of server name, database name and table name. - */ - public static String getTablePath(TPrivilege privilege) { - Preconditions.checkState(privilege.getScope() == TPrivilegeScope.COLUMN); - Joiner joiner = Joiner.on("."); - return joiner.join(privilege.getServer_name(), privilege.getDb_name(), - privilege.getTable_name()); - } - - @Override - public String toSql() { - StringBuilder sb = new StringBuilder(privilegeLevel_.toString()); - sb.append(" ON "); - sb.append(scope_.toString()); - if (scope_ == TPrivilegeScope.SERVER && serverName_ != null) { - sb.append(" " + serverName_); - } else if (scope_ == TPrivilegeScope.DATABASE) { - sb.append(" " + dbName_); - } else if (scope_ == TPrivilegeScope.TABLE) { - sb.append(" " + tableName_.toString()); - } else if (scope_ == TPrivilegeScope.COLUMN) { - sb.append("("); - sb.append(Joiner.on(",").join(columnNames_)); - sb.append(")"); - sb.append(" " + tableName_.toString()); - } else if (scope_ == TPrivilegeScope.URI) { - sb.append(" '" + uri_.getLocation() + "'"); - } - return sb.toString(); - } - - @Override - public void analyze(Analyzer analyzer) throws AnalysisException { - String configServerName = analyzer.getAuthzConfig().getServerName(); - if (serverName_ != null && !serverName_.equals(configServerName)) { - throw new AnalysisException(String.format("Specified server name '%s' does not " + - "match the configured server name '%s'", serverName_, configServerName)); - } - serverName_ = configServerName; - Preconditions.checkState(!Strings.isNullOrEmpty(serverName_)); - Preconditions.checkNotNull(scope_); - - switch (scope_) { - case SERVER: - if (privilegeLevel_ != TPrivilegeLevel.ALL) { - throw new AnalysisException("Only 'ALL' privilege may be applied at " + - "SERVER scope in privilege spec."); - } - break; - case DATABASE: - Preconditions.checkState(!Strings.isNullOrEmpty(dbName_)); - try { - analyzer.getDb(dbName_, true); - } catch (AnalysisException e) { - throw new AnalysisException(String.format("Error setting privileges for " + - "database '%s'. Verify that the database exists and that you have " + - "permissions to issue a GRANT/REVOKE statement.", dbName_)); - } - break; - case URI: - Preconditions.checkNotNull(uri_); - if (privilegeLevel_ != TPrivilegeLevel.ALL) { - throw new AnalysisException("Only 'ALL' privilege may be applied at " + - "URI scope in privilege spec."); - } - uri_.analyze(analyzer, Privilege.ALL, false); - break; - case TABLE: - analyzeTargetTable(analyzer); - break; - case COLUMN: - analyzeColumnPrivScope(analyzer); - break; - default: - throw new IllegalStateException("Unknown TPrivilegeScope in privilege spec: " + - scope_.toString()); - } - } - - /** - * Analyzes a privilege spec at the COLUMN scope. - * Throws an AnalysisException in the following cases: - * 1. No columns are specified. - * 2. Privilege is applied on a view or an external data source. - * 3. Referenced table and/or columns do not exist. - * 4. Privilege level is not SELECT. - */ - private void analyzeColumnPrivScope(Analyzer analyzer) throws AnalysisException { - Preconditions.checkState(scope_ == TPrivilegeScope.COLUMN); - Preconditions.checkNotNull(columnNames_); - if (columnNames_.isEmpty()) { - throw new AnalysisException("Empty column list in column privilege spec."); - } - if (privilegeLevel_ != TPrivilegeLevel.SELECT) { - throw new AnalysisException("Only 'SELECT' privileges are allowed " + - "in a column privilege spec."); - } - Table table = analyzeTargetTable(analyzer); - if (table instanceof View) { - throw new AnalysisException("Column-level privileges on views are not " + - "supported."); - } - if (table instanceof DataSourceTable) { - throw new AnalysisException("Column-level privileges on external data " + - "source tables are not supported."); - } - for (String columnName: columnNames_) { - if (table.getColumn(columnName) == null) { - // The error message should not reveal the existence or absence of a column. - throw new AnalysisException(String.format("Error setting column-level " + - "privileges for table '%s'. Verify that both table and columns exist " + - "and that you have permissions to issue a GRANT/REVOKE statement.", - tableName_.toString())); - } - } - } - - /** - * Verifies that the table referenced in the privilege spec exists in the catalog and - * returns the catalog object. - * Throws an AnalysisException in the following cases: - * 1. The table name is not valid. - * 2. Table is not loaded in the catalog. - * 3. Table does not exist. - */ - private Table analyzeTargetTable(Analyzer analyzer) throws AnalysisException { - Preconditions.checkState(scope_ == TPrivilegeScope.TABLE || - scope_ == TPrivilegeScope.COLUMN); - Preconditions.checkState(!Strings.isNullOrEmpty(tableName_.getTbl())); - Table table = null; - try { - dbName_ = analyzer.getTargetDbName(tableName_); - Preconditions.checkNotNull(dbName_); - table = analyzer.getTable(dbName_, tableName_.getTbl()); - } catch (TableLoadingException e) { - throw new AnalysisException(e.getMessage(), e); - } catch (AnalysisException e) { - if (analyzer.hasMissingTbls()) throw e; - throw new AnalysisException(String.format("Error setting privileges for " + - "table '%s'. Verify that the table exists and that you have permissions " + - "to issue a GRANT/REVOKE statement.", tableName_.toString())); - } - Preconditions.checkNotNull(table); - return table; - } -}
