001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.expression;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.engine.Mode;
011: import org.h2.engine.Session;
012: import org.h2.message.Message;
013: import org.h2.table.ColumnResolver;
014: import org.h2.table.TableFilter;
015: import org.h2.util.MathUtils;
016: import org.h2.value.Value;
017: import org.h2.value.ValueNull;
018: import org.h2.value.ValueString;
019:
020: /**
021: * A mathematical expression, or string concatenation.
022: */
023: public class Operation extends Expression {
024: public static final int CONCAT = 0, PLUS = 1, MINUS = 2,
025: MULTIPLY = 3, DIVIDE = 4, NEGATE = 5;
026: private int opType;
027: private Expression left, right;
028: private int dataType;
029:
030: public Operation(int opType, Expression left, Expression right) {
031: this .opType = opType;
032: this .left = left;
033: this .right = right;
034: }
035:
036: public String getSQL() {
037: String sql;
038: switch (opType) {
039: case NEGATE:
040: // don't remove the space, otherwise it might end up some thing line
041: // --1 which is a remark
042: // TODO need to () everything correctly, but avoiding double (())
043: sql = "- " + left.getSQL();
044: break;
045: case CONCAT:
046: sql = left.getSQL() + " || " + right.getSQL();
047: break;
048: case PLUS:
049: sql = left.getSQL() + " + " + right.getSQL();
050: break;
051: case MINUS:
052: sql = left.getSQL() + " - " + right.getSQL();
053: break;
054: case MULTIPLY:
055: sql = left.getSQL() + " * " + right.getSQL();
056: break;
057: case DIVIDE:
058: sql = left.getSQL() + " / " + right.getSQL();
059: break;
060: default:
061: throw Message.getInternalError("opType=" + opType);
062: }
063: return "(" + sql + ")";
064: }
065:
066: public Value getValue(Session session) throws SQLException {
067: Value l = left.getValue(session).convertTo(dataType);
068: Value r = right == null ? null : right.getValue(session)
069: .convertTo(dataType);
070:
071: switch (opType) {
072: case NEGATE:
073: return l == ValueNull.INSTANCE ? l : l.negate();
074: case CONCAT: {
075: Mode mode = session.getDatabase().getMode();
076: if (l == ValueNull.INSTANCE) {
077: if (mode.nullConcatIsNull) {
078: return ValueNull.INSTANCE;
079: } else {
080: return r;
081: }
082: } else if (r == ValueNull.INSTANCE) {
083: if (mode.nullConcatIsNull) {
084: return ValueNull.INSTANCE;
085: } else {
086: return l;
087: }
088: }
089: String s1 = l.getString(), s2 = r.getString();
090: StringBuffer buff = new StringBuffer(s1.length()
091: + s2.length());
092: buff.append(s1);
093: buff.append(s2);
094: return ValueString.get(buff.toString());
095: }
096: case PLUS:
097: if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
098: return ValueNull.INSTANCE;
099: }
100: return l.add(r);
101: case MINUS:
102: if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
103: return ValueNull.INSTANCE;
104: }
105: return l.subtract(r);
106: case MULTIPLY:
107: if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
108: return ValueNull.INSTANCE;
109: }
110: return l.multiply(r);
111: case DIVIDE:
112: if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
113: return ValueNull.INSTANCE;
114: }
115: return l.divide(r);
116: default:
117: throw Message.getInternalError("type=" + opType);
118: }
119: }
120:
121: public void mapColumns(ColumnResolver resolver, int level)
122: throws SQLException {
123: left.mapColumns(resolver, level);
124: if (right != null) {
125: right.mapColumns(resolver, level);
126: }
127: }
128:
129: public Expression optimize(Session session) throws SQLException {
130: left = left.optimize(session);
131: switch (opType) {
132: case NEGATE:
133: dataType = left.getType();
134: if (dataType == Value.UNKNOWN) {
135: dataType = Value.DECIMAL;
136: }
137: break;
138: case CONCAT:
139: right = right.optimize(session);
140: dataType = Value.STRING;
141: if (left.isConstant() && right.isConstant()) {
142: return ValueExpression.get(getValue(session));
143: }
144: break;
145: case PLUS:
146: case MINUS:
147: case MULTIPLY:
148: case DIVIDE:
149: right = right.optimize(session);
150: int l = left.getType();
151: int r = right.getType();
152: if ((l == Value.NULL && r == Value.NULL)
153: || (l == Value.UNKNOWN && r == Value.UNKNOWN)) {
154: // example: (? + ?) - the most safe data type is probably
155: // decimal
156: dataType = Value.DECIMAL;
157: } else if (l == Value.DATE || l == Value.TIMESTAMP) {
158: if (r == Value.INT
159: && (opType == PLUS || opType == MINUS)) {
160: // Oracle date add
161: Function f = Function.getFunction(session
162: .getDatabase(), "DATEADD");
163: f.setParameter(0, ValueExpression.get(ValueString
164: .get("DAY")));
165: if (opType == MINUS) {
166: right = new Operation(NEGATE, right, null);
167: right = right.optimize(session);
168: }
169: f.setParameter(1, right);
170: f.setParameter(2, left);
171: f.doneWithParameters();
172: return f.optimize(session);
173: } else if (opType == MINUS
174: && (l == Value.DATE || l == Value.TIMESTAMP)) {
175: // Oracle date subtract
176: Function f = Function.getFunction(session
177: .getDatabase(), "DATEDIFF");
178: f.setParameter(0, ValueExpression.get(ValueString
179: .get("DAY")));
180: f.setParameter(1, right);
181: f.setParameter(2, left);
182: f.doneWithParameters();
183: return f.optimize(session);
184: }
185: } else {
186: dataType = Value.getHigherOrder(l, r);
187: }
188: break;
189: default:
190: throw Message.getInternalError("type=" + opType);
191: }
192: if (left.isConstant() && (right == null || right.isConstant())) {
193: return ValueExpression.get(getValue(session));
194: }
195: return this ;
196: }
197:
198: public void setEvaluatable(TableFilter tableFilter, boolean b) {
199: left.setEvaluatable(tableFilter, b);
200: if (right != null) {
201: right.setEvaluatable(tableFilter, b);
202: }
203: }
204:
205: public int getType() {
206: return dataType;
207: }
208:
209: public long getPrecision() {
210: if (right != null) {
211: switch (opType) {
212: case CONCAT:
213: return left.getPrecision() + right.getPrecision();
214: default:
215: return Math.max(left.getPrecision(), right
216: .getPrecision());
217: }
218: }
219: return left.getPrecision();
220: }
221:
222: public int getDisplaySize() {
223: if (right != null) {
224: switch (opType) {
225: case CONCAT:
226: return MathUtils.convertLongToInt((long) left
227: .getDisplaySize()
228: + (long) right.getDisplaySize());
229: default:
230: return Math.max(left.getDisplaySize(), right
231: .getDisplaySize());
232: }
233: }
234: return left.getDisplaySize();
235: }
236:
237: public int getScale() {
238: if (right != null) {
239: return Math.max(left.getScale(), right.getScale());
240: }
241: return left.getScale();
242: }
243:
244: public void updateAggregate(Session session) throws SQLException {
245: left.updateAggregate(session);
246: if (right != null) {
247: right.updateAggregate(session);
248: }
249: }
250:
251: public boolean isEverything(ExpressionVisitor visitor) {
252: return left.isEverything(visitor)
253: && (right == null || right.isEverything(visitor));
254: }
255:
256: public int getCost() {
257: return left.getCost() + 1
258: + (right == null ? 0 : right.getCost());
259: }
260:
261: }
|