Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
TimestampArithmeticExpr.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.HashMap;
18 import java.util.Map;
19 
23 import com.cloudera.impala.thrift.TExprNode;
24 import com.cloudera.impala.thrift.TExprNodeType;
25 import com.google.common.base.Preconditions;
26 
32 public class TimestampArithmeticExpr extends Expr {
33 
34  // Time units supported in timestamp arithmetic.
35  public static enum TimeUnit {
36  YEAR("YEAR"),
37  MONTH("MONTH"),
38  WEEK("WEEK"),
39  DAY("DAY"),
40  HOUR("HOUR"),
41  MINUTE("MINUTE"),
42  SECOND("SECOND"),
43  MILLISECOND("MILLISECOND"),
44  MICROSECOND("MICROSECOND"),
45  NANOSECOND("NANOSECOND");
46 
47  private final String description_;
48 
49  private TimeUnit(String description) {
50  this.description_ = description;
51  }
52 
53  @Override
54  public String toString() {
55  return description_;
56  }
57  }
58 
59  private static Map<String, TimeUnit> TIME_UNITS_MAP = new HashMap<String, TimeUnit>();
60  static {
61  for (TimeUnit timeUnit : TimeUnit.values()) {
62  TIME_UNITS_MAP.put(timeUnit.toString(), timeUnit);
63  TIME_UNITS_MAP.put(timeUnit.toString() + "S", timeUnit);
64  }
65  }
66 
67  // Set for function call-like arithmetic.
68  private final String funcName_;
69  private ArithmeticExpr.Operator op_;
70 
71  // Keep the original string passed in the c'tor to resolve
72  // ambiguities with other uses of IDENT during query parsing.
73  private final String timeUnitIdent_;
75 
76  // Indicates an expr where the interval comes first, e.g., 'interval b year + a'.
77  private final boolean intervalFirst_;
78 
79  // C'tor for function-call like arithmetic, e.g., 'date_add(a, interval b year)'.
80  public TimestampArithmeticExpr(String funcName, Expr e1, Expr e2,
81  String timeUnitIdent) {
82  this.funcName_ = funcName.toLowerCase();
83  this.timeUnitIdent_ = timeUnitIdent;
84  this.intervalFirst_ = false;
85  children_.add(e1);
86  children_.add(e2);
87  }
88 
89  // C'tor for non-function-call like arithmetic, e.g., 'a + interval b year'.
90  // e1 always refers to the timestamp to be added/subtracted from, and e2
91  // to the time value (even in the interval-first case).
92  public TimestampArithmeticExpr(ArithmeticExpr.Operator op, Expr e1, Expr e2,
93  String timeUnitIdent, boolean intervalFirst) {
94  Preconditions.checkState(op == Operator.ADD || op == Operator.SUBTRACT);
95  this.funcName_ = null;
96  this.op_ = op;
97  this.timeUnitIdent_ = timeUnitIdent;
98  this.intervalFirst_ = intervalFirst;
99  children_.add(e1);
100  children_.add(e2);
101  }
102 
107  super(other);
108  funcName_ = other.funcName_;
109  op_ = other.op_;
110  timeUnitIdent_ = other.timeUnitIdent_;
111  timeUnit_ = other.timeUnit_;
112  intervalFirst_ = other.intervalFirst_;
113  }
114 
115  @Override
116  public void analyze(Analyzer analyzer) throws AnalysisException {
117  if (isAnalyzed_) return;
118  super.analyze(analyzer);
119 
120  if (funcName_ != null) {
121  // Set op based on funcName for function-call like version.
122  if (funcName_.equals("date_add")) {
124  } else if (funcName_.equals("date_sub")) {
126  } else {
127  throw new AnalysisException("Encountered function name '" + funcName_ +
128  "' in timestamp arithmetic expression '" + toSql() + "'. " +
129  "Expected function name 'DATE_ADD' or 'DATE_SUB'.");
130  }
131  }
132 
133  timeUnit_ = TIME_UNITS_MAP.get(timeUnitIdent_.toUpperCase());
134  if (timeUnit_ == null) {
135  throw new AnalysisException("Invalid time unit '" + timeUnitIdent_ +
136  "' in timestamp arithmetic expression '" + toSql() + "'.");
137  }
138 
139  // The first child must return a timestamp or null.
140  if (!getChild(0).getType().isTimestamp() && !getChild(0).getType().isNull()) {
141  throw new AnalysisException("Operand '" + getChild(0).toSql() +
142  "' of timestamp arithmetic expression '" + toSql() + "' returns type '" +
143  getChild(0).getType().toSql() + "'. Expected type 'TIMESTAMP'.");
144  }
145 
146  // The second child must be an integer type.
147  if (!getChild(1).getType().isIntegerType() &&
148  !getChild(1).getType().isNull()) {
149  throw new AnalysisException("Operand '" + getChild(1).toSql() +
150  "' of timestamp arithmetic expression '" + toSql() + "' returns type '" +
151  getChild(1).getType().toSql() + "'. Expected an integer type.");
152  }
153 
154  String funcOpName = String.format("%sS_%s", timeUnit_.toString(),
155  (op_ == ArithmeticExpr.Operator.ADD) ? "ADD" : "SUB");
156 
157  fn_ = getBuiltinFunction(analyzer, funcOpName.toLowerCase(),
159  castForFunctionCall(false);
160 
161  Preconditions.checkNotNull(fn_);
162  Preconditions.checkState(fn_.getReturnType().isTimestamp());
163  type_ = fn_.getReturnType();
164  }
165 
166  @Override
167  protected void toThrift(TExprNode msg) {
168  msg.node_type = TExprNodeType.FUNCTION_CALL;
169  }
170 
171  public String getTimeUnitIdent() { return timeUnitIdent_; }
172  public TimeUnit getTimeUnit() { return timeUnit_; }
173  public ArithmeticExpr.Operator getOp() { return op_; }
174 
175  @Override
176  public String toSqlImpl() {
177  StringBuilder strBuilder = new StringBuilder();
178  if (funcName_ != null) {
179  // Function-call like version.
180  strBuilder.append(funcName_.toUpperCase() + "(");
181  strBuilder.append(getChild(0).toSql() + ", ");
182  strBuilder.append("INTERVAL ");
183  strBuilder.append(getChild(1).toSql());
184  strBuilder.append(" " + timeUnitIdent_);
185  strBuilder.append(")");
186  return strBuilder.toString();
187  }
188  if (intervalFirst_) {
189  // Non-function-call like version with interval as first operand.
190  strBuilder.append("INTERVAL ");
191  strBuilder.append(getChild(1).toSql() + " ");
192  strBuilder.append(timeUnitIdent_);
193  strBuilder.append(" " + op_.toString() + " ");
194  strBuilder.append(getChild(0).toSql());
195  } else {
196  // Non-function-call like version with interval as second operand.
197  strBuilder.append(getChild(0).toSql());
198  strBuilder.append(" " + op_.toString() + " ");
199  strBuilder.append("INTERVAL ");
200  strBuilder.append(getChild(1).toSql() + " ");
201  strBuilder.append(timeUnitIdent_);
202  }
203  return strBuilder.toString();
204  }
205 
206  @Override
207  public Expr clone() { return new TimestampArithmeticExpr(this); }
208 }
TimestampArithmeticExpr(String funcName, Expr e1, Expr e2, String timeUnitIdent)
void castForFunctionCall(boolean ignoreWildcardDecimals)
Definition: Expr.java:312
TimestampArithmeticExpr(ArithmeticExpr.Operator op, Expr e1, Expr e2, String timeUnitIdent, boolean intervalFirst)
Function getBuiltinFunction(Analyzer analyzer, String name, Type[] argTypes, CompareMode mode)
Definition: Expr.java:290