Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
CaseExpr.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.List;
18 
26 import com.cloudera.impala.thrift.TCaseExpr;
27 import com.cloudera.impala.thrift.TExprNode;
28 import com.cloudera.impala.thrift.TExprNodeType;
29 import com.google.common.annotations.VisibleForTesting;
30 import com.google.common.base.Preconditions;
31 import com.google.common.collect.Lists;
32 
65 public class CaseExpr extends Expr {
66 
67  // Set if constructed from a DECODE, null otherwise.
69 
70  private boolean hasCaseExpr_;
71  private boolean hasElseExpr_;
72 
73  public CaseExpr(Expr caseExpr, List<CaseWhenClause> whenClauses, Expr elseExpr) {
74  super();
75  if (caseExpr != null) {
76  children_.add(caseExpr);
77  hasCaseExpr_ = true;
78  }
79  for (CaseWhenClause whenClause: whenClauses) {
80  Preconditions.checkNotNull(whenClause.getWhenExpr());
81  children_.add(whenClause.getWhenExpr());
82  Preconditions.checkNotNull(whenClause.getThenExpr());
83  children_.add(whenClause.getThenExpr());
84  }
85  if (elseExpr != null) {
86  children_.add(elseExpr);
87  hasElseExpr_ = true;
88  }
89  }
90 
107  public CaseExpr(FunctionCallExpr decodeExpr) {
108  super();
109  decodeExpr_ = decodeExpr;
110  hasCaseExpr_ = false;
111 
112  int childIdx = 0;
113  Expr encoded = null;
114  Expr encodedIsNull = null;
115  if (!decodeExpr.getChildren().isEmpty()) {
116  encoded = decodeExpr.getChild(childIdx++);
117  encodedIsNull = new IsNullPredicate(encoded, false);
118  }
119 
120  // Add the key_expr/val_expr pairs
121  while (childIdx + 2 <= decodeExpr.getChildren().size()) {
122  Expr candidate = decodeExpr.getChild(childIdx++);
123  if (candidate.isLiteral()) {
124  if (candidate.isNullLiteral()) {
125  // An example case is DECODE(foo, NULL, bar), since NULLs are considered
126  // equal, this becomes CASE WHEN foo IS NULL THEN bar END.
127  children_.add(encodedIsNull);
128  } else {
129  children_.add(new BinaryPredicate(
130  BinaryPredicate.Operator.EQ, encoded, candidate));
131  }
132  } else {
133  children_.add(new CompoundPredicate(CompoundPredicate.Operator.OR,
134  new CompoundPredicate(CompoundPredicate.Operator.AND,
135  encodedIsNull, new IsNullPredicate(candidate, false)),
136  new BinaryPredicate(BinaryPredicate.Operator.EQ, encoded, candidate)));
137  }
138 
139  // Add the value
140  children_.add(decodeExpr.getChild(childIdx++));
141  }
142 
143  // Add the default value
144  if (childIdx < decodeExpr.getChildren().size()) {
145  hasElseExpr_ = true;
146  children_.add(decodeExpr.getChild(childIdx));
147  }
148  }
149 
153  protected CaseExpr(CaseExpr other) {
154  super(other);
155  decodeExpr_ = other.decodeExpr_;
156  hasCaseExpr_ = other.hasCaseExpr_;
157  hasElseExpr_ = other.hasElseExpr_;
158  }
159 
160  public static void initBuiltins(Db db) {
161  for (Type t: Type.getSupportedTypes()) {
162  if (t.isNull()) continue;
163  if (t.isScalarType(PrimitiveType.CHAR)) continue;
164  // TODO: case is special and the signature cannot be represented.
165  // It is alternating varargs
166  // e.g. case(bool, type, bool type, bool type, etc).
167  // Instead we just add a version for each of the return types
168  // e.g. case(BOOLEAN), case(INT), etc
169  db.addBuiltin(ScalarFunction.createBuiltinOperator(
170  "case", "", Lists.newArrayList(t), t));
171  // Same for DECODE
172  db.addBuiltin(ScalarFunction.createBuiltinOperator(
173  "decode", "", Lists.newArrayList(t), t));
174  }
175  }
176 
177  @Override
178  public boolean equals(Object obj) {
179  if (!super.equals(obj)) return false;
180  CaseExpr expr = (CaseExpr) obj;
181  return hasCaseExpr_ == expr.hasCaseExpr_
182  && hasElseExpr_ == expr.hasElseExpr_
183  && isDecode() == expr.isDecode();
184  }
185 
186  @Override
187  public String toSqlImpl() {
188  return (decodeExpr_ == null) ? toCaseSql() : decodeExpr_.toSqlImpl();
189  }
190 
191  @VisibleForTesting
192  String toCaseSql() {
193  StringBuilder output = new StringBuilder("CASE");
194  int childIdx = 0;
195  if (hasCaseExpr_) {
196  output.append(" " + children_.get(childIdx++).toSql());
197  }
198  while (childIdx + 2 <= children_.size()) {
199  output.append(" WHEN " + children_.get(childIdx++).toSql());
200  output.append(" THEN " + children_.get(childIdx++).toSql());
201  }
202  if (hasElseExpr_) {
203  output.append(" ELSE " + children_.get(children_.size() - 1).toSql());
204  }
205  output.append(" END");
206  return output.toString();
207  }
208 
209  @Override
210  protected void toThrift(TExprNode msg) {
211  msg.node_type = TExprNodeType.CASE_EXPR;
212  msg.case_expr = new TCaseExpr(hasCaseExpr_, hasElseExpr_);
213  }
214 
215  @Override
216  public void analyze(Analyzer analyzer) throws AnalysisException {
217  if (isAnalyzed_) return;
218  super.analyze(analyzer);
219  castChildCharsToStrings(analyzer);
220 
221  if (isDecode()) {
222  Preconditions.checkState(!hasCaseExpr_);
223  // decodeExpr_.analyze() would fail validating function existence. The complex
224  // vararg signature is currently unsupported.
225  FunctionCallExpr.validateScalarFnParams(decodeExpr_.getParams());
226  if (decodeExpr_.getChildren().size() < 3) {
227  throw new AnalysisException("DECODE in '" + toSql() + "' requires at least 3 "
228  + "arguments.");
229  }
230  }
231 
232  // Keep track of maximum compatible type of case expr and all when exprs.
233  Type whenType = null;
234  // Keep track of maximum compatible type of else expr and all then exprs.
235  Type returnType = null;
236  // Remember last of these exprs for error reporting.
237  Expr lastCompatibleThenExpr = null;
238  Expr lastCompatibleWhenExpr = null;
239  int loopEnd = children_.size();
240  if (hasElseExpr_) {
241  --loopEnd;
242  }
243  int loopStart;
244  Expr caseExpr = null;
245  // Set loop start, and initialize returnType as type of castExpr.
246  if (hasCaseExpr_) {
247  loopStart = 1;
248  caseExpr = children_.get(0);
249  caseExpr.analyze(analyzer);
250  whenType = caseExpr.getType();
251  lastCompatibleWhenExpr = children_.get(0);
252  } else {
253  whenType = Type.BOOLEAN;
254  loopStart = 0;
255  }
256 
257  // Go through when/then exprs and determine compatible types.
258  for (int i = loopStart; i < loopEnd; i += 2) {
259  Expr whenExpr = children_.get(i);
260  if (hasCaseExpr_) {
261  // Determine maximum compatible type of the case expr,
262  // and all when exprs seen so far. We will add casts to them at the very end.
263  whenType = analyzer.getCompatibleType(whenType,
264  lastCompatibleWhenExpr, whenExpr);
265  lastCompatibleWhenExpr = whenExpr;
266  } else {
267  // If no case expr was given, then the when exprs should always return
268  // boolean or be castable to boolean.
269  if (!Type.isImplicitlyCastable(whenExpr.getType(),
270  Type.BOOLEAN)) {
271  Preconditions.checkState(isCase());
272  throw new AnalysisException("When expr '" + whenExpr.toSql() + "'" +
273  " is not of type boolean and not castable to type boolean.");
274  }
275  // Add a cast if necessary.
276  if (!whenExpr.getType().isBoolean()) castChild(Type.BOOLEAN, i);
277  }
278  // Determine maximum compatible type of the then exprs seen so far.
279  // We will add casts to them at the very end.
280  Expr thenExpr = children_.get(i + 1);
281  returnType = analyzer.getCompatibleType(returnType,
282  lastCompatibleThenExpr, thenExpr);
283  lastCompatibleThenExpr = thenExpr;
284  }
285  if (hasElseExpr_) {
286  Expr elseExpr = children_.get(children_.size() - 1);
287  returnType = analyzer.getCompatibleType(returnType,
288  lastCompatibleThenExpr, elseExpr);
289  }
290 
291  // Make sure BE doesn't see TYPE_NULL by picking an arbitrary type
292  if (whenType.isNull()) whenType = ScalarType.BOOLEAN;
293  if (returnType.isNull()) returnType = ScalarType.BOOLEAN;
294 
295  // Add casts to case expr to compatible type.
296  if (hasCaseExpr_) {
297  // Cast case expr.
298  if (!children_.get(0).type_.equals(whenType)) {
299  castChild(whenType, 0);
300  }
301  // Add casts to when exprs to compatible type.
302  for (int i = loopStart; i < loopEnd; i += 2) {
303  if (!children_.get(i).type_.equals(whenType)) {
304  castChild(whenType, i);
305  }
306  }
307  }
308  // Cast then exprs to compatible type.
309  for (int i = loopStart + 1; i < children_.size(); i += 2) {
310  if (!children_.get(i).type_.equals(returnType)) {
311  castChild(returnType, i);
312  }
313  }
314  // Cast else expr to compatible type.
315  if (hasElseExpr_) {
316  if (!children_.get(children_.size() - 1).type_.equals(returnType)) {
317  castChild(returnType, children_.size() - 1);
318  }
319  }
320 
321  // Do the function lookup just based on the whenType.
322  Type[] args = new Type[1];
323  args[0] = whenType;
324  fn_ = getBuiltinFunction(analyzer, "case", args, CompareMode.IS_SUPERTYPE_OF);
325  Preconditions.checkNotNull(fn_);
326  type_ = returnType;
327  }
328 
329  private boolean isCase() { return !isDecode(); }
330  private boolean isDecode() { return decodeExpr_ != null; }
331 
332  @Override
333  public Expr clone() { return new CaseExpr(this); }
334 }
CaseExpr(Expr caseExpr, List< CaseWhenClause > whenClauses, Expr elseExpr)
Definition: CaseExpr.java:73
static final ScalarType BOOLEAN
Definition: Type.java:46
CaseExpr(FunctionCallExpr decodeExpr)
Definition: CaseExpr.java:107
static boolean isImplicitlyCastable(Type t1, Type t2)
Definition: Type.java:259
PrimitiveType
Definition: types.h:27
void analyze(Analyzer analyzer)
Definition: CaseExpr.java:216
Function getBuiltinFunction(Analyzer analyzer, String name, Type[] argTypes, CompareMode mode)
Definition: Expr.java:290
void castChild(Type targetType, int childIndex)
Definition: Expr.java:1008
static ArrayList< ScalarType > getSupportedTypes()
Definition: Type.java:109
void castChildCharsToStrings(Analyzer analyzer)
Definition: Expr.java:277