Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ArithmeticExpr.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 
23 import com.cloudera.impala.thrift.TExprNode;
24 import com.cloudera.impala.thrift.TExprNodeType;
25 import com.google.common.base.Objects;
26 import com.google.common.base.Preconditions;
27 import com.google.common.collect.Lists;
28 
29 public class ArithmeticExpr extends Expr {
30  enum Operator {
31  MULTIPLY("*", "multiply"),
32  DIVIDE("/", "divide"),
33  MOD("%", "mod"),
34  INT_DIVIDE("DIV", "int_divide"),
35  ADD("+", "add"),
36  SUBTRACT("-", "subtract"),
37  BITAND("&", "bitand"),
38  BITOR("|", "bitor"),
39  BITXOR("^", "bitxor"),
40  BITNOT("~", "bitnot");
41 
42  private final String description_;
43  private final String name_;
44 
45  private Operator(String description, String name) {
46  this.description_ = description;
47  this.name_ = name;
48  }
49 
50  @Override
51  public String toString() { return description_; }
52  public String getName() { return name_; }
53  }
54 
55  private final Operator op_;
56 
57  public Operator getOp() { return op_; }
58 
59  public ArithmeticExpr(Operator op, Expr e1, Expr e2) {
60  super();
61  this.op_ = op;
62  Preconditions.checkNotNull(e1);
63  children_.add(e1);
64  Preconditions.checkArgument(op == Operator.BITNOT && e2 == null
65  || op != Operator.BITNOT && e2 != null);
66  if (e2 != null) children_.add(e2);
67  }
68 
72  protected ArithmeticExpr(ArithmeticExpr other) {
73  super(other);
74  op_ = other.op_;
75  }
76 
77  public static void initBuiltins(Db db) {
78  for (Type t: Type.getNumericTypes()) {
79  db.addBuiltin(ScalarFunction.createBuiltinOperator(
80  Operator.MULTIPLY.getName(), Lists.newArrayList(t, t), t));
81  db.addBuiltin(ScalarFunction.createBuiltinOperator(
82  Operator.ADD.getName(), Lists.newArrayList(t, t), t));
83  db.addBuiltin(ScalarFunction.createBuiltinOperator(
84  Operator.SUBTRACT.getName(), Lists.newArrayList(t, t), t));
85  }
86  db.addBuiltin(ScalarFunction.createBuiltinOperator(
87  Operator.DIVIDE.getName(),
88  Lists.<Type>newArrayList(Type.DOUBLE, Type.DOUBLE),
89  Type.DOUBLE));
90  db.addBuiltin(ScalarFunction.createBuiltinOperator(
91  Operator.DIVIDE.getName(),
92  Lists.<Type>newArrayList(Type.DECIMAL, Type.DECIMAL),
93  Type.DECIMAL));
94 
95  // MOD() is registered as a builtin, see impala_functions.py
96  for (Type t: Type.getIntegerTypes()) {
97  db.addBuiltin(ScalarFunction.createBuiltinOperator(
98  Operator.INT_DIVIDE.getName(), Lists.newArrayList(t, t), t));
99  db.addBuiltin(ScalarFunction.createBuiltinOperator(
100  Operator.BITAND.getName(), Lists.newArrayList(t, t), t));
101  db.addBuiltin(ScalarFunction.createBuiltinOperator(
102  Operator.BITOR.getName(), Lists.newArrayList(t, t), t));
103  db.addBuiltin(ScalarFunction.createBuiltinOperator(
104  Operator.BITXOR.getName(), Lists.newArrayList(t, t), t));
105  db.addBuiltin(ScalarFunction.createBuiltinOperator(
106  Operator.BITNOT.getName(), Lists.newArrayList(t), t));
107  }
108  }
109 
110  @Override
111  public String debugString() {
112  return Objects.toStringHelper(this)
113  .add("op", op_)
114  .addValue(super.debugString())
115  .toString();
116  }
117 
118  @Override
119  public String toSqlImpl() {
120  if (children_.size() == 1) {
121  return op_.toString() + getChild(0).toSql();
122  } else {
123  Preconditions.checkState(children_.size() == 2);
124  return getChild(0).toSql() + " " + op_.toString() + " " + getChild(1).toSql();
125  }
126  }
127 
128  @Override
129  protected void toThrift(TExprNode msg) {
130  msg.node_type = TExprNodeType.FUNCTION_CALL;
131  }
132 
139  void castChild(int childIdx, Type targetType) throws AnalysisException {
140  Type t = getChild(childIdx).getType();
141  if (t.matchesType(targetType)) return;
142  if (targetType.isDecimal() && !t.isNull()) {
143  Preconditions.checkState(t.isScalarType());
144  targetType = ((ScalarType) t).getMinResolutionDecimal();
145  }
146  castChild(targetType, childIdx);
147  }
148 
149  @Override
150  public void analyze(Analyzer analyzer) throws AnalysisException {
151  if (isAnalyzed_) return;
152  super.analyze(analyzer);
153  for (Expr child: children_) {
154  Expr operand = (Expr) child;
155  if (!operand.type_.isNumericType() && !operand.type_.isNull()) {
156  String errMsg = "Arithmetic operation requires numeric operands: " + toSql();
157  if (operand instanceof Subquery && !operand.type_.isScalarType()) {
158  errMsg = "Subquery must return a single row: " + operand.toSql();
159  }
160  throw new AnalysisException(errMsg);
161  }
162  }
163 
164  Type t0 = getChild(0).getType();
165  // bitnot is the only unary op, deal with it here
166  if (op_ == Operator.BITNOT) {
167  // Special case ~NULL to resolve to TYPE_INT.
168  if (!t0.isNull() && !t0.isIntegerType()) {
169  throw new AnalysisException("Bitwise operations only allowed on integer " +
170  "types: " + toSql());
171  }
172  if (t0.isNull()) castChild(0, Type.INT);
175  Preconditions.checkNotNull(fn_);
176  castForFunctionCall(false);
177  type_ = fn_.getReturnType();
178  return;
179  }
180 
181  Preconditions.checkState(children_.size() == 2); // only bitnot is unary
183  t0 = getChild(0).getType();
184  Type t1 = getChild(1).getType();
185 
186  String fnName = op_.getName();
187  switch (op_) {
188  case ADD:
189  case SUBTRACT:
190  case DIVIDE:
191  case MULTIPLY:
192  case MOD:
193  type_ = TypesUtil.getArithmeticResultType(t0, t1, op_);
194  // If both of the children are null, we'll default to the DOUBLE version of the
195  // operator. This prevents the BE from seeing NULL_TYPE.
196  if (type_.isNull()) type_ = Type.DOUBLE;
197  break;
198 
199  case INT_DIVIDE:
200  case BITAND:
201  case BITOR:
202  case BITXOR:
203  if ((!t0.isNull() & !t0.isIntegerType()) ||
204  (!t1.isNull() && !t1.isIntegerType())) {
205  throw new AnalysisException("Invalid non-integer argument to operation '" +
206  op_.toString() + "': " + this.toSql());
207  }
208  type_ = Type.getAssignmentCompatibleType(t0, t1);
209  // If both of the children are null, we'll default to the INT version of the
210  // operator. This prevents the BE from seeing NULL_TYPE.
211  if (type_.isNull()) type_ = Type.INT;
212  Preconditions.checkState(type_.isIntegerType());
213  break;
214 
215  default:
216  // the programmer forgot to deal with a case
217  Preconditions.checkState(false,
218  "Unknown arithmetic operation " + op_.toString() + " in: " + this.toSql());
219  break;
220  }
221 
222  // Don't cast from decimal to decimal. The BE function can just handle this.
223  if (!(type_.isDecimal() && t0.isDecimal())) castChild(0, type_);
224  if (!(type_.isDecimal() && t1.isDecimal())) castChild(1, type_);
225  t0 = getChild(0).getType();
226  t1 = getChild(1).getType();
227 
228  fn_ = getBuiltinFunction(analyzer, fnName, collectChildReturnTypes(),
230  if (fn_ == null) {
231  Preconditions.checkState(false, String.format("No match " +
232  "for '%s' with operand types %s and %s", toSql(), t0, t1));
233  }
234  Preconditions.checkState(type_.matchesType(fn_.getReturnType()));
235  }
236 
237  @Override
238  public Expr clone() { return new ArithmeticExpr(this); }
239 }
static ArrayList< ScalarType > getIntegerTypes()
Definition: Type.java:103
void castForFunctionCall(boolean ignoreWildcardDecimals)
Definition: Expr.java:312
void convertNumericLiteralsFromDecimal(Analyzer analyzer)
Definition: Expr.java:413
boolean matchesType(Type t)
Definition: Type.java:218
void castChild(int childIdx, Type targetType)
static final ScalarType DOUBLE
Definition: Type.java:52
static ArrayList< ScalarType > getNumericTypes()
Definition: Type.java:106
static final ScalarType INT
Definition: Type.java:49
static final ScalarType DECIMAL
Definition: Type.java:61
Function getBuiltinFunction(Analyzer analyzer, String name, Type[] argTypes, CompareMode mode)
Definition: Expr.java:290
ArithmeticExpr(Operator op, Expr e1, Expr e2)
string name
Definition: cpu-info.cc:50