File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\expression\Operation.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.expression;
8
9 import org.h2.engine.Mode;
10 import org.h2.engine.Session;
11 import org.h2.message.DbException;
12 import org.h2.table.ColumnResolver;
13 import org.h2.table.TableFilter;
14 import org.h2.util.MathUtils;
15 import org.h2.value.DataType;
16 import org.h2.value.Value;
17 import org.h2.value.ValueInt;
18 import org.h2.value.ValueNull;
19 import org.h2.value.ValueString;
20
21 /**
22 * A mathematical expression, or string concatenation.
23 */
24 public class Operation extends Expression {
25
26 /**
27 * This operation represents a string concatenation as in 'Hello' || 'World'.
28 */
29 public static final int CONCAT = 0;
30
31 /**
32 * This operation represents an addition as in 1 + 2.
33 */
34 public static final int PLUS = 1;
35
36 /**
37 * This operation represents a subtraction as in 2 - 1.
38 */
39 public static final int MINUS = 2;
40
41 /**
42 * This operation represents a multiplication as in 2 * 3.
43 */
44 public static final int MULTIPLY = 3;
45
46 /**
47 * This operation represents a division as in 4 * 2.
48 */
49 public static final int DIVIDE = 4;
50
51 /**
52 * This operation represents a negation as in - ID.
53 */
54 public static final int NEGATE = 5;
55
56 /**
57 * This operation represents a modulus as in 5 % 2.
58 */
59 public static final int MODULUS = 6;
60
61 private int opType;
62 private Expression left, right;
63 private int dataType;
64 private boolean convertRight = true;
65
66 public Operation(int opType, Expression left, Expression right) {
67 this.opType = opType;
68 this.left = left;
69 this.right = right;
70 }
71
72 public String getSQL() {
73 String sql;
74 if (opType == NEGATE) {
75 // don't remove the space, otherwise it might end up some thing like
76 // --1 which is a line remark
77 sql = "- " + left.getSQL();
78 } else {
79 // don't remove the space, otherwise it might end up some thing like
80 // --1 which is a line remark
81 sql = left.getSQL() + " " + getOperationToken() + " " + right.getSQL();
82 }
83 return "(" + sql + ")";
84 }
85
86 private String getOperationToken() {
87 switch (opType) {
88 case NEGATE:
89 return "-";
90 case CONCAT:
91 return "||";
92 case PLUS:
93 return "+";
94 case MINUS:
95 return "-";
96 case MULTIPLY:
97 return "*";
98 case DIVIDE:
99 return "/";
100 case MODULUS:
101 return "%";
102 default:
103 throw DbException.throwInternalError("opType=" + opType);
104 }
105 }
106
107 public Value getValue(Session session) {
108 Value l = left.getValue(session).convertTo(dataType);
109 Value r;
110 if (right == null) {
111 r = null;
112 } else {
113 r = right.getValue(session);
114 if (convertRight) {
115 r = r.convertTo(dataType);
116 }
117 }
118 switch (opType) {
119 case NEGATE:
120 return l == ValueNull.INSTANCE ? l : l.negate();
121 case CONCAT: {
122 Mode mode = session.getDatabase().getMode();
123 if (l == ValueNull.INSTANCE) {
124 if (mode.nullConcatIsNull) {
125 return ValueNull.INSTANCE;
126 }
127 return r;
128 } else if (r == ValueNull.INSTANCE) {
129 if (mode.nullConcatIsNull) {
130 return ValueNull.INSTANCE;
131 }
132 return l;
133 }
134 String s1 = l.getString(), s2 = r.getString();
135 StringBuilder buff = new StringBuilder(s1.length() + s2.length());
136 buff.append(s1).append(s2);
137 return ValueString.get(buff.toString());
138 }
139 case PLUS:
140 if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
141 return ValueNull.INSTANCE;
142 }
143 return l.add(r);
144 case MINUS:
145 if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
146 return ValueNull.INSTANCE;
147 }
148 return l.subtract(r);
149 case MULTIPLY:
150 if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
151 return ValueNull.INSTANCE;
152 }
153 return l.multiply(r);
154 case DIVIDE:
155 if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
156 return ValueNull.INSTANCE;
157 }
158 return l.divide(r);
159 case MODULUS:
160 if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
161 return ValueNull.INSTANCE;
162 }
163 return l.modulus(r);
164 default:
165 throw DbException.throwInternalError("type=" + opType);
166 }
167 }
168
169 public void mapColumns(ColumnResolver resolver, int level) {
170 left.mapColumns(resolver, level);
171 if (right != null) {
172 right.mapColumns(resolver, level);
173 }
174 }
175
176 public Expression optimize(Session session) {
177 left = left.optimize(session);
178 switch (opType) {
179 case NEGATE:
180 dataType = left.getType();
181 if (dataType == Value.UNKNOWN) {
182 dataType = Value.DECIMAL;
183 }
184 break;
185 case CONCAT:
186 right = right.optimize(session);
187 dataType = Value.STRING;
188 if (left.isConstant() && right.isConstant()) {
189 return ValueExpression.get(getValue(session));
190 }
191 break;
192 case PLUS:
193 case MINUS:
194 case MULTIPLY:
195 case DIVIDE:
196 case MODULUS:
197 right = right.optimize(session);
198 int l = left.getType();
199 int r = right.getType();
200 if ((l == Value.NULL && r == Value.NULL) || (l == Value.UNKNOWN && r == Value.UNKNOWN)) {
201 // (? + ?) - use decimal by default (the most safe data type) or
202 // string when text concatenation with + is enabled
203 if (opType == PLUS && session.getDatabase().getMode().allowPlusForStringConcat) {
204 dataType = Value.STRING;
205 opType = CONCAT;
206 } else {
207 dataType = Value.DECIMAL;
208 }
209 } else if (l == Value.DATE || l == Value.TIMESTAMP || l == Value.TIME ||
210 r == Value.DATE || r == Value.TIMESTAMP || r == Value.TIME) {
211 if (opType == PLUS) {
212 if (r != Value.getHigherOrder(l, r)) {
213 // order left and right: INT < TIME < DATE < TIMESTAMP
214 swap();
215 int t = l;
216 l = r;
217 r = t;
218 }
219 if (l == Value.INT) {
220 // Oracle date add
221 Function f = Function.getFunction(session.getDatabase(), "DATEADD");
222 f.setParameter(0, ValueExpression.get(ValueString.get("DAY")));
223 f.setParameter(1, left);
224 f.setParameter(2, right);
225 f.doneWithParameters();
226 return f.optimize(session);
227 } else if (l == Value.DECIMAL || l == Value.FLOAT || l == Value.DOUBLE) {
228 // Oracle date add
229 Function f = Function.getFunction(session.getDatabase(), "DATEADD");
230 f.setParameter(0, ValueExpression.get(ValueString.get("SECOND")));
231 left = new Operation(Operation.MULTIPLY, ValueExpression.get(ValueInt
232 .get(60 * 60 * 24)), left);
233 f.setParameter(1, left);
234 f.setParameter(2, right);
235 f.doneWithParameters();
236 return f.optimize(session);
237 } else if (l == Value.TIME && r == Value.TIME) {
238 dataType = Value.TIME;
239 return this;
240 } else if (l == Value.TIME) {
241 dataType = Value.TIMESTAMP;
242 return this;
243 }
244 } else if (opType == MINUS) {
245 if ((l == Value.DATE || l == Value.TIMESTAMP) && r == Value.INT) {
246 // Oracle date subtract
247 Function f = Function.getFunction(session.getDatabase(), "DATEADD");
248 f.setParameter(0, ValueExpression.get(ValueString.get("DAY")));
249 right = new Operation(NEGATE, right, null);
250 right = right.optimize(session);
251 f.setParameter(1, right);
252 f.setParameter(2, left);
253 f.doneWithParameters();
254 return f.optimize(session);
255 } else if ((l == Value.DATE || l == Value.TIMESTAMP) && (r == Value.DECIMAL || r == Value.FLOAT || r == Value.DOUBLE)) {
256 // Oracle date subtract
257 Function f = Function.getFunction(session.getDatabase(), "DATEADD");
258 f.setParameter(0, ValueExpression.get(ValueString.get("SECOND")));
259 right = new Operation(Operation.MULTIPLY, ValueExpression.get(ValueInt
260 .get(60 * 60 * 24)), right);
261 right = new Operation(NEGATE, right, null);
262 right = right.optimize(session);
263 f.setParameter(1, right);
264 f.setParameter(2, left);
265 f.doneWithParameters();
266 return f.optimize(session);
267 } else if (l == Value.DATE || l == Value.TIMESTAMP) {
268 if (r == Value.TIME) {
269 dataType = Value.TIMESTAMP;
270 return this;
271 } else if (r == Value.DATE || r == Value.TIMESTAMP) {
272 // Oracle date subtract
273 Function f = Function.getFunction(session.getDatabase(), "DATEDIFF");
274 f.setParameter(0, ValueExpression.get(ValueString.get("DAY")));
275 f.setParameter(1, right);
276 f.setParameter(2, left);
277 f.doneWithParameters();
278 return f.optimize(session);
279 }
280 } else if (l == Value.TIME && r == Value.TIME) {
281 dataType = Value.TIME;
282 return this;
283 }
284 } else if (opType == MULTIPLY) {
285 if (l == Value.TIME) {
286 dataType = Value.TIME;
287 convertRight = false;
288 return this;
289 } else if (r == Value.TIME) {
290 swap();
291 dataType = Value.TIME;
292 convertRight = false;
293 return this;
294 }
295 } else if (opType == DIVIDE) {
296 if (l == Value.TIME) {
297 dataType = Value.TIME;
298 convertRight = false;
299 return this;
300 }
301 }
302 throw DbException.getUnsupportedException(
303 DataType.getDataType(l).name + " " +
304 getOperationToken() + " " +
305 DataType.getDataType(r).name);
306 } else {
307 dataType = Value.getHigherOrder(l, r);
308 if (DataType.isStringType(dataType) && session.getDatabase().getMode().allowPlusForStringConcat) {
309 opType = CONCAT;
310 }
311 }
312 break;
313 default:
314 DbException.throwInternalError("type=" + opType);
315 }
316 if (left.isConstant() && (right == null || right.isConstant())) {
317 return ValueExpression.get(getValue(session));
318 }
319 return this;
320 }
321
322 private void swap() {
323 Expression temp = left;
324 left = right;
325 right = temp;
326 }
327
328 public void setEvaluatable(TableFilter tableFilter, boolean b) {
329 left.setEvaluatable(tableFilter, b);
330 if (right != null) {
331 right.setEvaluatable(tableFilter, b);
332 }
333 }
334
335 public int getType() {
336 return dataType;
337 }
338
339 public long getPrecision() {
340 if (right != null) {
341 switch (opType) {
342 case CONCAT:
343 return left.getPrecision() + right.getPrecision();
344 default:
345 return Math.max(left.getPrecision(), right.getPrecision());
346 }
347 }
348 return left.getPrecision();
349 }
350
351 public int getDisplaySize() {
352 if (right != null) {
353 switch (opType) {
354 case CONCAT:
355 return MathUtils.convertLongToInt((long) left.getDisplaySize() + (long) right.getDisplaySize());
356 default:
357 return Math.max(left.getDisplaySize(), right.getDisplaySize());
358 }
359 }
360 return left.getDisplaySize();
361 }
362
363 public int getScale() {
364 if (right != null) {
365 return Math.max(left.getScale(), right.getScale());
366 }
367 return left.getScale();
368 }
369
370 public void updateAggregate(Session session) {
371 left.updateAggregate(session);
372 if (right != null) {
373 right.updateAggregate(session);
374 }
375 }
376
377 public boolean isEverything(ExpressionVisitor visitor) {
378 return left.isEverything(visitor) && (right == null || right.isEverything(visitor));
379 }
380
381 public int getCost() {
382 return left.getCost() + 1 + (right == null ? 0 : right.getCost());
383 }
384
385 }
386