001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.db.sql;
031:
032: import com.caucho.db.table.Column;
033: import com.caucho.db.table.Table;
034: import com.caucho.log.Log;
035: import com.caucho.sql.SQLExceptionWrapper;
036: import com.caucho.util.L10N;
037: import com.caucho.util.QDate;
038:
039: import java.io.InputStream;
040: import java.sql.SQLException;
041: import java.sql.Types;
042: import java.util.ArrayList;
043: import java.util.logging.Logger;
044:
045: abstract public class Expr {
046: protected static final L10N L = new L10N(Expr.class);
047: private static final Logger log = Log.open(Expr.class);
048:
049: public static final int UNKNOWN = -1;
050: public static final int FALSE = 0;
051: public static final int TRUE = 1;
052:
053: final static long COST_INVALID = Long.MAX_VALUE / 1000;
054: final static long COST_NO_TABLE = Integer.MAX_VALUE;
055: final static long COST_SCAN = 1000000L;
056: final static long COST_UNIQUE = 10000L;
057: final static long COST_INDEX = 100L;
058: final static long COST_CONSTANT = 0L;
059:
060: private static QDate _gmtDate = new QDate();
061:
062: protected Expr bind(Query query) throws SQLException {
063: return this ;
064: }
065:
066: /**
067: * Returns the expected result type of the expression.
068: */
069: public Class getType() {
070: return Object.class;
071: }
072:
073: /**
074: * Returns true if the expression returns a long.
075: */
076: public boolean isLong() {
077: Class type = getType();
078:
079: return (int.class.equals(type) || long.class.equals(type) || java.sql.Date.class
080: .equals(type));
081: }
082:
083: /**
084: * Returns true if the expression returns a double.
085: */
086: public boolean isDouble() {
087: Class type = getType();
088:
089: return isLong() || double.class.isAssignableFrom(type);
090: }
091:
092: /**
093: * Returns true if the expression returns a boolean.
094: */
095: public boolean isBoolean() {
096: return boolean.class.equals(getType());
097: }
098:
099: /**
100: * Returns true if the expression returns a binary stream.
101: */
102: public boolean isBinaryStream() {
103: Class type = getType();
104:
105: return (InputStream.class.equals(type));
106: }
107:
108: /**
109: * Returns any column name.
110: */
111: public String getName() {
112: return "";
113: }
114:
115: /**
116: * Returns the column type.
117: */
118: public int getSQLType() {
119: return Types.VARCHAR;
120: }
121:
122: /**
123: * Returns the table.
124: */
125: public Table getTable() {
126: return null;
127: }
128:
129: /**
130: * Splits the expr into and blocks.
131: */
132: public void splitAnd(ArrayList<Expr> andProduct) {
133: andProduct.add(this );
134: }
135:
136: /**
137: * Returns the cost based on the given FromList.
138: */
139: public long cost(ArrayList<FromItem> fromList) {
140: return subCost(fromList);
141: }
142:
143: /**
144: * Returns the cost based on the given FromList.
145: */
146: public long subCost(ArrayList<FromItem> fromList) {
147: return COST_INVALID;
148: }
149:
150: /**
151: * Returns an index expression if available.
152: */
153: public RowIterateExpr getIndexExpr(FromItem fromItem) {
154: return null;
155: }
156:
157: /**
158: * Returns the order.
159: */
160: public Order createOrder(int index) {
161: if (int.class.equals(getType()))
162: return new IntOrder(index);
163: else if (isLong())
164: return new LongOrder(index);
165: else if (isDouble())
166: return new DoubleOrder(index);
167: else
168: return new StringOrder(index);
169: }
170:
171: /**
172: * Returns true if result is null
173: *
174: * @param rows the current database tuple
175: *
176: * @return true if null
177: */
178: public boolean isNull(QueryContext context) throws SQLException {
179: return false;
180: }
181:
182: /**
183: * Returns true if the expression selects the row.
184: *
185: * @param rows the current database tuple
186: *
187: * @return the boolean value
188: */
189: public boolean isSelect(QueryContext context) throws SQLException {
190: return evalBoolean(context) == TRUE;
191: }
192:
193: /**
194: * Evaluates the expression as a boolean.
195: *
196: * @param rows the current database tuple
197: *
198: * @return the boolean value
199: */
200: public int evalBoolean(QueryContext context) throws SQLException {
201: throw new UnsupportedOperationException(getClass().getName());
202: }
203:
204: /**
205: * Evaluates the expression as a string.
206: *
207: * @param rows the current database tuple
208: *
209: * @return the string value
210: */
211: public String evalString(QueryContext context) throws SQLException {
212: throw new UnsupportedOperationException(getClass().getName());
213: }
214:
215: /**
216: * Evaluates the expression as a long.
217: *
218: * @param rows the current database tuple
219: *
220: * @return the long value
221: */
222: public long evalLong(QueryContext context) throws SQLException {
223: String strValue = evalString(context);
224:
225: if (strValue == null)
226: return 0;
227: else
228: return Long.parseLong(strValue);
229: }
230:
231: /**
232: * Evaluates the expression as a double.
233: *
234: * @param rows the current database tuple
235: *
236: * @return the double value
237: */
238: public double evalDouble(QueryContext context) throws SQLException {
239: String strValue = evalString(context);
240:
241: if (strValue == null)
242: return 0;
243: else
244: return Double.parseDouble(strValue);
245: }
246:
247: /**
248: * Evaluates the expression as a date.
249: *
250: * @param rows the current database tuple
251: *
252: * @return the double value
253: */
254: public long evalDate(QueryContext context) throws SQLException {
255: String dateString = evalString(context);
256:
257: try {
258: synchronized (_gmtDate) {
259: return _gmtDate.parseDate(dateString);
260: }
261: } catch (Exception e) {
262: throw new SQLExceptionWrapper(e);
263: }
264: }
265:
266: /**
267: * Evaluates the expression, writing to the result stream.
268: *
269: * @param result the output result
270: */
271: public void evalToResult(QueryContext context, SelectResult result)
272: throws SQLException {
273: String s = evalString(context);
274:
275: if (s == null) {
276: result.writeNull();
277: return;
278: }
279:
280: result.writeString(s);
281: }
282:
283: /**
284: * Evaluates the expression as a string.
285: *
286: * @param rows the current database tuple
287: *
288: * @return the string value
289: */
290: public InputStream evalStream(QueryContext context)
291: throws SQLException {
292: throw new UnsupportedOperationException(getClass().getName());
293: }
294:
295: /**
296: * Evaluates the expression to a buffer
297: *
298: * @param result the result buffer
299: *
300: * @return the length of the result
301: */
302: public int evalToBuffer(QueryContext context, byte[] buffer,
303: int columnType) throws SQLException {
304: switch (columnType) {
305: case Column.INT: {
306: int v = (int) evalLong(context);
307:
308: buffer[0] = (byte) (v >> 24);
309: buffer[1] = (byte) (v >> 16);
310: buffer[2] = (byte) (v >> 8);
311: buffer[3] = (byte) (v);
312:
313: return 4;
314: }
315:
316: case Column.LONG:
317: case Column.DATE: {
318: long v = evalLong(context);
319:
320: buffer[0] = (byte) (v >> 56);
321: buffer[1] = (byte) (v >> 48);
322: buffer[2] = (byte) (v >> 40);
323: buffer[3] = (byte) (v >> 32);
324:
325: buffer[4] = (byte) (v >> 24);
326: buffer[5] = (byte) (v >> 16);
327: buffer[6] = (byte) (v >> 8);
328: buffer[7] = (byte) (v);
329:
330: return 8;
331: }
332:
333: case Column.VARCHAR: {
334: String v = evalString(context);
335:
336: if (v == null)
337: return -1;
338:
339: int length = v.length();
340: int offset = 0;
341:
342: buffer[offset++] = (byte) length;
343: for (int i = 0; i < length; i++) {
344: int ch = v.charAt(i);
345:
346: buffer[offset++] = (byte) (ch >> 8);
347: buffer[offset++] = (byte) (ch);
348: }
349:
350: return offset;
351: }
352:
353: case Column.VARBINARY: {
354: String v = evalString(context);
355:
356: if (v == null)
357: return -1;
358:
359: int length = v.length();
360: int offset = 1;
361:
362: for (int i = 0; i < length; i++) {
363: int ch = v.charAt(i);
364:
365: if (ch < 0x80)
366: buffer[offset++] = (byte) (ch & 0xff);
367: else if (ch < 0x800) {
368: buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f));
369: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
370: } else {
371: buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0x0f));
372: buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f));
373: buffer[offset++] = (byte) (0x80 + (ch & 0x3f));
374: }
375: }
376: buffer[0] = (byte) (offset - 1);
377:
378: return offset;
379: }
380:
381: default:
382: throw new UnsupportedOperationException("unknown column: "
383: + columnType);
384: }
385: }
386:
387: /**
388: * Initializes aggregate functions during the group phase.
389: *
390: * @param context the current database tuple
391: */
392: public void initGroup(QueryContext context) throws SQLException {
393: }
394:
395: /**
396: * Evaluates aggregate functions during the group phase.
397: *
398: * @param context the current database tuple
399: */
400: public void evalGroup(QueryContext context) throws SQLException {
401: }
402: }
|