Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
BinaryPredicate.java
Go to the documentation of this file.
1 // Copyright 2012 Cloudera Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.cloudera.impala.analysis;
16 
17 import java.util.ArrayList;
18 import java.util.List;
19 
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22 
28 import com.cloudera.impala.common.Pair;
29 import com.cloudera.impala.common.Reference;
30 import com.cloudera.impala.extdatasource.thrift.TComparisonOp;
31 import com.cloudera.impala.thrift.TExprNode;
32 import com.cloudera.impala.thrift.TExprNodeType;
33 import com.google.common.base.Objects;
34 import com.google.common.base.Preconditions;
35 import com.google.common.base.Predicates;
36 import com.google.common.collect.Lists;
37 
42 public class BinaryPredicate extends Predicate {
43  private final static Logger LOG = LoggerFactory.getLogger(BinaryPredicate.class);
44 
45  public enum Operator {
46  EQ("=", "eq", TComparisonOp.EQ),
47  NE("!=", "ne", TComparisonOp.NE),
48  LE("<=", "le", TComparisonOp.LE),
49  GE(">=", "ge", TComparisonOp.GE),
50  LT("<", "lt", TComparisonOp.LT),
51  GT(">", "gt", TComparisonOp.GT),
52  // Same as EQ, except it returns True if the rhs is NULL. There is no backend
53  // function for this. The functionality is embedded in the hash-join
54  // implementation.
55  NULL_MATCHING_EQ("=", "null_matching_eq", TComparisonOp.EQ);
56 
57  private final String description_;
58  private final String name_;
59  private final TComparisonOp thriftOp_;
60 
61  private Operator(String description, String name, TComparisonOp thriftOp) {
62  this.description_ = description;
63  this.name_ = name;
64  this.thriftOp_ = thriftOp;
65  }
66 
67  @Override
68  public String toString() { return description_; }
69  public String getName() { return name_; }
70  public TComparisonOp getThriftOp() { return thriftOp_; }
71 
72  public Operator converse() {
73  switch (this) {
74  case EQ: return EQ;
75  case NE: return NE;
76  case LE: return GE;
77  case GE: return LE;
78  case LT: return GT;
79  case GT: return LT;
80  case NULL_MATCHING_EQ:
81  throw new IllegalStateException("Not implemented");
82  default: throw new IllegalStateException("Invalid operator");
83  }
84  }
85  }
86 
87  public static void initBuiltins(Db db) {
88  for (Type t: Type.getSupportedTypes()) {
89  if (t.isNull()) continue; // NULL is handled through type promotion.
90  db.addBuiltin(ScalarFunction.createBuiltinOperator(
91  Operator.EQ.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
92  db.addBuiltin(ScalarFunction.createBuiltinOperator(
93  Operator.NE.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
94  db.addBuiltin(ScalarFunction.createBuiltinOperator(
95  Operator.LE.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
96  db.addBuiltin(ScalarFunction.createBuiltinOperator(
97  Operator.GE.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
98  db.addBuiltin(ScalarFunction.createBuiltinOperator(
99  Operator.LT.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
100  db.addBuiltin(ScalarFunction.createBuiltinOperator(
101  Operator.GT.getName(), Lists.newArrayList(t, t), Type.BOOLEAN));
102  }
103  }
104 
105  private Operator op_;
106 
107  public Operator getOp() { return op_; }
108  public void setOp(Operator op) { op_ = op; }
109 
110  public BinaryPredicate(Operator op, Expr e1, Expr e2) {
111  super();
112  this.op_ = op;
113  Preconditions.checkNotNull(e1);
114  children_.add(e1);
115  Preconditions.checkNotNull(e2);
116  children_.add(e2);
117  }
118 
119  protected BinaryPredicate(BinaryPredicate other) {
120  super(other);
121  op_ = other.op_;
122  }
123 
124  public boolean isNullMatchingEq() { return op_ == Operator.NULL_MATCHING_EQ; }
125 
126  @Override
127  public String toSqlImpl() {
128  return getChild(0).toSql() + " " + op_.toString() + " " + getChild(1).toSql();
129  }
130 
131  @Override
132  protected void toThrift(TExprNode msg) {
133  Preconditions.checkState(children_.size() == 2);
134  // Cannot serialize a nested predicate.
135  Preconditions.checkState(!contains(Subquery.class));
136  // This check is important because we often clone and/or evaluate predicates,
137  // and it's easy to get the casting logic wrong, e.g., cloned predicates
138  // with expr substitutions need to be re-analyzed with reanalyze().
139  Preconditions.checkState(getChild(0).getType().getPrimitiveType() ==
140  getChild(1).getType().getPrimitiveType(),
141  "child 0 type: " + getChild(0).getType() +
142  " child 1 type: " + getChild(1).getType());
143  msg.node_type = TExprNodeType.FUNCTION_CALL;
144  }
145 
146  @Override
147  public String debugString() {
148  return Objects.toStringHelper(this)
149  .add("op", op_)
150  .addValue(super.debugString())
151  .toString();
152  }
153 
154  @Override
155  public void analyze(Analyzer analyzer) throws AnalysisException {
156  if (isAnalyzed_) return;
157  super.analyze(analyzer);
158 
160  String opName = op_.getName().equals("null_matching_eq") ? "eq" : op_.getName();
161  fn_ = getBuiltinFunction(analyzer, opName, collectChildReturnTypes(),
163  if (fn_ == null) {
164  // Construct an appropriate error message and throw an AnalysisException.
165  String errMsg = "operands of type " + getChild(0).getType().toSql() + " and " +
166  getChild(1).getType().toSql() + " are not comparable: " + toSql();
167 
168  // Check if any of the children is a Subquery that does not return a
169  // scalar.
170  for (Expr expr: children_) {
171  if (expr instanceof Subquery && !expr.getType().isScalarType()) {
172  errMsg = "Subquery must return a single row: " + expr.toSql();
173  break;
174  }
175  }
176 
177  throw new AnalysisException(errMsg);
178  }
179  Preconditions.checkState(fn_.getReturnType().isBoolean());
180 
181  ArrayList<Expr> subqueries = Lists.newArrayList();
182  collectAll(Predicates.instanceOf(Subquery.class), subqueries);
183  if (subqueries.size() > 1) {
184  // TODO Remove that restriction when we add support for independent subquery
185  // evaluation.
186  throw new AnalysisException("Multiple subqueries are not supported in binary " +
187  "predicates: " + toSql());
188  }
189  if (contains(ExistsPredicate.class)) {
190  throw new AnalysisException("EXISTS subquery predicates are not " +
191  "supported in binary predicates: " + toSql());
192  }
193 
194  List<InPredicate> inPredicates = Lists.newArrayList();
195  collect(InPredicate.class, inPredicates);
196  for (InPredicate inPredicate: inPredicates) {
197  if (inPredicate.contains(Subquery.class)) {
198  throw new AnalysisException("IN subquery predicates are not supported in " +
199  "binary predicates: " + toSql());
200  }
201  }
202 
203  // Don't perform any casting for predicates with subqueries here. Any casting
204  // required will be performed when the subquery is unnested.
205  if (!contains(Subquery.class)) castForFunctionCall(true);
206 
207  // determine selectivity
208  // TODO: Compute selectivity for nested predicates
209  Reference<SlotRef> slotRefRef = new Reference<SlotRef>();
210  if (op_ == Operator.EQ
211  && isSingleColumnPredicate(slotRefRef, null)
212  && slotRefRef.getRef().getNumDistinctValues() > 0) {
213  Preconditions.checkState(slotRefRef.getRef() != null);
214  selectivity_ = 1.0 / slotRefRef.getRef().getNumDistinctValues();
215  selectivity_ = Math.max(0, Math.min(1, selectivity_));
216  } else {
217  // TODO: improve using histograms, once they show up
219  }
220  }
221 
228  // check left operand
229  SlotRef slotRef = getChild(0).unwrapSlotRef(false);
230  if (slotRef != null && slotRef.getSlotId() == id) return getChild(1);
231  // check right operand
232  slotRef = getChild(1).unwrapSlotRef(false);
233  if (slotRef != null && slotRef.getSlotId() == id) return getChild(0);
234  return null;
235  }
236 
241  public static Pair<SlotId, SlotId> getEqSlots(Expr e) {
242  if (!(e instanceof BinaryPredicate)) return null;
243  return ((BinaryPredicate) e).getEqSlots();
244  }
245 
250  @Override
251  public Pair<SlotId, SlotId> getEqSlots() {
252  if (op_ != Operator.EQ) return null;
253  SlotRef lhs = getChild(0).unwrapSlotRef(true);
254  if (lhs == null) return null;
255  SlotRef rhs = getChild(1).unwrapSlotRef(true);
256  if (rhs == null) return null;
257  return new Pair<SlotId, SlotId>(lhs.getSlotId(), rhs.getSlotId());
258  }
259 
264  @Override
266  SlotRef slotRef = getChild(0).unwrapSlotRef(true);
267  if (slotRef != null) return slotRef;
268  return getChild(1).unwrapSlotRef(true);
269  }
270 
274  @Override
275  public Expr negate() {
276  Operator newOp = null;
277  switch (op_) {
278  case EQ:
279  newOp = Operator.NE;
280  break;
281  case NE:
282  newOp = Operator.EQ;
283  break;
284  case LT:
285  newOp = Operator.GE;
286  break;
287  case LE:
288  newOp = Operator.GT;
289  break;
290  case GE:
291  newOp = Operator.LT;
292  break;
293  case GT:
294  newOp = Operator.LE;
295  break;
296  case NULL_MATCHING_EQ:
297  throw new IllegalStateException("Not implemented");
298  }
299  return new BinaryPredicate(newOp, getChild(0), getChild(1));
300  }
301 
302  @Override
303  public boolean equals(Object obj) {
304  if (!super.equals(obj)) return false;
305  BinaryPredicate other = (BinaryPredicate) obj;
306  return op_.equals(other.op_);
307  }
308 
309  @Override
310  public Expr clone() { return new BinaryPredicate(this); }
311 }
void castForFunctionCall(boolean ignoreWildcardDecimals)
Definition: Expr.java:312
BinaryPredicate(Operator op, Expr e1, Expr e2)
void convertNumericLiteralsFromDecimal(Analyzer analyzer)
Definition: Expr.java:413
static final ScalarType BOOLEAN
Definition: Type.java:46
Operator(String description, String name, TComparisonOp thriftOp)
Function getBuiltinFunction(Analyzer analyzer, String name, Type[] argTypes, CompareMode mode)
Definition: Expr.java:290
boolean isSingleColumnPredicate(Reference< SlotRef > slotRefRef, Reference< Integer > idxRef)
Definition: Predicate.java:64
string name
Definition: cpu-info.cc:50
static ArrayList< ScalarType > getSupportedTypes()
Definition: Type.java:109
static double DEFAULT_SELECTIVITY
Definition: Expr.java:63
static Pair< SlotId, SlotId > getEqSlots(Expr e)