Java tutorial
// 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 java.util.List; import org.apache.impala.common.Pair; 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 org.apache.impala.common.AnalysisException; import org.apache.impala.util.KuduUtil; import org.apache.impala.thrift.TRangePartition; /** * Represents a range partition of a Kudu table. * * The following cases are supported: * - Bounded on both ends: * PARTITION l_val <[=] VALUES <[=] u_val * - Unbounded lower: * PARTITION VALUES <[=] u_val * - Unbounded upper: * PARTITION l_val <[=] VALUES * - Single value (no range): * PARTITION VALUE = val * - Multi-value: * PARTITION VALUE = (val1, val2, ..., valn) * * Internally, all these cases are represented using the quadruplet: * [(l_val1,..., l_valn), l_bound_type, (u_val1,..., u_valn), u_bound_type], * where l_bound_type, u_bound_type are boolean values indicating if the associated bounds * are inclusive (true) or exclusive (false). */ public class RangePartition implements ParseNode { private final List<Expr> lowerBound_; private final boolean lowerBoundInclusive_; private final List<Expr> upperBound_; private final boolean upperBoundInclusive_; private final boolean isSingletonRange_; private RangePartition(List<Expr> lowerBoundValues, boolean lowerBoundInclusive, List<Expr> upperBoundValues, boolean upperBoundInclusive) { Preconditions.checkNotNull(lowerBoundValues); Preconditions.checkNotNull(upperBoundValues); Preconditions.checkState(!lowerBoundValues.isEmpty() || !upperBoundValues.isEmpty()); lowerBound_ = lowerBoundValues; lowerBoundInclusive_ = lowerBoundInclusive; upperBound_ = upperBoundValues; upperBoundInclusive_ = upperBoundInclusive; isSingletonRange_ = (upperBoundInclusive && lowerBoundInclusive && lowerBoundValues == upperBoundValues); } /** * Constructs a range partition. The range is specified in the CREATE TABLE statement * using the 'PARTITION <expr> OP VALUES OP <expr>' clause. 'lower' corresponds to * the '<expr> OP' pair which defines an optional lower bound. 'upper' corresponds to * the 'OP <expr>' pair which defines an optional upper bound. Since only '<' and * '<=' operators are allowed, operators are represented with boolean values that * indicate inclusive or exclusive bounds. */ public static RangePartition createFromRange(Pair<Expr, Boolean> lower, Pair<Expr, Boolean> upper) { List<Expr> lowerBoundExprs = Lists.newArrayListWithCapacity(1); boolean lowerBoundInclusive = false; List<Expr> upperBoundExprs = Lists.newArrayListWithCapacity(1); boolean upperBoundInclusive = false; if (lower != null) { lowerBoundExprs.add(lower.first); lowerBoundInclusive = lower.second; } if (upper != null) { upperBoundExprs.add(upper.first); upperBoundInclusive = upper.second; } return new RangePartition(lowerBoundExprs, lowerBoundInclusive, upperBoundExprs, upperBoundInclusive); } /** * Constructs a range partition from a set of values. The values are specified in the * CREATE TABLE statement using the 'PARTITION VALUE = <expr>' or the * 'PARTITION VALUE = (<expr>,...,<expr>)' clause. For both cases, the generated * range partition has the same lower and upper bounds. */ public static RangePartition createFromValues(List<Expr> values) { return new RangePartition(values, true, values, true); } @Override public void analyze(Analyzer analyzer) throws AnalysisException { throw new IllegalStateException("Not implemented"); } public void analyze(Analyzer analyzer, List<ColumnDef> partColDefs) throws AnalysisException { analyzeBoundaryValues(lowerBound_, partColDefs, analyzer); if (!isSingletonRange_) { analyzeBoundaryValues(upperBound_, partColDefs, analyzer); } } private void analyzeBoundaryValues(List<Expr> boundaryValues, List<ColumnDef> partColDefs, Analyzer analyzer) throws AnalysisException { if (!boundaryValues.isEmpty() && boundaryValues.size() != partColDefs.size()) { throw new AnalysisException(String.format( "Number of specified range " + "partition values is different than the number of partitioning " + "columns: (%d vs %d). Range partition: '%s'", boundaryValues.size(), partColDefs.size(), toSql())); } for (int i = 0; i < boundaryValues.size(); ++i) { LiteralExpr literal = analyzeBoundaryValue(boundaryValues.get(i), partColDefs.get(i), analyzer); Preconditions.checkNotNull(literal); boundaryValues.set(i, literal); } } private LiteralExpr analyzeBoundaryValue(Expr value, ColumnDef pkColumn, Analyzer analyzer) throws AnalysisException { try { value.analyze(analyzer); } catch (AnalysisException e) { throw new AnalysisException(String.format( "Only constant values are allowed " + "for range-partition bounds: %s", value.toSql()), e); } if (!value.isConstant()) { throw new AnalysisException(String .format("Only constant values are allowed " + "for range-partition bounds: %s", value.toSql())); } LiteralExpr literal = LiteralExpr.create(value, analyzer.getQueryCtx()); if (literal == null) { throw new AnalysisException(String .format("Only constant values are allowed " + "for range-partition bounds: %s", value.toSql())); } if (literal.getType().isNull()) { throw new AnalysisException( String.format("Range partition values cannot be " + "NULL. Range partition: '%s'", toSql())); } org.apache.impala.catalog.Type colType = pkColumn.getType(); Preconditions.checkState(KuduUtil.isSupportedKeyType(colType)); org.apache.impala.catalog.Type literalType = literal.getType(); if (!org.apache.impala.catalog.Type.isImplicitlyCastable(literalType, colType, true)) { throw new AnalysisException(String.format( "Range partition value %s " + "(type: %s) is not type compatible with partitioning column '%s' (type: %s).", literal.toSql(), literalType, pkColumn.getColName(), colType.toSql())); } if (!literalType.equals(colType)) { Expr castLiteral = literal.uncheckedCastTo(colType); Preconditions.checkNotNull(castLiteral); literal = LiteralExpr.create(castLiteral, analyzer.getQueryCtx()); } Preconditions.checkNotNull(literal); return literal; } @Override public String toSql() { StringBuilder output = new StringBuilder(); output.append("PARTITION "); if (isSingletonRange_) { output.append("VALUE = "); if (lowerBound_.size() > 1) output.append("("); List<String> literals = Lists.newArrayList(); for (Expr literal : lowerBound_) literals.add(literal.toSql()); output.append(Joiner.on(",").join(literals)); if (lowerBound_.size() > 1) output.append(")"); } else { if (!lowerBound_.isEmpty()) { Preconditions.checkState(lowerBound_.size() == 1); output.append(lowerBound_.get(0).toSql() + " " + (lowerBoundInclusive_ ? "<=" : "<")); output.append(" "); } output.append("VALUES"); if (!upperBound_.isEmpty()) { Preconditions.checkState(upperBound_.size() == 1); output.append(" "); output.append((upperBoundInclusive_ ? "<=" : "<") + " " + upperBound_.get(0).toSql()); } } return output.toString(); } public TRangePartition toThrift() { TRangePartition tRangePartition = new TRangePartition(); for (Expr literal : lowerBound_) { tRangePartition.addToLower_bound_values(literal.treeToThrift()); } if (!lowerBound_.isEmpty()) { tRangePartition.setIs_lower_bound_inclusive(lowerBoundInclusive_); } for (Expr literal : upperBound_) { tRangePartition.addToUpper_bound_values(literal.treeToThrift()); } if (!upperBound_.isEmpty()) { tRangePartition.setIs_upper_bound_inclusive(upperBoundInclusive_); } Preconditions .checkState(tRangePartition.isSetLower_bound_values() || tRangePartition.isSetUpper_bound_values()); return tRangePartition; } }