Expression.java

Index Score
org.hsqldb
HSQL Database Engine

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
CYCLOMATICCyclomatic complexity
RETURNSNumber of return points from functions
COMPARISONSNumber of comparison operators
INTERFACE_COMPLEXITYInterface complexity
ELOCEffective lines of code
LOCLines of code
SIZESize of the file in bytes
LINESNumber of lines in the source file
OPERATORSNumber of operators
PROGRAM_LENGTHHalstead program length
LOGICAL_LINESNumber of statements
BLOCKSNumber of blocks
OPERANDSNumber of operands
WHITESPACENumber of whitespace lines
LOOPSNumber of loops
DECL_COMMENTSComments in declarations
LINE_COMMENTNumber of line comments
EXEC_COMMENTSComments in executable code
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
PARAMSNumber of formal parameter declarations
UNIQUE_OPERANDSNumber of unique operands
FUNCTIONSNumber of function declarations
PROGRAM_VOCABHalstead program vocabulary
EXITSProcedure exits
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
JAVA0031JAVA0031 Case statement not properly closed
COMMENTSComment lines
JAVA0032JAVA0032 Switch statement missing default
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0116JAVA0116 Missing javadoc: field 'field'
DOC_COMMENTNumber of javadoc comment lines
JAVA0115JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0040JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0177JAVA0177 Variable declaration missing initializer
JAVA0081JAVA0081 Boolean literal in comparison
JAVA0173JAVA0173 Unused method parameter
JAVA0034JAVA0034 Missing braces in if statement
JAVA0064JAVA0064 N variations of identifier name (maximum: M)
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0174JAVA0174 Assigned local variable never used
UNIQUE_OPERATORSNumber of unique operators
JAVA0123JAVA0123 Use all three components of for loop
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
JAVA0082JAVA0082 Unnecessary widening cast
NEST_DEPTHMaximum nesting depth
PROGRAM_VOLUMEHalstead program volume
JAVA0136JAVA0136 N methods defined in class (maximum: M)
BLOCK_COMMENTNumber of block comment lines
JAVA0160JAVA0160 Method does not throw specified exception
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0100JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0130JAVA0130 Non-static method does not use instance fields
JAVA0145JAVA0145 Tab character used in source file
/* Copyright (c) 1995-2000, The Hypersonic SQL Group. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the Hypersonic SQL Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Hypersonic SQL Group. * * * For work added by the HSQL Development Group: * * Copyright (c) 2001-2007, The HSQL Development Group * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the HSQL Development Group nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.hsqldb; import org.hsqldb.HsqlNameManager.HsqlName; import org.hsqldb.Parser.CompileContext; import org.hsqldb.index.Index; import org.hsqldb.lib.HashSet; import org.hsqldb.lib.Iterator; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.OrderedIntHashSet; import org.hsqldb.lib.Set; import org.hsqldb.navigator.RowIterator; import org.hsqldb.result.Result; import org.hsqldb.store.ValuePool; import org.hsqldb.types.CharacterType; import org.hsqldb.types.DateTimeType; import org.hsqldb.types.NullType; import org.hsqldb.types.NumberType; import org.hsqldb.types.Type; // fredt@users 20020215 - patch 1.7.0 by fredt // to preserve column size etc. when SELECT INTO TABLE is used // tony_lai@users 20021020 - patch 1.7.2 - improved aggregates and HAVING // fredt@users 20021112 - patch 1.7.2 by Nitin Chauhan - use of switch // rewrite of the majority of multiple if(){}else{} chains with switch(){} // vorburger@users 20021229 - patch 1.7.2 - null handling // boucherb@users 200307?? - patch 1.7.2 - resolve param nodes // boucherb@users 200307?? - patch 1.7.2 - compress constant expr during resolve // boucherb@users 200307?? - patch 1.7.2 - eager pmd and rsmd // boucherb@users 20031005 - patch 1.7.2 - optimised LIKE // boucherb@users 20031005 - patch 1.7.2 - improved IN value lists // fredt@users 20031012 - patch 1.7.2 - better OUTER JOIN implementation // thomasm@users 20041001 - patch 1.7.3 - BOOLEAN undefined handling // fredt@users 200412xx - patch 1.7.2 - evaluation of time functions // boucherb@users 20050516 - patch 1.8.0 - remove DITypeInfo usage for faster // statement compilation // fredt@users 2006 - 1.9.0 - restructuring, constructors rewritten and several methods made redundant and removed /** * Expression class. * * The core functionality of this class was inherited from HypersonicSQL and * extensively rewritten and extended in successive versions of HSQLDB. * * @author Thomas Mueller (Hypersonic SQL Group) * @version 1.8.0 * @since Hypersonic SQL */ /** @todo - fredt - constant TRUE and FALSE type expressions have valueData of * type BOOLEAN, while computed expressions have no valueData; this should be * normalised in future */ public class Expression { // leaf types public static final int VALUE = 1, COLUMN = 2, DEFAULT = 4, ALTERNATIVE = 5; public static final int ORDER_BY = 10, LIMIT = 11, SEQUENCE = 12, ASTERISK = 13; static final int SCALAR_SUBQUERY = 21, ROW_SUBQUERY = 22, TABLE_SUBQUERY = 23, ROW = 25, TABLE = 26, FUNCTION = 27, SQL_FUNCTION = 28; // -- // operations public static final int NEGATE = 31, ADD = 32, SUBTRACT = 33, MULTIPLY = 34, DIVIDE = 35, CONCAT = 36; // logical operations public static final int NOT = 41, EQUAL = 42, GREATER_EQUAL = 43, GREATER = 44, SMALLER = 45, SMALLER_EQUAL = 46, NOT_EQUAL = 47, IS_NULL = 48, AND = 49, OR = 50, LIKE = 51, IN = 52, EXISTS = 53, ALL = 54, ANY = 55, OVERLAPS = 56, UNIQUE = 57, NOT_DISTINCT = 58, MATCH_SIMPLE = 61, MATCH_PARTIAL = 62, MATCH_FULL = 63, MATCH_UNIQUE_SIMPLE = 64, MATCH_UNIQUE_PARTIAL = 65, MATCH_UNIQUE_FULL = 66; // aggregate functions static final int COUNT = 71, SUM = 72, MIN = 73, MAX = 74, AVG = 75, EVERY = 76, SOME = 77, STDDEV_POP = 78, STDDEV_SAMP = 79, VAR_POP = 80, VAR_SAMP = 81; // system functions static final int CONVERT = 91, CASEWHEN = 92; // temporary used during parsing static final int VIEW = 93; // static final int AGGREGATE_SELF = -1; static final int AGGREGATE_NONE = 0; static final int AGGREGATE_LEFT = 1; static final int AGGREGATE_RIGHT = 2; static final int AGGREGATE_BOTH = 3; // static final Integer INTEGER_0 = ValuePool.getInt(0); static final Integer INTEGER_1 = ValuePool.getInt(1); // static Expression EXPR_TRUE = new Expression(true); static Expression EXPR_FALSE = new Expression(false); // static OrderedIntHashSet aggregateFunctionSet = new OrderedIntHashSet(); static { aggregateFunctionSet.add(COUNT); aggregateFunctionSet.add(SUM); aggregateFunctionSet.add(MIN); aggregateFunctionSet.add(MAX); aggregateFunctionSet.add(AVG); aggregateFunctionSet.add(EVERY); aggregateFunctionSet.add(SOME); aggregateFunctionSet.add(STDDEV_POP); aggregateFunctionSet.add(STDDEV_SAMP); aggregateFunctionSet.add(VAR_POP); aggregateFunctionSet.add(VAR_SAMP); } static OrderedIntHashSet columnExpressionSet = new OrderedIntHashSet(); static { columnExpressionSet.add(COLUMN); } static OrderedIntHashSet subqueryExpressionSet = new OrderedIntHashSet(); static { subqueryExpressionSet.add(TABLE_SUBQUERY); } static OrderedIntHashSet subqueryAggregateExpressionSet = new OrderedIntHashSet(); static { subqueryAggregateExpressionSet.add(COUNT); subqueryAggregateExpressionSet.add(SUM); subqueryAggregateExpressionSet.add(MIN); subqueryAggregateExpressionSet.add(MAX); subqueryAggregateExpressionSet.add(AVG); subqueryAggregateExpressionSet.add(EVERY); subqueryAggregateExpressionSet.add(SOME); subqueryAggregateExpressionSet.add(STDDEV_POP); subqueryAggregateExpressionSet.add(STDDEV_SAMP); subqueryAggregateExpressionSet.add(VAR_POP); subqueryAggregateExpressionSet.add(VAR_SAMP); // subqueryAggregateExpressionSet.add(TABLE_SUBQUERY); } static OrderedIntHashSet emptyExpressionSet = new OrderedIntHashSet(); // type protected int exprType; // aggregate type protected int aggregateSpec = AGGREGATE_NONE; // nodes protected Expression eArg, eArg2; // VALUE protected Object valueData; // VALUE LIST protected Expression[] argList; private boolean isConstantValueList; boolean isCorrelated; // also for query query Type[] argListDataType; Type[] argListOpDataType; // QUERY - in single value selects, IN, EXISTS etc. SubQuery subQuery; // LIKE private Like likeObject; // COLUMN Column column; String schema; String tableName; String columnName; private int columnIndex; private boolean columnQuoted; RangeVariable rangeVariable; // null if not yet resolved String columnAlias; // if it is a column of a select column list boolean aliasQuoted; // data type protected Type dataType; protected Type opDataType; // private boolean isDescending; // if it is a column in a order by int queryTableColumnIndex = -1; // >= 0 when it is used for order by boolean isDistinctAggregate; private boolean isParam; // index of a session-dependent field int parameterIndex; // boolean isColumnEqual; boolean isIndexed; Expression(int type) { exprType = type; } /** * Creates a boolean expression */ Expression(boolean b) { exprType = VALUE; dataType = Type.SQL_BOOLEAN; valueData = b ? Boolean.TRUE : Boolean.FALSE; } /** * Creates a SEQUENCE expression */ Expression(NumberSequence sequence) { exprType = SEQUENCE; valueData = sequence; dataType = sequence.getType(); } // IN condition optimisation /** * Create a equality expressions using existing columns and * range variables. The expression is fully resolved in constructor. */ Expression(RangeVariable leftRangeVar, Column left, RangeVariable rightRangeVar, Column right) { exprType = EQUAL; dataType = Type.SQL_BOOLEAN; eArg = new Expression(leftRangeVar, left); eArg.rangeVariable = leftRangeVar; eArg2 = new Expression(rightRangeVar, right); eArg2.rangeVariable = rightRangeVar; eArg.dataType = left.getType(); eArg2.dataType = right.getType(); } /** * Creates a SCALAR SUBQUERY expression. Is called as ROW_SUBQUERY */ Expression(int exprType, SubQuery sq) { this.exprType = TABLE_SUBQUERY; subQuery = sq; } /** * ROW or VALUELIST */ Expression(int type, Expression[] list) { exprType = type; this.argList = list; } /** * Creates an equality expression */ Expression(Expression e, Expression e2) { exprType = EQUAL; eArg = e; eArg2 = e2; if (eArg.exprType == COLUMN && eArg2.exprType == COLUMN) { isColumnEqual = true; } else { setAggregateSpec(); } dataType = Type.SQL_BOOLEAN; } /** * Creates a binary operation expression */ Expression(int type, Expression e, Expression e2) { exprType = type; eArg = e; eArg2 = e2; setAggregateSpec(); switch (exprType) { case EQUAL : if (eArg.exprType == COLUMN && eArg2.exprType == COLUMN) { isColumnEqual = true; } case GREATER_EQUAL : case GREATER : case SMALLER : case SMALLER_EQUAL : case NOT_EQUAL : case OVERLAPS : case NOT_DISTINCT : // temporary until RangeVariable code is fixed - then use unary constructor case IS_NULL : case IN : case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : dataType = Type.SQL_BOOLEAN; return; case AND : case OR : dataType = Type.SQL_BOOLEAN; break; case ADD : case SUBTRACT : case MULTIPLY : case DIVIDE : return; case CONCAT : case ALTERNATIVE : case CASEWHEN : case LIMIT : return; default : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); } } /** * Creates a unary operation expression */ Expression(int type, Expression e) { exprType = type; eArg = e; setAggregateSpec(); switch (exprType) { case UNIQUE : case EXISTS : case IS_NULL : case NOT : dataType = Type.SQL_BOOLEAN; break; case ORDER_BY : case NEGATE : break; case ANY : case ALL : break; default : Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); } } /** * creates a CONVERT expression */ Expression(Expression e, Type dataType) { this.exprType = CONVERT; this.eArg = e; this.dataType = dataType; this.columnAlias = e.columnAlias; this.aliasQuoted = e.aliasQuoted; setAggregateSpec(); } /** * Creates a LIKE expression */ Expression(Expression e, Expression e2, Character escape, boolean hasCollation) { dataType = Type.SQL_BOOLEAN; exprType = LIKE; eArg = e; eArg2 = e2; likeObject = new Like(escape, hasCollation); setAggregateSpec(); } /** * Creates an ASTERISK expression */ Expression(String schema, String table) { exprType = ASTERISK; this.schema = schema; tableName = table; } /** * Creates a possibly quoted COLUMN expression */ Expression(String schema, String table, String column, boolean isquoted) { exprType = COLUMN; this.schema = schema; tableName = table; columnName = column; columnQuoted = isquoted; } Expression(RangeVariable rangeVar, Column column) { exprType = COLUMN; rangeVariable = rangeVar; columnIndex = rangeVar.rangeTable.findColumn(column.getName().name); setAttributesAsColumn(rangeVar.rangeTable, columnIndex); rangeVar.usedColumns[columnIndex] = true; } /** * Creates a VALUE expression */ Expression(Object o, Type datatype) { exprType = VALUE; dataType = datatype; valueData = o; } /** * Creates an aggregate expression */ Expression(int type, boolean distinct, Expression e) { exprType = type; isDistinctAggregate = distinct; eArg = e; aggregateSpec = AGGREGATE_SELF; } /** * Creates a (possibly PARAM) VALUE expression */ Expression(Object o, Type dataType, boolean isParam) { this(o, dataType); this.isParam = isParam; if (isParam) { paramMode = PARAM_IN; } } /** * Creates a column not null expression */ Expression(Column column) { exprType = NOT; dataType = Type.SQL_BOOLEAN; Expression e = new Expression(Expression.COLUMN); e.columnName = column.getName().name; e = new Expression(IS_NULL, e); eArg = e; } boolean hasSequence(NumberSequence sequence) { return exprType == Expression.SEQUENCE && valueData == sequence; } boolean isConstantCondition() { return dataType.type == Types.SQL_BOOLEAN && exprType == VALUE && !isParam; } void setAggregateSpec() { if (isSelfAggregate()) { aggregateSpec = AGGREGATE_SELF; } else { aggregateSpec = AGGREGATE_NONE; if ((eArg != null) && eArg.isAggregate()) { aggregateSpec += AGGREGATE_LEFT; } if ((eArg2 != null) && eArg2.isAggregate()) { aggregateSpec += AGGREGATE_RIGHT; } } } public String describe(Session session) { return describe(session, 0); } static String getContextDDL(Expression expression) { String ddl = expression.getDDL(); if (expression.exprType != VALUE && expression.exprType != COLUMN && expression.exprType != ROW && expression.exprType != FUNCTION && expression.exprType != SQL_FUNCTION && expression.exprType != ALTERNATIVE && expression.exprType != CASEWHEN && expression.exprType != CONVERT) { StringBuffer temp = new StringBuffer(); ddl = temp.append('(').append(ddl).append(')').toString(); } return ddl; } /** * For use with CHECK constraints. Under development. * * Currently supports a subset of expressions and is suitable for CHECK * search conditions that refer only to the inserted/updated row. * * For full DDL reporting of VIEW select queries and CHECK search * conditions, future improvements here are dependent upon improvements to * SELECT query parsing, so that it is performed in a number of passes. * An early pass should result in the query turned into an Expression tree * that contains the information in the original SQL without any * alterations, and with tables and columns all resolved. This Expression * can then be preserved for future use. Table and column names that * are not user-defined aliases should be kept as the HsqlName structures * so that table or column renaming is reflected in the precompiled * query. */ public String getDDL() { StringBuffer buf = new StringBuffer(64); String left = null; String right = null; if (eArg != null) { left = Expression.getContextDDL(eArg); } if (eArg2 != null) { right = Expression.getContextDDL(eArg2); } switch (exprType) { case FUNCTION : case SQL_FUNCTION : // must go to Function throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); case VALUE : if (isParam) { return Token.T_QUESTION; } if (valueData == null) { return Token.T_NULL; } if (dataType == null) { throw Trace.runtimeError( Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression.getDDL()"); } return dataType.convertToSQLString(valueData); case COLUMN : // this is a limited solution if (rangeVariable != null) { Table table = rangeVariable.getTable(); if (tableName != null) { buf.append(table.tableName.statementName); buf.append('.'); } buf.append( table.getColumn(columnIndex).columnName.statementName); } else { buf.append(getAlias()); } return buf.toString(); case DEFAULT : return Token.T_DEFAULT; case ROW : buf.append('('); for (int i = 0; i < argList.length; i++) { buf.append(argList[i].getDDL()); if (i < argList.length - 1) { buf.append(','); } } buf.append(')'); return buf.toString(); // case TABLE : for (int i = 0; i < argList.length; i++) { buf.append(argList[i].getDDL()); if (i < argList.length - 1) { buf.append(','); } } return buf.toString(); case ASTERISK : buf.append('*'); return buf.toString(); case ORDER_BY : if (left == null) { buf.append(left); } else if (columnAlias != null) {} if (isDescending) { buf.append(' ').append(Token.T_DESC); } return buf.toString(); case NEGATE : buf.append('-').append(left); return buf.toString(); case ADD : buf.append(left).append('+').append(right); return buf.toString(); case SUBTRACT : buf.append(left).append('-').append(right); return buf.toString(); case MULTIPLY : buf.append(left).append('*').append(right); return buf.toString(); case DIVIDE : buf.append(left).append('/').append(right); return buf.toString(); case CONCAT : buf.append(left).append("||").append(right); return buf.toString(); case NOT : if (eArg.exprType == IS_NULL) { buf.append(getContextDDL(eArg.eArg)).append(' ').append( Token.T_IS).append(' ').append(Token.T_NOT).append( ' ').append(Token.T_NULL); return buf.toString(); } if (eArg.exprType == NOT_DISTINCT) { buf.append(getContextDDL(eArg.eArg)).append(' ').append( Token.T_IS).append(' ').append( Token.T_DISTINCT).append(' ').append( Token.T_FROM).append(' ').append( getContextDDL(eArg.eArg2)); return buf.toString(); } buf.append(Token.T_NOT).append(' ').append(left); return buf.toString(); case NOT_DISTINCT : buf.append(Token.T_NOT).append(' ').append( getContextDDL(eArg.eArg)).append(' ').append( Token.T_IS).append(' ').append(Token.T_DISTINCT).append( ' ').append(Token.T_FROM).append(' ').append( getContextDDL(eArg.eArg2)); return buf.toString(); case EQUAL : buf.append(left).append('=').append(right); return buf.toString(); case GREATER_EQUAL : buf.append(left).append(">=").append(right); return buf.toString(); case GREATER : buf.append(left).append('>').append(right); return buf.toString(); case SMALLER : buf.append(left).append('<').append(right); return buf.toString(); case SMALLER_EQUAL : buf.append(left).append("<=").append(right); return buf.toString(); case NOT_EQUAL : if (Token.T_NULL.equals(right)) { buf.append(left).append(" IS NOT ").append(right); } else { buf.append(left).append("!=").append(right); } return buf.toString(); case LIKE : buf.append(left).append(' ').append(Token.T_LIKE).append(' '); buf.append(right); /** @todo fredt - scripting of non-ascii escapes needs changes to general script logging */ if (likeObject.escapeChar != null) { buf.append(' ').append(Token.T_ESCAPE).append(' ').append( '\''); buf.append(likeObject.escapeChar.toString()).append('\''); buf.append(' '); } return buf.toString(); case AND : buf.append(left).append(' ').append(Token.T_AND).append( ' ').append(right); return buf.toString(); case OR : buf.append(left).append(' ').append(Token.T_OR).append( ' ').append(right); return buf.toString(); case ALL : buf.append(left).append(' ').append(Token.T_ALL).append( ' ').append(right); return buf.toString(); case ANY : buf.append(left).append(' ').append(Token.T_ANY).append( ' ').append(right); return buf.toString(); case IN : buf.append(left).append(' ').append(Token.T_IN).append( ' ').append(right); return buf.toString(); case MATCH_SIMPLE : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(right); return buf.toString(); case MATCH_PARTIAL : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(Token.PARTIAL).append(right); return buf.toString(); case MATCH_FULL : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(Token.FULL).append(right); return buf.toString(); case MATCH_UNIQUE_SIMPLE : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(Token.UNIQUE).append(right); return buf.toString(); case MATCH_UNIQUE_PARTIAL : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(Token.UNIQUE).append(' ').append( Token.PARTIAL).append(right); return buf.toString(); case MATCH_UNIQUE_FULL : buf.append(left).append(' ').append(Token.T_MATCH).append( ' ').append(Token.UNIQUE).append(' ').append( Token.FULL).append(right); return buf.toString(); case CONVERT : buf.append(' ').append(Token.T_CONVERT).append('('); buf.append(left).append(','); buf.append(dataType.getDefinition()); buf.append(')'); return buf.toString(); case CASEWHEN : buf.append(' ').append(Token.T_CASEWHEN).append('('); buf.append(left).append(',').append(right).append(')'); return buf.toString(); case IS_NULL : buf.append(left).append(' ').append(Token.T_IS).append( ' ').append(Token.T_NULL); return buf.toString(); case ALTERNATIVE : buf.append(left).append(',').append(right); return buf.toString(); case TABLE_SUBQUERY : /* buf.append('('); buf.append(subSelect.getDDL()); buf.append(')'); */ break; case UNIQUE : buf.append(' ').append(Token.T_UNIQUE).append(' '); break; case EXISTS : buf.append(' ').append(Token.T_EXISTS).append(' '); break; case COUNT : buf.append(' ').append(Token.T_COUNT).append('('); break; case SUM : buf.append(' ').append(Token.T_SUM).append('('); buf.append(left).append(')'); break; case MIN : buf.append(' ').append(Token.T_MIN).append('('); buf.append(left).append(')'); break; case MAX : buf.append(' ').append(Token.T_MAX).append('('); buf.append(left).append(')'); break; case AVG : buf.append(' ').append(Token.T_AVG).append('('); buf.append(left).append(')'); break; case EVERY : buf.append(' ').append(Token.T_EVERY).append('('); buf.append(left).append(')'); break; case SOME : buf.append(' ').append(Token.T_SOME).append('('); buf.append(left).append(')'); break; case STDDEV_POP : buf.append(' ').append(Token.T_STDDEV_POP).append('('); buf.append(left).append(')'); break; case STDDEV_SAMP : buf.append(' ').append(Token.T_STDDEV_SAMP).append('('); buf.append(left).append(')'); break; case VAR_POP : buf.append(' ').append(Token.T_VAR_POP).append('('); buf.append(left).append(')'); break; case VAR_SAMP : buf.append(' ').append(Token.T_VAR_SAMP).append('('); buf.append(left).append(')'); break; default : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); } return buf.toString(); } private String describe(Session session, int blanks) { StringBuffer buf = new StringBuffer(64); buf.append('\n'); for (int i = 0; i < blanks; i++) { buf.append(' '); } switch (exprType) { case FUNCTION : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression.describe()"); case VALUE : if (isParam) { buf.append("PARAM "); } buf.append("VALUE = ").append(valueData); buf.append(", TYPE = ").append(dataType.getNameString()); return buf.toString(); case COLUMN : buf.append("COLUMN "); if (tableName != null) { buf.append(tableName); buf.append('.'); } buf.append(columnName); return buf.toString(); case DEFAULT : return Token.T_DEFAULT; case TABLE_SUBQUERY : buf.append("QUERY "); buf.append(subQuery.select.describe(session)); return buf.toString(); case ROW : // case TABLE : buf.append("VALUELIST "); buf.append(" TYPE = ").append(dataType.getNameString()); if (argList != null) { for (int i = 0; i < argList.length; i++) { buf.append(argList[i].describe(session, blanks + blanks)); buf.append(' '); } } break; case ASTERISK : buf.append("* "); break; case ORDER_BY : buf.append(Token.T_ORDER).append(' ').append(Token.T_BY); buf.append(' '); if (isDescending) { buf.append(Token.T_DESC).append(' '); } break; case NEGATE : buf.append("NEGATE "); break; case ADD : buf.append("ADD "); break; case SUBTRACT : buf.append("SUBTRACT "); break; case MULTIPLY : buf.append("MULTIPLY "); break; case DIVIDE : buf.append("DIVIDE "); break; case CONCAT : buf.append("CONCAT "); break; case NOT : if (eArg.exprType == NOT_DISTINCT) { buf.append(Token.T_DISTINCT); return buf.toString(); } buf.append("NOT "); break; case NOT_DISTINCT : buf.append("NOT "); buf.append("DISTINCT "); break; case EQUAL : buf.append("EQUAL "); break; case GREATER_EQUAL : buf.append("GREATER_EQUAL "); break; case GREATER : buf.append("GREATER "); break; case SMALLER : buf.append("SMALLER "); break; case SMALLER_EQUAL : buf.append("SMALLER_EQUAL "); break; case NOT_EQUAL : buf.append("NOT_EQUAL "); break; case LIKE : buf.append("LIKE "); buf.append(likeObject.describe(session)); break; case AND : buf.append("AND "); break; case OR : buf.append("OR "); break; case ALL : buf.append("ALL "); break; case ANY : buf.append("ANY "); break; case IN : buf.append("IN "); break; case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : buf.append("MATCH "); break; case IS_NULL : buf.append("IS_NULL "); break; case UNIQUE : buf.append("UNIQUE "); break; case EXISTS : buf.append("EXISTS "); break; case COUNT : buf.append("COUNT "); break; case SUM : buf.append("SUM "); break; case MIN : buf.append("MIN "); break; case MAX : buf.append("MAX "); break; case AVG : buf.append("AVG "); break; case EVERY : buf.append(Token.T_EVERY).append(' '); break; case SOME : buf.append(Token.T_SOME).append(' '); break; case STDDEV_POP : buf.append(Token.T_STDDEV_POP).append(' '); break; case STDDEV_SAMP : buf.append(Token.T_STDDEV_SAMP).append(' '); break; case VAR_POP : buf.append(Token.T_VAR_POP).append(' '); break; case VAR_SAMP : buf.append(Token.T_VAR_SAMP).append(' '); break; case CONVERT : buf.append("CONVERT "); buf.append(dataType.getDefinition()); buf.append(' '); break; case CASEWHEN : buf.append("CASEWHEN "); break; } if (eArg != null) { buf.append(" arg1=["); buf.append(eArg.describe(session, blanks + 1)); buf.append(']'); } if (eArg2 != null) { buf.append(" arg2=["); buf.append(eArg2.describe(session, blanks + 1)); buf.append(']'); } return buf.toString(); } /** * Set the data type */ void setDataType(Type type) { dataType = type; } private boolean equals(Expression other) { if (other == this) { return true; } if (other == null) { return false; } /** @todo fredt - equals() method for subSelect needed */ if (exprType != other.exprType || !equals(dataType, other.dataType)) { return false; } switch (exprType) { case COLUMN : return equals(tableName, other.tableName) && equals(columnName, other.columnName) && equals(schema, other.schema) && equals(rangeVariable, other.rangeVariable); case VALUE : return equals(valueData, other.valueData); default : return equals(argList, other.argList) && equals(subQuery, other.subQuery) && equals(eArg, other.eArg) && equals(eArg2, other.eArg2); } } public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof Expression) { return equals((Expression) other); } return false; } private static boolean equals(Object o1, Object o2) { if (o1 == o2) { return true; } return (o1 == null) ? o2 == null : o1.equals(o2); } private static boolean equals(Expression[] row1, Expression[] row2) { if (row1 == row2) { return true; } if (row1.length != row2.length) { return false; } int len = row1.length; for (int i = 0; i < len; i++) { Expression e1 = row1[i]; Expression e2 = row2[i]; if (!equals(e1, e2)) { return false; } } return true; } static boolean equals(Expression e1, Expression e2) { return (e1 == null) ? e2 == null : e1.equals(e2); } boolean isComposedOf(Expression exprList[], int start, int end, OrderedIntHashSet excludeSet) { if (exprType == VALUE) { return true; } if (excludeSet.contains(exprType)) { return true; } for (int i = start; i < end; i++) { if (equals(exprList[i])) { return true; } } switch (exprType) { case LIKE : case ALL : case ANY : case IN : case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : case UNIQUE : case EXISTS : case TABLE_SUBQUERY : case COUNT : case SUM : case MIN : case MAX : case AVG : case EVERY : case SOME : case STDDEV_POP : case STDDEV_SAMP : case VAR_POP : case VAR_SAMP : return false; } boolean result = true; if (argList != null) { for (int i = 0; i < argList.length; i++) { result &= (argList[i] == null || argList[i].isComposedOf(exprList, start, end, excludeSet)); } return result; } if (eArg == null && eArg2 == null) { return false; } result = (eArg == null || eArg.isComposedOf(exprList, start, end, excludeSet)); result &= (eArg2 == null || eArg2.isComposedOf(exprList, start, end, excludeSet)); return result; } boolean isComposedOf(OrderedHashSet expressions, OrderedIntHashSet excludeSet) { if (exprType == VALUE) { return true; } if (excludeSet.contains(exprType)) { return true; } for (int i = 0; i < expressions.size(); i++) { if (equals(expressions.get(i))) { return true; } } switch (exprType) { case LIKE : case ALL : case ANY : case IN : case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : case UNIQUE : case EXISTS : case TABLE_SUBQUERY : case COUNT : case SUM : case MIN : case MAX : case AVG : case EVERY : case SOME : case STDDEV_POP : case STDDEV_SAMP : case VAR_POP : case VAR_SAMP : return false; } boolean result = true; if (argList != null) { for (int i = 0; i < argList.length; i++) { result &= (argList[i] == null || argList[i].isComposedOf(expressions, excludeSet)); } return result; } if (eArg == null && eArg2 == null) { return false; } result = (eArg == null || eArg.isComposedOf(expressions, excludeSet)); result &= (eArg2 == null || eArg2.isComposedOf(expressions, excludeSet)); return result; } /** * Check if this expression defines a constant value. * <p> * It does, if it is a constant value expression, or all the argument * expressions define constant values. */ boolean isConstant() { switch (exprType) { case VALUE : return true; case NEGATE : return eArg.isConstant(); case ADD : case SUBTRACT : case MULTIPLY : case DIVIDE : case CONCAT : return eArg.isConstant() && eArg2.isConstant(); } return false; } /** * Is this (indirectly) an aggregate expression */ boolean isAggregate() { return aggregateSpec != AGGREGATE_NONE; } boolean isSelfAggregate() { switch (exprType) { case COUNT : case MAX : case MIN : case SUM : case AVG : case EVERY : case SOME : case STDDEV_POP : case STDDEV_SAMP : case VAR_POP : case VAR_SAMP : return true; } return false; } /** * Set an ORDER BY column expression DESC */ void setDescending() { isDescending = true; } /** * Is an ORDER BY column expression DESC */ boolean isDescending() { return isDescending; } /** * Set the column alias and whether the name is quoted */ void setAlias(String s, boolean isquoted) { columnAlias = s; aliasQuoted = isquoted; } /** * Change the column name */ void setColumnName(String newname, boolean isquoted) { columnName = newname; columnQuoted = isquoted; } /** * Change the table name */ void setTableName(String newname) { tableName = newname; } /** * Return the user defined alias or null if none */ String getDefinedAlias() { return columnAlias; } /** * Get the column alias */ String getAlias() { if (columnAlias != null) { return columnAlias; } if (exprType == COLUMN) { return columnName; } return ""; } /** * Is a column alias quoted */ boolean isAliasQuoted() { if (columnAlias != null) { return aliasQuoted; } if (exprType == COLUMN) { return columnQuoted; } return false; } /** * Returns the type of expression */ public int getType() { return exprType; } /** * Returns the type of expression */ public Object getMetaData() { return null; } /** * Returns the left node */ Expression getArg() { return eArg; } /** * Returns the right node */ Expression getArg2() { return eArg2; } /** * Returns the range variable for a COLUMN expression */ RangeVariable getRangeVariable() { return rangeVariable; } /** * return the expression for an aliase used in an ORDER BY clause */ Expression replaceAliasInOrderBy(Expression[] columns, int length, Expression parent) { if (eArg != null) { eArg = eArg.replaceAliasInOrderBy(columns, length, this); if (eArg.isAggregate()) { setAggregateSpec(); } } if (eArg2 != null) { eArg2 = eArg2.replaceAliasInOrderBy(columns, length, this); if (eArg2.isAggregate()) { setAggregateSpec(); } } switch (exprType) { case FUNCTION : case SQL_FUNCTION : { Expression e; for (int i = 0; i < argList.length; i++) { e = argList[i]; if (e != null) { argList[i] = e.replaceAliasInOrderBy(columns, length, this); if (argList[i].isAggregate()) { setAggregateSpec(); } } } break; } case COLUMN : { for (int i = 0; i < length; i++) { if (columnName.equals(columns[i].columnAlias) && tableName == null && schema == null) { return columns[i]; } } for (int i = 0; i < length; i++) { if (this.equals(columns[i])) { return columns[i]; } if (columnName != null && columnName.equals(columns[i].columnName) && (tableName == null || tableName .equals(columns[i].tableName)) && (schema == null || schema .equals(columns[i].schema))) { return columns[i]; } } } default : } return this; } /** * Workaround for CHECK constraints. We don't want optimisation so we * flag all LIKE expressions as already optimised. */ void setLikeOptimised() throws HsqlException { if (eArg != null) { eArg.setLikeOptimised(); } if (eArg2 != null) { eArg2.setLikeOptimised(); } if (exprType == LIKE) { likeObject.optimised = true; } } /** * Find a range variable with the given table alias */ RangeVariable findMatchingRangeVariable(RangeVariable[] rangeVarArray) { for (int i = 0; i < rangeVarArray.length; i++) { RangeVariable rangeVar = rangeVarArray[i]; if (rangeVar.resolvesTableName(this)) { return rangeVar; } } return null; } /** * collects all range variables in expression tree */ void collectRangeVariables(RangeVariable[] rangeVariables, Set set) { if (eArg != null) { eArg.collectRangeVariables(rangeVariables, set); } if (eArg2 != null) { eArg2.collectRangeVariables(rangeVariables, set); } if (argList != null) { for (int i = 0; i < argList.length; i++) { if (argList[i] != null) { argList[i].collectRangeVariables(rangeVariables, set); } } } else if (rangeVariable != null) { for (int i = 0; i < rangeVariables.length; i++) { if (rangeVariables[i] == rangeVariable) { set.add(rangeVariables[i]); } } } else if (subQuery != null) { if (subQuery.select != null) { subQuery.select.collectRangeVariables(rangeVariables, set); } } } /** * collects all range variables in expression tree */ void markRangeVariables(RangeVariable[] rangeVariables, boolean[] flags) { if (eArg != null) { eArg.markRangeVariables(rangeVariables, flags); } if (eArg2 != null) { eArg2.markRangeVariables(rangeVariables, flags); } if (argList != null) { for (int i = 0; i < argList.length; i++) { if (argList[i] != null) { argList[i].markRangeVariables(rangeVariables, flags); } } } else if (rangeVariable != null) { for (int i = 0; i < rangeVariables.length; i++) { if (rangeVariables[i] == rangeVariable) { flags[i] = true; } } } else if (subQuery != null) { if (subQuery.select != null) { subQuery.select.markRangeVariables(rangeVariables, flags); } } } /** * return true if given RangeVariable is used in expression tree */ boolean hasReference(RangeVariable range) { if (range == rangeVariable) { return true; } if (eArg != null && eArg.hasReference(range)) { return true; } if (eArg2 != null && eArg2.hasReference(range)) { return true; } if (argList != null) { for (int i = 0; i < argList.length; i++) { if (argList[i] != null) { if (argList[i].hasReference(range)) { return true; } } } } else if (subQuery != null && subQuery.select != null) { if (subQuery.select.hasOuterReference(range)) { return true; } } return false; } /** * resolve tables and collect unresolved column expressions */ public OrderedHashSet resolveColumnReferences( RangeVariable[] rangeVarArray, OrderedHashSet unresolvedSet) throws HsqlException { if (rangeVarArray == null || rangeVarArray.length == 0 || exprType == Expression.VALUE || isParam) { return unresolvedSet; } if (eArg != null) { unresolvedSet = eArg.resolveColumnReferences(rangeVarArray, unresolvedSet); } if (eArg2 != null) { unresolvedSet = eArg2.resolveColumnReferences(rangeVarArray, unresolvedSet); } switch (exprType) { case COLUMN : if (rangeVariable != null) { break; } for (int i = 0; i < rangeVarArray.length; i++) { RangeVariable rangeVar = rangeVarArray[i]; if (rangeVar == null || !rangeVar.resolvesTableName(this)) { continue; } Table table = rangeVar.rangeTable; int colIndex = rangeVar.findColumn(columnName); if (colIndex != -1) { rangeVariable = rangeVar; setAttributesAsColumn(table, colIndex); rangeVariable.addColumn(colIndex); break; } } if (rangeVariable == null) { if (unresolvedSet == null) { unresolvedSet = new OrderedHashSet(); } unresolvedSet.add(this); } break; case TABLE_SUBQUERY : // we now (1_7_2_ALPHA_R) resolve independently first, then // resolve in the enclosing context if (subQuery != null) { unresolvedSet = subQuery.select.resolveCorrelatedColumns(rangeVarArray, unresolvedSet); } break; case FUNCTION : case SQL_FUNCTION : case TABLE : case ROW : if (argList == null) { break; } for (int i = 0; i < argList.length; i++) { if (argList[i] == null) { continue; } unresolvedSet = argList[i].resolveColumnReferences(rangeVarArray, unresolvedSet); } break; default : } return unresolvedSet; } /** * collects expressions without keys */ public OrderedHashSet getUnkeyedColumns(OrderedHashSet unresolvedSet) { if (isParam || exprType == Expression.VALUE) { return unresolvedSet; } if (eArg != null) { unresolvedSet = eArg.getUnkeyedColumns(unresolvedSet); } if (eArg2 != null) { unresolvedSet = eArg2.getUnkeyedColumns(unresolvedSet); } switch (exprType) { case COLUMN : if (!rangeVariable.hasKeyedColumnInGroupBy) { if (unresolvedSet == null) { unresolvedSet = new OrderedHashSet(); } unresolvedSet.add(this); } break; case TABLE_SUBQUERY : if (subQuery != null) { if (unresolvedSet == null) { unresolvedSet = new OrderedHashSet(); } unresolvedSet.add(this); } break; case FUNCTION : case SQL_FUNCTION : case TABLE : case ROW : if (argList == null) { break; } for (int i = 0; i < argList.length; i++) { if (argList[i] == null) { continue; } unresolvedSet = argList[i].getUnkeyedColumns(unresolvedSet); } break; default : } return unresolvedSet; } /** * For CASE WHEN and its special cases section 9.3 of the SQL standard * on type aggregation is implemented. */ void resolveCaseWhenTypes() throws HsqlException { if (dataType != null) { return; } Expression expr = this; while (expr.exprType == Expression.CASEWHEN) { expr.eArg.resolveTypes(expr); if (expr.eArg.isParam) { expr.eArg.dataType = Type.SQL_BOOLEAN; } expr.eArg2.eArg.resolveTypes(eArg2); expr.eArg2.eArg2.resolveTypes(eArg2); expr = expr.eArg2.eArg2; } expr = this; while (expr.exprType == Expression.CASEWHEN) { dataType = Type.getAggregatedType(expr.eArg2.eArg.dataType, dataType); dataType = Type.getAggregatedType(expr.eArg2.eArg2.dataType, dataType); expr = expr.eArg2.eArg2; } expr = this; while (expr.exprType == Expression.CASEWHEN) { if (expr.eArg2.eArg.dataType == null) { expr.eArg2.eArg.dataType = dataType; } if (expr.eArg2.eArg2.dataType == null) { expr.eArg2.eArg2.dataType = dataType; } if (expr.eArg2.dataType == null) { expr.eArg2.dataType = dataType; } expr = expr.eArg2.eArg2; } } private void resolveTypesForLogicalOp() throws HsqlException { if (eArg.isParam) { eArg.dataType = Type.SQL_BOOLEAN; } if (eArg2.isParam) { eArg2.dataType = Type.SQL_BOOLEAN; } if (eArg.dataType == null || eArg2.dataType == null) { throw Trace.error(Trace.NULL_LITERAL_NOT_ALLOWED); } if (eArg.exprType == Expression.ROW || eArg2.exprType == Expression.ROW || Type.SQL_BOOLEAN != eArg.dataType || Type.SQL_BOOLEAN != eArg2.dataType) { // todo SQL type throw Trace.error(Trace.WRONG_DATA_TYPE); } } private void resolveTypesForComparison(Expression parent) throws HsqlException { if (eArg.isParam && eArg2.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes3); } if (eArg2.exprType == Expression.ALL || eArg2.exprType == Expression.ANY) { resolveTypesForAllAny(); return; } if (eArg.exprType == Expression.ROW || eArg2.exprType == Expression.ROW) { if (eArg.exprType != Expression.ROW || eArg2.exprType != Expression.ROW || eArg.argList.length != eArg2.argList.length) { // todo better message throw Trace.error(Trace.WRONG_DATA_TYPE); } } if (isFixedConditional()) { valueData = getConstantValue(); exprType = VALUE; eArg = null; eArg2 = null; return; } if (eArg.exprType == Expression.ROW) { argListOpDataType = new Type[eArg.argList.length]; for (int i = 0; i < argListOpDataType.length; i++) { Type leftType = eArg.argListDataType[i]; Type rightType = eArg2.argListDataType[i]; argListOpDataType[i] = getComparisonType(leftType, rightType); } } else { if (eArg.isParam) { eArg.dataType = eArg2.dataType; } else if (eArg2.isParam) { eArg2.dataType = eArg.dataType; } if (eArg.dataType == null || eArg2.dataType == null) { throw Trace.error(Trace.NULL_LITERAL_NOT_ALLOWED); } opDataType = getComparisonType(eArg.dataType, eArg2.dataType); } } public Type getComparisonType(Type leftType, Type rightType) throws HsqlException { if (leftType.isNumberType()) { return leftType.getCombinedType(rightType, exprType); } else if (leftType.isDateTimeType()) { if (rightType.isCharacterType()) { return leftType; } } else if (rightType.isDateTimeType()) { return rightType; } else { // simply throw if not compatible, otherwise conversion not required leftType.getCombinedType(rightType, EQUAL); } return null; } public void resolveTypes(Expression parent) throws HsqlException { if (isParam) { return; } if (eArg != null) { eArg.resolveTypes(this); } if (eArg2 != null) { eArg2.resolveTypes(this); } switch (exprType) { case VALUE : break; case COLUMN : break; case TABLE : for (int i = 0; i < argList.length; i++) { if (argList[i] != null) { argList[i].resolveTypes(this); } } break; case ROW : argListDataType = new Type[argList.length]; for (int i = 0; i < argList.length; i++) { if (argList[i] != null) { argList[i].resolveTypes(this); argListDataType[i] = argList[i].dataType; } } break; case FUNCTION : case SQL_FUNCTION : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "resolveTypes()"); case TABLE_SUBQUERY : { subQuery.select.resolveTypes(); argListDataType = new Type[subQuery.select.visibleColumnCount]; for (int i = 0; i < argListDataType.length; i++) { argListDataType[i] = subQuery.select.exprColumns[i].dataType; } dataType = argListDataType[0]; break; } case ORDER_BY : if (eArg.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes1); } dataType = eArg.dataType; break; case NEGATE : if (eArg.isParam || eArg.dataType == null) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE); } dataType = eArg.dataType; if (!dataType.isNumberType()) { // todo SQL type throw Trace.error(Trace.WRONG_DATA_TYPE); } if (isFixedConstant()) { valueData = getConstantValue(dataType); eArg = null; exprType = VALUE; } break; case ADD : // special case for concat using + if ((eArg.dataType != null && eArg.dataType.isCharacterType()) || (eArg2.dataType != null && eArg2.dataType.isCharacterType())) { exprType = Expression.CONCAT; resolveTypesForConcat(); break; } case SUBTRACT : case MULTIPLY : case DIVIDE : resolveTypesForArithmetic(); break; case CONCAT : resolveTypesForConcat(); break; case NOT_DISTINCT : case EQUAL : case GREATER_EQUAL : case GREATER : case SMALLER : case SMALLER_EQUAL : case NOT_EQUAL : resolveTypesForComparison(parent); break; case LIKE : resolveTypesForLike(); break; case AND : { resolveTypesForLogicalOp(); if (eArg.isFixedConditional()) { if (eArg2.isFixedConditional()) { valueData = getConstantValue(); exprType = VALUE; eArg = null; eArg2 = null; } else { if (Boolean.FALSE.equals(eArg.getConstantValue())) { valueData = Boolean.FALSE; exprType = VALUE; eArg = null; eArg2 = null; } } } else if (eArg2.isFixedConditional()) { if (Boolean.FALSE.equals(eArg2.getConstantValue())) { valueData = Boolean.FALSE; exprType = VALUE; eArg = null; eArg2 = null; } } break; } case OR : { resolveTypesForLogicalOp(); // optimisation if (eArg.isFixedConditional()) { if (eArg2.isFixedConditional()) { valueData = getConstantValue(); exprType = VALUE; eArg = null; eArg2 = null; } else if (Boolean.TRUE.equals(eArg.getConstantValue())) { valueData = Boolean.TRUE; exprType = VALUE; eArg = null; eArg2 = null; } } else if (eArg2.isFixedConditional()) { if (Boolean.TRUE.equals(eArg2.getConstantValue())) { valueData = Boolean.TRUE; exprType = VALUE; eArg = null; eArg2 = null; } } break; } case IS_NULL : if (isFixedConditional()) { valueData = getConstantValue(); exprType = VALUE; eArg = null; } else if (eArg.dataType == null) { eArg.dataType = Type.SQL_VARCHAR; } break; case NOT : if (eArg.isParam) { eArg.dataType = Type.SQL_BOOLEAN; } if (!dataType.isBooleanType()) { // todo SQL type throw Trace.error(Trace.WRONG_DATA_TYPE); } if (isFixedConditional()) { valueData = (Boolean) eArg.getConstantValue(); exprType = VALUE; eArg = null; eArg2 = null; } break; case OVERLAPS : resolveTypesForOverlaps(); break; case ALL : case ANY : break; case IN : case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : resolveTypesForIn(); break; case UNIQUE : case EXISTS : break; case COUNT : if (eArg.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes4); } dataType = Type.SQL_INTEGER; break; case MAX : case MIN : case SUM : case AVG : case EVERY : case SOME : case STDDEV_POP : case STDDEV_SAMP : case VAR_POP : case VAR_SAMP : if (eArg.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes4); } dataType = SetFunction.getType(exprType, eArg.dataType); break; case CONVERT : // NOTE: both iDataType for this expr and for eArg (if isParm) // are already set in Parser during read if (eArg.isFixedConstant() || eArg.isFixedConditional()) { valueData = getConstantValue(); exprType = VALUE; eArg = null; } break; case CASEWHEN : // We use CASEWHEN as parent type. // In the parent, eArg is the condition, and eArg2 is // the leaf, tagged as type ALTERNATIVE; its eArg is // case 1 (how to get the value when the condition in // the parent evaluates to true), while its eArg2 is case 2 // (how to get the value when the condition in // the parent evaluates to false). resolveCaseWhenTypes(); if (dataType == null) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes6); } break; case ALTERNATIVE : { break; } } } void resolveTypesForArithmetic() throws HsqlException { if (eArg.isParam && eArg2.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes2); } if (eArg.isParam) { eArg.dataType = eArg2.dataType; } else if (eArg2.isParam) { eArg2.dataType = eArg.dataType; } if (eArg.dataType == null || eArg2.dataType == null) { throw Trace.error(Trace.NULL_LITERAL_NOT_ALLOWED); } // datetime subtract - type predetermined if (eArg.dataType.isDateTimeType() && eArg2.dataType.isDateTimeType()) { if (dataType == null) { throw Trace.error(Trace.UNRESOLVED_TYPE); } else if (!dataType.isIntervalType() || eArg.dataType.type != eArg2.dataType.type) { throw Trace.error(Trace.WRONG_DATA_TYPE); } } else { dataType = eArg.dataType.getCombinedType(eArg2.dataType, exprType); } if (isFixedConstant()) { valueData = getConstantValue(dataType); eArg = null; eArg2 = null; exprType = VALUE; } } void resolveTypesForConcat() throws HsqlException { if (dataType != null) { return; } if (eArg.isParam && eArg2.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes2); } if (eArg.isParam) { eArg.dataType = eArg2.dataType; } else if (eArg2.isParam) { eArg2.dataType = eArg.dataType; } if (eArg.dataType == null || eArg2.dataType == null) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypes2); } if (eArg.dataType.isBinaryType() ^ eArg2.dataType.isBinaryType()) { throw Trace.error(Trace.WRONG_DATA_TYPE); } // conversion of second arg to character for backward compatibility if (eArg.dataType.isCharacterType() && !eArg2.dataType.isCharacterType()) { Type newType = CharacterType.getCharacterType(Types.SQL_VARCHAR, eArg2.dataType.displaySize()); eArg2 = new Expression(eArg2, newType); } dataType = eArg.dataType.getCombinedType(eArg2.dataType, Expression.CONCAT); if (isFixedConstant()) { valueData = getConstantValue(dataType); eArg = null; eArg2 = null; exprType = VALUE; } } void resolveTypesForLike() throws HsqlException { if (eArg.isParam && eArg2.isParam) { throw Trace.error(Trace.UNRESOLVED_PARAMETER_TYPE, Trace.Expression_resolveTypeForLike); } if (isFixedConditional()) { valueData = (Boolean) eArg.getConstantValue(); exprType = VALUE; eArg = null; eArg2 = null; } else if (eArg.isParam) { eArg.dataType = eArg2.dataType; } else if (eArg2.isParam) { eArg2.dataType = eArg.dataType; } if (eArg.dataType == null || eArg2.dataType == null) { throw Trace.error(Trace.NULL_LITERAL_NOT_ALLOWED); } // boucherb@users 2003-09-25 - patch 1.7.2 Alpha P // // Some optimizations for LIKE // // TODO: // // See if the same optimizations can be done dynamically at execute time when // eArg2 is PARAM. Unfortunately, this currently requires re-resolving from // the root any expression containing at least one parameterized LIKE in the // compiled statement and reseting conditions on any involved range variable, // so the answer is: probably not, at least not under the current code. // // CHECKME: // // Test for correct results under all XXXCHAR types (padding, etc.?) // // NOTE: // // For the old behaviour, simply comment out the block below if (likeObject.optimised) { return; } boolean isRightArgFixedConstant = eArg2.isFixedConstant(); String likeStr = isRightArgFixedConstant ? (String) eArg2.getConstantValue(Type.SQL_VARCHAR) : null; boolean ignoreCase = eArg.dataType.type == Types.VARCHAR_IGNORECASE || eArg2.dataType.type == Types.VARCHAR_IGNORECASE; likeObject.setParams(likeStr, ignoreCase); if (!isRightArgFixedConstant) { // Then we are done here, since it's impossible // to determine at this point if the right expression // will have a fixed prefix that can be used to optimize // any involved range variable return; } if (likeObject.isEquivalentToFalsePredicate()) { valueData = Boolean.FALSE; exprType = VALUE; eArg = null; eArg2 = null; likeObject = null; } else if (likeObject.isEquivalentToEqualsPredicate()) { exprType = EQUAL; eArg2 = new Expression(likeObject.getRangeLow(), Type.SQL_VARCHAR); likeObject = null; } else if (likeObject.isEquivalentToNotNullPredicate()) {} else { if (eArg.exprType != Expression.COLUMN) { return; } if (!eArg.dataType.isCharacterType()) { return; } boolean between = false; boolean like = false; boolean larger = false; if (likeObject.isEquivalentToBetweenPredicate()) { // X LIKE 'abc%' <=> X >= 'abc' AND X <= 'abc' || max_collation_char larger = likeObject.hasCollation; between = !larger; like = larger; } else if (likeObject .isEquivalentToBetweenPredicateAugmentedWithLike()) { // X LIKE 'abc%...' <=> X >= 'abc' AND X <= 'abc' || max_collation_char AND X LIKE 'abc%...' larger = likeObject.hasCollation; between = !larger; like = true; } if (between == false && larger == false) { return; } Expression eFirst = new Expression(likeObject.getRangeLow(), Type.SQL_VARCHAR); Expression eLast = new Expression(likeObject.getRangeHigh(), Type.SQL_VARCHAR); if (between && !like) { Expression eArgOld = eArg; eArg = new Expression(GREATER_EQUAL, eArgOld, eFirst); eArg2 = new Expression(SMALLER_EQUAL, eArgOld, eLast); exprType = AND; likeObject = null; } else if (between && like) { Expression gte = new Expression(GREATER_EQUAL, eArg, eFirst); Expression lte = new Expression(SMALLER_EQUAL, eArg, eLast); eArg2 = new Expression(eArg, eArg2, likeObject.escapeChar, likeObject.hasCollation); eArg2.likeObject = likeObject; eArg = new Expression(AND, gte, lte); exprType = AND; likeObject = null; } else if (larger) { Expression gte = new Expression(GREATER_EQUAL, eArg, eFirst); eArg2 = new Expression(eArg, eArg2, likeObject.escapeChar, likeObject.hasCollation); eArg2.likeObject = likeObject; eArg = gte; exprType = AND; likeObject = null; } } } void resolveTypesForOverlaps() throws HsqlException { if (eArg.argList[0].isParam) { eArg.argList[0].dataType = eArg2.argList[0].dataType; } if (eArg2.argList[0].isParam) { eArg2.argList[0].dataType = eArg.argList[0].dataType; } if (eArg.argList[0].dataType == null) { eArg.argList[0].dataType = eArg2.argList[0].dataType = Type.SQL_TIMESTAMP; } if (eArg.argList[1].isParam) { eArg.argList[1].dataType = eArg2.argList[0].dataType; } if (eArg2.argList[1].isParam) { eArg2.argList[1].dataType = eArg.argList[0].dataType; } if (!isValidDatetimeRange(eArg.argList) || !isValidDatetimeRange(eArg2.argList)) { Trace.error(Trace.WRONG_DATA_TYPE); } if (!isValidDatetimeRange(eArg.argList[0].dataType, eArg.argList[1].dataType)) { Trace.error(Trace.WRONG_DATA_TYPE); } eArg.argListDataType[0] = eArg.argList[0].dataType; eArg.argListDataType[1] = eArg.argList[1].dataType; eArg2.argListDataType[0] = eArg2.argList[0].dataType; eArg2.argListDataType[1] = eArg2.argList[1].dataType; } boolean isValidDatetimeRange(Expression[] pair) { return isValidDatetimeRange(pair[0].dataType, pair[1].dataType); } boolean isValidDatetimeRange(Type a, Type b) { if (!a.isDateTimeType()) { return false; } if (b.isDateTimeType()) { if ((a.type == Types.SQL_TIME && b.type == Types.SQL_DATE) || (a.type == Types.SQL_DATE && b.type == Types.SQL_TIME)) { return false; } return true; } if (b.isIntervalType()) { return ((DateTimeType) a).canAdd(b); } return false; } void resolveTypesForAllAny() throws HsqlException { eArg2.argListDataType = eArg2.eArg.argListDataType; int degree = eArg.exprType == ROW ? eArg.argList.length : 1; if (degree == 1 && eArg.exprType != ROW) { eArg = new Expression(Expression.ROW, new Expression[]{ eArg }); } if (degree != eArg2.argListDataType.length) { // todo SQL message throw Trace.error(Trace.WRONG_DATA_TYPE); } for (int j = 0; j < degree; j++) { if (eArg.argList[j].isParam) { eArg.argList[j].dataType = eArg2.eArg.argListDataType[j]; } } eArg.argListDataType = new Type[eArg.argList.length]; for (int i = 0; i < eArg.argListDataType.length; i++) { eArg.argListDataType[i] = eArg.argList[i].dataType; } } /** * Parametric or fixed value lists plus queries are handled. * * Empty lists are not allowed. * * Parametric predicand is resolved against the value list and vice versa. */ void resolveTypesForIn() throws HsqlException { int degree = eArg.exprType == ROW ? eArg.argList.length : 1; if (degree == 1 && eArg.exprType != ROW) { eArg = new Expression(Expression.ROW, new Expression[]{ eArg }); } if (eArg2.exprType == TABLE_SUBQUERY) { if (degree != eArg2.argListDataType.length) { // todo SQL message throw Trace.error(Trace.WRONG_DATA_TYPE); } for (int j = 0; j < degree; j++) { if (eArg.argList[j].isParam) { eArg.argList[j].dataType = eArg2.argListDataType[j]; } } eArg2.isCorrelated = eArg2.subQuery.isCorrelated; } else { eArg2.prepareTable(eArg, degree); if (degree != eArg2.argListDataType.length) { // todo SQL message throw Trace.error(Trace.WRONG_DATA_TYPE); } // IN condition optimisation if (eArg2.isCorrelated) { eArg2.subQuery.table = null; } else { eArg2.subQuery.setAsInSubquery(eArg2); } } eArg.argListDataType = new Type[eArg.argList.length]; for (int i = 0; i < eArg.argListDataType.length; i++) { Type type = eArg.argList[i].dataType; if (type == null) { type = eArg2.argListDataType[i]; } if (type == null) { throw Trace.error(Trace.UNRESOLVED_TYPE); } eArg.argListDataType[i] = type; } } void prepareTable(Expression row, int degree) throws HsqlException { if (argListDataType != null) { return; } for (int i = 0; i < argList.length; i++) { Expression e = argList[i]; if (e.exprType == ROW) { if (degree != e.argList.length) { // todo - SQL error message throw Trace.error(Trace.UNEXPECTED_TOKEN); } } else if (degree == 1) { argList[i] = new Expression(Expression.ROW); argList[i].argList = new Expression[]{ e }; } else { // todo - SQL error message throw Trace.error(Trace.UNEXPECTED_TOKEN); } } argListDataType = new Type[degree]; for (int j = 0; j < degree; j++) { Type type = row == null ? null : row.argList[j].dataType; for (int i = 0; i < argList.length; i++) { argList[i].argList[j].resolveTypes(this); type = Type.getAggregatedType(argList[i].argList[j].dataType, type); } if (type == null) { throw Trace.error(Trace.UNRESOLVED_TYPE); } // needs a new variable for row type array argListDataType[j] = type; if (row != null && row.argList[j].isParam) { row.argList[j].dataType = type; } for (int i = 0; i < argList.length; i++) { if (argList[i].argList[j].isParam) { isConstantValueList = false; argList[i].argList[j].dataType = argListDataType[j]; continue; } if (argList[i].argList[j].exprType == VALUE) { if (argList[i].argList[j].valueData == null) { argList[i].argList[j].dataType = argListDataType[j]; } } else { isCorrelated = true; isConstantValueList = false; } } if (argListDataType[j].isCharacterType() && !((CharacterType) argListDataType[j]) .isEqualIdentical()) { isConstantValueList = false; } } } /** * Details of IN condition optimisation for 1.9.0 * Predicates with SELECT are QUERY expressions * * Predicates with IN list * * Parser adds a SubQuery to the list for each predicate * At type resolution IN lists that are entirely fixed constant or parameter * values are selected for possible optimisation. The flags: * * IN expression eArg2.isCorrelated == true if there are non-constant, * non-param expressions in the list (Expressions may have to be resolved * against the full set of columns of the query, so must be re-evaluated * for each row and evaluated after all the joins have been made) * * VALUELIST expression isFixedConstantValueList == true when all * expressions are fixed constant and none is a param. With this flag, * a single-column VALUELIST can be accessed as a HashMap. * * Predicates may be optimised as joins if isCorrelated == false * */ void insertValuesIntoSubqueryTable(Session session) throws HsqlException { Table table = subQuery.table; for (int i = 0; i < argList.length; i++) { Object[] data = (Object[]) argList[i].getRowValue(session); for (int j = 0; j < argListDataType.length; j++) { data[j] = argListDataType[j].convertToType(session, data[j], argList[i].argList[j].dataType); } Row row = table.newRow(data); try { table.indexRow(session, row); } catch (HsqlException e) {} } } /** * Is the argument expression type a comparison expression * * @param i expresion type * * @return boolean */ static boolean isCompare(int i) { switch (i) { case NOT_DISTINCT : case EQUAL : case GREATER_EQUAL : case GREATER : case SMALLER : case SMALLER_EQUAL : case NOT_EQUAL : return true; } return false; } /** * Returns the table name for a column expression as a string * * @return table name */ String getTableName() { if (exprType == ASTERISK) { return tableName; } if (exprType == COLUMN) { if (rangeVariable == null) { return tableName; } else { return rangeVariable.getTable().getName().name; } } // todo return ""; } /** * Returns the HsqlName of the table for a column expression * * @return table name */ public HsqlName getTableHsqlName() { if (rangeVariable == null) { return null; } else { return rangeVariable.getTable().getName(); } } public Table getTable() { if (rangeVariable == null) { return null; } else { return rangeVariable.getTable(); } } public String getTableSchemaName() { if (rangeVariable == null) { return null; } else { return rangeVariable.getTable().getName().schema.name; } } /** * Returns the name of a column as string * * @return column name */ String getColumnName() { if (exprType == COLUMN && column != null) { return column.getName().name; } return getAlias(); } Column getColumn() { return column; } /** * Returns the name of a column as string */ public String getBaseColumnName() { if (exprType == COLUMN && rangeVariable != null) { return rangeVariable.getTable().getColumn( columnIndex).columnName.name; } return null; } /** * Returns the column index in the table */ int getColumnIndex() { return columnIndex; } /** * Returns the column size */ long getColumnSize() { return dataType == null ? 0 : dataType.size(); } /** * Returns the column scale */ int getColumnScale() { return dataType == null ? 0 : dataType.scale(); } /** * Set this as a set function with / without DISTINCT */ void setDistinctAggregate(boolean distinct) { isDistinctAggregate = distinct && (eArg.exprType != ASTERISK); if (exprType == COUNT) { dataType = distinct ? dataType : Type.SQL_INTEGER; } } /** * Returns the data type */ Type getDataType() { return dataType; } /** * Get the value in the given type in the given session context */ Object getConstantValue(Type type) throws HsqlException { Object o = getConstantValue(); if (o == null || dataType == type) { return o; } return type.convertToType(null, o, dataType); } /** * Get the value in the given type in the given session context */ Object getValue(Session session, Type type) throws HsqlException { Object o = getValue(session); if (o == null || dataType == type) { return o; } return type.convertToType(session, o, dataType); } /** * Get the result of a SetFunction or an ordinary value */ public Object getAggregatedValue(Session session, Object currValue) throws HsqlException { boolean compareNulls = false; Object leftValue = null, rightValue = null; switch (aggregateSpec) { case AGGREGATE_SELF : { // handles results of aggregates plus NEGATE and CONVERT switch (exprType) { case COUNT : if (currValue == null) { return INTEGER_0; } return ((SetFunction) currValue).getValue(); case MAX : case MIN : case SUM : case AVG : case EVERY : case SOME : case STDDEV_POP : case STDDEV_SAMP : case VAR_POP : case VAR_SAMP : if (currValue == null) { return null; } return ((SetFunction) currValue).getValue(); } } case AGGREGATE_LEFT : leftValue = eArg.getAggregatedValue(session, currValue == null ? null : ((Object[]) currValue)[0]); if (currValue == null) { rightValue = eArg2 == null ? null : eArg2.getValue(session); } else { rightValue = ((Object[]) currValue)[1]; } break; case AGGREGATE_RIGHT : if (currValue == null) { leftValue = eArg == null ? null : eArg.getValue(session); } else { leftValue = ((Object[]) currValue)[0]; } rightValue = eArg2.getAggregatedValue(session, currValue == null ? null : ((Object[]) currValue)[1]); break; case AGGREGATE_BOTH : if (currValue == null) { currValue = new Object[2]; } leftValue = eArg.getAggregatedValue(session, ((Object[]) currValue)[0]); rightValue = eArg2.getAggregatedValue(session, ((Object[]) currValue)[1]); break; case AGGREGATE_NONE : return currValue; default : return currValue; } // handle other operations switch (exprType) { case ORDER_BY : return leftValue; case NEGATE : return ((NumberType) dataType).negate(leftValue); case CONVERT : return dataType.castToType(session, leftValue, eArg.dataType); case NOT : if (leftValue == null) { return null; } return ((Boolean) leftValue).booleanValue() ? Boolean.FALSE : Boolean.TRUE; case AND : if (Boolean.FALSE.equals(leftValue)) { return Boolean.FALSE; } if (Boolean.FALSE.equals(rightValue)) { return Boolean.FALSE; } if (leftValue == null || rightValue == null) { return null; } return Boolean.TRUE; case OR : if (Boolean.TRUE.equals(leftValue)) { return Boolean.TRUE; } if (Boolean.TRUE.equals(rightValue)) { return Boolean.TRUE; } if (leftValue == null || rightValue == null) { return null; } return Boolean.FALSE; case IS_NULL : return leftValue == null ? Boolean.TRUE : Boolean.FALSE; case LIKE : String s = (String) Type.SQL_VARCHAR.convertToType(session, rightValue, eArg2.dataType); if (eArg2.isParam || eArg2.exprType != VALUE) { likeObject.resetPattern(s); } String c = (String) Type.SQL_VARCHAR.convertToType(session, leftValue, eArg.dataType); return likeObject.compare(session, c); case ALL : case ANY : return null; case IN : return testInCondition(session, (Object[]) leftValue); case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : return testMatchCondition(session, (Object[]) leftValue); case UNIQUE : return eArg.subQuery.hasUniqueNotNullRows(session) ? Boolean.FALSE : Boolean.TRUE; case EXISTS : return eArg.testExistsCondition(session); case CASEWHEN : boolean test = Boolean.TRUE.equals(leftValue); Object result; Type type; if (test) { result = ((Object[]) rightValue)[0]; type = eArg2.eArg.dataType; } else { result = ((Object[]) rightValue)[1]; type = eArg2.eArg2.dataType; } return dataType.convertToType(session, result, type); case ALTERNATIVE : leftValue = dataType.convertToType(session, leftValue, eArg.dataType); rightValue = dataType.convertToType(session, rightValue, eArg2.dataType); Object[] objectPair = new Object[2]; objectPair[0] = leftValue; objectPair[1] = rightValue; return objectPair; case ROW : if (currValue == null) { currValue = new Object[argList.length]; } case FUNCTION : case SQL_FUNCTION : throw Trace.error(Trace.UNSUPPORTED_INTERNAL_OPERATION); case NOT_DISTINCT : compareNulls = true; case EQUAL : case GREATER_EQUAL : case GREATER : case SMALLER : case SMALLER_EQUAL : case NOT_EQUAL : { if (eArg2.exprType == Expression.ANY || eArg2.exprType == Expression.ALL) { return testAnyAllCondition(session, (Object[]) leftValue); } return compareValues(session, leftValue, rightValue); } case ADD : return dataType.add(leftValue, rightValue); case SUBTRACT : return dataType.subtract(leftValue, rightValue); case MULTIPLY : return dataType.multiply(leftValue, rightValue); case DIVIDE : return dataType.divide(leftValue, rightValue); case CONCAT : return dataType.concat(null, leftValue, rightValue); default : throw Trace.error(Trace.NEED_AGGREGATE, this.describe(session)); } } /** * Instantiate the SetFunction or recurse, returning the result */ public Object updateAggregatingValue(Session session, Object currValue) throws HsqlException { switch (aggregateSpec) { case AGGREGATE_SELF : { if (currValue == null) { currValue = new SetFunction(exprType, eArg.dataType, isDistinctAggregate); } Object newValue = eArg.exprType == ASTERISK ? INTEGER_1 : eArg.getValue( session); ((SetFunction) currValue).add(session, newValue); return currValue; } case AGGREGATE_BOTH : { Object[] valuePair = (Object[]) currValue; if (valuePair == null) { valuePair = new Object[2]; } valuePair[0] = eArg.updateAggregatingValue(session, valuePair[0]); valuePair[1] = eArg2.updateAggregatingValue(session, valuePair[1]); return valuePair; } case AGGREGATE_LEFT : { Object[] valuePair = (Object[]) currValue; if (valuePair == null) { valuePair = new Object[2]; } valuePair[0] = eArg.updateAggregatingValue(session, valuePair[0]); if (eArg2 != null) { valuePair[1] = eArg2.getValue(session); } return valuePair; } case AGGREGATE_RIGHT : { Object[] valuePair = (Object[]) currValue; if (valuePair == null) { valuePair = new Object[2]; } if (eArg != null) { valuePair[0] = eArg.getValue(session); } valuePair[1] = eArg2.updateAggregatingValue(session, valuePair[1]); return valuePair; } default : // never gets here return currValue; } } public Object[] getRowValue(Session session) throws HsqlException { switch (exprType) { case ROW : { Object[] data = new Object[argList.length]; for (int i = 0; i < argList.length; i++) { data[i] = argList[i].getValue(session); } return data; } case Expression.TABLE_SUBQUERY : { return subQuery.select.getValues(session); } default : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); } } public Object getConstantValue() throws HsqlException { return getValue(null); } public Object getValue(Session session) throws HsqlException { switch (exprType) { case VALUE : return isParam ? session.compiledStatementExecutor .paramValues[parameterIndex] : valueData; case COLUMN : try { return session.compiledStatementExecutor .rangeIterators[rangeVariable.index] .currentData[columnIndex]; } catch (NullPointerException e) { throw Trace.error(Trace.COLUMN_NOT_FOUND, columnName); } case DEFAULT : return null; case ROW : if (argList.length == 1) { return argList[0].getValue(session); } Object[] row = new Object[argList.length]; for (int i = 0; i < argList.length; i++) { row[i] = argList[i].getValue(session); } return row; case FUNCTION : case SQL_FUNCTION : throw Trace.error(Trace.UNSUPPORTED_INTERNAL_OPERATION); case TABLE_SUBQUERY : Object value = subQuery.getSingleObjectResult(session); return value; case ORDER_BY : return eArg.getValue(session); case NEGATE : return ((NumberType) dataType).negate(eArg.getValue(session, eArg.dataType)); case ALL : case ANY : return null; case IS_NULL : return eArg.getValue(session) == null ? Boolean.TRUE : Boolean.FALSE; case LIKE : String s = (String) eArg2.getValue(session, Type.SQL_VARCHAR); if (eArg2.isParam || eArg2.exprType != VALUE) { likeObject.resetPattern(s); } String c = (String) eArg.getValue(session, Type.SQL_VARCHAR); return likeObject.compare(session, c); case OVERLAPS : Object[] left = eArg.getRowValue(session); Object[] right = eArg2.getRowValue(session); return DateTimeType.overlaps(session, left, eArg.argListDataType, right, eArg2.argListDataType); case IN : return testInCondition(session, eArg.getRowValue(session)); case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : return testMatchCondition(session, eArg.getRowValue(session)); case UNIQUE : return eArg.subQuery.hasUniqueNotNullRows(session) ? Boolean.TRUE : Boolean.FALSE; case EXISTS : return eArg.testExistsCondition(session); case NOT : { Boolean result = (Boolean) eArg.getValue(session); return result == null ? null : result.booleanValue() ? Boolean.FALSE : Boolean.TRUE; } case AND : { Boolean r1 = (Boolean) eArg.getValue(session); if (Boolean.FALSE.equals(r1)) { return Boolean.FALSE; } Boolean r2 = (Boolean) eArg2.getValue(session); if (Boolean.FALSE.equals(r2)) { return Boolean.FALSE; } if (r1 == null || r2 == null) { return null; } return Boolean.TRUE; } case OR : { Boolean r1 = (Boolean) eArg.getValue(session); if (Boolean.TRUE.equals(r1)) { return Boolean.TRUE; } Boolean r2 = (Boolean) eArg2.getValue(session); if (Boolean.TRUE.equals(r2)) { return Boolean.TRUE; } if (r1 == null || r2 == null) { return null; } return Boolean.FALSE; } case CONVERT : return dataType.castToType(session, eArg.getValue(session), eArg.dataType); case CASEWHEN : { Boolean result = (Boolean) eArg.getValue(session); if (Boolean.TRUE.equals(result)) { return eArg2.eArg.getValue(session, dataType); } else { return eArg2.eArg2.getValue(session, dataType); } } // gets here from getAggregatedValue() case ALTERNATIVE : { return new Object[] { eArg.getValue(session, dataType), eArg2.getValue(session, dataType) }; } case NOT_DISTINCT : case EQUAL : case GREATER : case GREATER_EQUAL : case SMALLER_EQUAL : case SMALLER : case NOT_EQUAL : { if (eArg2.exprType == Expression.ANY || eArg2.exprType == Expression.ALL) { return testAnyAllCondition( session, (Object[]) eArg.getRowValue(session)); } Object o1 = eArg.getValue(session); Object o2 = eArg2.getValue(session); return compareValues(session, o1, o2); } case SEQUENCE : NumberSequence sequence = (NumberSequence) valueData; session.getUser().checkAccess(sequence); return sequence.getValueObject(); } Object a = null, b = null; if (eArg != null) { a = eArg.getValue(session); } if (eArg2 != null) { b = eArg2.getValue(session); } switch (exprType) { case ADD : return dataType.add(a, b); case SUBTRACT : return dataType.subtract(a, b); case MULTIPLY : return dataType.multiply(a, b); case DIVIDE : return dataType.divide(a, b); case CONCAT : return dataType.concat(null, a, b); default : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression"); } } boolean testCondition(Session session) throws HsqlException { return Boolean.TRUE.equals(getValue(session)); } /** * For MATCH SIMPLE and FULL expressions, nulls in left are handled * prior to calling this method */ private Boolean compareValues(Session session, Object left, Object right) throws HsqlException { int result = 0; boolean hasNull = false; if (left == null || right == null) { return null; } if (left instanceof Object[]) { Object[] leftList = (Object[]) left; Object[] rightList = (Object[]) right; for (int i = 0; i < eArg.argList.length; i++) { if (leftList[i] == null) { if (exprType == MATCH_PARTIAL || exprType == MATCH_UNIQUE_PARTIAL) { continue; } hasNull = true; } if (rightList[i] == null) { hasNull = true; } Object leftValue = leftList[i]; Object rightValue = rightList[i]; Type[] types = eArg.argListDataType; if (argListOpDataType != null && argListOpDataType[i] != null) { leftValue = argListOpDataType[i].convertToType(session, leftValue, eArg.argListDataType[i]); rightValue = argListOpDataType[i].convertToType(session, rightValue, eArg2.argListDataType[i]); types = argListOpDataType; } result = types[i].compare(leftValue, rightValue); if (result != 0) { break; } } } else { Type type = eArg.dataType; if (opDataType != null) { left = opDataType.convertToType(session, left, eArg.dataType); right = opDataType.convertToType(session, right, eArg2.dataType); } result = type.compare(left, right); } switch (exprType) { case MATCH_SIMPLE : case MATCH_UNIQUE_SIMPLE : case MATCH_PARTIAL : case MATCH_UNIQUE_PARTIAL : case MATCH_FULL : case MATCH_UNIQUE_FULL : case NOT_DISTINCT : return result == 0 ? Boolean.TRUE : Boolean.FALSE; case IN : case EQUAL : if (hasNull) { return null; } return result == 0 ? Boolean.TRUE : Boolean.FALSE; case NOT_EQUAL : if (hasNull) { return null; } return result != 0 ? Boolean.TRUE : Boolean.FALSE; case GREATER : if (hasNull) { return null; } return result > 0 ? Boolean.TRUE : Boolean.FALSE; case GREATER_EQUAL : if (hasNull) { return null; } return result >= 0 ? Boolean.TRUE : Boolean.FALSE; case SMALLER_EQUAL : if (hasNull) { return null; } return result <= 0 ? Boolean.TRUE : Boolean.FALSE; case SMALLER : if (hasNull) { return null; } return result < 0 ? Boolean.TRUE : Boolean.FALSE; default : throw Trace.error(Trace.GENERAL_ERROR, Trace.Expression_compareValues); } } static int countNulls(Object[] a) { int nulls = 0; for (int i = 0; i < a.length; i++) { if (a[i] == null) { nulls++; } } return nulls; } static void convertToType(Session session, Object[] data, Type[] dataType, Type[] newType) throws HsqlException { for (int i = 0; i < data.length; i++) { data[i] = newType[i].convertToType(session, data[i], dataType[i]); } } /** * Returns the result of testing a VALUE_LIST expression */ private Boolean testInCondition(Session session, Object[] data) throws HsqlException { if (data == null) { return null; } if (Expression.countNulls(data) != 0) { return null; } if (eArg2.exprType == TABLE) { final int length = eArg2.argList.length; for (int i = 0; i < length; i++) { Object[] rowData = eArg2.argList[i].getRowValue(session); if (Boolean.TRUE.equals(compareValues(session, data, rowData))) { return Boolean.TRUE; } } return Boolean.FALSE; } else if (eArg2.exprType == TABLE_SUBQUERY) { if (eArg2.subQuery.isCorrelated) { eArg2.subQuery.materialise(session); } convertToType(session, data, eArg.argListDataType, eArg2.argListDataType); RowIterator it = eArg2.subQuery.table.getPrimaryIndex().findFirstRow(session, data); Boolean result = it.hasNext() ? Boolean.TRUE : Boolean.FALSE; eArg2.subQuery.dematerialiseCorrelated(session); return result; } throw Trace.error(Trace.WRONG_DATA_TYPE); } private Boolean testMatchCondition(Session session, Object[] data) throws HsqlException { int nulls; if (data == null) { return Boolean.TRUE; } nulls = countNulls(data); if (nulls != 0) { switch (exprType) { case MATCH_SIMPLE : case MATCH_UNIQUE_SIMPLE : return Boolean.TRUE; case MATCH_PARTIAL : case MATCH_UNIQUE_PARTIAL : if (nulls == data.length) { return Boolean.TRUE; } break; case MATCH_FULL : case MATCH_UNIQUE_FULL : return nulls == data.length ? Boolean.TRUE : Boolean.FALSE; } } if (eArg2.exprType == TABLE) { final int length = eArg2.argList.length; boolean hasMatch = false; for (int i = 0; i < length; i++) { Object[] rowData = eArg2.argList[i].getRowValue(session); Boolean result = compareValues(session, data, rowData); if (result == null || !result.booleanValue()) { continue; } switch (exprType) { case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : return Boolean.TRUE; case MATCH_UNIQUE_SIMPLE : case MATCH_UNIQUE_PARTIAL : case MATCH_UNIQUE_FULL : if (hasMatch) { return Boolean.FALSE; } hasMatch = true; } } return hasMatch ? Boolean.TRUE : Boolean.FALSE; } else if (eArg2.exprType == TABLE_SUBQUERY) { if (eArg2.subQuery.isCorrelated) { eArg2.subQuery.materialise(session); } convertToType(session, data, eArg.argListDataType, eArg2.argListDataType); if (nulls != 0 && (exprType == MATCH_PARTIAL || exprType == MATCH_UNIQUE_PARTIAL)) { boolean hasMatch = false; RowIterator it = eArg2.subQuery.table.getPrimaryIndex().firstRow(session); while (it.hasNext()) { Object[] rowData = it.getNext().getData(); Boolean result = compareValues(session, data, rowData); if (result.booleanValue()) { if (exprType == MATCH_PARTIAL) { return Boolean.TRUE; } if (hasMatch) { return Boolean.FALSE; } hasMatch = true; } } return hasMatch ? Boolean.TRUE : Boolean.FALSE; } RowIterator it = eArg2.subQuery.table.getPrimaryIndex().findFirstRow(session, data); boolean result = it.hasNext(); if (!result) { eArg2.subQuery.dematerialiseCorrelated(session); return Boolean.FALSE; } switch (exprType) { case MATCH_SIMPLE : case MATCH_PARTIAL : case MATCH_FULL : eArg2.subQuery.dematerialiseCorrelated(session); return Boolean.TRUE; } it.getNext(); result = it.hasNext(); if (!result) { eArg2.subQuery.dematerialiseCorrelated(session); return Boolean.TRUE; } Object[] rowData = it.getNext().getData(); Boolean returnValue = Boolean.TRUE.equals(compareValues(session, data, rowData)) ? Boolean.FALSE : Boolean.TRUE; eArg2.subQuery.dematerialiseCorrelated(session); return returnValue; } throw Trace.error(Trace.WRONG_DATA_TYPE); } private Boolean testExistsCondition(Session session) throws HsqlException { if (!subQuery.isCorrelated) { return subQuery.table.isEmpty(session) ? Boolean.FALSE : Boolean.TRUE; } else { Result r = subQuery.select.getResult(session, 1); // 1 is already enough return r.getNavigator().isEmpty() ? Boolean.FALSE : Boolean.TRUE; } } private Boolean testAnyAllCondition(Session session, Object[] o) throws HsqlException { if (o == null) { return null; } SubQuery subquery = eArg2.eArg.subQuery; boolean populate = subquery.isCorrelated; if (populate) { subquery.materialise(session); } Boolean result = getAnyAllValue(session, o, subquery); subquery.dematerialiseCorrelated(session); return result; } private Boolean getAnyAllValue(Session session, Object[] data, SubQuery subquery) throws HsqlException { boolean empty = subquery.table.isEmpty(session); Index index = subquery.table.getPrimaryIndex(); RowIterator it = index.findFirstRowNotNull(session); Row firstrow = it.getNext(); if (data[0] == null) { return null; } switch (eArg2.exprType) { case ANY : { if (empty) { return Boolean.FALSE; } if (firstrow == null) { return null; } int range = eArg2.eArg.argListDataType[0].compareToTypeRange(data[0]); if (range != 0) { switch (exprType) { case EQUAL : return Boolean.FALSE; case NOT_EQUAL : return Boolean.TRUE; case GREATER : case GREATER_EQUAL : return range > 0 ? Boolean.TRUE : Boolean.FALSE; case SMALLER_EQUAL : case SMALLER : return range < 0 ? Boolean.TRUE : Boolean.FALSE; } } convertToType(session, data, eArg.argListDataType, eArg2.eArg.argListDataType); if (exprType == EQUAL) { it = index.findFirstRow(session, data); return it.hasNext() ? Boolean.TRUE : Boolean.FALSE; } Row lastrow = index.lastRow(session); Object[] firstdata = firstrow.getData(); Object[] lastdata = lastrow.getData(); Boolean comparefirst = compareValues(session, data, firstdata); Boolean comparelast = compareValues(session, data, lastdata); switch (exprType) { case NOT_EQUAL : return comparefirst.booleanValue() && comparelast.booleanValue() ? Boolean.FALSE : Boolean.TRUE; case GREATER : return comparefirst; case GREATER_EQUAL : return comparefirst; case SMALLER : return comparelast; case SMALLER_EQUAL : return comparelast; } break; } case ALL : { if (empty) { return Boolean.TRUE; } if (firstrow == null) { return null; } int range = eArg2.eArg.argListDataType[0].compareToTypeRange(data[0]); if (range != 0) { switch (exprType) { case EQUAL : return Boolean.FALSE; case NOT_EQUAL : return Boolean.TRUE; case GREATER : case GREATER_EQUAL : return range > 0 ? Boolean.TRUE : Boolean.FALSE; case SMALLER_EQUAL : case SMALLER : return range < 0 ? Boolean.TRUE : Boolean.FALSE; } } convertToType(session, data, eArg.argListDataType, eArg2.eArg.argListDataType); if (exprType == EQUAL || exprType == NOT_EQUAL) { it = index.findFirstRow(session, data); if (exprType == EQUAL) { return (it.hasNext() && subquery.table.getRowCount(session) == 1) ? Boolean.TRUE : Boolean.FALSE; } return (it.hasNext()) ? Boolean.FALSE : Boolean.TRUE; } Row lastrow = index.lastRow(session); Object[] firstdata = firstrow.getData(); Object[] lastdata = lastrow.getData(); Boolean comparefirst = compareValues(session, data, firstdata); Boolean comparelast = compareValues(session, data, lastdata); switch (exprType) { case NOT_EQUAL : return comparefirst.booleanValue() && comparelast.booleanValue() ? Boolean.TRUE : Boolean.FALSE; case GREATER : return comparelast; case GREATER_EQUAL : return comparelast; case SMALLER : return comparefirst; case SMALLER_EQUAL : return comparefirst; } break; } } return null; } /** * Returns a Select object that can be used for checking the contents * of an existing table against the given CHECK search condition. */ static Select getCheckSelect(Session session, Table t, Expression e) throws HsqlException { CompileContext compileContext = new CompileContext(); Select s = new Select(compileContext, true); s.exprColumns = new Expression[1]; s.exprColumns[0] = EXPR_TRUE; RangeVariable range = new RangeVariable(t, null, null, compileContext); s.rangeVariables = new RangeVariable[]{ range }; OrderedHashSet set = e.resolveColumnReferences(s.rangeVariables, null); Expression.checkColumnsResolved(set); e.resolveTypes(null); if (Types.SQL_BOOLEAN != e.getDataType().type) { throw Trace.error(Trace.NOT_A_CONDITION); } Expression condition = new Expression(NOT, e); s.queryCondition = condition; s.resolveTypesAndPrepare(); return s; } /** * Sets the left leaf. */ void setLeftExpression(Expression e) { eArg = e; } void setRightExpression(Expression e) { eArg2 = e; } /** * Gets the right leaf. */ Expression getRightExpression() { return eArg2; } boolean isParam() { return isParam; } boolean isFixedConstant() { switch (exprType) { case VALUE : return !isParam; case NEGATE : return eArg.isFixedConstant(); case ADD : case SUBTRACT : case MULTIPLY : case DIVIDE : case CONCAT : return eArg.isFixedConstant() && eArg2.isFixedConstant(); } return false; } boolean isFixedConditional() { switch (exprType) { case VALUE : return !isParam && dataType.type == Types.SQL_BOOLEAN; case NOT_DISTINCT : case EQUAL : case GREATER_EQUAL : case GREATER : case SMALLER : case SMALLER_EQUAL : case NOT_EQUAL : case LIKE : //case IN : todo return eArg.isFixedConstant() && eArg2.isFixedConstant(); case IS_NULL : return eArg.isFixedConstant(); case NOT : return eArg.isFixedConditional(); case AND : case OR : return eArg.isFixedConditional() && eArg2.isFixedConditional(); default : return false; } } void setTableColumnTypeAttributes(Expression e) { dataType = e.dataType; isIdentity = e.isIdentity; nullability = e.nullability; isWritable = e.isWritable; schema = e.schema; } void setAttributesAsColumn(Table t, int i) { columnIndex = i; column = t.getColumn(i); dataType = column.getType(); isWritable = t.isWritable(); isIdentity = column.isIdentity(); nullability = column.isNullable() && !column.isPrimaryKey() ? NULLABLE : NO_NULLS; columnName = column.columnName.name; columnQuoted = column.columnName.isNameQuoted; tableName = t.getName().name; schema = t.getSchemaName().name; isIndexed = t.isIndexed(i); } String getValueClassName() { // boucherb@users 20050516 - patch 1.8.0 removed DITypeInfo dependency if (valueClassName == null) { Type type = dataType == null ? NullType.getNullType() : dataType; return type.getJDBCClassName(); } return valueClassName; } public void collectAllBaseColumnExpressions(OrderedHashSet set) { Expression.collectAllExpressions(set, this, Expression.COLUMN); Iterator iterator = set.iterator(); // calculate distinct column references while (iterator.hasNext()) { Expression expression = (Expression) iterator.next(); if (expression.rangeVariable == null) { iterator.remove(); continue; } Table table = expression.rangeVariable.getTable(); if (table == null || table.getTableType() == Table.SYSTEM_SUBQUERY) { iterator.remove(); } } } public void collectAllFunctionExpressions(HashSet set) { Expression.collectAllExpressions(set, this, Expression.FUNCTION); } // parameter modes public static final int PARAM_UNKNOWN = 0; // java.sql.ParameterMetaData.parameterModeUnknown public static final int PARAM_IN = 1; // java.sql.ParameterMetaData.parameterModeIn public static final int PARAM_IN_OUT = 2; // java.sql.ParameterMetaData.parameterModeInOut public static final int PARAM_OUT = 4; // java.sql.ParameterMetaData.parameterModeOut // result set (output column value) or parameter expression nullability static final byte NO_NULLS = 0; // java.sql.ResultSetMetaData.columnNoNulls static final byte NULLABLE = 1; // java.sql.ResultSetMetaData.columnNullable static final byte NULLABLE_UNKNOWN = 2; // java.sql.ResultSetMetaData.columnNullableUnknown // output column and parameter expression metadata values boolean isIdentity; // = false byte nullability = NULLABLE_UNKNOWN; boolean isWritable; // = false; true if column of writable table byte paramMode = PARAM_UNKNOWN; String valueClassName; // = null // boucherb@users 20040111 - patch 1.7.2 RC1 - metadata xxxusage support //------------------------------------------------------------------- // TODO: Maybe provide an interface or abstract class + a default // implementation instead? This would allow a more powerful system // of collectors to be created, for example to assist in the optimization // of condition expression trees: // // HashSet joins = new JoinConditionCollector(); // joins.addAll(select.whereCondition); // for(Iterator it = joins.iterator(); it.hasNext();) { // process((it.next()); // } static void collectAllExpressions(HashSet set, Select select, int type) { for (; select != null; select = select.unionSelect) { Expression[] list = select.exprColumns; for (int i = 0; i < select.orderByLimitIndex; i++) { collectAllExpressions(set, list[i], type); } collectAllExpressions(set, select.queryCondition, type); collectAllExpressions(set, select.havingCondition, type); } } static void collectAllExpressions(HashSet set, Expression e, int type) { Expression[] list; if (e == null) { return; } collectAllExpressions(set, e.getArg(), type); collectAllExpressions(set, e.getArg2(), type); if (e.exprType == type) { set.add(e); } if (e.subQuery != null) { collectAllExpressions(set, e.subQuery.select, type); } if (e.exprType == FUNCTION || e.exprType == SQL_FUNCTION) { if (e.argList != null) { for (int i = 0; i < e.argList.length; i++) { collectAllExpressions(set, e.argList[i], type); } } } list = e.argList; if (list != null) { for (int i = 0; i < list.length; i++) { collectAllExpressions(set, list[i], type); } } } /** * collect all extrassions of a set of expression types appearing anywhere * in a select statement and its subselects, etc. */ static void collectAllExpressions(HashSet set, Select select, OrderedIntHashSet typeSet, OrderedIntHashSet stopAtTypeSet) { for (; select != null; select = select.unionSelect) { Expression[] list = select.exprColumns; for (int i = 0; i < select.orderByLimitIndex; i++) { collectAllExpressions(set, list[i], typeSet, stopAtTypeSet); } collectAllExpressions(set, select.queryCondition, typeSet, stopAtTypeSet); collectAllExpressions(set, select.havingCondition, typeSet, stopAtTypeSet); // todo order by columns } } static void collectAllExpressions(HashSet set, Expression e, OrderedIntHashSet typeSet, OrderedIntHashSet stopAtTypeSet) { Expression[] list; if (e == null) { return; } if (stopAtTypeSet.contains(e.exprType)) { return; } collectAllExpressions(set, e.getArg(), typeSet, stopAtTypeSet); collectAllExpressions(set, e.getArg2(), typeSet, stopAtTypeSet); if (typeSet.contains(e.exprType)) { set.add(e); } if (e.subQuery != null) { collectAllExpressions(set, e.subQuery.select, typeSet, stopAtTypeSet); } if (e.exprType == FUNCTION || e.exprType == SQL_FUNCTION) { if (e.argList != null) { for (int i = 0; i < e.argList.length; i++) { collectAllExpressions(set, e.argList[i], typeSet, stopAtTypeSet); } } } list = e.argList; if (list != null) { for (int i = 0; i < list.length; i++) { collectAllExpressions(set, list[i], typeSet, stopAtTypeSet); } } } /** * Returns the schema name for a column expression as a string */ String getSchemaName() { return schema; } /** * isCorrelated */ public boolean isCorrelated() { if (exprType == TABLE_SUBQUERY && subQuery != null && subQuery.isCorrelated) { return true; } return false; } /** * checkValidCheckConstraint */ public void checkValidCheckConstraint() throws HsqlException { HashSet set = new HashSet(); Expression.collectAllExpressions(set, this, Expression.TABLE_SUBQUERY); if (!set.isEmpty()) { throw Trace.error(Trace.OPERATION_NOT_SUPPORTED, "CHECK CONSTRAINT"); } } static void checkColumnsResolved(OrderedHashSet set) throws HsqlException { if (set != null && !set.isEmpty()) { Expression e = (Expression) set.get(0); StringBuffer sb = new StringBuffer(); if (e.getSchemaName() != null) { sb.append(e.getSchemaName() + '.'); } if (e.getTableName() != null) { sb.append(e.getTableName() + '.'); } throw Trace.error(Trace.COLUMN_NOT_FOUND, sb.toString() + e.getColumnName()); } } /** * Converts an OR containing an AND to an AND */ void distributeOr() { if (exprType != Expression.OR) { return; } if (eArg.exprType == Expression.AND) { exprType = Expression.AND; Expression temp = new Expression(Expression.OR, eArg.eArg2, eArg2); eArg.exprType = Expression.OR; eArg.eArg2 = eArg2; eArg2 = temp; } else if (eArg2.exprType == Expression.AND) { Expression temp = eArg; eArg = eArg2; eArg2 = temp; distributeOr(); return; } eArg.distributeOr(); eArg2.distributeOr(); } /** * */ Expression getIndexableExpression(RangeVariable rangeVar) throws HsqlException { switch (exprType) { case Expression.IN : if (eArg2.isCorrelated) { return null; } return eArg.argList[0].exprType == COLUMN && eArg.argList[0].rangeVariable == rangeVar ? this : null; case Expression.IS_NULL : return eArg.exprType == COLUMN && eArg.rangeVariable == rangeVar ? this : null; case Expression.EQUAL : case Expression.GREATER : case Expression.GREATER_EQUAL : case Expression.SMALLER : case Expression.SMALLER_EQUAL : reorderComparison(); if (eArg.exprType == COLUMN && eArg.rangeVariable == rangeVar) { if (eArg2.hasReference(rangeVar)) { return null; } return this; } if (eArg2.exprType == COLUMN && eArg2.rangeVariable == rangeVar) { swapCondition(); if (eArg2.hasReference(rangeVar)) { return null; } return this; } default : return null; } } /** * Called only on comparison expressions after reordering which have * a COLUMN left leaf */ boolean isSimpleBound() { if (exprType == Expression.IS_NULL) { return true; } if (eArg2 != null) { if (eArg2.exprType == Expression.VALUE) { return true; } if (eArg2.exprType == Expression.SQL_FUNCTION) { if (((SQLFunction) eArg2).isValueFunction()) { return true; } } } return false; } /** * Swap the condition with its complement */ void swapCondition() { int i = EQUAL; switch (exprType) { case GREATER_EQUAL : i = SMALLER_EQUAL; break; case SMALLER_EQUAL : i = GREATER_EQUAL; break; case SMALLER : i = GREATER; break; case GREATER : i = SMALLER; break; case NOT_DISTINCT : i = NOT_DISTINCT; break; case EQUAL : break; default : throw Trace.runtimeError(Trace.UNSUPPORTED_INTERNAL_OPERATION, "Expression.swapCondition"); } exprType = i; Expression e = eArg; eArg = eArg2; eArg2 = e; } boolean reorderComparison() throws HsqlException { Expression colExpression = null; Expression nonColExpression = null; boolean left = false; boolean replaceColumn = false; int operation = 0; if (eArg.exprType == Expression.ADD) { operation = Expression.SUBTRACT; left = true; } else if (eArg.exprType == Expression.SUBTRACT) { operation = Expression.ADD; left = true; } else if (eArg2.exprType == Expression.ADD) { operation = Expression.SUBTRACT; } else if (eArg2.exprType == Expression.SUBTRACT) { operation = Expression.ADD; } if (operation == 0) { return false; } if (left) { if (eArg.eArg.exprType == Expression.COLUMN) { colExpression = eArg.eArg; nonColExpression = eArg.eArg2; } else if (eArg.eArg2.exprType == Expression.COLUMN) { replaceColumn = operation == Expression.ADD; colExpression = eArg.eArg2; nonColExpression = eArg.eArg; } } else { if (eArg2.eArg.exprType == Expression.COLUMN) { colExpression = eArg2.eArg; nonColExpression = eArg2.eArg2; } else if (eArg2.eArg2.exprType == Expression.COLUMN) { replaceColumn = operation == Expression.ADD; colExpression = eArg2.eArg2; nonColExpression = eArg2.eArg; } } if (colExpression == null) { return false; } Expression otherExpression = left ? eArg2 : eArg; Expression newArg = null; if (!replaceColumn) { newArg = new Expression(operation, otherExpression, nonColExpression); newArg.resolveTypesForArithmetic(); } if (left) { if (replaceColumn) { eArg2 = colExpression; eArg.eArg2 = otherExpression; eArg.resolveTypesForArithmetic(); } else { eArg = colExpression; eArg2 = newArg; } } else { if (replaceColumn) { eArg = colExpression; eArg2.eArg2 = otherExpression; eArg2.resolveTypesForArithmetic(); } else { eArg2 = colExpression; eArg = newArg; } } return true; } }

The table below shows all metrics for Expression.java.

MetricValueDescription
BLOCKS608.00Number of blocks
BLOCK_COMMENT69.00Number of block comment lines
COMMENTS478.00Comment lines
COMMENT_DENSITY 0.17Comment density
COMPARISONS543.00Number of comparison operators
CYCLOMATIC1201.00Cyclomatic complexity
DECL_COMMENTS119.00Comments in declarations
DOC_COMMENT286.00Number of javadoc comment lines
ELOC2810.00Effective lines of code
EXEC_COMMENTS59.00Comments in executable code
EXITS188.00Procedure exits
FUNCTIONS126.00Number of function declarations
HALSTEAD_DIFFICULTY215.63Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY787.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 0.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 1.00JAVA0008 Empty catch block
JAVA0009 0.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 0.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 0.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA003114.00JAVA0031 Case statement not properly closed
JAVA003218.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA0034 0.00JAVA0034 Missing braces in if statement
JAVA0035 0.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 5.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 0.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA0049 2.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 8.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 0.00JAVA0067 Array descriptor on identifier name
JAVA0068 0.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 0.00JAVA0075 Method parameter hides field
JAVA0076 2.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 2.00JAVA0081 Boolean literal in comparison
JAVA0082 2.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 1.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103 0.00JAVA0103 Explicit call to finalize()
JAVA0104 0.00JAVA0104 finalize() only calls super.finalize()
JAVA0105 0.00JAVA0105 Duplicate import declaration
JAVA0106 0.00JAVA0106 Unnecessary import from current package
JAVA010879.00JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0109 0.00JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA011034.00JAVA0110 Incorrect javadoc: no @return tag
JAVA0111 0.00JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0112 0.00JAVA0112 Incorrect javadoc: no exception 'exception' in throws
JAVA0113 1.00JAVA0113 Incorrect javadoc: no @author tag
JAVA0114 1.00JAVA0114 Incorrect javadoc: no @version tag
JAVA011514.00JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA011642.00JAVA0116 Missing javadoc: field 'field'
JAVA011710.00JAVA0117 Missing javadoc: method 'method'
JAVA0118 0.00JAVA0118 Missing javadoc: type 'type'
JAVA0119 0.00JAVA0119 Control variable changed within body of for loop
JAVA0123 2.00JAVA0123 Use all three components of for loop
JAVA0125 0.00JAVA0125 Continue statement with label
JAVA0126 0.00JAVA0126 Method declares unchecked exception in throws
JAVA0128 0.00JAVA0128 Public constructor in non-public class
JAVA0130 1.00JAVA0130 Non-static method does not use instance fields
JAVA0131 0.00JAVA0131 Compatible method does not override base
JAVA0132 1.00JAVA0132 Method overload with compatible signature
JAVA0133 0.00JAVA0133 Non-synchronized method overrides synchronized method
JAVA0135 1.00JAVA0135 Only one of Object.equals and Object.hashCode defined: missing 'method'
JAVA0136 1.00JAVA0136 N methods defined in class (maximum: M)
JAVA0137 0.00JAVA0137 Non-abstract class missing constructor
JAVA0138 0.00JAVA0138 N parameters defined for method (maximum: M)
JAVA0139 0.00JAVA0139 Definition of main other than public static void main(java.lang.String[])
JAVA0141 0.00JAVA0141 Unnecessary modifier for method in interface
JAVA0143 0.00JAVA0143 Synchronized method
JAVA0144 0.00JAVA0144 Line exceeds maximum M characters
JAVA0145 0.00JAVA0145 Tab character used in source file
JAVA0150 0.00JAVA0150 java.lang.Error (or subclass) thrown
JAVA0153 0.00JAVA0153 Inefficient conversion of integer to string
JAVA0159 0.00JAVA0159 Inefficient conversion of string to integer
JAVA0160 1.00JAVA0160 Method does not throw specified exception
JAVA0161 0.00JAVA0161 Conditional wait() not in loop
JAVA0163 0.00JAVA0163 Empty statement
JAVA0165 0.00JAVA0165 Conflicting return statement in finally block
JAVA0166 0.00JAVA0166 Generic exception caught
JAVA0167 0.00JAVA0167 ThreadDeath not rethrown
JAVA0169 0.00JAVA0169 Unnecessary catch block: exception 'exception'
JAVA0170 0.00JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0171 0.00JAVA0171 Unused local variable
JAVA0173 4.00JAVA0173 Unused method parameter
JAVA0174 2.00JAVA0174 Assigned local variable never used
JAVA0175 0.00JAVA0175 Successive assignment to variable
JAVA0176 0.00JAVA0176 Local variable name does not have required form
JAVA0177 6.00JAVA0177 Variable declaration missing initializer
JAVA0179 0.00JAVA0179 Local variable hides visible field
JAVA0233 0.00JAVA0233 Definition of serialVersionUID other than 'private static final long serialVersionUID'
JAVA0234 0.00JAVA0234 Class is Serializable but does not define serialVersionUID
JAVA0235 0.00JAVA0235 Class defines serialVersionUID but does not implement Serializable
JAVA0236 0.00JAVA0236 Attempt to clone an object which does not implement Cloneable
JAVA0237 0.00JAVA0237 Class implements Cloneable but does not have public clone method
JAVA0238 0.00JAVA0238 Clone method does not call super.clone()
JAVA0239 0.00JAVA0239 Class declares 'readObject' or 'writeObject' but does not implement Serializable
JAVA0240 0.00JAVA0240 Serializable class which declares readObject or writeObject but not both
JAVA0241 0.00JAVA0241 'readObject' or 'writeObject' should be declared private in Serializable class
JAVA0242 0.00JAVA0242 Transient field in non-Serializable class
JAVA0243 0.00JAVA0243 'readResolve' or 'writeReplace' should be declared private or protected
JAVA0244 0.00JAVA0244 Field or method name in subclass differs only by case from inherited field or method
JAVA0245 0.00JAVA0245 JUnit TestCase with non-trivial constructor
JAVA0246 0.00JAVA0246 JUnit assertXXX statement missing message parameter
JAVA0247 0.00JAVA0247 JUnit 'setUp()' and 'tearDown()' should call super method
JAVA0248 0.00JAVA0248 JUnit method 'setUp' or 'tearDown' with incorrect signature
JAVA0249 0.00JAVA0249 JUnit TestCase 'suite()' should be declared static
JAVA0250 0.00JAVA0250 JUnit TestCase declares testXXX method with incorrect signature
JAVA0251 0.00JAVA0251 Use '%n' for line breaks in printf/format for platform independence
JAVA0252 0.00JAVA0252 'enum' is a Java 1.5 reserved word
JAVA0253 0.00JAVA0253 Not all enum constants consumed in switch statement
JAVA0254 0.00JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0255 0.00JAVA0255 Result of method invocation not used
JAVA0256 1.00JAVA0256 Assignment of external collection/array to field
JAVA0257 0.00JAVA0257 Use of 'Constant Interface' anti-pattern
JAVA0258 0.00JAVA0258 Implement Iterable for foreach compatibility
JAVA0259 0.00JAVA0259 Return of collection/array field
JAVA0260 0.00JAVA0260 Use 'enum' instead of Enumerated Type pattern
JAVA0261 0.00JAVA0261 Use specialized Enum collection types
JAVA0262 0.00JAVA0262 Use of char in integer context
JAVA0263 0.00JAVA0263 Long literal ends with 'l' instead of 'L'
JAVA0264 0.00JAVA0264 Integer math in long context - check for overflow
JAVA0265 0.00JAVA0265 Use of Throwable.printStackTrace()
JAVA0266 0.00JAVA0266 Use of System.out
JAVA0267 0.00JAVA0267 Use of System.err
JAVA0269 0.00JAVA0269 Contents of StringBuffer never used
JAVA027018.00JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0271 0.00JAVA0271 Minimize use of on-demand (.*) static imports
JAVA0272 0.00JAVA0272 Thread.run() called
JAVA0273 0.00JAVA0273 Non-final derivative of Thread calls start() in constructor
JAVA0274 0.00JAVA0274 Serializable class has a synchronized readObject()
JAVA0275 0.00JAVA0275 Serializable class has a synchronized writeObject() and no other synchronized methods
JAVA0276 0.00JAVA0276 Unnecessary use of String constructor
JAVA0277 0.00JAVA0277 Iterator.next() implementation does not throw NoSuchElementException
JAVA0278 0.00JAVA0278 Unnecessary use of Boolean constructor
JAVA0279 0.00JAVA0279 Serialization method readObject or readObjectNoData calls an overridable method
JAVA0280 0.00JAVA0280 IllegalMonitorStateException caught
JAVA0281 0.00JAVA0281 Iterator.next() not called in loop
JAVA0282 0.00JAVA0282 Call to Iterator.next() in loop which does not test Iterator.hasNext()
JAVA0283 0.00JAVA0283 Control variable not updated in loop body
JAVA0284 0.00JAVA0284 Explicit garbage collection
JAVA0285 0.00JAVA0285 Dereference of potentially null variable
JAVA0286 0.00JAVA0286 Dereference of null variable
JAVA0287 0.00JAVA0287 Unnecessary null check
JAVA0288 0.00JAVA0288 Inconsistent null check
LINES4934.00Number of lines in the source file
LINE_COMMENT123.00Number of line comments
LOC3390.00Lines of code
LOGICAL_LINES1490.00Number of statements
LOOPS55.00Number of loops
NEST_DEPTH 6.00Maximum nesting depth
OPERANDS6900.00Number of operands
OPERATORS13055.00Number of operators
PARAMS144.00Number of formal parameter declarations
PROGRAM_LENGTH19955.00Halstead program length
PROGRAM_VOCAB1020.00Halstead program vocabulary
PROGRAM_VOLUME 0.00Halstead program volume
RETURNS643.00Number of return points from functions
SIZE147385.00Size of the file in bytes
UNIQUE_OPERANDS960.00Number of unique operands
UNIQUE_OPERATORS60.00Number of unique operators
WHITESPACE1066.00Number of whitespace lines