EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.expression]

COVERAGE SUMMARY FOR SOURCE FILE [Operation.java]

nameclass, %method, %block, %line, %
Operation.java100% (1/1)100% (15/15)90%  (827/915)92%  (174.4/190)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Operation100% (1/1)100% (15/15)90%  (827/915)92%  (174.4/190)
getOperationToken (): String 100% (1/1)54%  (15/28)78%  (7/9)
getValue (Session): Value 100% (1/1)86%  (136/158)84%  (31/37)
getCost (): int 100% (1/1)87%  (13/15)86%  (0.9/1)
optimize (Session): Expression 100% (1/1)90%  (460/511)93%  (94.5/102)
Operation (int, Expression, Expression): void 100% (1/1)100% (15/15)100% (6/6)
getDisplaySize (): int 100% (1/1)100% (29/29)100% (5/5)
getPrecision (): long 100% (1/1)100% (26/26)100% (5/5)
getSQL (): String 100% (1/1)100% (47/47)100% (4/4)
getScale (): int 100% (1/1)100% (15/15)100% (3/3)
getType (): int 100% (1/1)100% (3/3)100% (1/1)
isEverything (ExpressionVisitor): boolean 100% (1/1)100% (17/17)100% (1/1)
mapColumns (ColumnResolver, int): void 100% (1/1)100% (14/14)100% (4/4)
setEvaluatable (TableFilter, boolean): void 100% (1/1)100% (14/14)100% (4/4)
swap (): void 100% (1/1)100% (11/11)100% (4/4)
updateAggregate (Session): void 100% (1/1)100% (12/12)100% (4/4)

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

[all classes][org.h2.expression]
EMMA 2.0.5312 (C) Vladimir Roubtsov