Impala
Impalaistheopensource,nativeanalyticdatabaseforApacheHadoop.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
NumericLiteral.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.math.BigDecimal;
18 import java.math.BigInteger;
19 
24 import com.cloudera.impala.thrift.TDecimalLiteral;
25 import com.cloudera.impala.thrift.TExprNode;
26 import com.cloudera.impala.thrift.TExprNodeType;
27 import com.cloudera.impala.thrift.TFloatLiteral;
28 import com.cloudera.impala.thrift.TIntLiteral;
29 import com.google.common.base.Objects;
30 import com.google.common.base.Preconditions;
31 
36 public class NumericLiteral extends LiteralExpr {
37  // Use the java BigDecimal (arbitrary scale/precision) to represent the value.
38  // This object has notions of precision and scale but they do *not* match what
39  // we need. BigDecimal's precision is similar to significant figures and scale
40  // is the exponent.
41  // ".1" could be represented with an unscaled value = 1 and scale = 1 or
42  // unscaled value = 100 and scale = 3. Manipulating the value_ (e.g. multiplying
43  // it by 10) does not unnecessarily change the unscaled value. Special care
44  // needs to be taken when converting between the big decimals unscaled value
45  // and ours. (See getUnscaledValue()).
46  private BigDecimal value_;
47 
48  // If true, this literal has been explicitly cast to a type and should not
49  // be analyzed (which infers the type from value_).
50  private boolean explicitlyCast_;
51 
52  public NumericLiteral(BigDecimal value) {
53  init(value);
54  }
55 
56  public NumericLiteral(String value, Type t) throws AnalysisException {
57  BigDecimal val = null;
58  try {
59  val = new BigDecimal(value);
60  } catch (NumberFormatException e) {
61  throw new AnalysisException("invalid numeric literal: " + value, e);
62  }
63  init(val);
64  this.analyze(null);
65  if (type_.isDecimal() && t.isDecimal()) {
66  // Verify that the input decimal value is consistent with the specified
67  // column type.
68  ScalarType scalarType = (ScalarType) t;
69  if (!scalarType.isSupertypeOf((ScalarType) type_)) {
70  StringBuilder errMsg = new StringBuilder();
71  errMsg.append("invalid ").append(t);
72  errMsg.append(" value: " + value);
73  throw new AnalysisException(errMsg.toString());
74  }
75  }
76  if (t.isFloatingPointType()) explicitlyCastToFloat(t);
77  }
78 
84  public NumericLiteral(BigInteger value, Type type) {
85  isAnalyzed_ = true;
86  value_ = new BigDecimal(value);
87  type_ = type;
88  explicitlyCast_ = true;
89  }
90 
91  public NumericLiteral(BigDecimal value, Type type) {
92  isAnalyzed_ = true;
93  value_ = value;
94  type_ = type;
95  explicitlyCast_ = true;
96  }
97 
101  protected NumericLiteral(NumericLiteral other) {
102  super(other);
103  value_ = other.value_;
104  explicitlyCast_ = other.explicitlyCast_;
105  }
106 
107  @Override
108  public String debugString() {
109  return Objects.toStringHelper(this)
110  .add("value", value_)
111  .add("type", type_)
112  .toString();
113  }
114 
115  @Override
116  public boolean equals(Object obj) {
117  if (!super.equals(obj)) return false;
118  return ((NumericLiteral) obj).value_.equals(value_);
119  }
120 
121  @Override
122  public String toSqlImpl() { return getStringValue(); }
123  @Override
124  public String getStringValue() { return value_.toString(); }
125  public double getDoubleValue() { return value_.doubleValue(); }
126  public long getLongValue() { return value_.longValue(); }
127  public long getIntValue() { return value_.intValue(); }
128 
129  @Override
130  protected void toThrift(TExprNode msg) {
131  switch (type_.getPrimitiveType()) {
132  case TINYINT:
133  case SMALLINT:
134  case INT:
135  case BIGINT:
136  msg.node_type = TExprNodeType.INT_LITERAL;
137  msg.int_literal = new TIntLiteral(value_.longValue());
138  break;
139  case FLOAT:
140  case DOUBLE:
141  msg.node_type = TExprNodeType.FLOAT_LITERAL;
142  msg.float_literal = new TFloatLiteral(value_.doubleValue());
143  break;
144  case DECIMAL:
145  msg.node_type = TExprNodeType.DECIMAL_LITERAL;
146  TDecimalLiteral literal = new TDecimalLiteral();
147  literal.setValue(getUnscaledValue().toByteArray());
148  msg.decimal_literal = literal;
149  break;
150  default:
151  Preconditions.checkState(false);
152  }
153  }
154 
155  public BigDecimal getValue() { return value_; }
156 
157  @Override
158  public void analyze(Analyzer analyzer) throws AnalysisException {
159  if (isAnalyzed_) return;
160  super.analyze(analyzer);
161  if (!explicitlyCast_) {
162  // Compute the precision and scale from the BigDecimal.
163  type_ = TypesUtil.computeDecimalType(value_);
164  if (type_ == null) {
165  Double d = new Double(value_.doubleValue());
166  if (d.isInfinite()) {
167  throw new AnalysisException("Numeric literal '" + toSql() +
168  "' exceeds maximum range of doubles.");
169  } else if (d.doubleValue() == 0 && value_ != BigDecimal.ZERO) {
170  throw new AnalysisException("Numeric literal '" + toSql() +
171  "' underflows minimum resolution of doubles.");
172  }
173 
174  // Literal could not be stored in any of the supported decimal precisions and
175  // scale. Store it as a float/double instead.
176  float fvalue;
177  fvalue = value_.floatValue();
178  if (fvalue == value_.doubleValue()) {
179  type_ = Type.FLOAT;
180  } else {
181  type_ = Type.DOUBLE;
182  }
183  } else {
184  // Check for integer types.
185  Preconditions.checkState(type_.isScalarType());
186  ScalarType scalarType = (ScalarType) type_;
187  if (scalarType.decimalScale() == 0) {
188  if (value_.compareTo(BigDecimal.valueOf(Byte.MAX_VALUE)) <= 0 &&
189  value_.compareTo(BigDecimal.valueOf(Byte.MIN_VALUE)) >= 0) {
191  } else if (value_.compareTo(BigDecimal.valueOf(Short.MAX_VALUE)) <= 0 &&
192  value_.compareTo(BigDecimal.valueOf(Short.MIN_VALUE)) >= 0) {
194  } else if (value_.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE)) <= 0 &&
195  value_.compareTo(BigDecimal.valueOf(Integer.MIN_VALUE)) >= 0) {
196  type_ = Type.INT;
197  } else if (value_.compareTo(BigDecimal.valueOf(Long.MAX_VALUE)) <= 0 &&
198  value_.compareTo(BigDecimal.valueOf(Long.MIN_VALUE)) >= 0) {
199  type_ = Type.BIGINT;
200  }
201  }
202  }
203  }
204  isAnalyzed_ = true;
205  }
206 
211  protected void explicitlyCastToFloat(Type targetType) {
212  Preconditions.checkState(targetType.isFloatingPointType());
213  type_ = targetType;
214  explicitlyCast_ = true;
215  }
216 
217  @Override
218  protected Expr uncheckedCastTo(Type targetType) throws AnalysisException {
219  Preconditions.checkState(targetType.isNumericType());
220  // Implicit casting to decimals allows truncating digits from the left of the
221  // decimal point (see TypesUtil). A literal that is implicitly cast to a decimal
222  // with truncation is wrapped into a CastExpr so the BE can evaluate it and report
223  // a warning. This behavior is consistent with casting/overflow of non-constant
224  // exprs that return decimal.
225  // IMPALA-1837: Without the CastExpr wrapping, such literals can exceed the max
226  // expected byte size sent to the BE in toThrift().
227  if (targetType.isDecimal()) {
228  ScalarType decimalType = (ScalarType) targetType;
229  // analyze() ensures that value_ never exceeds the maximum scale and precision.
230  Preconditions.checkState(isAnalyzed_);
231  // Sanity check that our implicit casting does not allow a reduced precision or
232  // truncating values from the right of the decimal point.
233  Preconditions.checkState(value_.precision() <= decimalType.decimalPrecision());
234  Preconditions.checkState(value_.scale() <= decimalType.decimalScale());
235  int valLeftDigits = value_.precision() - value_.scale();
236  int typeLeftDigits = decimalType.decimalPrecision() - decimalType.decimalScale();
237  if (typeLeftDigits < valLeftDigits) return new CastExpr(targetType, this);
238  }
239  type_ = targetType;
240  return this;
241  }
242 
243  @Override
244  public void swapSign() throws NotImplementedException {
245  // swapping sign does not change the type
246  value_ = value_.negate();
247  }
248 
249  @Override
250  public int compareTo(LiteralExpr o) {
251  int ret = super.compareTo(o);
252  if (ret != 0) return ret;
253  NumericLiteral other = (NumericLiteral) o;
254  return value_.compareTo(other.value_);
255  }
256 
257  private void init(BigDecimal value) {
258  isAnalyzed_ = false;
259  value_ = value;
260  }
261 
262  // Returns the unscaled value of this literal. BigDecimal doesn't treat scale
263  // the way we do. We need to pad it out with zeros or truncate as necessary.
264  private BigInteger getUnscaledValue() {
265  Preconditions.checkState(type_.isDecimal());
266  BigInteger result = value_.unscaledValue();
267  int valueScale = value_.scale();
268  // If valueScale is less than 0, it indicates the power of 10 to multiply the
269  // unscaled value. This path also handles this case by padding with zeros.
270  // e.g. unscaled value = 123, value scale = -2 means 12300.
271  ScalarType decimalType = (ScalarType) type_;
272  return result.multiply(BigInteger.TEN.pow(decimalType.decimalScale() - valueScale));
273  }
274 
275  @Override
276  public Expr clone() { return new NumericLiteral(this); }
277 }
NumericLiteral(BigDecimal value, Type type)
NumericLiteral(BigInteger value, Type type)
static final ScalarType BIGINT
Definition: Type.java:50
PrimitiveType getPrimitiveType()
Definition: Type.java:188
static final ScalarType SMALLINT
Definition: Type.java:48
static final ScalarType FLOAT
Definition: Type.java:51
static final ScalarType DOUBLE
Definition: Type.java:52
static final ScalarType TINYINT
Definition: Type.java:47
static final ScalarType INT
Definition: Type.java:49