Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
PartitionSpec.java
Go to the documentation of this file.
1 package com.cloudera.impala.analysis;
2 
3 import java.util.List;
4 import java.util.Set;
5 
6 import org.apache.hadoop.hive.metastore.api.FieldSchema;
7 
14 import com.cloudera.impala.thrift.TPartitionKeyValue;
15 import com.google.common.base.Joiner;
16 import com.google.common.base.Preconditions;
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Sets;
20 
21 /*
22  * Represents a partition spec - a collection of partition key/values.
23  */
24 public class PartitionSpec implements ParseNode {
25  private final ImmutableList<PartitionKeyValue> partitionSpec_;
27  private Boolean partitionShouldExist_;
29 
30  // Flag to determine if the partition already exists in the target table.
31  // Set during analysis.
32  private Boolean partitionExists_;
33 
34  // The value Hive is configured to use for NULL partition key values.
35  // Set during analysis.
36  private String nullPartitionKeyValue_;
37 
38  public PartitionSpec(List<PartitionKeyValue> partitionSpec) {
39  this.partitionSpec_ = ImmutableList.copyOf(partitionSpec);
40  }
41 
42  public List<PartitionKeyValue> getPartitionSpecKeyValues() {
43  return partitionSpec_;
44  }
45 
46  public String getTbl() { return tableName_.getTbl(); }
47  public void setTableName(TableName tableName) { this.tableName_ = tableName; }
48  public boolean partitionExists() {
49  Preconditions.checkNotNull(partitionExists_);
50  return partitionExists_;
51  }
52 
53  // The value Hive is configured to use for NULL partition key values.
54  // Set during analysis.
55  public String getNullPartitionKeyValue() {
56  Preconditions.checkNotNull(nullPartitionKeyValue_);
58  }
59 
60  // If set, an additional analysis check will be performed to validate the target table
61  // contains the given partition spec.
62  public void setPartitionShouldExist() { partitionShouldExist_ = Boolean.TRUE; }
63 
64  // If set, an additional analysis check will be performed to validate the target table
65  // does not contain the given partition spec.
66  public void setPartitionShouldNotExist() { partitionShouldExist_ = Boolean.FALSE; }
67 
68  // Set the privilege requirement for this partition spec. Must be set prior to
69  // analysis.
70  public void setPrivilegeRequirement(Privilege privilege) {
71  privilegeRequirement_ = privilege;
72  }
73 
74  @Override
75  public void analyze(Analyzer analyzer) throws AnalysisException {
76  Preconditions.checkNotNull(tableName_);
77  Preconditions.checkNotNull(privilegeRequirement_);
78 
79  // Skip adding an audit event when analyzing partitions. The parent table should
80  // be audited outside of the PartitionSpec.
81  Table table = analyzer.getTable(tableName_, privilegeRequirement_, false);
82  String tableName = table.getDb().getName() + "." + getTbl();
83 
84  // Make sure the target table is partitioned.
85  if (table.getMetaStoreTable().getPartitionKeysSize() == 0) {
86  throw new AnalysisException("Table is not partitioned: " + tableName);
87  }
88 
89  // Make sure static partition key values only contain constant exprs.
91  kv.analyze(analyzer);
92  }
93 
94  // Get all keys in the target table.
95  Set<String> targetPartitionKeys = Sets.newHashSet();
96  for (FieldSchema fs: table.getMetaStoreTable().getPartitionKeys()) {
97  targetPartitionKeys.add(fs.getName().toLowerCase());
98  }
99 
100  // All partition keys need to be specified.
101  if (targetPartitionKeys.size() != partitionSpec_.size()) {
102  throw new AnalysisException(String.format("Items in partition spec must exactly " +
103  "match the partition columns in the table definition: %s (%d vs %d)",
104  tableName, partitionSpec_.size(), targetPartitionKeys.size()));
105  }
106 
107  Set<String> keyNames = Sets.newHashSet();
108  // Validate each partition key/value specified, ensuring a matching partition column
109  // exists in the target table, no duplicate keys were specified, and that all the
110  // column types are compatible.
111  for (PartitionKeyValue pk: partitionSpec_) {
112  if (!keyNames.add(pk.getColName().toLowerCase())) {
113  throw new AnalysisException("Duplicate partition key name: " + pk.getColName());
114  }
115 
116  Column c = table.getColumn(pk.getColName());
117  if (c == null) {
118  throw new AnalysisException(String.format(
119  "Partition column '%s' not found in table: %s", pk.getColName(), tableName));
120  } else if (!targetPartitionKeys.contains(pk.getColName().toLowerCase())) {
121  throw new AnalysisException(String.format(
122  "Column '%s' is not a partition column in table: %s",
123  pk.getColName(), tableName));
124  } else if (pk.getValue() instanceof NullLiteral) {
125  // No need for further analysis checks of this partition key value.
126  continue;
127  }
128 
129  Type colType = c.getType();
130  Type literalType = pk.getValue().getType();
131  Type compatibleType =
132  Type.getAssignmentCompatibleType(colType, literalType);
133  if (!compatibleType.isValid()) {
134  throw new AnalysisException(String.format("Value of partition spec (column=%s) "
135  + "has incompatible type: '%s'. Expected type: '%s'.",
136  pk.getColName(), literalType, colType));
137  }
138  // Check for loss of precision with the partition value
139  if (!compatibleType.equals(colType)) {
140  throw new AnalysisException(
141  String.format("Partition key value may result in loss of precision.\n" +
142  "Would need to cast '%s' to '%s' for partition column: %s",
143  pk.getValue().toSql(), colType.toString(), pk.getColName()));
144  }
145  }
146  // Only HDFS tables are partitioned.
147  Preconditions.checkState(table instanceof HdfsTable);
148  HdfsTable hdfsTable = (HdfsTable) table;
149  nullPartitionKeyValue_ = hdfsTable.getNullPartitionKeyValue();
150 
151  partitionExists_ = hdfsTable.getPartition(partitionSpec_) != null;
152  if (partitionShouldExist_ != null) {
153  if (partitionShouldExist_ && !partitionExists_) {
154  throw new AnalysisException("Partition spec does not exist: (" +
155  Joiner.on(", ").join(partitionSpec_) + ").");
156  } else if (!partitionShouldExist_ && partitionExists_) {
157  throw new AnalysisException("Partition spec already exists: (" +
158  Joiner.on(", ").join(partitionSpec_) + ").");
159  }
160  }
161  }
162 
163  /*
164  * Returns the Thrift representation of this PartitionSpec.
165  */
166  public List<TPartitionKeyValue> toThrift() {
167  List<TPartitionKeyValue> thriftPartitionSpec = Lists.newArrayList();
169  String value = PartitionKeyValue.getPartitionKeyValueString(
170  kv.getLiteralValue(), getNullPartitionKeyValue());
171  thriftPartitionSpec.add(new TPartitionKeyValue(kv.getColName(), value));
172  }
173  return thriftPartitionSpec;
174  }
175 
176  @Override
177  public String toSql() {
178  List<String> partitionSpecStr = Lists.newArrayList();
180  partitionSpecStr.add(kv.getColName() + "=" + kv.getValue().toSql());
181  }
182  return String.format("PARTITION (%s)", Joiner.on(", ").join(partitionSpecStr));
183  }
184 }
List< TPartitionKeyValue > toThrift()
List< PartitionKeyValue > getPartitionSpecKeyValues()
final ImmutableList< PartitionKeyValue > partitionSpec_
PartitionSpec(List< PartitionKeyValue > partitionSpec)
void setPrivilegeRequirement(Privilege privilege)