0001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
0002: * All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * Redistributions of source code must retain the above copyright notice, this
0008: * list of conditions and the following disclaimer.
0009: *
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * Neither the name of the Hypersonic SQL Group nor the names of its
0015: * contributors may be used to endorse or promote products derived from this
0016: * software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
0022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: *
0030: * This software consists of voluntary contributions made by many individuals
0031: * on behalf of the Hypersonic SQL Group.
0032: *
0033: *
0034: * For work added by the HSQL Development Group:
0035: *
0036: * Copyright (c) 2001-2005, The HSQL Development Group
0037: * All rights reserved.
0038: *
0039: * Redistribution and use in source and binary forms, with or without
0040: * modification, are permitted provided that the following conditions are met:
0041: *
0042: * Redistributions of source code must retain the above copyright notice, this
0043: * list of conditions and the following disclaimer.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution.
0048: *
0049: * Neither the name of the HSQL Development Group nor the names of its
0050: * contributors may be used to endorse or promote products derived from this
0051: * software without specific prior written permission.
0052: *
0053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0064: */
0065:
0066: package org.hsqldb;
0067:
0068: import org.hsqldb.HsqlNameManager.HsqlName;
0069: import org.hsqldb.index.RowIterator;
0070: import org.hsqldb.lib.HashSet;
0071: import org.hsqldb.lib.HsqlArrayList;
0072: import org.hsqldb.store.ValuePool;
0073:
0074: // fredt@users 20020215 - patch 1.7.0 by fredt
0075: // to preserve column size etc. when SELECT INTO TABLE is used
0076: // tony_lai@users 20021020 - patch 1.7.2 - improved aggregates and HAVING
0077: // fredt@users 20021112 - patch 1.7.2 by Nitin Chauhan - use of switch
0078: // rewrite of the majority of multiple if(){}else{} chains with switch(){}
0079: // vorburger@users 20021229 - patch 1.7.2 - null handling
0080: // boucherb@users 200307?? - patch 1.7.2 - resolve param nodes
0081: // boucherb@users 200307?? - patch 1.7.2 - compress constant expr during resolve
0082: // boucherb@users 200307?? - patch 1.7.2 - eager pmd and rsmd
0083: // boucherb@users 20031005 - patch 1.7.2 - optimised LIKE
0084: // boucherb@users 20031005 - patch 1.7.2 - improved IN value lists
0085: // fredt@users 20031012 - patch 1.7.2 - better OUTER JOIN implementation
0086: // thomasm@users 20041001 - patch 1.7.3 - BOOLEAN undefined handling
0087: // fredt@users 200412xx - patch 1.7.2 - evaluation of time functions
0088: // boucherb@users 20050516 - patch 1.8.0 - remove DITypeInfo usage for faster
0089: // statement compilation
0090:
0091: /**
0092: * Expression class.
0093: *
0094: * The core functionality of this class was inherited from HypersonicSQL and
0095: * extensively rewritten and extended in successive versions of HSQLDB.
0096: *
0097: * @author Thomas Mueller (Hypersonic SQL Group)
0098: * @version 1.8.0
0099: * @since Hypersonic SQL
0100: */
0101:
0102: /** @todo - fredt - constant TRUE and FALSE type expressions have valueData of
0103: * type BOOLEAN, while computed expressions have no valueData; this should be
0104: * normalised in future
0105: */
0106: public class Expression {
0107:
0108: // leaf types
0109: static final int VALUE = 1, COLUMN = 2, QUERY = 3,
0110: TRUE = 4,
0111: FALSE = -4, // arbitrary
0112: VALUELIST = 5, ASTERISK = 6, FUNCTION = 7, LIMIT = 8,
0113: ROW = 9;
0114:
0115: // boucherb@users 20020410 - parametric compiled statements
0116: // new leaf type
0117: static final int PARAM = 9;
0118:
0119: // --
0120: // operations
0121: static final int NEGATE = 10, ADD = 11, SUBTRACT = 12,
0122: MULTIPLY = 13, DIVIDE = 14, CONCAT = 15;
0123:
0124: // logical operations
0125: static final int NOT = 20, EQUAL = 21, BIGGER_EQUAL = 22,
0126: BIGGER = 23, SMALLER = 24, SMALLER_EQUAL = 25,
0127: NOT_EQUAL = 26, LIKE = 27, AND = 28, OR = 29, IN = 30,
0128: EXISTS = 31, ALL = 32, ANY = 33, IS_NULL = 34;
0129:
0130: // aggregate functions
0131: static final int COUNT = 40, SUM = 41, MIN = 42, MAX = 43,
0132: AVG = 44, EVERY = 45, SOME = 46, STDDEV_POP = 47,
0133: STDDEV_SAMP = 48, VAR_POP = 49, VAR_SAMP = 50;
0134:
0135: // system functions
0136: static final int IFNULL = 60, CONVERT = 61, CASEWHEN = 62,
0137: EXTRACT = 63, POSITION = 64, TRIM = 65, SUBSTRING = 66,
0138: NULLIF = 67, CASE = 68, COALESCE = 69, ALTERNATIVE = 70,
0139: SEQUENCE = 71;
0140:
0141: // temporary used during parsing
0142: static final int PLUS = 100, OPEN = 101, CLOSE = 102, SELECT = 103,
0143: COMMA = 104, BETWEEN = 106, CAST = 107, END = 108,
0144: IS = 109, WHEN = 110, THEN = 111, ELSE = 112,
0145: ENDWHEN = 113, DISTINCT = 114, VIEW = 115;
0146:
0147: // used inside brackets for system functions
0148: static final int AS = 122, FOR = 123, FROM = 124, BOTH = 125,
0149: LEADING = 126, TRAILING = 127, YEAR = 128, MONTH = 129,
0150: DAY = 130, HOUR = 131, MINUTE = 132, SECOND = 133,
0151: TIMEZONE_HOUR = 134, T_TIMEZONE_MINUTE = 135, DOW = 136;
0152: static final HashSet SQL_EXTRACT_FIELD_NAMES = new HashSet();
0153: static final HashSet SQL_TRIM_SPECIFICATION = new HashSet();
0154:
0155: static {
0156: SQL_EXTRACT_FIELD_NAMES.addAll(new Object[] { Token.T_YEAR,
0157: Token.T_MONTH, Token.T_DAY, Token.T_HOUR,
0158: Token.T_MINUTE, Token.T_SECOND, Token.T_TIMEZONE_HOUR,
0159: Token.T_TIMEZONE_MINUTE, Token.T_DOW });
0160: SQL_TRIM_SPECIFICATION.addAll(new Object[] { Token.T_LEADING,
0161: Token.T_TRAILING, Token.T_BOTH });
0162: }
0163:
0164: private static final int AGGREGATE_SELF = -1;
0165: private static final int AGGREGATE_NONE = 0;
0166: private static final int AGGREGATE_LEFT = 1;
0167: private static final int AGGREGATE_RIGHT = 2;
0168: private static final int AGGREGATE_BOTH = 3;
0169: private static final int AGGREGATE_FUNCTION = 4;
0170:
0171: // type
0172: int exprType;
0173: private int aggregateSpec = AGGREGATE_NONE;
0174:
0175: // nodes
0176: Expression eArg, eArg2;
0177:
0178: // VALUE
0179: Object valueData;
0180: private int dataType;
0181:
0182: // VALUE LIST NEW
0183: HashSet hList;
0184: Expression[] valueList;
0185: private boolean isFixedConstantValueList;
0186:
0187: // QUERY - in single value selects, IN or EXISTS predicates
0188: SubQuery subQuery;
0189: boolean isQueryCorrelated;
0190:
0191: // FUNCTION
0192: Function function;
0193:
0194: // LIKE
0195: private Like likeObject;
0196:
0197: // COLUMN
0198: private String catalog;
0199: private String schema;
0200: private String tableName;
0201: private String columnName;
0202: private TableFilter tableFilter; // null if not yet resolved
0203: TableFilter outerFilter; // defined if this is part of an OUTER JOIN condition tree
0204:
0205: // COLUMN
0206: private int columnIndex;
0207: private boolean columnQuoted;
0208: private int precision;
0209: private int scale;
0210: private String columnAlias; // if it is a column of a select column list
0211: private boolean aliasQuoted;
0212:
0213: //
0214: private boolean isDescending; // if it is a column in a order by
0215: int joinedTableColumnIndex = -1; // >= 0 when it is used for order by
0216: boolean isDistinctAggregate;
0217:
0218: // PARAM
0219: private boolean isParam;
0220:
0221: // does Expression stem from a JOIN <table> ON <expression>
0222: boolean isInJoin;
0223:
0224: //
0225: static final Integer INTEGER_0 = ValuePool.getInt(0);
0226: static final Integer INTEGER_1 = ValuePool.getInt(1);
0227:
0228: /**
0229: * Creates a new boolean expression
0230: * @param b boolean constant
0231: */
0232: Expression(boolean b) {
0233: exprType = b ? TRUE : FALSE;
0234: }
0235:
0236: /**
0237: * Creates a new FUNCTION expression
0238: * @param f function
0239: */
0240: Expression(Function f) {
0241:
0242: exprType = FUNCTION;
0243: function = f;
0244:
0245: if (f.hasAggregate) {
0246: aggregateSpec = AGGREGATE_FUNCTION;
0247: }
0248: }
0249:
0250: /**
0251: * Creates a new SEQUENCE expression
0252: * @param sequence number sequence
0253: */
0254: Expression(NumberSequence sequence) {
0255:
0256: exprType = SEQUENCE;
0257: valueData = sequence;
0258: dataType = sequence.getType();
0259: }
0260:
0261: /**
0262: * Copy Constructor. Used by TableFilter to move a condition to a filter.
0263: * @param e source expression
0264: */
0265: Expression(Expression e) {
0266:
0267: exprType = e.exprType;
0268: dataType = e.dataType;
0269: eArg = e.eArg;
0270: eArg2 = e.eArg2;
0271: isInJoin = e.isInJoin;
0272:
0273: //
0274: likeObject = e.likeObject;
0275: subQuery = e.subQuery;
0276: function = e.function;
0277:
0278: checkAggregate();
0279: }
0280:
0281: /**
0282: * Creates a new QUERY expression
0283: * @param sq subquery
0284: */
0285: Expression(SubQuery sq) {
0286: exprType = QUERY;
0287: subQuery = sq;
0288: }
0289:
0290: /**
0291: * Creates a new VALUELIST expression
0292: * @param valueList array of Expression
0293: */
0294: Expression(Expression[] valueList) {
0295: exprType = VALUELIST;
0296: this .valueList = valueList;
0297: }
0298:
0299: /**
0300: * Creates a new binary (or unary) operation expression
0301: *
0302: * @param type operator type
0303: * @param e operand 1
0304: * @param e2 operand 2
0305: */
0306: Expression(int type, Expression e, Expression e2) {
0307:
0308: exprType = type;
0309: eArg = e;
0310: eArg2 = e2;
0311:
0312: checkAggregate();
0313: }
0314:
0315: /**
0316: * creates a CONVERT expression
0317: */
0318: Expression(Expression e, int dataType, int precision, int scale) {
0319:
0320: this .exprType = CONVERT;
0321: this .eArg = e;
0322: this .dataType = dataType;
0323: this .precision = precision;
0324: this .scale = scale;
0325: this .columnAlias = e.columnAlias;
0326: this .aliasQuoted = e.aliasQuoted;
0327:
0328: checkAggregate();
0329: }
0330:
0331: /**
0332: * Creates a new LIKE expression
0333: *
0334: * @param e operand 1
0335: * @param e2 operand 2
0336: * @param escape escape character
0337: */
0338: Expression(Expression e, Expression e2, Character escape,
0339: boolean hasCollation) {
0340:
0341: exprType = LIKE;
0342: eArg = e;
0343: eArg2 = e2;
0344: likeObject = new Like(escape, hasCollation);
0345:
0346: checkAggregate();
0347: }
0348:
0349: /**
0350: * Creates a new ASTERISK or COLUMN expression
0351: * @param table table
0352: * @param column column
0353: */
0354: Expression(String schema, String table, String column) {
0355:
0356: this .schema = schema;
0357: tableName = table;
0358:
0359: if (column == null) {
0360: exprType = ASTERISK;
0361: } else {
0362: exprType = COLUMN;
0363: columnName = column;
0364: }
0365: }
0366:
0367: /**
0368: * Creates a new ASTERIX or possibly quoted COLUMN expression
0369: * @param table table
0370: * @param column column name
0371: * @param isquoted boolean
0372: */
0373: Expression(String table, String column, boolean isquoted) {
0374:
0375: tableName = table;
0376:
0377: if (column == null) {
0378: exprType = ASTERISK;
0379: } else {
0380: exprType = COLUMN;
0381: columnName = column;
0382: columnQuoted = isquoted;
0383: }
0384: }
0385:
0386: Expression(TableFilter filter, Column column) {
0387:
0388: schema = filter.filterTable.tableName.schema.name;
0389: tableName = filter.getName();
0390:
0391: if (column == null) {
0392: exprType = ASTERISK;
0393: } else {
0394: exprType = COLUMN;
0395: columnName = column.columnName.name;
0396: columnQuoted = column.columnName.isNameQuoted;
0397: dataType = column.getType();
0398: }
0399: }
0400:
0401: /**
0402: * Creates a new VALUE expression
0403: *
0404: * @param datatype data type
0405: * @param o data
0406: */
0407: Expression(int datatype, Object o) {
0408:
0409: exprType = VALUE;
0410: dataType = datatype;
0411: valueData = o;
0412: }
0413:
0414: /**
0415: * Creates a new (possibly PARAM) VALUE expression
0416: *
0417: * @param datatype initial datatype
0418: * @param o initial value
0419: * @param isParam true if this is to be a PARAM VALUE expression
0420: */
0421: Expression(int datatype, Object o, boolean isParam) {
0422:
0423: this (datatype, o);
0424:
0425: this .isParam = isParam;
0426:
0427: if (isParam) {
0428: paramMode = PARAM_IN;
0429: }
0430: }
0431:
0432: boolean isTypeEqual(Expression other) {
0433: return dataType == other.dataType
0434: && precision == other.precision && scale == other.scale;
0435: }
0436:
0437: private void checkAggregate() {
0438:
0439: if (isAggregate(exprType)) {
0440: aggregateSpec = AGGREGATE_SELF;
0441: } else {
0442: aggregateSpec = AGGREGATE_NONE;
0443:
0444: if ((eArg != null) && eArg.isAggregate()) {
0445: aggregateSpec += AGGREGATE_LEFT;
0446: }
0447:
0448: if ((eArg2 != null) && eArg2.isAggregate()) {
0449: aggregateSpec += AGGREGATE_RIGHT;
0450: }
0451: }
0452: }
0453:
0454: public String describe(Session session) {
0455: return describe(session, 0);
0456: }
0457:
0458: static String getContextDDL(Expression expression)
0459: throws HsqlException {
0460:
0461: String ddl = expression.getDDL();
0462:
0463: if (expression.exprType != VALUE
0464: && expression.exprType != COLUMN
0465: && expression.exprType != FUNCTION
0466: && expression.exprType != ALTERNATIVE
0467: && expression.exprType != CASEWHEN
0468: && expression.exprType != CONVERT) {
0469: StringBuffer temp = new StringBuffer();
0470:
0471: ddl = temp.append('(').append(ddl).append(')').toString();
0472: }
0473:
0474: return ddl;
0475: }
0476:
0477: /**
0478: * returns the complete name of the column represented by the expression
0479: *
0480: * If an alias is known for the column's table, this alias will precede the
0481: * column name, except it's "SYSTEM_SUBQUERY".
0482: * If no alias is known, the column's table will be asked for its
0483: * statementName, which then will precede the column name.
0484: */
0485: String getColumnDDL() throws HsqlException {
0486:
0487: Trace.doAssert(exprType == COLUMN);
0488:
0489: StringBuffer buf = new StringBuffer();
0490:
0491: Table table = tableFilter.getTable();
0492:
0493: if (tableName != null) {
0494: if (!tableName.equals("SYSTEM_SUBQUERY"))
0495: buf.append('"').append(tableName).append('"').append(
0496: '.');
0497: } else
0498: buf.append(table.tableName.statementName).append('.');
0499:
0500: buf
0501: .append(table.getColumn(columnIndex).columnName.statementName);
0502:
0503: return buf.toString();
0504: }
0505:
0506: /**
0507: * For use with CHECK constraints. Under development.
0508: *
0509: * Currently supports a subset of expressions and is suitable for CHECK
0510: * search conditions that refer only to the inserted/updated row.
0511: *
0512: * For full DDL reporting of VIEW select queries and CHECK search
0513: * conditions, future improvements here are dependent upon improvements to
0514: * SELECT query parsing, so that it is performed in a number of passes.
0515: * An early pass should result in the query turned into an Expression tree
0516: * that contains the information in the original SQL without any
0517: * alterations, and with tables and columns all resolved. This Expression
0518: * can then be preserved for future use. Table and column names that
0519: * are not user-defined aliases should be kept as the HsqlName structures
0520: * so that table or column renaming is reflected in the precompiled
0521: * query.
0522: *
0523: * @return DDL
0524: * @throws HsqlException
0525: */
0526: String getDDL() throws HsqlException {
0527:
0528: StringBuffer buf = new StringBuffer(64);
0529: String left = null;
0530: String right = null;
0531:
0532: if (eArg != null) {
0533: left = Expression.getContextDDL(eArg);
0534: }
0535:
0536: if (eArg2 != null) {
0537: right = Expression.getContextDDL(eArg2);
0538: }
0539:
0540: switch (exprType) {
0541:
0542: case FUNCTION:
0543: return function.getDLL();
0544:
0545: case VALUE:
0546: try {
0547: return isParam ? Token.T_QUESTION : Column
0548: .createSQLString(valueData, dataType);
0549: } catch (HsqlException e) {
0550: }
0551:
0552: return buf.toString();
0553:
0554: case COLUMN:
0555:
0556: // this is a limited solution
0557: Table table = tableFilter.getTable();
0558:
0559: if (tableName != null) {
0560: buf.append(table.tableName.statementName);
0561: buf.append('.');
0562: }
0563:
0564: buf
0565: .append(table.getColumn(columnIndex).columnName.statementName);
0566:
0567: return buf.toString();
0568:
0569: case TRUE:
0570: return Token.T_TRUE;
0571:
0572: case FALSE:
0573: return Token.T_FALSE;
0574:
0575: case VALUELIST:
0576: for (int i = 0; i < valueList.length; i++) {
0577: buf.append(valueList[i].getDDL());
0578:
0579: if (i < valueList.length - 1) {
0580: buf.append(',');
0581: }
0582: }
0583:
0584: return buf.toString();
0585:
0586: case ASTERISK:
0587: buf.append('*');
0588:
0589: return buf.toString();
0590:
0591: case NEGATE:
0592: buf.append('-').append(left);
0593:
0594: return buf.toString();
0595:
0596: case ADD:
0597: buf.append(left).append('+').append(right);
0598:
0599: return buf.toString();
0600:
0601: case SUBTRACT:
0602: buf.append(left).append('-').append(right);
0603:
0604: return buf.toString();
0605:
0606: case MULTIPLY:
0607: buf.append(left).append('*').append(right);
0608:
0609: return buf.toString();
0610:
0611: case DIVIDE:
0612: buf.append(left).append('/').append(right);
0613:
0614: return buf.toString();
0615:
0616: case CONCAT:
0617: buf.append(left).append("||").append(right);
0618:
0619: return buf.toString();
0620:
0621: case NOT:
0622: if (eArg.exprType == IS_NULL) {
0623: buf.append(getContextDDL(eArg.eArg)).append(' ')
0624: .append(Token.T_IS).append(' ').append(
0625: Token.T_NOT).append(' ').append(
0626: Token.T_NULL);
0627:
0628: return buf.toString();
0629: }
0630:
0631: buf.append(Token.T_NOT).append(' ').append(left);
0632:
0633: return buf.toString();
0634:
0635: case EQUAL:
0636: buf.append(left).append('=').append(right);
0637:
0638: return buf.toString();
0639:
0640: case BIGGER_EQUAL:
0641: buf.append(left).append(">=").append(right);
0642:
0643: return buf.toString();
0644:
0645: case BIGGER:
0646: buf.append(left).append('>').append(right);
0647:
0648: return buf.toString();
0649:
0650: case SMALLER:
0651: buf.append(left).append('<').append(right);
0652:
0653: return buf.toString();
0654:
0655: case SMALLER_EQUAL:
0656: buf.append(left).append("<=").append(right);
0657:
0658: return buf.toString();
0659:
0660: case NOT_EQUAL:
0661: if (Token.T_NULL.equals(right)) {
0662: buf.append(left).append(" IS NOT ").append(right);
0663: } else {
0664: buf.append(left).append("!=").append(right);
0665: }
0666:
0667: return buf.toString();
0668:
0669: case LIKE:
0670: buf.append(left).append(' ').append(Token.T_LIKE).append(
0671: ' ');
0672: buf.append(right);
0673:
0674: /** @todo fredt - scripting of non-ascii escapes needs changes to general script logging */
0675: if (likeObject.escapeChar != null) {
0676: buf.append(' ').append(Token.T_ESCAPE).append(' ')
0677: .append('\'');
0678: buf.append(likeObject.escapeChar.toString()).append(
0679: '\'');
0680: buf.append(' ');
0681: }
0682:
0683: return buf.toString();
0684:
0685: case AND:
0686: buf.append(left).append(' ').append(Token.T_AND)
0687: .append(' ').append(right);
0688:
0689: return buf.toString();
0690:
0691: case OR:
0692: buf.append(left).append(' ').append(Token.T_OR).append(' ')
0693: .append(right);
0694:
0695: return buf.toString();
0696:
0697: case ALL:
0698: buf.append(left).append(' ').append(Token.T_ALL)
0699: .append(' ').append(right);
0700:
0701: return buf.toString();
0702:
0703: case ANY:
0704: buf.append(left).append(' ').append(Token.T_ANY)
0705: .append(' ').append(right);
0706:
0707: return buf.toString();
0708:
0709: case IN:
0710: buf.append(left).append(' ').append(Token.T_IN).append(' ')
0711: .append(right);
0712:
0713: return buf.toString();
0714:
0715: case CONVERT:
0716: buf.append(' ').append(Token.T_CONVERT).append('(');
0717: buf.append(left).append(',');
0718: buf.append(Types.getTypeString(dataType, precision, scale));
0719: buf.append(')');
0720:
0721: return buf.toString();
0722:
0723: case CASEWHEN:
0724: buf.append(' ').append(Token.T_CASEWHEN).append('(');
0725: buf.append(left).append(',').append(right).append(')');
0726:
0727: return buf.toString();
0728:
0729: case IS_NULL:
0730: buf.append(left).append(' ').append(Token.T_IS).append(' ')
0731: .append(Token.T_NULL);
0732:
0733: return buf.toString();
0734:
0735: case ALTERNATIVE:
0736: buf.append(left).append(',').append(right);
0737:
0738: return buf.toString();
0739:
0740: case QUERY:
0741: /*
0742: buf.append('(');
0743: buf.append(subSelect.getDDL());
0744: buf.append(')');
0745: */
0746: break;
0747:
0748: case EXISTS:
0749: buf.append(' ').append(Token.T_EXISTS).append(' ');
0750: break;
0751:
0752: case COUNT:
0753: buf.append(' ').append(Token.T_COUNT).append('(');
0754: break;
0755:
0756: case SUM:
0757: buf.append(' ').append(Token.T_SUM).append('(');
0758: buf.append(left).append(')');
0759: break;
0760:
0761: case MIN:
0762: buf.append(' ').append(Token.T_MIN).append('(');
0763: buf.append(left).append(')');
0764: break;
0765:
0766: case MAX:
0767: buf.append(' ').append(Token.T_MAX).append('(');
0768: buf.append(left).append(')');
0769: break;
0770:
0771: case AVG:
0772: buf.append(' ').append(Token.T_AVG).append('(');
0773: buf.append(left).append(')');
0774: break;
0775:
0776: case EVERY:
0777: buf.append(' ').append(Token.T_EVERY).append('(');
0778: buf.append(left).append(')');
0779: break;
0780:
0781: case SOME:
0782: buf.append(' ').append(Token.T_SOME).append('(');
0783: buf.append(left).append(')');
0784: break;
0785:
0786: case STDDEV_POP:
0787: buf.append(' ').append(Token.T_STDDEV_POP).append('(');
0788: buf.append(left).append(')');
0789: break;
0790:
0791: case STDDEV_SAMP:
0792: buf.append(' ').append(Token.T_STDDEV_SAMP).append('(');
0793: buf.append(left).append(')');
0794: break;
0795:
0796: case VAR_POP:
0797: buf.append(' ').append(Token.T_VAR_POP).append('(');
0798: buf.append(left).append(')');
0799: break;
0800:
0801: case VAR_SAMP:
0802: buf.append(' ').append(Token.T_VAR_SAMP).append('(');
0803: buf.append(left).append(')');
0804: break;
0805: }
0806:
0807: throw Trace.error(Trace.EXPRESSION_NOT_SUPPORTED);
0808: }
0809:
0810: private String describe(Session session, int blanks) {
0811:
0812: int lIType;
0813: StringBuffer buf = new StringBuffer(64);
0814:
0815: buf.append('\n');
0816:
0817: for (int i = 0; i < blanks; i++) {
0818: buf.append(' ');
0819: }
0820:
0821: if (oldIType != -1) {
0822: buf.append("SET TRUE, WAS: ");
0823: }
0824:
0825: lIType = oldIType == -1 ? exprType : oldIType;
0826:
0827: switch (lIType) {
0828:
0829: case FUNCTION:
0830: buf.append("FUNCTION ");
0831: buf.append(function.describe(session));
0832:
0833: return buf.toString();
0834:
0835: case VALUE:
0836: if (isParam) {
0837: buf.append("PARAM ");
0838: }
0839:
0840: buf.append("VALUE = ").append(valueData);
0841: buf.append(", TYPE = ").append(
0842: Types.getTypeString(dataType));
0843:
0844: return buf.toString();
0845:
0846: case COLUMN:
0847: buf.append("COLUMN ");
0848:
0849: if (tableName != null) {
0850: buf.append(tableName);
0851: buf.append('.');
0852: }
0853:
0854: buf.append(columnName);
0855:
0856: return buf.toString();
0857:
0858: case QUERY:
0859: buf.append("QUERY ");
0860: buf.append(subQuery.select.describe(session));
0861:
0862: return buf.toString();
0863:
0864: case TRUE:
0865: buf.append("TRUE ");
0866: break;
0867:
0868: case FALSE:
0869: buf.append("FALSE ");
0870: break;
0871:
0872: case VALUELIST:
0873: buf.append("VALUELIST ");
0874: buf.append(" TYPE = ")
0875: .append(Types.getTypeString(dataType));
0876:
0877: if (valueList != null) {
0878: for (int i = 0; i < valueList.length; i++) {
0879: buf.append(valueList[i].describe(session, blanks
0880: + blanks));
0881: buf.append(' ');
0882: }
0883: }
0884: break;
0885:
0886: case ASTERISK:
0887: buf.append("* ");
0888: break;
0889:
0890: case NEGATE:
0891: buf.append("NEGATE ");
0892: break;
0893:
0894: case ADD:
0895: buf.append("ADD ");
0896: break;
0897:
0898: case SUBTRACT:
0899: buf.append("SUBTRACT ");
0900: break;
0901:
0902: case MULTIPLY:
0903: buf.append("MULTIPLY ");
0904: break;
0905:
0906: case DIVIDE:
0907: buf.append("DIVIDE ");
0908: break;
0909:
0910: case CONCAT:
0911: buf.append("CONCAT ");
0912: break;
0913:
0914: case NOT:
0915: buf.append("NOT ");
0916: break;
0917:
0918: case EQUAL:
0919: buf.append("EQUAL ");
0920: break;
0921:
0922: case BIGGER_EQUAL:
0923: buf.append("BIGGER_EQUAL ");
0924: break;
0925:
0926: case BIGGER:
0927: buf.append("BIGGER ");
0928: break;
0929:
0930: case SMALLER:
0931: buf.append("SMALLER ");
0932: break;
0933:
0934: case SMALLER_EQUAL:
0935: buf.append("SMALLER_EQUAL ");
0936: break;
0937:
0938: case NOT_EQUAL:
0939: buf.append("NOT_EQUAL ");
0940: break;
0941:
0942: case LIKE:
0943: buf.append("LIKE ");
0944: buf.append(likeObject.describe(session));
0945: break;
0946:
0947: case AND:
0948: buf.append("AND ");
0949: break;
0950:
0951: case OR:
0952: buf.append("OR ");
0953: break;
0954:
0955: case ALL:
0956: buf.append("ALL ");
0957: break;
0958:
0959: case ANY:
0960: buf.append("ANY ");
0961: break;
0962:
0963: case IN:
0964: buf.append("IN ");
0965: break;
0966:
0967: case IS_NULL:
0968: buf.append("IS_NULL ");
0969: break;
0970:
0971: case EXISTS:
0972: buf.append("EXISTS ");
0973: break;
0974:
0975: case COUNT:
0976: buf.append("COUNT ");
0977: break;
0978:
0979: case SUM:
0980: buf.append("SUM ");
0981: break;
0982:
0983: case MIN:
0984: buf.append("MIN ");
0985: break;
0986:
0987: case MAX:
0988: buf.append("MAX ");
0989: break;
0990:
0991: case AVG:
0992: buf.append("AVG ");
0993: break;
0994:
0995: case EVERY:
0996: buf.append(Token.T_EVERY).append(' ');
0997: break;
0998:
0999: case SOME:
1000: buf.append(Token.T_SOME).append(' ');
1001: break;
1002:
1003: case STDDEV_POP:
1004: buf.append(Token.T_STDDEV_POP).append(' ');
1005: break;
1006:
1007: case STDDEV_SAMP:
1008: buf.append(Token.T_STDDEV_SAMP).append(' ');
1009: break;
1010:
1011: case VAR_POP:
1012: buf.append(Token.T_VAR_POP).append(' ');
1013: break;
1014:
1015: case VAR_SAMP:
1016: buf.append(Token.T_VAR_SAMP).append(' ');
1017: break;
1018:
1019: case CONVERT:
1020: buf.append("CONVERT ");
1021: buf.append(Types.getTypeString(dataType, precision, scale));
1022: buf.append(' ');
1023: break;
1024:
1025: case CASEWHEN:
1026: buf.append("CASEWHEN ");
1027: break;
1028: }
1029:
1030: if (isInJoin) {
1031: buf.append(" join");
1032: }
1033:
1034: if (eArg != null) {
1035: buf.append(" arg1=[");
1036: buf.append(eArg.describe(session, blanks + 1));
1037: buf.append(']');
1038: }
1039:
1040: if (eArg2 != null) {
1041: buf.append(" arg2=[");
1042: buf.append(eArg2.describe(session, blanks + 1));
1043: buf.append(']');
1044: }
1045:
1046: return buf.toString();
1047: }
1048:
1049: /**
1050: * Set the data type
1051: *
1052: *
1053: * @param type data type
1054: */
1055: void setDataType(int type) {
1056: dataType = type;
1057: }
1058:
1059: int oldIType = -1;
1060:
1061: /**
1062: * When an Expression is assigned to a TableFilter, a copy is made for use
1063: * there and the original is set to Expression.TRUE
1064: *
1065: */
1066: void setTrue() {
1067:
1068: if (oldIType == -1) {
1069: oldIType = exprType;
1070: }
1071:
1072: exprType = TRUE;
1073: }
1074:
1075: void setNull() {
1076:
1077: isParam = false;
1078: exprType = VALUE;
1079: dataType = Types.NULL;
1080: valueData = null;
1081: eArg = null;
1082: eArg2 = null;
1083: }
1084:
1085: /**
1086: * Check if the given expression defines similar operation as this
1087: * expression. This method is used for ensuring an expression in
1088: * the ORDER BY clause has a matching column in the SELECT list. This check
1089: * is necessary with a SELECT DISTINCT query.<br>
1090: *
1091: * In the future we may perform the test when evaluating the search
1092: * condition to get a more accurate match.
1093: *
1094: * @param exp expression
1095: * @return boolean
1096: */
1097: public boolean similarTo(Expression exp) {
1098:
1099: if (exp == null) {
1100: return false;
1101: }
1102:
1103: if (exp == this ) {
1104: return true;
1105: }
1106:
1107: /** @todo fredt - equals() method for valueList, subSelect and function are needed */
1108: return exprType == exp.exprType && dataType == exp.dataType
1109: && equals(valueData, exp.valueData)
1110: && equals(valueList, exp.valueList)
1111: && equals(subQuery, exp.subQuery)
1112: && equals(function, exp.function)
1113: && equals(tableName, exp.tableName)
1114: && equals(columnName, exp.columnName)
1115: && similarTo(eArg, exp.eArg)
1116: && similarTo(eArg2, exp.eArg2);
1117: }
1118:
1119: static boolean equals(Object o1, Object o2) {
1120: return (o1 == null) ? o2 == null : o1.equals(o2);
1121: }
1122:
1123: static boolean equals(Expression[] ae1, Expression[] ae2) {
1124:
1125: if (ae1 == ae2) {
1126: return true;
1127: }
1128:
1129: if (ae1.length != ae2.length) {
1130: return false;
1131: }
1132:
1133: int len = ae1.length;
1134: boolean equals = true;
1135:
1136: for (int i = 0; i < len; i++) {
1137: Expression e1 = ae1[i];
1138: Expression e2 = ae2[i];
1139:
1140: equals = (e1 == null) ? e2 == null : e1.equals(e2);
1141: }
1142:
1143: return equals;
1144: }
1145:
1146: static boolean similarTo(Expression e1, Expression e2) {
1147: return (e1 == null) ? e2 == null : e1.similarTo(e2);
1148: }
1149:
1150: /** @todo fredt - workaround for functions in ORDER BY and GROUP BY needs
1151: * checking the argument of the function to ensure they are valid. */
1152:
1153: /**
1154: * Check if this expression can be included in a group by clause.
1155: * <p>
1156: * It can, if itself is a column expression, and it is not an aggregate
1157: * expression.
1158: *
1159: * @return boolean
1160: */
1161: boolean canBeInGroupBy() {
1162:
1163: if (exprType == FUNCTION) {
1164: return true;
1165: }
1166:
1167: return isColumn() && (!(isAggregate()));
1168: }
1169:
1170: /**
1171: * Check if this expression can be included in an order by clause.
1172: * <p>
1173: * It can, if itself is a column expression.
1174: *
1175: * @return boolean
1176: */
1177: boolean canBeInOrderBy() {
1178: return exprType == FUNCTION || joinedTableColumnIndex != -1
1179: || isColumn() || isAggregate();
1180: }
1181:
1182: /**
1183: * Check if this expression defines at least one column.
1184: * <p>
1185: * It is, if itself is a column expression, or any the argument
1186: * expressions is a column expression.
1187: *
1188: * @return boolean
1189: */
1190: private boolean isColumn() {
1191:
1192: switch (exprType) {
1193:
1194: case COLUMN:
1195: return true;
1196:
1197: case NEGATE:
1198: return eArg.isColumn();
1199:
1200: case ADD:
1201: case SUBTRACT:
1202: case MULTIPLY:
1203: case DIVIDE:
1204: case CONCAT:
1205: return eArg.isColumn() || eArg2.isColumn();
1206: }
1207:
1208: return false;
1209: }
1210:
1211: /**
1212: * Collect column name used in this expression.
1213: *
1214: * @param columnNames set to be filled
1215: * @return true if a column name is used in this expression
1216: */
1217: boolean collectColumnName(HashSet columnNames) {
1218:
1219: boolean result = exprType == COLUMN;
1220:
1221: if (result) {
1222: columnNames.add(columnName);
1223: }
1224:
1225: return result;
1226: }
1227:
1228: /**
1229: * Collect all column names used in this expression or any of nested
1230: * expression.
1231: *
1232: * @param columnNames set to be filled
1233: */
1234: void collectAllColumnNames(HashSet columnNames) {
1235:
1236: if (!collectColumnName(columnNames)) {
1237: if (eArg != null) {
1238: eArg.collectAllColumnNames(columnNames);
1239: }
1240:
1241: if (eArg2 != null) {
1242: eArg2.collectAllColumnNames(columnNames);
1243: }
1244: }
1245: }
1246:
1247: /**
1248: * Check if this expression defines a constant value.
1249: * <p>
1250: * It does, if it is a constant value expression, or all the argument
1251: * expressions define constant values.
1252: *
1253: * @return boolean
1254: */
1255: boolean isConstant() {
1256:
1257: switch (exprType) {
1258:
1259: case VALUE:
1260: return true;
1261:
1262: case NEGATE:
1263: return eArg.isConstant();
1264:
1265: case ADD:
1266: case SUBTRACT:
1267: case MULTIPLY:
1268: case DIVIDE:
1269: case CONCAT:
1270: return eArg.isConstant() && eArg2.isConstant();
1271: }
1272:
1273: return false;
1274: }
1275:
1276: /**
1277: * Check if this expression can be included as a result column in an
1278: * aggregated select statement.
1279: * <p>
1280: * It can, if itself is an aggregate expression, or it results a constant
1281: * value.
1282: *
1283: * @return boolean
1284: */
1285: boolean canBeInAggregate() {
1286: return isAggregate() || isConstant();
1287: }
1288:
1289: /**
1290: * Is this (indirectly) an aggregate expression
1291: *
1292: * @return boolean
1293: */
1294: boolean isAggregate() {
1295: return aggregateSpec != AGGREGATE_NONE;
1296: }
1297:
1298: /**
1299: * Is this directly an aggregate expression
1300: *
1301: *
1302: * @return boolean
1303: */
1304: boolean isSelfAggregate() {
1305: return aggregateSpec == AGGREGATE_SELF;
1306: }
1307:
1308: static boolean isAggregate(int type) {
1309:
1310: switch (type) {
1311:
1312: case COUNT:
1313: case MAX:
1314: case MIN:
1315: case SUM:
1316: case AVG:
1317: case EVERY:
1318: case SOME:
1319: case STDDEV_POP:
1320: case STDDEV_SAMP:
1321: case VAR_POP:
1322: case VAR_SAMP:
1323: return true;
1324: }
1325:
1326: return false;
1327: }
1328:
1329: // tony_lai@users having
1330:
1331: /**
1332: * Checks for conditional expression.
1333: *
1334: *
1335: * @return boolean
1336: */
1337: boolean isConditional() {
1338:
1339: switch (exprType) {
1340:
1341: case TRUE:
1342: case FALSE:
1343: case EQUAL:
1344: case BIGGER_EQUAL:
1345: case BIGGER:
1346: case SMALLER:
1347: case SMALLER_EQUAL:
1348: case NOT_EQUAL:
1349: case LIKE:
1350: case IN:
1351: case EXISTS:
1352: case IS_NULL:
1353: return true;
1354:
1355: case NOT:
1356: return eArg.isConditional();
1357:
1358: case AND:
1359: case OR:
1360: return eArg.isConditional() && eArg2.isConditional();
1361:
1362: default:
1363: return false;
1364: }
1365: }
1366:
1367: /**
1368: * Collects all expressions that must be in the GROUP BY clause, for a
1369: * grouped select statement.
1370: *
1371: * @param colExps expression list
1372: */
1373: void collectInGroupByExpressions(HsqlArrayList colExps) {
1374:
1375: if (!(isConstant() || isSelfAggregate())) {
1376: if (isColumn()) {
1377: colExps.add(this );
1378: } else if (exprType == FUNCTION) {
1379:
1380: // function.collectInGroupByExpressions(colExps);
1381: } else if (exprType == CASEWHEN) {
1382: eArg2.collectInGroupByExpressions(colExps);
1383: } else {
1384: if (eArg != null) {
1385: eArg.collectInGroupByExpressions(colExps);
1386: }
1387:
1388: if (eArg2 != null) {
1389: eArg2.collectInGroupByExpressions(colExps);
1390: }
1391: }
1392: }
1393: }
1394:
1395: /**
1396: * Set an ORDER BY column expression DESC
1397: *
1398: */
1399: void setDescending() {
1400: isDescending = true;
1401: }
1402:
1403: /**
1404: * Is an ORDER BY column expression DESC
1405: *
1406: *
1407: * @return boolean
1408: */
1409: boolean isDescending() {
1410: return isDescending;
1411: }
1412:
1413: /**
1414: * Set the column alias and whether the name is quoted
1415: *
1416: * @param s alias
1417: * @param isquoted boolean
1418: */
1419: void setAlias(String s, boolean isquoted) {
1420: columnAlias = s;
1421: aliasQuoted = isquoted;
1422: }
1423:
1424: /**
1425: * Change the column name
1426: *
1427: * @param newname name
1428: * @param isquoted quoted
1429: */
1430: void setColumnName(String newname, boolean isquoted) {
1431: columnName = newname;
1432: columnQuoted = isquoted;
1433: }
1434:
1435: /**
1436: * Change the table name
1437: *
1438: * @param newname table name for column expression
1439: */
1440: void setTableName(String newname) {
1441: tableName = newname;
1442: }
1443:
1444: /**
1445: * Return the user defined alias or null if none
1446: *
1447: * @return alias
1448: */
1449: String getDefinedAlias() {
1450: return columnAlias;
1451: }
1452:
1453: /**
1454: * Get the column alias
1455: *
1456: *
1457: * @return alias
1458: */
1459: String getAlias() {
1460:
1461: if (columnAlias != null) {
1462: return columnAlias;
1463: }
1464:
1465: if (exprType == COLUMN) {
1466: return columnName;
1467: }
1468:
1469: return "";
1470: }
1471:
1472: /**
1473: * Is a column alias quoted
1474: *
1475: * @return boolean
1476: */
1477: boolean isAliasQuoted() {
1478:
1479: if (columnAlias != null) {
1480: return aliasQuoted;
1481: }
1482:
1483: if (exprType == COLUMN) {
1484: return columnQuoted;
1485: }
1486:
1487: return false;
1488: }
1489:
1490: /**
1491: * Returns the type of expression
1492: *
1493: *
1494: * @return type
1495: */
1496: int getType() {
1497: return exprType;
1498: }
1499:
1500: /**
1501: * Returns the left node
1502: *
1503: *
1504: * @return argument
1505: */
1506: Expression getArg() {
1507: return eArg;
1508: }
1509:
1510: /**
1511: * Returns the right node
1512: *
1513: *
1514: * @return argument
1515: */
1516: Expression getArg2() {
1517: return eArg2;
1518: }
1519:
1520: /**
1521: * Returns the table filter for a COLUMN expression
1522: *
1523: * @return table filter
1524: */
1525: TableFilter getFilter() {
1526: return tableFilter;
1527: }
1528:
1529: /**
1530: * Final check for all expressions.
1531: *
1532: * @param check boolean
1533: * @return boolean
1534: * @throws HsqlException
1535: */
1536: boolean checkResolved(boolean check) throws HsqlException {
1537:
1538: boolean result = true;
1539:
1540: if (eArg != null) {
1541: result = result && eArg.checkResolved(check);
1542: }
1543:
1544: if (eArg2 != null) {
1545: result = result && eArg2.checkResolved(check);
1546: }
1547:
1548: if (subQuery != null && subQuery.select != null) {
1549: result = result && subQuery.select.checkResolved(check);
1550: }
1551:
1552: if (function != null) {
1553: result = result && function.checkResolved(check);
1554: }
1555:
1556: if (valueList != null) {
1557: for (int i = 0; i < valueList.length; i++) {
1558: result = result && valueList[i].checkResolved(check);
1559: }
1560: }
1561:
1562: if (exprType == COLUMN) {
1563: if (tableFilter == null) {
1564:
1565: // if an order by column alias
1566: result = joinedTableColumnIndex != -1;
1567:
1568: if (!result && check) {
1569: String err = tableName == null ? columnName
1570: : tableName + "." + columnName;
1571:
1572: throw Trace.error(Trace.COLUMN_NOT_FOUND, err);
1573: }
1574: } else {
1575: tableFilter.usedColumns[this .columnIndex] = true;
1576: }
1577: }
1578:
1579: return result;
1580: }
1581:
1582: /**
1583: * Resolve the table names for columns and throws if a column remains
1584: * unresolved.
1585: *
1586: * @param filters list of filters
1587: *
1588: * @throws HsqlException
1589: */
1590: void checkTables(HsqlArrayList filters) throws HsqlException {
1591:
1592: if (filters == null || exprType == Expression.VALUE) {
1593: return;
1594: }
1595:
1596: if (eArg != null) {
1597: eArg.checkTables(filters);
1598: }
1599:
1600: if (eArg2 != null) {
1601: eArg2.checkTables(filters);
1602: }
1603:
1604: switch (exprType) {
1605:
1606: case COLUMN:
1607: boolean found = false;
1608: int len = filters.size();
1609:
1610: for (int j = 0; j < len; j++) {
1611: TableFilter filter = (TableFilter) filters.get(j);
1612: String filterName = filter.getName();
1613:
1614: if (tableName == null || filterName.equals(tableName)) {
1615: Table table = filter.getTable();
1616: int i = table.findColumn(columnName);
1617:
1618: if (i != -1) {
1619: if (tableName == null) {
1620: if (found) {
1621: throw Trace
1622: .error(
1623: Trace.AMBIGUOUS_COLUMN_REFERENCE,
1624: columnName);
1625: }
1626:
1627: //
1628: found = true;
1629: } else {
1630: return;
1631: }
1632: }
1633: }
1634: }
1635:
1636: if (found) {
1637: return;
1638: }
1639:
1640: throw Trace.error(Trace.COLUMN_NOT_FOUND, columnName);
1641: case QUERY:
1642:
1643: // fredt - subquery in join condition !
1644: break;
1645:
1646: case FUNCTION:
1647: if (function != null) {
1648: function.checkTables(filters);
1649: }
1650: break;
1651:
1652: case ALL:
1653: case ANY:
1654: break;
1655:
1656: case IN:
1657: if (eArg2.exprType != QUERY) {
1658: Expression[] vl = eArg2.valueList;
1659:
1660: for (int i = 0; i < vl.length; i++) {
1661: vl[i].checkTables(filters);
1662: }
1663: }
1664: break;
1665:
1666: default:
1667: }
1668: }
1669:
1670: /**
1671: * return the expression for an aliases
1672: */
1673: Expression getExpressionForAlias(Expression[] columns, int length) {
1674:
1675: for (int i = 0; i < length; i++) {
1676: if (columnName.equals(columns[i].columnAlias)
1677: && (tableName == null || tableName
1678: .equals(columns[i].tableName))) {
1679: return columns[i];
1680: }
1681: }
1682:
1683: return this ;
1684: }
1685:
1686: /**
1687: * Replace aliases with expression trees
1688: */
1689: void replaceAliases(Expression[] columns, int length)
1690: throws HsqlException {
1691:
1692: if (eArg != null) {
1693: if (eArg.exprType == Expression.COLUMN) {
1694: eArg = eArg.getExpressionForAlias(columns, length);
1695: } else {
1696: eArg.replaceAliases(columns, length);
1697: }
1698: }
1699:
1700: if (eArg2 != null) {
1701: if (eArg2.exprType == Expression.COLUMN) {
1702: eArg2 = eArg2.getExpressionForAlias(columns, length);
1703: } else {
1704: eArg2.replaceAliases(columns, length);
1705: }
1706: }
1707:
1708: switch (exprType) {
1709:
1710: case QUERY:
1711: break;
1712:
1713: case FUNCTION:
1714: if (function != null) {
1715: function.replaceAliases(columns, length);
1716: }
1717: break;
1718:
1719: case ALL:
1720: case ANY:
1721: break;
1722:
1723: case IN:
1724: if (eArg2.exprType != QUERY) {
1725: Expression[] vl = eArg2.valueList;
1726:
1727: for (int i = 0; i < vl.length; i++) {
1728: if (vl[i].exprType == Expression.COLUMN) {
1729: vl[i] = vl[i].getExpressionForAlias(columns,
1730: length);
1731: } else {
1732: vl[i].replaceAliases(columns, length);
1733: }
1734: }
1735: }
1736: break;
1737:
1738: default:
1739: }
1740: }
1741:
1742: /**
1743: * Workaround for CHECK constraints. We don't want optimisation so we
1744: * flag all LIKE expressions as already optimised.
1745: *
1746: * @throws HsqlException
1747: */
1748: void setLikeOptimised() throws HsqlException {
1749:
1750: if (eArg != null) {
1751: eArg.setLikeOptimised();
1752: }
1753:
1754: if (eArg2 != null) {
1755: eArg2.setLikeOptimised();
1756: }
1757:
1758: if (exprType == LIKE) {
1759: likeObject.optimised = true;
1760: }
1761: }
1762:
1763: /**
1764: * Removes table filter resolution from an Expression tree.
1765: */
1766: /*
1767: void removeFilters() throws HsqlException {
1768:
1769: if (eArg != null) {
1770: eArg.removeFilters();
1771: }
1772:
1773: if (eArg2 != null) {
1774: eArg2.removeFilters();
1775: }
1776:
1777: switch (exprType) {
1778:
1779: case COLUMN :
1780: tableFilter = null;
1781:
1782: return;
1783:
1784: case QUERY :
1785: if (subSelect != null) {
1786: subSelect.removeFilters();
1787: }
1788: break;
1789:
1790: case FUNCTION :
1791: if (function != null) {
1792: function.removeFilters();
1793: }
1794: break;
1795:
1796: case IN :
1797: if (eArg2.exprType != QUERY) {
1798: Expression[] vl = eArg2.valueList;
1799:
1800: for (int i = 0; i < vl.length; i++) {
1801: vl[i].removeFilters();
1802: }
1803: }
1804: break;
1805:
1806: default :
1807: }
1808: }
1809: */
1810:
1811: /**
1812: * set boolean flags and expressions for columns in a join
1813: *
1814: * @param filter target table filter
1815: * @param columns boolean array
1816: * @param elist expression list
1817: */
1818: void getEquiJoinColumns(TableFilter filter, boolean[] columns,
1819: Expression[] elist) {
1820:
1821: if (eArg != null) {
1822: eArg.getEquiJoinColumns(filter, columns, elist);
1823: }
1824:
1825: if (eArg2 != null) {
1826: eArg2.getEquiJoinColumns(filter, columns, elist);
1827: }
1828:
1829: if (exprType == EQUAL) {
1830: if (eArg.tableFilter == eArg2.tableFilter) {
1831: return;
1832: }
1833:
1834: // an elist element may be set more than once - OK
1835: if (eArg.tableFilter == filter) {
1836: if (eArg2.exprType == COLUMN || eArg2.exprType == VALUE) {
1837: columns[eArg.columnIndex] = true;
1838: elist[eArg.columnIndex] = eArg2;
1839: }
1840:
1841: return;
1842: }
1843:
1844: if (eArg2.tableFilter == filter) {
1845: if (eArg.exprType == COLUMN || eArg.exprType == VALUE) {
1846: columns[eArg2.columnIndex] = true;
1847: elist[eArg2.columnIndex] = eArg;
1848: }
1849: }
1850: }
1851: }
1852:
1853: /**
1854: * Find a table filter with the given table alias
1855: */
1856: TableFilter findTableFilter(TableFilter[] list) {
1857:
1858: for (int t = 0; t < list.length; t++) {
1859: TableFilter f = list[t];
1860:
1861: if (schema == null
1862: || f.filterTable.getSchemaName().equals(schema)) {
1863: if (f.getName().equals(tableName)) {
1864: return f;
1865: }
1866: }
1867: }
1868:
1869: return null;
1870: }
1871:
1872: /**
1873: * Resolve the table names for columns
1874: *
1875: * @param f table filter
1876: *
1877: * @throws HsqlException
1878: */
1879: void resolveTables(TableFilter f) throws HsqlException {
1880:
1881: if (isParam || f == null || exprType == Expression.VALUE) {
1882: return;
1883: }
1884:
1885: if (eArg != null) {
1886: eArg.resolveTables(f);
1887: }
1888:
1889: if (eArg2 != null) {
1890: eArg2.resolveTables(f);
1891: }
1892:
1893: switch (exprType) {
1894:
1895: case COLUMN:
1896: if (tableFilter != null) {
1897: break;
1898: }
1899:
1900: String filterName = f.getName();
1901:
1902: if (tableName == null || tableName.equals(filterName)) {
1903: Table table = f.getTable();
1904: int i = table.findColumn(columnName);
1905:
1906: if (i != -1) {
1907: tableFilter = f;
1908: columnIndex = i;
1909: tableName = filterName;
1910:
1911: setTableColumnAttributes(table, i);
1912:
1913: // COLUMN is leaf; we are done
1914: return;
1915: }
1916: }
1917: break;
1918:
1919: case QUERY:
1920:
1921: // we now (1_7_2_ALPHA_R) resolve independently first, then
1922: // resolve in the enclosing context
1923: if (subQuery != null) {
1924: subQuery.select.resolveTablesUnion(f);
1925: }
1926: break;
1927:
1928: case FUNCTION:
1929: if (function != null) {
1930: function.resolveTables(f);
1931: }
1932: break;
1933:
1934: case ALL:
1935: case ANY:
1936: break;
1937:
1938: case IN:
1939: if (eArg2.exprType != QUERY) {
1940: Expression[] vl = eArg2.valueList;
1941:
1942: for (int i = 0; i < vl.length; i++) {
1943: vl[i].resolveTables(f);
1944: }
1945: }
1946: break;
1947:
1948: default:
1949: }
1950: }
1951:
1952: /**
1953: * For CASE WHEN and its special cases section 9.3 of the SQL standard
1954: * on type aggregation should be implemented.
1955: */
1956: int getCaseWhenType(Session session) throws HsqlException {
1957:
1958: /*
1959: find data type in condition
1960: int type = eArg.eArg.getDataType();
1961: then recurse on eArg2
1962:
1963: */
1964: return eArg2.dataType;
1965: }
1966:
1967: void resolveTypes(Session session) throws HsqlException {
1968:
1969: if (isParam) {
1970: return;
1971: }
1972:
1973: if (eArg != null) {
1974: eArg.resolveTypes(session);
1975: }
1976:
1977: if (eArg2 != null) {
1978: eArg2.resolveTypes(session);
1979: }
1980:
1981: switch (exprType) {
1982:
1983: case VALUE:
1984: if (dataType == Types.BOOLEAN && valueData != null) {
1985: exprType = ((Boolean) valueData).booleanValue() ? TRUE
1986: : FALSE;
1987: }
1988: break;
1989:
1990: case COLUMN:
1991: break;
1992:
1993: case FUNCTION:
1994: function.resolveType(session);
1995:
1996: dataType = function.getReturnType();
1997: break;
1998:
1999: case QUERY: {
2000: subQuery.select.resolveTypes(session);
2001:
2002: dataType = subQuery.select.exprColumns[0].dataType;
2003:
2004: break;
2005: }
2006: case NEGATE:
2007: if (eArg.isParam) {
2008: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2009: Trace.Expression_resolveTypes1);
2010: }
2011:
2012: dataType = eArg.dataType;
2013:
2014: if (isFixedConstant()) {
2015: valueData = getValue(session, dataType);
2016: eArg = null;
2017: exprType = VALUE;
2018: }
2019: break;
2020:
2021: case ADD:
2022:
2023: // concat using + operator
2024: // non-standard concat operator to be deprecated
2025: if (Types.isCharacterType(eArg.dataType)
2026: || Types.isCharacterType(eArg2.dataType)) {
2027: exprType = Expression.CONCAT;
2028: dataType = Types.VARCHAR;
2029:
2030: if (isFixedConstant()) {
2031: valueData = getValue(session, dataType);
2032: eArg = null;
2033: eArg2 = null;
2034: exprType = VALUE;
2035: } else {
2036: if (eArg.isParam) {
2037: eArg.dataType = Types.VARCHAR;
2038: }
2039:
2040: if (eArg2.isParam) {
2041: eArg2.dataType = Types.VARCHAR;
2042: }
2043: }
2044:
2045: break;
2046: }
2047: case SUBTRACT:
2048: case MULTIPLY:
2049: case DIVIDE:
2050: if (eArg.isParam && eArg2.isParam) {
2051: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2052: Trace.Expression_resolveTypes2);
2053: }
2054:
2055: if (isFixedConstant()) {
2056: dataType = Column.getCombinedNumberType(eArg.dataType,
2057: eArg2.dataType, exprType);
2058: valueData = getValue(session, dataType);
2059: eArg = null;
2060: eArg2 = null;
2061: exprType = VALUE;
2062: } else {
2063: if (eArg.isParam) {
2064: eArg.dataType = eArg2.dataType;
2065: } else if (eArg2.isParam) {
2066: eArg2.dataType = eArg.dataType;
2067: }
2068:
2069: // fredt@users 20011010 - patch 442993 by fredt
2070: dataType = Column.getCombinedNumberType(eArg.dataType,
2071: eArg2.dataType, exprType);
2072: }
2073: break;
2074:
2075: case CONCAT:
2076: dataType = Types.VARCHAR;
2077:
2078: if (isFixedConstant()) {
2079: valueData = getValue(session, dataType);
2080: eArg = null;
2081: eArg2 = null;
2082: exprType = VALUE;
2083: } else {
2084: if (eArg.isParam) {
2085: eArg.dataType = Types.VARCHAR;
2086: }
2087:
2088: if (eArg2.isParam) {
2089: eArg2.dataType = Types.VARCHAR;
2090: }
2091: }
2092: break;
2093:
2094: case EQUAL:
2095: case BIGGER_EQUAL:
2096: case BIGGER:
2097: case SMALLER:
2098: case SMALLER_EQUAL:
2099: case NOT_EQUAL:
2100: if (eArg.isParam && eArg2.isParam) {
2101: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2102: Trace.Expression_resolveTypes3);
2103: }
2104:
2105: if (isFixedConditional()) {
2106: Boolean result = test(session);
2107:
2108: if (result == null) {
2109: setNull();
2110: } else if (result.booleanValue()) {
2111: exprType = TRUE;
2112: } else {
2113: exprType = FALSE;
2114: }
2115:
2116: eArg = null;
2117: eArg2 = null;
2118: } else if (eArg.isParam) {
2119: eArg.dataType = eArg2.dataType == Types.NULL ? Types.VARCHAR
2120: : eArg2.dataType;
2121:
2122: if (eArg2.exprType == COLUMN) {
2123: eArg.setTableColumnAttributes(eArg2);
2124: }
2125: } else if (eArg2.isParam) {
2126: eArg2.dataType = eArg.dataType == Types.NULL ? Types.VARCHAR
2127: : eArg.dataType;
2128:
2129: if (eArg.exprType == COLUMN) {
2130: eArg2.setTableColumnAttributes(eArg);
2131: }
2132: }
2133:
2134: dataType = Types.BOOLEAN;
2135: break;
2136:
2137: case LIKE:
2138: resolveTypeForLike(session);
2139:
2140: dataType = Types.BOOLEAN;
2141: break;
2142:
2143: case AND: {
2144: boolean argFixed = eArg.isFixedConditional();
2145: boolean arg2Fixed = eArg2.isFixedConditional();
2146: Boolean arg = argFixed ? (eArg.test(session)) : null;
2147: Boolean arg2 = arg2Fixed ? eArg2.test(session) : null;
2148:
2149: if (argFixed && arg2Fixed) {
2150: if (arg == null || arg2 == null) {
2151: setNull();
2152: } else {
2153: exprType = arg.booleanValue()
2154: && arg2.booleanValue() ? TRUE : FALSE;
2155: eArg = null;
2156: eArg2 = null;
2157: }
2158: } else if ((argFixed && !Boolean.TRUE.equals(arg))
2159: || (arg2Fixed && !Boolean.TRUE.equals(arg2))) {
2160: exprType = FALSE;
2161: eArg = null;
2162: eArg2 = null;
2163: } else {
2164: if (eArg.isParam) {
2165: eArg.dataType = Types.BOOLEAN;
2166: }
2167:
2168: if (eArg2.isParam) {
2169: eArg2.dataType = Types.BOOLEAN;
2170: }
2171: }
2172:
2173: dataType = Types.BOOLEAN;
2174:
2175: break;
2176: }
2177: case OR: {
2178: boolean argFixed = eArg.isFixedConditional();
2179: boolean arg2Fixed = eArg2.isFixedConditional();
2180: Boolean arg = argFixed ? (eArg.test(session)) : null;
2181: Boolean arg2 = arg2Fixed ? eArg2.test(session) : null;
2182:
2183: if (argFixed && arg2Fixed) {
2184: if (arg == null || arg2 == null) {
2185: setNull();
2186: } else {
2187: exprType = arg.booleanValue()
2188: || arg2.booleanValue() ? TRUE : FALSE;
2189: eArg = null;
2190: eArg2 = null;
2191: }
2192: } else if ((argFixed && Boolean.TRUE.equals(arg))
2193: || (arg2Fixed && Boolean.TRUE.equals(arg2))) {
2194: exprType = TRUE;
2195: eArg = null;
2196: eArg2 = null;
2197: } else {
2198: if (eArg.isParam) {
2199: eArg.dataType = Types.BOOLEAN;
2200: }
2201:
2202: if (eArg2.isParam) {
2203: eArg2.dataType = Types.BOOLEAN;
2204: }
2205: }
2206:
2207: dataType = Types.BOOLEAN;
2208:
2209: break;
2210: }
2211: case IS_NULL:
2212: if (isFixedConditional()) {
2213: exprType = Boolean.TRUE.equals(test(session)) ? TRUE
2214: : FALSE;
2215: eArg = null;
2216: } else if (eArg.dataType == Types.NULL) {
2217: eArg.dataType = Types.VARCHAR;
2218: }
2219:
2220: dataType = Types.BOOLEAN;
2221: break;
2222:
2223: case NOT:
2224: if (isFixedConditional()) {
2225: Boolean arg = test(session);
2226:
2227: if (arg == null) {
2228: setNull();
2229: } else {
2230: exprType = arg.booleanValue() ? TRUE : FALSE;
2231: eArg = null;
2232: }
2233: } else if (eArg.isParam) {
2234: eArg.dataType = Types.BOOLEAN;
2235: }
2236:
2237: dataType = Types.BOOLEAN;
2238: break;
2239:
2240: case ALL:
2241: case ANY:
2242: dataType = eArg.dataType;
2243: break;
2244:
2245: case IN:
2246: resolveTypeForIn(session);
2247:
2248: dataType = Types.BOOLEAN;
2249: break;
2250:
2251: case EXISTS:
2252:
2253: // NOTE: no such thing as a param arg if expression is EXISTS
2254: // Also, cannot detect if result is fixed value
2255: dataType = Types.BOOLEAN;
2256: break;
2257:
2258: /** @todo fredt - set the correct return type */
2259: case COUNT:
2260: if (eArg.isParam) {
2261: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2262: Trace.Expression_resolveTypes4);
2263: }
2264:
2265: dataType = Types.INTEGER;
2266: break;
2267:
2268: case MAX:
2269: case MIN:
2270: case SUM:
2271: case AVG:
2272: case EVERY:
2273: case SOME:
2274: case STDDEV_POP:
2275: case STDDEV_SAMP:
2276: case VAR_POP:
2277: case VAR_SAMP:
2278: if (eArg.isParam) {
2279: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2280: Trace.Expression_resolveTypes4);
2281: }
2282:
2283: dataType = SetFunction.getType(exprType, eArg.dataType);
2284: break;
2285:
2286: case CONVERT:
2287:
2288: // NOTE: both iDataType for this expr and for eArg (if isParm)
2289: // are already set in Parser during read
2290: if (eArg.isFixedConstant() || eArg.isFixedConditional()) {
2291: valueData = getValue(session);
2292: exprType = VALUE;
2293: eArg = null;
2294: }
2295: break;
2296:
2297: case CASEWHEN:
2298:
2299: // We use CASEWHEN as parent type.
2300: // In the parent, eArg is the condition, and eArg2 is
2301: // the leaf, tagged as type ALTERNATIVE; its eArg is
2302: // case 1 (how to get the value when the condition in
2303: // the parent evaluates to true), while its eArg2 is case 2
2304: // (how to get the value when the condition in
2305: // the parent evaluates to false).
2306: if (eArg.isParam) {
2307:
2308: // condition is a paramter marker,
2309: // as in casewhen(?, v1, v2)
2310: eArg.dataType = Types.BOOLEAN;
2311: }
2312:
2313: dataType = getCaseWhenType(session);
2314: break;
2315:
2316: case ALTERNATIVE: {
2317: Expression case1 = eArg;
2318: Expression case2 = eArg2;
2319:
2320: if (case1.isParam && case2.isParam) {
2321: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2322: Trace.Expression_resolveTypes6);
2323: }
2324:
2325: if (case1.isParam || case1.dataType == Types.NULL) {
2326: case1.dataType = case2.dataType;
2327: } else if (case2.isParam || case2.dataType == Types.NULL) {
2328: case2.dataType = case1.dataType;
2329: }
2330:
2331: if (case1.dataType == Types.NULL
2332: && case2.dataType == Types.NULL) {
2333: dataType = Types.NULL;
2334: }
2335:
2336: if (Types.isNumberType(case1.dataType)
2337: && Types.isNumberType(case2.dataType)) {
2338: dataType = Column.getCombinedNumberType(case1.dataType,
2339: case2.dataType, ALTERNATIVE);
2340: } else if (Types.isCharacterType(case1.dataType)
2341: && Types.isCharacterType(case2.dataType)) {
2342: dataType = Types.LONGVARCHAR;
2343: } else if (case1.dataType != case2.dataType) {
2344: if (case2.exprType == Expression.VALUE) {
2345: dataType = case2.dataType = case1.dataType;
2346: case2.valueData = Column.convertObject(
2347: case2.valueData, dataType);
2348: } else if (case1.exprType == Expression.VALUE) {
2349: dataType = case1.dataType = case2.dataType;
2350: case1.valueData = Column.convertObject(
2351: case1.valueData, dataType);
2352: } else {
2353: throw Trace
2354: .error(
2355: Trace.UNRESOLVED_PARAMETER_TYPE,
2356: Trace.Expression_resolveTypes7,
2357: new String[] {
2358: Types
2359: .getTypeString(case1.dataType),
2360: Types
2361: .getTypeString(case2.dataType) });
2362: }
2363: } else {
2364: dataType = case1.dataType;
2365: }
2366:
2367: break;
2368: }
2369: }
2370: }
2371:
2372: void resolveTypeForLike(Session session) throws HsqlException {
2373:
2374: if (eArg.isParam && eArg2.isParam) {
2375: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2376: Trace.Expression_resolveTypeForLike);
2377: }
2378:
2379: if (isFixedConditional()) {
2380: Boolean arg = test(session);
2381:
2382: if (arg == null) {
2383: setNull();
2384: } else {
2385: exprType = arg.booleanValue() ? TRUE : FALSE;
2386: eArg = null;
2387: eArg2 = null;
2388: }
2389: } else if (eArg.isParam) {
2390: eArg.dataType = Types.VARCHAR;
2391: } else if (eArg2.isParam) {
2392: eArg2.dataType = Types.VARCHAR;
2393: }
2394:
2395: // boucherb@users 2003-09-25 - patch 1.7.2 Alpha P
2396: //
2397: // Some optimizations for LIKE
2398: //
2399: // TODO:
2400: //
2401: // See if the same optimizations can be done dynamically at execute time when
2402: // eArg2 is PARAM. Unfortunately, this currently requires re-resolving from
2403: // the root any expression containing at least one parameterized LIKE in the
2404: // compiled statement and reseting conditions on any involved table filters,
2405: // so the answer is: probably not, at least not under the current code.
2406: //
2407: // CHECKME:
2408: //
2409: // Test for correct results under all XXXCHAR types (padding, etc.?)
2410: //
2411: // NOTE:
2412: //
2413: // For the old behaviour, simply comment out the block below
2414: if (likeObject.optimised) {
2415: return;
2416: }
2417:
2418: boolean isRightArgFixedConstant = eArg2.isFixedConstant();
2419: String likeStr = isRightArgFixedConstant ? (String) eArg2
2420: .getValue(session, Types.VARCHAR) : null;
2421: boolean ignoreCase = eArg.dataType == Types.VARCHAR_IGNORECASE
2422: || eArg2.dataType == Types.VARCHAR_IGNORECASE;
2423:
2424: likeObject.setParams(session, likeStr, ignoreCase);
2425:
2426: if (!isRightArgFixedConstant) {
2427:
2428: // Then we are done here, since it's impossible
2429: // to determine at this point if the right expression
2430: // will have a fixed prefix that can be used to optimize
2431: // any involved table filters
2432: return;
2433: }
2434:
2435: if (likeObject.isEquivalentToFalsePredicate()) {
2436: exprType = FALSE;
2437: eArg = null;
2438: eArg2 = null;
2439: likeObject = null;
2440: } else if (likeObject.isEquivalentToEqualsPredicate()) {
2441: exprType = EQUAL;
2442: eArg2 = new Expression(Types.VARCHAR, likeObject
2443: .getRangeLow());
2444: likeObject = null;
2445: } else if (likeObject.isEquivalentToNotNullPredicate()) {
2446: } else {
2447: if (eArg.exprType != Expression.COLUMN) {
2448:
2449: // Then we are done here, since range predicates are
2450: // not picked up for use to optimize table filters
2451: // unless the predicate is on the first column of
2452: // an index.
2453: // TODO:
2454: // We might go one step further here and check if the
2455: // column is elligible (is the first column of some
2456: // index on its table). If it is not, it may be that
2457: // substituting/inserting range predicate below
2458: // can actually lower performance.
2459: // Indeed, we might better consider delaying the
2460: // optimizations below till the TableFilter.setConditions()
2461: // phase.
2462: return;
2463: }
2464:
2465: if (!Types.isCharacterType(eArg.dataType)) {
2466:
2467: // TODO:
2468: // correct range low / range high generation for
2469: // types other than XXXCHAR
2470: return;
2471: }
2472:
2473: boolean between = false;
2474: boolean like = false;
2475: boolean larger = false;
2476:
2477: if (likeObject.isEquivalentToBetweenPredicate()) {
2478:
2479: // X LIKE 'abc%' <=> X >= 'abc' AND X <= 'abc' || max_collation_char
2480: larger = likeObject.hasCollation;
2481: between = !larger;
2482: like = larger;
2483: } else if (likeObject
2484: .isEquivalentToBetweenPredicateAugmentedWithLike()) {
2485:
2486: // X LIKE 'abc%...' <=> X >= 'abc' AND X <= 'abc' || max_collation_char AND X LIKE 'abc%...'
2487: larger = likeObject.hasCollation;
2488: between = !larger;
2489: like = true;
2490: }
2491:
2492: if (between == false && larger == false) {
2493: return;
2494: }
2495:
2496: Expression eFirst = new Expression(Types.VARCHAR,
2497: likeObject.getRangeLow());
2498: Expression eLast = new Expression(Types.VARCHAR, likeObject
2499: .getRangeHigh());
2500:
2501: if (between && !like) {
2502: Expression eArgOld = eArg;
2503:
2504: eArg = new Expression(BIGGER_EQUAL, eArgOld, eFirst);
2505: eArg2 = new Expression(SMALLER_EQUAL, eArgOld, eLast);
2506: exprType = AND;
2507: likeObject = null;
2508: } else if (between && like) {
2509: Expression gte = new Expression(BIGGER_EQUAL, eArg,
2510: eFirst);
2511: Expression lte = new Expression(SMALLER_EQUAL, eArg,
2512: eLast);
2513:
2514: eArg2 = new Expression(eArg, eArg2,
2515: likeObject.escapeChar, likeObject.hasCollation);
2516: eArg2.likeObject = likeObject;
2517: eArg = new Expression(AND, gte, lte);
2518: exprType = AND;
2519: likeObject = null;
2520: } else if (larger) {
2521: Expression gte = new Expression(BIGGER_EQUAL, eArg,
2522: eFirst);
2523:
2524: eArg2 = new Expression(eArg, eArg2,
2525: likeObject.escapeChar, likeObject.hasCollation);
2526: eArg2.likeObject = likeObject;
2527: eArg = gte;
2528: exprType = AND;
2529: likeObject = null;
2530: }
2531: }
2532: }
2533:
2534: /**
2535: * Parametric or fixed value lists plus queries are handled.
2536: *
2537: * Empty lists are not allowed.
2538: *
2539: * Parametric predicand is resolved against the value list and vice versa.
2540: */
2541: void resolveTypeForIn(Session session) throws HsqlException {
2542:
2543: if (eArg2.exprType == QUERY) {
2544: if (eArg.isParam) {
2545: eArg.dataType = eArg2.dataType;
2546: }
2547:
2548: isQueryCorrelated = !eArg2.subQuery.isResolved;
2549: } else {
2550: Expression[] vl = eArg2.valueList;
2551: int len = vl.length;
2552:
2553: if (eArg.isParam) {
2554: if (vl[0].isParam) {
2555: throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE,
2556: Trace.Expression_resolveTypeForIn2);
2557: }
2558:
2559: Expression e = vl[0];
2560: int dt = e.dataType;
2561:
2562: // PARAM datatype same as first value list expression
2563: // should never be Types.NULL when all is said and done
2564: if (dt == Types.NULL) {
2565:
2566: // do nothing...
2567: } else {
2568: if (eArg.dataType == Types.NULL) {
2569: eArg.dataType = dt;
2570: }
2571:
2572: if (eArg2.dataType == Types.NULL) {
2573: eArg2.dataType = dt;
2574: }
2575: }
2576:
2577: for (int i = 1; i < len; i++) {
2578: e = vl[i];
2579:
2580: if (e.isParam) {
2581: if (e.dataType == Types.NULL
2582: && dt != Types.NULL) {
2583: e.dataType = dt;
2584: }
2585: } else {
2586: e.resolveTypes(session);
2587: }
2588: }
2589: } else {
2590: int dt = eArg.dataType;
2591:
2592: if (eArg2.dataType == Types.NULL && dt != Types.NULL) {
2593: eArg2.dataType = dt;
2594: }
2595:
2596: for (int i = 0; i < len; i++) {
2597: Expression e = vl[i];
2598:
2599: if (e.isParam) {
2600: if (e.dataType == Types.NULL
2601: && dt != Types.NULL) {
2602: e.dataType = dt;
2603: }
2604: } else {
2605: e.resolveTypes(session);
2606: }
2607: }
2608: }
2609:
2610: eArg2.isFixedConstantValueList = eArg2.dataType != Types.VARCHAR_IGNORECASE;
2611:
2612: for (int i = 0; i < len; i++) {
2613: if (!vl[i].isFixedConstant()) {
2614: eArg2.isFixedConstantValueList = false;
2615: isQueryCorrelated = true;
2616:
2617: break;
2618: }
2619: }
2620:
2621: if (eArg2.isFixedConstantValueList) {
2622: eArg2.hList = new HashSet();
2623:
2624: for (int i = 0; i < len; i++) {
2625: try {
2626: Object value = eArg2.valueList[i]
2627: .getValue(session);
2628:
2629: value = Column.convertObject(value,
2630: eArg2.dataType);
2631:
2632: if (eArg2.dataType == Types.CHAR
2633: && value != null) {
2634: value = Library.rtrim((String) value);
2635: }
2636:
2637: eArg2.hList.add(value);
2638: } catch (HsqlException e) {
2639: }
2640: }
2641: }
2642: }
2643: }
2644:
2645: /**
2646: * Has this expression been resolved
2647: *
2648: *
2649: * @return boolean
2650: */
2651: boolean isResolved() {
2652:
2653: switch (exprType) {
2654:
2655: case VALUE:
2656: case NEGATE:
2657: return true;
2658:
2659: case COLUMN:
2660: return tableFilter != null && tableFilter.isAssigned;
2661:
2662: case QUERY:
2663: return subQuery.isResolved;
2664: }
2665:
2666: // todo: could recurse here, but never miss a 'false'!
2667: return false;
2668: }
2669:
2670: /**
2671: * Is the argument expression type a comparison expression
2672: *
2673: * @param i expresion type
2674: *
2675: * @return boolean
2676: */
2677: static boolean isCompare(int i) {
2678:
2679: switch (i) {
2680:
2681: case EQUAL:
2682: case BIGGER_EQUAL:
2683: case BIGGER:
2684: case SMALLER:
2685: case SMALLER_EQUAL:
2686: case NOT_EQUAL:
2687: return true;
2688: }
2689:
2690: return false;
2691: }
2692:
2693: /**
2694: * Returns the table name for a column expression as a string
2695: *
2696: * @return table name
2697: */
2698: String getTableName() {
2699:
2700: if (exprType == ASTERISK) {
2701: return tableName;
2702: }
2703:
2704: if (exprType == COLUMN) {
2705: if (tableFilter == null) {
2706: return tableName;
2707: } else {
2708: return tableFilter.getTable().getName().name;
2709: }
2710: }
2711:
2712: // todo
2713: return "";
2714: }
2715:
2716: /**
2717: * Returns the table name for a column expression as a string
2718: *
2719: * @return table name
2720: */
2721: String getFilterTableName() {
2722:
2723: if (tableFilter == null) {
2724: return "";
2725: } else {
2726: return tableFilter.getTable().getName().name;
2727: }
2728: }
2729:
2730: /**
2731: * Returns the HsqlName of the table for a column expression
2732: *
2733: * @return table name
2734: */
2735: HsqlName getTableHsqlName() {
2736:
2737: if (tableFilter == null) {
2738: return null;
2739: } else {
2740: return tableFilter.getTable().getName();
2741: }
2742: }
2743:
2744: String getTableSchemaName() {
2745:
2746: if (tableFilter == null) {
2747: return null;
2748: } else {
2749: return tableFilter.getTable().getName().schema.name;
2750: }
2751: }
2752:
2753: /**
2754: * Returns the name of a column as string
2755: *
2756: * @return column name
2757: */
2758: String getColumnName() {
2759:
2760: if (exprType == COLUMN) {
2761: if (tableFilter == null) {
2762: return columnName;
2763: } else {
2764: return tableFilter.getTable().getColumn(columnIndex).columnName.name;
2765: }
2766: }
2767:
2768: return getAlias();
2769: }
2770:
2771: /**
2772: * Returns the name of a column as string
2773: *
2774: * @return column name
2775: */
2776: String getBaseColumnName() {
2777:
2778: if (exprType == COLUMN && tableFilter != null) {
2779: return tableFilter.getTable().getColumn(columnIndex).columnName.name;
2780: }
2781:
2782: return null;
2783: }
2784:
2785: /**
2786: * Returns the column index in the table
2787: *
2788: * @return column index
2789: */
2790: int getColumnNr() {
2791: return columnIndex;
2792: }
2793:
2794: /**
2795: * Returns the column size
2796: *
2797: * @return size
2798: */
2799: int getColumnSize() {
2800: return precision;
2801: }
2802:
2803: /**
2804: * Returns the column scale
2805: *
2806: *
2807: * @return scale
2808: */
2809: int getColumnScale() {
2810: return scale;
2811: }
2812:
2813: /**
2814: * Set this as a set function with / without DISTINCT
2815: *
2816: * @param distinct is distinct
2817: */
2818: void setDistinctAggregate(boolean distinct) {
2819:
2820: isDistinctAggregate = distinct && (eArg.exprType != ASTERISK);
2821:
2822: if (exprType == COUNT) {
2823: dataType = distinct ? dataType : Types.INTEGER;
2824: }
2825: }
2826:
2827: /**
2828: * Swap the condition with its complement
2829: *
2830: * @throws HsqlException
2831: */
2832: void swapCondition() throws HsqlException {
2833:
2834: int i = EQUAL;
2835:
2836: switch (exprType) {
2837:
2838: case BIGGER_EQUAL:
2839: i = SMALLER_EQUAL;
2840: break;
2841:
2842: case SMALLER_EQUAL:
2843: i = BIGGER_EQUAL;
2844: break;
2845:
2846: case SMALLER:
2847: i = BIGGER;
2848: break;
2849:
2850: case BIGGER:
2851: i = SMALLER;
2852: break;
2853:
2854: case EQUAL:
2855: break;
2856:
2857: default:
2858: Trace.doAssert(false, "Expression.swapCondition");
2859: }
2860:
2861: exprType = i;
2862:
2863: Expression e = eArg;
2864:
2865: eArg = eArg2;
2866: eArg2 = e;
2867: }
2868:
2869: /**
2870: * Returns the data type
2871: *
2872: *
2873: * @return type
2874: */
2875: int getDataType() {
2876: return dataType;
2877: }
2878:
2879: /**
2880: * Get the value in the given type in the given session context
2881: *
2882: *
2883: * @param type returned type
2884: * @param session context
2885: * @return value
2886: *
2887: * @throws HsqlException
2888: */
2889: Object getValue(Session session, int type) throws HsqlException {
2890:
2891: Object o = getValue(session);
2892:
2893: if ((o == null) || (dataType == type)) {
2894: return o;
2895: }
2896:
2897: return Column.convertObject(o, type);
2898: }
2899:
2900: /**
2901: * Get the result of a SetFunction or an ordinary value
2902: *
2903: * @param currValue instance of set function or value
2904: * @param session context
2905: * @return object
2906: *
2907: * @throws HsqlException
2908: */
2909: Object getAggregatedValue(Session session, Object currValue)
2910: throws HsqlException {
2911:
2912: if (!isAggregate()) {
2913: return currValue;
2914: }
2915:
2916: // handle expressions
2917: Object leftValue = null, rightValue = null;
2918:
2919: switch (aggregateSpec) {
2920:
2921: case AGGREGATE_SELF: {
2922:
2923: // handles results of aggregates plus NEGATE and CONVERT
2924: switch (exprType) {
2925:
2926: case COUNT:
2927: if (currValue == null) {
2928: return INTEGER_0;
2929: }
2930:
2931: return ((SetFunction) currValue).getValue();
2932:
2933: case MAX:
2934: case MIN:
2935: case SUM:
2936: case AVG:
2937: case EVERY:
2938: case SOME:
2939: case STDDEV_POP:
2940: case STDDEV_SAMP:
2941: case VAR_POP:
2942: case VAR_SAMP:
2943: if (currValue == null) {
2944: return null;
2945: }
2946:
2947: return ((SetFunction) currValue).getValue();
2948: }
2949: }
2950: case AGGREGATE_LEFT:
2951: leftValue = eArg.getAggregatedValue(session,
2952: currValue == null ? null
2953: : ((Object[]) currValue)[0]);
2954:
2955: if (currValue == null) {
2956: rightValue = eArg2 == null ? null : eArg2
2957: .getValue(session);
2958: } else {
2959: rightValue = ((Object[]) currValue)[1];
2960: }
2961: break;
2962:
2963: case AGGREGATE_RIGHT:
2964: if (currValue == null) {
2965: leftValue = eArg == null ? null : eArg
2966: .getValue(session);
2967: } else {
2968: leftValue = ((Object[]) currValue)[0];
2969: }
2970:
2971: rightValue = eArg2.getAggregatedValue(session,
2972: currValue == null ? null
2973: : ((Object[]) currValue)[1]);
2974: break;
2975:
2976: case AGGREGATE_BOTH:
2977: if (currValue == null) {
2978: currValue = new Object[2];
2979: }
2980:
2981: leftValue = eArg.getAggregatedValue(session,
2982: ((Object[]) currValue)[0]);
2983: rightValue = eArg2.getAggregatedValue(session,
2984: ((Object[]) currValue)[1]);
2985: break;
2986: }
2987:
2988: // handle other operations
2989: switch (exprType) {
2990:
2991: case NEGATE:
2992: return Column.negate(leftValue, dataType);
2993:
2994: case CONVERT:
2995: return Column.convertObject(session, leftValue, dataType,
2996: precision, scale);
2997:
2998: case TRUE:
2999: return Boolean.TRUE;
3000:
3001: case FALSE:
3002: return Boolean.FALSE;
3003:
3004: case NOT:
3005: if (leftValue == null) {
3006: return null;
3007: }
3008:
3009: return ((Boolean) leftValue).booleanValue() ? Boolean.FALSE
3010: : Boolean.TRUE;
3011:
3012: case AND:
3013: if (leftValue == null || rightValue == null) {
3014: return null;
3015: }
3016:
3017: return ((Boolean) leftValue).booleanValue()
3018: && ((Boolean) rightValue).booleanValue() ? Boolean.TRUE
3019: : Boolean.FALSE;
3020:
3021: case OR:
3022: if (Boolean.TRUE.equals(leftValue)) {
3023: return Boolean.TRUE;
3024: }
3025:
3026: return Boolean.TRUE.equals(rightValue) ? Boolean.TRUE
3027: : Boolean.FALSE;
3028:
3029: case IS_NULL:
3030: return leftValue == null ? Boolean.TRUE : Boolean.FALSE;
3031:
3032: case LIKE:
3033: String s = (String) Column.convertObject(rightValue,
3034: Types.VARCHAR);
3035:
3036: if (eArg2.isParam || eArg2.exprType != VALUE) {
3037: likeObject.resetPattern(session, s);
3038: }
3039:
3040: String c = (String) Column.convertObject(leftValue,
3041: Types.VARCHAR);
3042:
3043: return likeObject.compare(session, c);
3044:
3045: case ALL:
3046: case ANY:
3047: return null;
3048:
3049: case IN:
3050: return eArg2.testInCondition(session, leftValue);
3051:
3052: case EXISTS:
3053: if (!eArg.subQuery.isResolved) {
3054: Result r = eArg.subQuery.select.getResult(session, 1); // 1 is already enough
3055:
3056: return r.rRoot == null ? Boolean.FALSE : Boolean.TRUE;
3057: } else {
3058: return subQuery.table.isEmpty(session) ? Boolean.FALSE
3059: : Boolean.TRUE;
3060: }
3061: case CASEWHEN:
3062: leftValue = Column.convertObject(leftValue, Types.BOOLEAN);
3063:
3064: boolean test = ((Boolean) leftValue).booleanValue();
3065: Object result = test ? ((Object[]) rightValue)[0]
3066: : ((Object[]) rightValue)[1];
3067:
3068: return Column.convertObject(result, dataType);
3069:
3070: case ALTERNATIVE:
3071: leftValue = Column.convertObject(leftValue, dataType);
3072: rightValue = Column.convertObject(rightValue, dataType);
3073:
3074: Object[] objectPair = new Object[2];
3075:
3076: objectPair[0] = leftValue;
3077: objectPair[1] = rightValue;
3078:
3079: return objectPair;
3080:
3081: case FUNCTION:
3082: return function.getAggregatedValue(session, currValue);
3083: }
3084:
3085: // handle comparisons
3086: if (isCompare(exprType)) {
3087: if (eArg2.exprType == Expression.ANY
3088: || eArg2.exprType == Expression.ALL) {
3089: return testAnyAllCondition(session, leftValue);
3090: }
3091:
3092: return compareValues(session, leftValue, rightValue);
3093: }
3094:
3095: // handle arithmetic and concat operations
3096: if (leftValue != null) {
3097: leftValue = Column.convertObject(leftValue, dataType);
3098: }
3099:
3100: if (rightValue != null) {
3101: rightValue = Column.convertObject(rightValue, dataType);
3102: }
3103:
3104: switch (exprType) {
3105:
3106: case ADD:
3107: return Column.add(leftValue, rightValue, dataType);
3108:
3109: case SUBTRACT:
3110: return Column.subtract(leftValue, rightValue, dataType);
3111:
3112: case MULTIPLY:
3113: return Column.multiply(leftValue, rightValue, dataType);
3114:
3115: case DIVIDE:
3116: return Column.divide(leftValue, rightValue, dataType);
3117:
3118: case CONCAT:
3119: return Column.concat(leftValue, rightValue);
3120:
3121: default:
3122: throw Trace.error(Trace.NEED_AGGREGATE, this
3123: .describe(session));
3124: }
3125: }
3126:
3127: /**
3128: * Instantiate the SetFunction or recurse, returning the result
3129: *
3130: * @param currValue setFunction
3131: * @param session context
3132: * @return a normal value or SetFunction instance
3133: *
3134: * @throws HsqlException
3135: */
3136: Object updateAggregatingValue(Session session, Object currValue)
3137: throws HsqlException {
3138:
3139: switch (aggregateSpec) {
3140:
3141: case AGGREGATE_SELF: {
3142: if (currValue == null) {
3143: currValue = new SetFunction(exprType, eArg.dataType,
3144: isDistinctAggregate);
3145: }
3146:
3147: Object newValue = eArg.exprType == ASTERISK ? INTEGER_1
3148: : eArg.getValue(session);
3149:
3150: ((SetFunction) currValue).add(session, newValue);
3151:
3152: return currValue;
3153: }
3154: case AGGREGATE_BOTH: {
3155: Object[] valuePair = (Object[]) currValue;
3156:
3157: if (valuePair == null) {
3158: valuePair = new Object[2];
3159: }
3160:
3161: valuePair[0] = eArg.updateAggregatingValue(session,
3162: valuePair[0]);
3163: valuePair[1] = eArg2.updateAggregatingValue(session,
3164: valuePair[1]);
3165:
3166: return valuePair;
3167: }
3168: case AGGREGATE_LEFT: {
3169: Object[] valuePair = (Object[]) currValue;
3170:
3171: if (valuePair == null) {
3172: valuePair = new Object[2];
3173: }
3174:
3175: valuePair[0] = eArg.updateAggregatingValue(session,
3176: valuePair[0]);
3177:
3178: if (eArg2 != null) {
3179: valuePair[1] = eArg2.getValue(session);
3180: }
3181:
3182: return valuePair;
3183: }
3184: case AGGREGATE_RIGHT: {
3185: Object[] valuePair = (Object[]) currValue;
3186:
3187: if (valuePair == null) {
3188: valuePair = new Object[2];
3189: }
3190:
3191: if (eArg != null) {
3192: valuePair[0] = eArg.getValue(session);
3193: }
3194:
3195: valuePair[1] = eArg2.updateAggregatingValue(session,
3196: valuePair[1]);
3197:
3198: return valuePair;
3199: }
3200: case AGGREGATE_FUNCTION:
3201: return function.updateAggregatingValue(session, currValue);
3202:
3203: default:
3204:
3205: // never gets here
3206: return currValue;
3207: }
3208: }
3209:
3210: Object getValue(Session session) throws HsqlException {
3211:
3212: switch (exprType) {
3213:
3214: case VALUE:
3215: return valueData;
3216:
3217: case COLUMN:
3218: try {
3219: return tableFilter.currentData[columnIndex];
3220: } catch (NullPointerException e) {
3221: String name = tableName == null ? columnName
3222: : tableName + '.' + columnName;
3223:
3224: throw Trace.error(Trace.COLUMN_NOT_FOUND, name);
3225: }
3226: case FUNCTION:
3227: return function.getValue(session);
3228:
3229: case QUERY:
3230: return subQuery.select.getValue(session, dataType);
3231:
3232: case NEGATE:
3233: return Column.negate(eArg.getValue(session, dataType),
3234: dataType);
3235:
3236: case ALL:
3237: case ANY:
3238: return null;
3239:
3240: case AND:
3241: case OR:
3242: case LIKE:
3243: case EXISTS:
3244: case IN:
3245: return test(session);
3246:
3247: case CONVERT:
3248: return Column.convertObject(session,
3249: eArg.getValue(session), dataType, precision, scale);
3250:
3251: case CASEWHEN:
3252: Boolean result = eArg.test(session);
3253:
3254: if (Boolean.TRUE.equals(result)) {
3255: return eArg2.eArg.getValue(session, dataType);
3256: } else {
3257: return eArg2.eArg2.getValue(session, dataType);
3258: }
3259:
3260: // gets here from getAggregatedValue()
3261: case ALTERNATIVE:
3262: return new Object[] { eArg.getValue(session, dataType),
3263: eArg2.getValue(session, dataType) };
3264: }
3265:
3266: // todo: simplify this
3267: Object a = null, b = null;
3268:
3269: if (eArg != null) {
3270: a = eArg.getValue(session, dataType);
3271: }
3272:
3273: if (eArg2 != null) {
3274: b = eArg2.getValue(session, dataType);
3275: }
3276:
3277: switch (exprType) {
3278:
3279: case ADD:
3280: return Column.add(a, b, dataType);
3281:
3282: case SUBTRACT:
3283: return Column.subtract(a, b, dataType);
3284:
3285: case MULTIPLY:
3286: return Column.multiply(a, b, dataType);
3287:
3288: case DIVIDE:
3289: return Column.divide(a, b, dataType);
3290:
3291: case CONCAT:
3292: return Column.concat(a, b);
3293:
3294: case SEQUENCE:
3295: return ((NumberSequence) valueData).getValueObject();
3296:
3297: default:
3298:
3299: /** @todo fredt - make sure the expression type is always comparison here */
3300: return test(session);
3301: }
3302: }
3303:
3304: boolean testCondition(Session session) throws HsqlException {
3305: return Boolean.TRUE.equals(test(session));
3306: }
3307:
3308: /**
3309: * Returns the test result of a conditional expression
3310: *
3311: * @param session session
3312: * @return boolean
3313: * @throws HsqlException
3314: */
3315: Boolean test(Session session) throws HsqlException {
3316:
3317: switch (exprType) {
3318:
3319: case TRUE:
3320: return Boolean.TRUE;
3321:
3322: case FALSE:
3323: return Boolean.FALSE;
3324:
3325: case NOT:
3326: if (eArg2 != null) {
3327: Trace.doAssert(false, "Expression.test");
3328: }
3329:
3330: Boolean result = eArg.test(session);
3331:
3332: return result == null ? null
3333: : result.booleanValue() ? Boolean.FALSE
3334: : Boolean.TRUE;
3335:
3336: case AND: {
3337: Boolean r1 = eArg.test(session);
3338:
3339: if (r1 == null) {
3340: return null;
3341: }
3342:
3343: Boolean r2 = eArg2.test(session);
3344:
3345: if (r2 == null) {
3346: return null;
3347: }
3348:
3349: return r1.booleanValue() && r2.booleanValue() ? Boolean.TRUE
3350: : Boolean.FALSE;
3351: }
3352: case OR: {
3353: boolean r1 = Boolean.TRUE.equals(eArg.test(session));
3354:
3355: if (r1) {
3356: return Boolean.TRUE;
3357: }
3358:
3359: return Boolean.TRUE.equals(eArg2.test(session)) ? Boolean.TRUE
3360: : Boolean.FALSE;
3361: }
3362: case IS_NULL:
3363: return eArg.getValue(session) == null ? Boolean.TRUE
3364: : Boolean.FALSE;
3365:
3366: case LIKE:
3367: String s = (String) eArg2.getValue(session, Types.VARCHAR);
3368:
3369: if (eArg2.isParam || eArg2.exprType != VALUE) {
3370: likeObject.resetPattern(session, s);
3371: }
3372:
3373: String c = (String) eArg.getValue(session, Types.VARCHAR);
3374:
3375: return likeObject.compare(session, c);
3376:
3377: case IN:
3378: return eArg2.testInCondition(session, eArg
3379: .getValue(session));
3380:
3381: case EXISTS:
3382: return eArg.testExistsCondition(session);
3383:
3384: case FUNCTION:
3385: Object value = Column.convertObject(function
3386: .getValue(session), Types.BOOLEAN);
3387:
3388: return (Boolean) value;
3389: }
3390:
3391: if (eArg == null || eArg2 == null) {
3392: if (exprType == COLUMN) {
3393: if (dataType == Types.BOOLEAN
3394: || Types.isNumberType(dataType)) {
3395: Object value = Column.convertObject(
3396: getValue(session), Types.BOOLEAN);
3397:
3398: return (Boolean) value;
3399: }
3400: }
3401:
3402: throw Trace.error(Trace.NOT_A_CONDITION);
3403: }
3404:
3405: if (eArg2.exprType == Expression.ANY
3406: || eArg2.exprType == Expression.ALL) {
3407: return testAnyAllCondition(session, eArg.getValue(session));
3408: }
3409:
3410: Object o1 = eArg.getValue(session);
3411: Object o2 = eArg2.getValue(session);
3412:
3413: if (o1 == null || o2 == null) {
3414: /*
3415: TableFilter.swapCondition() ensures that with LEFT OUTER, eArg is the
3416: column expression for the table on the right hand side.
3417: We do not join tables on nulls apart from outer joins
3418: Any comparison operator can exist in WHERE or JOIN conditions
3419: */
3420: if (eArg.tableFilter != null
3421: && eArg.tableFilter.isOuterJoin) {
3422: if (isInJoin) {
3423: if (eArg.tableFilter.isCurrentOuter && o1 == null) {
3424: return Boolean.TRUE;
3425: }
3426: } else {
3427:
3428: // this is used in WHERE <OUTER JOIN COL> IS [NOT] NULL
3429: eArg.tableFilter.nonJoinIsNull = o2 == null;
3430: }
3431: }
3432:
3433: return null;
3434: }
3435:
3436: return compareValues(session, o1, o2);
3437: }
3438:
3439: private Boolean compareValues(Session session, Object o1, Object o2)
3440: throws HsqlException {
3441:
3442: int type = eArg.dataType;
3443:
3444: if (eArg.dataType != eArg2.dataType) {
3445: if (Types.isNumberType(eArg.dataType)
3446: && Types.isNumberType(eArg2.dataType)) {
3447: type = Column.getCombinedNumberType(eArg.dataType,
3448: eArg2.dataType, exprType);
3449: }
3450:
3451: o1 = Column.convertObject(o1, type);
3452: o2 = Column.convertObject(o2, type);
3453: }
3454:
3455: int result = Column.compare(session.database.collation, o1, o2,
3456: type);
3457:
3458: switch (exprType) {
3459:
3460: case EQUAL:
3461: return result == 0 ? Boolean.TRUE : Boolean.FALSE;
3462:
3463: case BIGGER:
3464: return result > 0 ? Boolean.TRUE : Boolean.FALSE;
3465:
3466: case BIGGER_EQUAL:
3467: return result >= 0 ? Boolean.TRUE : Boolean.FALSE;
3468:
3469: case SMALLER_EQUAL:
3470: return result <= 0 ? Boolean.TRUE : Boolean.FALSE;
3471:
3472: case SMALLER:
3473: return result < 0 ? Boolean.TRUE : Boolean.FALSE;
3474:
3475: case NOT_EQUAL:
3476: return result != 0 ? Boolean.TRUE : Boolean.FALSE;
3477:
3478: default:
3479: throw Trace.error(Trace.GENERAL_ERROR,
3480: Trace.Expression_compareValues);
3481: }
3482: }
3483:
3484: /**
3485: * Returns the result of testing a VALUE_LIST expression
3486: *
3487: * @param o value to check against
3488: * @param session context
3489: * @return boolean
3490: * @throws HsqlException
3491: */
3492: private Boolean testInCondition(Session session, Object o)
3493: throws HsqlException {
3494:
3495: if (o == null) {
3496: return null;
3497: }
3498:
3499: if (exprType == VALUELIST) {
3500: try {
3501: o = Column.convertObject(o, dataType);
3502: } catch (HsqlException e) {
3503: return Boolean.FALSE;
3504: }
3505:
3506: if (isFixedConstantValueList) {
3507: if (dataType == Types.CHAR) {
3508: o = Library.rtrim((String) o);
3509: }
3510:
3511: return hList.contains(o) ? Boolean.TRUE : Boolean.FALSE;
3512: }
3513:
3514: final int len = valueList.length;
3515:
3516: for (int i = 0; i < len; i++) {
3517: Object o2 = valueList[i].getValue(session, dataType);
3518:
3519: if (Column.compare(session.database.collation, o, o2,
3520: dataType) == 0) {
3521: return Boolean.TRUE;
3522: }
3523: }
3524:
3525: return Boolean.FALSE;
3526: } else if (exprType == QUERY) {
3527:
3528: /** @todo fredt - convert to join */
3529: try {
3530: o = Column.convertObject(o, subQuery.table
3531: .getColumnTypes()[0]);
3532: } catch (HsqlException e) {
3533: return Boolean.FALSE;
3534: }
3535:
3536: if (!subQuery.isResolved) {
3537: subQuery.populateTable(session);
3538: }
3539:
3540: Boolean result = subQuery.table.getPrimaryIndex()
3541: .findFirstRow(session, o, Expression.EQUAL)
3542: .hasNext() ? Boolean.TRUE : Boolean.FALSE;
3543:
3544: if (!subQuery.isResolved) {
3545: subQuery.table.clearAllRows(session);
3546: }
3547:
3548: return result;
3549: }
3550:
3551: throw Trace.error(Trace.WRONG_DATA_TYPE);
3552: }
3553:
3554: private Boolean testExistsCondition(Session session)
3555: throws HsqlException {
3556:
3557: if (subQuery.isResolved) {
3558: return subQuery.table.isEmpty(session) ? Boolean.FALSE
3559: : Boolean.TRUE;
3560: } else {
3561: Result r = subQuery.select.getResult(session, 1); // 1 is already enough
3562:
3563: return r.rRoot == null ? Boolean.FALSE : Boolean.TRUE;
3564: }
3565: }
3566:
3567: private Boolean testAnyAllCondition(Session session, Object o)
3568: throws HsqlException {
3569:
3570: if (o == null) {
3571: return null;
3572: }
3573:
3574: SubQuery subquery = eArg2.eArg.subQuery;
3575: boolean populate = !subquery.isResolved;
3576:
3577: if (populate) {
3578: subquery.populateTable(session);
3579: }
3580:
3581: Boolean result = getAnyAllValue(session, o, subquery);
3582:
3583: if (populate) {
3584: subquery.table.clearAllRows(session);
3585: }
3586:
3587: return result;
3588: }
3589:
3590: private Boolean getAnyAllValue(Session session, Object o,
3591: SubQuery subquery) throws HsqlException {
3592:
3593: boolean empty = subquery.table.isEmpty(session);
3594: Index index = subquery.table.getPrimaryIndex();
3595: RowIterator it = index.findFirstRowNotNull(session);
3596: Row firstrow = it.next();
3597:
3598: switch (eArg2.exprType) {
3599:
3600: case ANY: {
3601: if (empty) {
3602: return Boolean.FALSE;
3603: }
3604:
3605: if (firstrow == null) {
3606: return null;
3607: }
3608:
3609: int range = Column.compareToTypeRange(o, eArg2.eArg
3610: .getDataType());
3611:
3612: if (range != 0) {
3613: switch (exprType) {
3614:
3615: case EQUAL:
3616: return Boolean.FALSE;
3617:
3618: case NOT_EQUAL:
3619: return Boolean.TRUE;
3620:
3621: case BIGGER:
3622: case BIGGER_EQUAL:
3623: return range > 0 ? Boolean.TRUE : Boolean.FALSE;
3624:
3625: case SMALLER_EQUAL:
3626: case SMALLER:
3627: return range < 0 ? Boolean.TRUE : Boolean.FALSE;
3628: }
3629: }
3630:
3631: o = Column.convertObject(o, eArg2.eArg.getDataType());
3632:
3633: if (exprType == EQUAL) {
3634: it = index.findFirstRow(session, o, EQUAL);
3635:
3636: return it.hasNext() ? Boolean.TRUE : Boolean.FALSE;
3637: }
3638:
3639: Row lastrow = index.lastRow(session);
3640: Object firstdata = firstrow.getData()[0];
3641: Object lastdata = lastrow.getData()[0];
3642: int comparefirst = Column.compare(
3643: session.database.collation, o, firstdata, eArg
3644: .getDataType());
3645: int comparelast = Column.compare(
3646: session.database.collation, o, lastdata, eArg
3647: .getDataType());
3648:
3649: switch (exprType) {
3650:
3651: case NOT_EQUAL:
3652: return (comparefirst == 0 && comparelast == 0) ? Boolean.FALSE
3653: : Boolean.TRUE;
3654:
3655: case BIGGER:
3656: return comparefirst > 0 ? Boolean.TRUE : Boolean.FALSE;
3657:
3658: case BIGGER_EQUAL:
3659: return comparefirst >= 0 ? Boolean.TRUE : Boolean.FALSE;
3660:
3661: case SMALLER:
3662: return comparelast < 0 ? Boolean.TRUE : Boolean.FALSE;
3663:
3664: case SMALLER_EQUAL:
3665: return comparelast <= 0 ? Boolean.TRUE : Boolean.FALSE;
3666: }
3667:
3668: break;
3669: }
3670: case ALL: {
3671: if (empty) {
3672: return Boolean.TRUE;
3673: }
3674:
3675: if (firstrow == null) {
3676: return null;
3677: }
3678:
3679: int range = Column.compareToTypeRange(o, eArg2.eArg
3680: .getDataType());
3681:
3682: if (range != 0) {
3683: switch (exprType) {
3684:
3685: case EQUAL:
3686: return Boolean.FALSE;
3687:
3688: case NOT_EQUAL:
3689: return Boolean.TRUE;
3690:
3691: case BIGGER:
3692: case BIGGER_EQUAL:
3693: return range > 0 ? Boolean.TRUE : Boolean.FALSE;
3694:
3695: case SMALLER_EQUAL:
3696: case SMALLER:
3697: return range < 0 ? Boolean.TRUE : Boolean.FALSE;
3698: }
3699: }
3700:
3701: o = Column.convertObject(o, eArg2.eArg.getDataType());
3702:
3703: if (exprType == EQUAL || exprType == NOT_EQUAL) {
3704: it = index.findFirstRow(session, o, EQUAL);
3705:
3706: if (exprType == EQUAL) {
3707: return (it.hasNext() && subquery.table
3708: .getRowCount(session) == 1) ? Boolean.TRUE
3709: : Boolean.FALSE;
3710: }
3711:
3712: return (it.hasNext()) ? Boolean.FALSE : Boolean.TRUE;
3713: }
3714:
3715: Row lastrow = index.lastRow(session);
3716: Object firstdata = firstrow.getData()[0];
3717: Object lastdata = lastrow.getData()[0];
3718:
3719: o = Column.convertObject(o, eArg2.eArg.getDataType());
3720:
3721: int comparefirst = Column.compare(
3722: session.database.collation, o, firstdata, eArg
3723: .getDataType());
3724: int comparelast = Column.compare(
3725: session.database.collation, o, lastdata, eArg
3726: .getDataType());
3727:
3728: switch (exprType) {
3729:
3730: case NOT_EQUAL:
3731: return (comparefirst == 0 || comparelast == 0) ? Boolean.FALSE
3732: : Boolean.TRUE;
3733:
3734: case BIGGER:
3735: return comparelast > 0 ? Boolean.TRUE : Boolean.FALSE;
3736:
3737: case BIGGER_EQUAL:
3738: return comparelast >= 0 ? Boolean.TRUE : Boolean.FALSE;
3739:
3740: case SMALLER:
3741: return comparefirst < 0 ? Boolean.TRUE : Boolean.FALSE;
3742:
3743: case SMALLER_EQUAL:
3744: return comparefirst <= 0 ? Boolean.TRUE : Boolean.FALSE;
3745: }
3746:
3747: break;
3748: }
3749: }
3750:
3751: return null;
3752: }
3753:
3754: /**
3755: * Marks all the expressions in the tree for a condition that is part
3756: * of a JOIN .. ON ....<br>
3757: *
3758: * For LEFT OUTER joins, also tests the expression tree for the join
3759: * condition to ensure only permitted expression types are there.
3760: *
3761: * If we want to exapand the expressions to include arithmetic operations
3762: * or functions ...
3763: *
3764: * (fredt@users)
3765: *
3766: * @param tf table filter
3767: * @param outer boolean
3768: * @return boolean
3769: */
3770: boolean setForJoin(TableFilter tf, boolean outer) {
3771:
3772: isInJoin = outer;
3773:
3774: if (outer) {
3775: outerFilter = tf;
3776: }
3777:
3778: if (eArg != null) {
3779: if (eArg.setForJoin(tf, outer) == false) {
3780: return false;
3781: }
3782: }
3783:
3784: if (eArg2 != null) {
3785: if (eArg2.setForJoin(tf, outer) == false) {
3786: return false;
3787: }
3788: }
3789:
3790: return !outer
3791: || (exprType == Expression.AND
3792: || exprType == Expression.OR
3793: || exprType == Expression.COLUMN
3794: || exprType == Expression.VALUE
3795: || exprType == Expression.EQUAL
3796: || exprType == Expression.NOT_EQUAL
3797: || exprType == Expression.BIGGER
3798: || exprType == Expression.BIGGER_EQUAL
3799: || exprType == Expression.SMALLER
3800: || exprType == Expression.SMALLER_EQUAL || exprType == Expression.IS_NULL);
3801: }
3802:
3803: /**
3804: * Returns a Select object that can be used for checking the contents
3805: * of an existing table against the given CHECK search condition.
3806: *
3807: * @param t table
3808: * @param e expression
3809: * @return select object
3810: * @throws HsqlException
3811: */
3812: static Select getCheckSelect(Session session, Table t, Expression e)
3813: throws HsqlException {
3814:
3815: Select s = new Select();
3816:
3817: s.exprColumns = new Expression[1];
3818: s.exprColumns[0] = new Expression(VALUE, Boolean.TRUE);
3819: s.tFilter = new TableFilter[1];
3820: s.tFilter[0] = new TableFilter(t, null, null, false);
3821:
3822: Expression condition = new Expression(NOT, e, null);
3823:
3824: s.queryCondition = condition;
3825:
3826: s.resolveAll(session, true);
3827:
3828: return s;
3829: }
3830:
3831: /**
3832: * Sets the left leaf.
3833: *
3834: * @param e expression
3835: */
3836: void setLeftExpression(Expression e) {
3837: eArg = e;
3838: }
3839:
3840: void setRightExpression(Expression e) {
3841: eArg2 = e;
3842: }
3843:
3844: /**
3845: * Gets the right leaf.
3846: *
3847: * @return expression
3848: */
3849: Expression getRightExpression() {
3850: return eArg2;
3851: }
3852:
3853: // boucherb@users 20030417 - patch 1.7.2 - compiled statement support
3854: void bind(Object o) {
3855: valueData = o;
3856: }
3857:
3858: boolean isParam() {
3859: return isParam;
3860: }
3861:
3862: boolean isFixedConstant() {
3863:
3864: switch (exprType) {
3865:
3866: case VALUE:
3867: return !isParam;
3868:
3869: case NEGATE:
3870: return eArg.isFixedConstant();
3871:
3872: case ADD:
3873: case SUBTRACT:
3874: case MULTIPLY:
3875: case DIVIDE:
3876: case CONCAT:
3877: return eArg.isFixedConstant() && eArg2.isFixedConstant();
3878: }
3879:
3880: return false;
3881: }
3882:
3883: boolean isFixedConditional() {
3884:
3885: switch (exprType) {
3886:
3887: case TRUE:
3888: case FALSE:
3889: return true;
3890:
3891: case EQUAL:
3892: case BIGGER_EQUAL:
3893: case BIGGER:
3894: case SMALLER:
3895: case SMALLER_EQUAL:
3896: case NOT_EQUAL:
3897: case LIKE:
3898:
3899: //case IN : TODO
3900: return eArg.isFixedConstant() && eArg2.isFixedConstant();
3901:
3902: case IS_NULL:
3903: return eArg.isFixedConstant();
3904:
3905: case NOT:
3906: return eArg.isFixedConditional();
3907:
3908: case AND:
3909: case OR:
3910: return eArg.isFixedConditional()
3911: && eArg2.isFixedConditional();
3912:
3913: default:
3914: return false;
3915: }
3916: }
3917:
3918: void setTableColumnAttributes(Expression e) {
3919:
3920: precision = e.precision;
3921: scale = e.scale;
3922: isIdentity = e.isIdentity;
3923: nullability = e.nullability;
3924: isWritable = e.isWritable;
3925: catalog = e.catalog;
3926: schema = e.schema;
3927: }
3928:
3929: void setTableColumnAttributes(Table t, int i) {
3930:
3931: Column c = t.getColumn(i);
3932:
3933: dataType = c.getType();
3934: precision = c.getSize();
3935: scale = c.getScale();
3936: isIdentity = c.isIdentity();
3937:
3938: // IDENTITY columns are not nullable but NULLs are accepted
3939: // and converted into the next identity value for the table
3940: nullability = c.isNullable() && !isIdentity ? NULLABLE
3941: : NO_NULLS;
3942: isWritable = t.isWritable();
3943: catalog = t.getCatalogName();
3944: schema = t.getSchemaName();
3945: }
3946:
3947: String getValueClassName() {
3948:
3949: // boucherb@users 20050516 - patch 1.8.0 removed DITypeInfo dependency
3950: if (valueClassName == null) {
3951: if (function == null) {
3952: valueClassName = Types
3953: .getColStClsName((dataType == Types.VARCHAR_IGNORECASE) ? Types.VARCHAR
3954: : dataType);
3955: } else {
3956: valueClassName = function.getReturnClassName();
3957: }
3958: }
3959:
3960: return valueClassName;
3961: }
3962:
3963: // parameter modes
3964: static final int PARAM_UNKNOWN = 0;
3965: public static final int PARAM_IN = 1;
3966: public static final int PARAM_IN_OUT = 2;
3967: public static final int PARAM_OUT = 4;
3968:
3969: // result set (output column value) or parameter expression nullability
3970: static final int NO_NULLS = 0;
3971: static final int NULLABLE = 1;
3972: static final int NULLABLE_UNKNOWN = 2;
3973:
3974: // output column and parameter expression metadata values
3975: boolean isIdentity; // = false
3976: int nullability = NULLABLE_UNKNOWN;
3977: boolean isWritable; // = false; true if column of writable table
3978: int paramMode = PARAM_UNKNOWN;
3979: String valueClassName; // = null
3980:
3981: // boucherb@users 20040111 - patch 1.7.2 RC1 - metadata xxxusage support
3982: //-------------------------------------------------------------------
3983: // TODO: Maybe provide an interface or abstract class + a default
3984: // implementation instead? This would allow a more powerful system
3985: // of collectors to be created, for example to assist in the optimization
3986: // of condition expression trees:
3987: //
3988: // HashSet joins = new JoinConditionCollector();
3989: // joins.addAll(select.whereCondition);
3990: // for(Iterator it = joins.iterator(); it.hasNext();) {
3991: // process((it.next());
3992: // }
3993:
3994: /**
3995: * Provides a generic way to collect a set of distinct expressions
3996: * of some type from a tree rooted at a specified Expression.
3997: */
3998: static class Collector extends HashSet {
3999:
4000: Collector() {
4001: super ();
4002: }
4003:
4004: void addAll(Expression e, int type) {
4005:
4006: Function function;
4007: Expression[] list;
4008:
4009: if (e == null) {
4010: return;
4011: }
4012:
4013: addAll(e.getArg(), type);
4014: addAll(e.getArg2(), type);
4015:
4016: // CHECKME: What about setTrue() Expressions?
4017: if (e.exprType == type) {
4018: add(e);
4019: }
4020:
4021: if (e.subQuery != null) {
4022: addAll(e.subQuery.select, type);
4023: }
4024:
4025: function = e.function;
4026:
4027: if (function != null) {
4028: list = function.eArg;
4029:
4030: if (list != null) {
4031: for (int i = 0; i < list.length; i++) {
4032: addAll(list[i], type);
4033: }
4034: }
4035: }
4036:
4037: list = e.valueList;
4038:
4039: if (list != null) {
4040: for (int i = 0; i < list.length; i++) {
4041: addAll(list[i], type);
4042: }
4043: }
4044: }
4045:
4046: void addAll(Select select, int type) {
4047:
4048: for (; select != null; select = select.unionSelect) {
4049: Expression[] list = select.exprColumns;
4050:
4051: for (int i = 0; i < list.length; i++) {
4052: addAll(list[i], type);
4053: }
4054:
4055: addAll(select.queryCondition, type);
4056: addAll(select.havingCondition, type);
4057:
4058: // todo order by columns
4059: }
4060: }
4061: }
4062: }
|