/* Copyright (c) 2001-2010, 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.HsqlNameManager.SimpleName; import org.hsqldb.ParserDQL.CompileContext; import org.hsqldb.error.Error; import org.hsqldb.error.ErrorCode; import org.hsqldb.lib.ArrayListIdentity; import org.hsqldb.lib.HsqlArrayList; import org.hsqldb.lib.HsqlList; import org.hsqldb.lib.OrderedHashSet; import org.hsqldb.lib.OrderedIntHashSet; import org.hsqldb.lib.Set; import org.hsqldb.navigator.RowSetNavigatorData; import org.hsqldb.persist.PersistentStore; import org.hsqldb.result.Result; import org.hsqldb.types.ArrayType; import org.hsqldb.types.CharacterType; import org.hsqldb.types.NullType; import org.hsqldb.types.Type; /** * Expression class. * * @author Campbell Boucher-Burnett (boucherb@users dot sourceforge.net) * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 1.9.0 * @since 1.9.0 */ public class Expression implements Cloneable { public static final int LEFT = 0; public static final int RIGHT = 1; public static final int UNARY = 1; public static final int BINARY = 2; // // static final Expression[] emptyArray = new Expression[]{}; // static final Expression EXPR_TRUE = new ExpressionLogical(true); static final Expression EXPR_FALSE = new ExpressionLogical(false); // static final OrderedIntHashSet aggregateFunctionSet = new OrderedIntHashSet(); static { aggregateFunctionSet.add(OpTypes.COUNT); aggregateFunctionSet.add(OpTypes.SUM); aggregateFunctionSet.add(OpTypes.MIN); aggregateFunctionSet.add(OpTypes.MAX); aggregateFunctionSet.add(OpTypes.AVG); aggregateFunctionSet.add(OpTypes.EVERY); aggregateFunctionSet.add(OpTypes.SOME); aggregateFunctionSet.add(OpTypes.STDDEV_POP); aggregateFunctionSet.add(OpTypes.STDDEV_SAMP); aggregateFunctionSet.add(OpTypes.VAR_POP); aggregateFunctionSet.add(OpTypes.VAR_SAMP); aggregateFunctionSet.add(OpTypes.USER_AGGREGATE); } static final OrderedIntHashSet columnExpressionSet = new OrderedIntHashSet(); static { columnExpressionSet.add(OpTypes.COLUMN); } static final OrderedIntHashSet subqueryExpressionSet = new OrderedIntHashSet(); static { subqueryExpressionSet.add(OpTypes.ROW_SUBQUERY); subqueryExpressionSet.add(OpTypes.TABLE_SUBQUERY); } static final OrderedIntHashSet subqueryAggregateExpressionSet = new OrderedIntHashSet(); static { subqueryAggregateExpressionSet.add(OpTypes.COUNT); subqueryAggregateExpressionSet.add(OpTypes.SUM); subqueryAggregateExpressionSet.add(OpTypes.MIN); subqueryAggregateExpressionSet.add(OpTypes.MAX); subqueryAggregateExpressionSet.add(OpTypes.AVG); subqueryAggregateExpressionSet.add(OpTypes.EVERY); subqueryAggregateExpressionSet.add(OpTypes.SOME); subqueryAggregateExpressionSet.add(OpTypes.STDDEV_POP); subqueryAggregateExpressionSet.add(OpTypes.STDDEV_SAMP); subqueryAggregateExpressionSet.add(OpTypes.VAR_POP); subqueryAggregateExpressionSet.add(OpTypes.VAR_SAMP); subqueryAggregateExpressionSet.add(OpTypes.USER_AGGREGATE); // subqueryAggregateExpressionSet.add(OpTypes.TABLE_SUBQUERY); subqueryAggregateExpressionSet.add(OpTypes.ROW_SUBQUERY); } static final OrderedIntHashSet functionExpressionSet = new OrderedIntHashSet(); static { functionExpressionSet.add(OpTypes.SQL_FUNCTION); functionExpressionSet.add(OpTypes.FUNCTION); } static final OrderedIntHashSet emptyExpressionSet = new OrderedIntHashSet(); // type protected int opType; // type qualifier protected int exprSubType; // SimpleName alias; // aggregate private boolean isAggregate; // VALUE protected Object valueData; protected Expression[] nodes; Type[] nodeDataTypes; // QUERY - in single value selects, IN, EXISTS etc. SubQuery subQuery; // for query and value lists, etc boolean isCorrelated; // for COLUMN int columnIndex = -1; // data type protected Type dataType; // int queryTableColumnIndex = -1; // >= 0 when it is used for order by // index of a session-dependent field int parameterIndex = -1; // int rangePosition = -1; // boolean isColumnEqual; Expression(int type) { opType = type; nodes = emptyArray; } // IN condition optimisation /** * Creates a SUBQUERY expression. */ Expression(int type, SubQuery sq) { switch (type) { case OpTypes.ARRAY : opType = OpTypes.ARRAY; break; case OpTypes.ARRAY_SUBQUERY : opType = OpTypes.ARRAY_SUBQUERY; break; case OpTypes.TABLE_SUBQUERY : opType = OpTypes.TABLE_SUBQUERY; break; case OpTypes.ROW_SUBQUERY : case OpTypes.SCALAR_SUBQUERY : opType = OpTypes.ROW_SUBQUERY; break; default : throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } nodes = emptyArray; subQuery = sq; } /** * ROW, ARRAY etc. */ Expression(int type, Expression[] list) { this(type); this.nodes = list; } static String getContextSQL(Expression expression) { if (expression == null) { return null; } String ddl = expression.getSQL(); switch (expression.opType) { case OpTypes.VALUE : case OpTypes.COLUMN : case OpTypes.ROW : case OpTypes.FUNCTION : case OpTypes.SQL_FUNCTION : case OpTypes.ALTERNATIVE : case OpTypes.CASEWHEN : case OpTypes.CAST : return ddl; } StringBuffer sb = new StringBuffer(); ddl = sb.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 getSQL() { StringBuffer sb = new StringBuffer(64); switch (opType) { case OpTypes.VALUE : if (valueData == null) { return Tokens.T_NULL; } return dataType.convertToSQLString(valueData); case OpTypes.ROW : sb.append('('); for (int i = 0; i < nodes.length; i++) { if (i > 0) { sb.append(','); } sb.append(nodes[i].getSQL()); } sb.append(')'); return sb.toString(); // case OpTypes.TABLE : for (int i = 0; i < nodes.length; i++) { if (i > 0) { sb.append(','); } sb.append(nodes[i].getSQL()); } return sb.toString(); } switch (opType) { case OpTypes.ARRAY : sb.append(Tokens.T_ARRAY).append('['); for (int i = 0; i < nodes.length; i++) { if (i > 0) { sb.append(','); } sb.append(nodes[i].getSQL()); } sb.append(']'); break; case OpTypes.ARRAY_SUBQUERY : // case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : sb.append('('); sb.append(')'); break; default : throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } return sb.toString(); } protected String describe(Session session, int blanks) { StringBuffer sb = new StringBuffer(64); sb.append('\n'); for (int i = 0; i < blanks; i++) { sb.append(' '); } switch (opType) { case OpTypes.VALUE : sb.append("VALUE = ").append(valueData); sb.append(", TYPE = ").append(dataType.getNameString()); return sb.toString(); case OpTypes.ARRAY : sb.append("ARRAY "); return sb.toString(); case OpTypes.ARRAY_SUBQUERY : sb.append("ARRAY SUBQUERY"); return sb.toString(); // case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : sb.append("QUERY "); sb.append(subQuery.queryExpression.describe(session, blanks)); return sb.toString(); case OpTypes.ROW : sb.append("ROW = "); for (int i = 0; i < nodes.length; i++) { sb.append(nodes[i].describe(session, blanks + 1)); sb.append(' '); } break; case OpTypes.TABLE : sb.append("VALUELIST "); for (int i = 0; i < nodes.length; i++) { sb.append(nodes[i].describe(session, blanks + 1)); sb.append(' '); } break; } return sb.toString(); } /** * Set the data type */ void setDataType(Session session, Type type) { if (opType == OpTypes.VALUE) { valueData = type.convertToType(session, valueData, dataType); } dataType = type; } public boolean equals(Expression other) { if (other == this) { return true; } if (other == null) { return false; } if (opType != other.opType || exprSubType != other.exprSubType || !equals(dataType, other.dataType)) { return false; } switch (opType) { case OpTypes.SIMPLE_COLUMN : return this.columnIndex == other.columnIndex; case OpTypes.VALUE : return equals(valueData, other.valueData); case OpTypes.ARRAY : // case OpTypes.ARRAY_SUBQUERY : case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : return (subQuery.queryExpression.isEquivalent( other.subQuery.queryExpression)); default : return equals(nodes, other.nodes) && equals(subQuery, other.subQuery); } } public boolean equals(Object other) { if (other == this) { return true; } if (other instanceof Expression) { return equals((Expression) other); } return false; } public int hashCode() { int val = opType + exprSubType; for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { val += nodes[i].hashCode(); } } return val; } static boolean equals(Object o1, Object o2) { if (o1 == o2) { return true; } return (o1 == null) ? o2 == null : o1.equals(o2); } 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]; boolean equals = (e1 == null) ? e2 == null : e1.equals(e2); if (!equals) { return false; } } return true; } /** * For GROUP only. */ boolean isComposedOf(Expression exprList[], int start, int end, OrderedIntHashSet excludeSet) { if (opType == OpTypes.VALUE) { return true; } if (excludeSet.contains(opType)) { return true; } for (int i = start; i < end; i++) { if (equals(exprList[i])) { return true; } } switch (opType) { case OpTypes.LIKE : case OpTypes.MATCH_SIMPLE : case OpTypes.MATCH_PARTIAL : case OpTypes.MATCH_FULL : case OpTypes.MATCH_UNIQUE_SIMPLE : case OpTypes.MATCH_UNIQUE_PARTIAL : case OpTypes.MATCH_UNIQUE_FULL : case OpTypes.UNIQUE : case OpTypes.EXISTS : case OpTypes.ARRAY : case OpTypes.ARRAY_SUBQUERY : case OpTypes.TABLE_SUBQUERY : case OpTypes.ROW_SUBQUERY : // case OpTypes.COUNT : case OpTypes.SUM : case OpTypes.MIN : case OpTypes.MAX : case OpTypes.AVG : case OpTypes.EVERY : case OpTypes.SOME : case OpTypes.STDDEV_POP : case OpTypes.STDDEV_SAMP : case OpTypes.VAR_POP : case OpTypes.VAR_SAMP : return false; } if (nodes.length == 0) { return false; } boolean result = true; for (int i = 0; i < nodes.length; i++) { result &= (nodes[i] == null || nodes[i].isComposedOf(exprList, start, end, excludeSet)); } return result; } /** * For HAVING only. */ boolean isComposedOf(OrderedHashSet expressions, OrderedIntHashSet excludeSet) { if (opType == OpTypes.VALUE || opType == OpTypes.DYNAMIC_PARAM || opType == OpTypes.PARAMETER || opType == OpTypes.VARIABLE) { return true; } if (excludeSet.contains(opType)) { return true; } for (int i = 0; i < expressions.size(); i++) { if (equals(expressions.get(i))) { return true; } } switch (opType) { case OpTypes.COUNT : case OpTypes.SUM : case OpTypes.MIN : case OpTypes.MAX : case OpTypes.AVG : case OpTypes.EVERY : case OpTypes.SOME : case OpTypes.STDDEV_POP : case OpTypes.STDDEV_SAMP : case OpTypes.VAR_POP : case OpTypes.VAR_SAMP : return false; } /* case OpCodes.LIKE : case OpCodes.ALL : case OpCodes.ANY : case OpCodes.IN : case OpCodes.MATCH_SIMPLE : case OpCodes.MATCH_PARTIAL : case OpCodes.MATCH_FULL : case OpCodes.MATCH_UNIQUE_SIMPLE : case OpCodes.MATCH_UNIQUE_PARTIAL : case OpCodes.MATCH_UNIQUE_FULL : case OpCodes.UNIQUE : case OpCodes.EXISTS : case OpCodes.TABLE_SUBQUERY : case OpCodes.ROW_SUBQUERY : */ if (nodes.length == 0) { return false; } boolean result = true; for (int i = 0; i < nodes.length; i++) { result &= (nodes[i] == null || nodes[i].isComposedOf(expressions, excludeSet)); } return result; } Expression replaceColumnReferences(RangeVariable range, Expression[] list) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } nodes[i] = nodes[i].replaceColumnReferences(range, list); } if (subQuery != null && subQuery.queryExpression != null) { subQuery.queryExpression.replaceColumnReference(range, list); } return this; } void replaceRangeVariables(RangeVariable[] ranges, RangeVariable[] newRanges) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } nodes[i].replaceRangeVariables(ranges, newRanges); } if (subQuery != null && subQuery.queryExpression != null) { subQuery.queryExpression.replaceRangeVariables(ranges, newRanges); } } void resetColumnReferences() { for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } nodes[i].resetColumnReferences(); } } void convertToSimpleColumn(OrderedHashSet expressions, OrderedHashSet replacements) { if (opType == OpTypes.VALUE) { return; } int index = expressions.getIndex(this); if (index != -1) { Expression e = (Expression) replacements.get(index); nodes = emptyArray; opType = OpTypes.SIMPLE_COLUMN; columnIndex = e.columnIndex; rangePosition = e.rangePosition; return; } for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } nodes[i].convertToSimpleColumn(expressions, replacements); } } boolean isAggregate() { return isAggregate; } void setAggregate() { isAggregate = true; } boolean isSelfAggregate() { return false; } /** * Set the column alias */ void setAlias(SimpleName name) { alias = name; } /** * Get the column alias */ String getAlias() { if (alias != null) { return alias.name; } return ""; } SimpleName getSimpleName() { if (alias != null) { return alias; } return null; } /** * Returns the type of expression */ public int getType() { return opType; } /** * Returns the left node */ Expression getLeftNode() { return nodes.length > 0 ? nodes[LEFT] : null; } /** * Returns the right node */ Expression getRightNode() { return nodes.length > 1 ? nodes[RIGHT] : null; } void setLeftNode(Expression e) { nodes[LEFT] = e; } void setRightNode(Expression e) { nodes[RIGHT] = e; } void setSubType(int i) { exprSubType = i; } /** * Returns the range variable for a COLUMN expression */ RangeVariable getRangeVariable() { return null; } /** * return the expression for an alias used in an ORDER BY clause */ Expression replaceAliasInOrderBy(Expression[] columns, int length) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } nodes[i] = nodes[i].replaceAliasInOrderBy(columns, length); } return this; } /** * Find a range variable with the given table alias */ int findMatchingRangeVariableIndex(RangeVariable[] rangeVarArray) { return -1; } /** * collects all range variables in expression tree */ void collectRangeVariables(RangeVariable[] rangeVariables, Set set) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { nodes[i].collectRangeVariables(rangeVariables, set); } } if (subQuery != null && subQuery.queryExpression != null) { HsqlList unresolvedExpressions = subQuery.queryExpression.getUnresolvedExpressions(); if (unresolvedExpressions != null) { for (int i = 0; i < unresolvedExpressions.size(); i++) { Expression e = (Expression) unresolvedExpressions.get(i); e.collectRangeVariables(rangeVariables, set); } } } } /** * collects all schema objects */ void collectObjectNames(Set set) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { nodes[i].collectObjectNames(set); } } if (subQuery != null) { if (subQuery.queryExpression != null) { subQuery.queryExpression.collectObjectNames(set); } } } /** * return true if given RangeVariable is used in expression tree */ boolean hasReference(RangeVariable range) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { if (nodes[i].hasReference(range)) { return true; } } } if (subQuery != null && subQuery.queryExpression != null) { if (subQuery.queryExpression.hasReference(range)) { return true; } } return false; } /** * resolve tables and collect unresolved column expressions */ public HsqlList resolveColumnReferences(RangeVariable[] rangeVarArray, HsqlList unresolvedSet) { return resolveColumnReferences(rangeVarArray, rangeVarArray.length, unresolvedSet, true); } public HsqlList resolveColumnReferences(RangeVariable[] rangeVarArray, int rangeCount, HsqlList unresolvedSet, boolean acceptsSequences) { if (opType == OpTypes.VALUE) { return unresolvedSet; } switch (opType) { case OpTypes.CASEWHEN : acceptsSequences = false; break; case OpTypes.TABLE : { HsqlList localSet = null; for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } localSet = nodes[i].resolveColumnReferences( RangeVariable.emptyArray, localSet); } if (localSet != null) { isCorrelated = true; if (subQuery != null) { subQuery.setCorrelated(); } for (int i = 0; i < localSet.size(); i++) { Expression e = (Expression) localSet.get(i); unresolvedSet = e.resolveColumnReferences(rangeVarArray, unresolvedSet); } unresolvedSet = Expression.resolveColumnSet(rangeVarArray, rangeVarArray.length, localSet, unresolvedSet); } return unresolvedSet; } } for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } unresolvedSet = nodes[i].resolveColumnReferences(rangeVarArray, rangeCount, unresolvedSet, acceptsSequences); } switch (opType) { case OpTypes.ARRAY : break; case OpTypes.ARRAY_SUBQUERY : case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : { QueryExpression queryExpression = subQuery.queryExpression; if (!queryExpression.areColumnsResolved()) { isCorrelated = true; subQuery.setCorrelated(); // take to enclosing context if (unresolvedSet == null) { unresolvedSet = new ArrayListIdentity(); } unresolvedSet.addAll( queryExpression.getUnresolvedExpressions()); } break; } default : } return unresolvedSet; } public OrderedHashSet getUnkeyedColumns(OrderedHashSet unresolvedSet) { if (opType == OpTypes.VALUE) { return unresolvedSet; } for (int i = 0; i < nodes.length; i++) { if (nodes[i] == null) { continue; } unresolvedSet = nodes[i].getUnkeyedColumns(unresolvedSet); } switch (opType) { case OpTypes.ARRAY : case OpTypes.ARRAY_SUBQUERY : case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : if (subQuery != null) { if (unresolvedSet == null) { unresolvedSet = new OrderedHashSet(); } unresolvedSet.add(this); } break; } return unresolvedSet; } public void resolveTypes(Session session, Expression parent) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { nodes[i].resolveTypes(session, this); } } switch (opType) { case OpTypes.VALUE : break; case OpTypes.TABLE : /** @todo - should it fall through */ break; case OpTypes.ROW : nodeDataTypes = new Type[nodes.length]; for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { nodeDataTypes[i] = nodes[i].dataType; } } break; case OpTypes.ARRAY : { boolean hasUndefined = false; for (int i = 0; i < nodes.length; i++) { if (nodes[i].dataType == null) { hasUndefined = true; } else { dataType = Type.getAggregateType(dataType, nodes[i].dataType); } } if (hasUndefined) { for (int i = 0; i < nodes.length; i++) { if (nodes[i].dataType == null) { nodes[i].dataType = dataType; } } } dataType = new ArrayType(dataType, nodes.length); return; } case OpTypes.ARRAY_SUBQUERY : { QueryExpression queryExpression = subQuery.queryExpression; queryExpression.resolveTypes(session); subQuery.prepareTable(session); nodeDataTypes = queryExpression.getColumnTypes(); dataType = nodeDataTypes[0]; if (nodeDataTypes.length > 1) { throw Error.error(ErrorCode.X_42564); } dataType = new ArrayType(dataType, nodes.length); break; } case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : { QueryExpression queryExpression = subQuery.queryExpression; queryExpression.resolveTypes(session); subQuery.prepareTable(session); nodeDataTypes = queryExpression.getColumnTypes(); dataType = nodeDataTypes[0]; break; } default : throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } } void setAsConstantValue(Session session) { valueData = getValue(session); opType = OpTypes.VALUE; nodes = emptyArray; } void setAsConstantValue(Object value) { valueData = value; opType = OpTypes.VALUE; nodes = emptyArray; } void prepareTable(Session session, Expression row, int degree) { if (nodeDataTypes != null) { return; } for (int i = 0; i < nodes.length; i++) { Expression e = nodes[i]; if (e.opType == OpTypes.ROW) { if (degree != e.nodes.length) { throw Error.error(ErrorCode.X_42564); } } else if (degree == 1) { nodes[i] = new Expression(OpTypes.ROW); nodes[i].nodes = new Expression[]{ e }; } else { throw Error.error(ErrorCode.X_42564); } } nodeDataTypes = new Type[degree]; for (int j = 0; j < degree; j++) { Type type = row == null ? null : row.nodes[j].dataType; for (int i = 0; i < nodes.length; i++) { type = Type.getAggregateType(nodes[i].nodes[j].dataType, type); } if (type == null) { throw Error.error(ErrorCode.X_42567); } nodeDataTypes[j] = type; if (row != null && row.nodes[j].isUnresolvedParam()) { row.nodes[j].dataType = type; } for (int i = 0; i < nodes.length; i++) { if (nodes[i].nodes[j].isUnresolvedParam()) { nodes[i].nodes[j].dataType = nodeDataTypes[j]; continue; } if (nodes[i].nodes[j].opType == OpTypes.VALUE) { if (nodes[i].nodes[j].valueData == null) { nodes[i].nodes[j].dataType = nodeDataTypes[j]; } } } if (nodeDataTypes[j].isCharacterType() && !((CharacterType) nodeDataTypes[j]) .isEqualIdentical()) { // collation issues } } } /** * 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 right side 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, PersistentStore store) { for (int i = 0; i < nodes.length; i++) { Object[] data = nodes[i].getRowValue(session); for (int j = 0; j < nodeDataTypes.length; j++) { data[j] = nodeDataTypes[j].convertToType(session, data[j], nodes[i].nodes[j].dataType); } Row row = (Row) store.getNewCachedObject(session, data); try { store.indexRow(session, row); } catch (HsqlException e) {} } } /** * Returns the name of a column as string * * @return column name */ String getColumnName() { return getAlias(); } ColumnSchema getColumn() { return null; } /** * Returns the column index in the table */ int getColumnIndex() { return columnIndex; } /** * Returns the data type */ Type getDataType() { return dataType; } Type getNodeDataType(int i) { if (nodeDataTypes == null) { if (i > 0) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } return dataType; } else { return nodeDataTypes[i]; } } Type[] getNodeDataTypes() { if (nodeDataTypes == null) { return new Type[]{ dataType }; } else { return nodeDataTypes; } } int getDegree() { switch (opType) { case OpTypes.ROW : return nodes.length; case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : if (subQuery == null) { // todo } return subQuery.queryExpression.getColumnCount(); default : return 1; } } public Table getTable() { return subQuery == null ? null : subQuery.getTable(); } public void materialise(Session session) { if (subQuery == null) { return; } if (subQuery.isCorrelated()) { subQuery.materialiseCorrelated(session); } else { subQuery.materialise(session); } } Object getValue(Session session, Type type) { Object o = getValue(session); if (o == null || dataType == type) { return o; } return type.convertToType(session, o, dataType); } public Object getConstantValueNoCheck(Session session) { try { return getValue(session); } catch (HsqlException e) { return null; } } public Object[] getRowValue(Session session) { switch (opType) { case OpTypes.ROW : { Object[] data = new Object[nodes.length]; for (int i = 0; i < nodes.length; i++) { data[i] = nodes[i].getValue(session); } return data; } case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : { return subQuery.queryExpression.getValues(session); } default : throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } } public Object getValue(Session session) { switch (opType) { case OpTypes.VALUE : return valueData; case OpTypes.SIMPLE_COLUMN : { Object[] data = session.sessionContext.rangeIterators[rangePosition] .getCurrent(); return data[columnIndex]; } case OpTypes.ROW : { if (nodes.length == 1) { return nodes[0].getValue(session); } Object[] row = new Object[nodes.length]; for (int i = 0; i < nodes.length; i++) { row[i] = nodes[i].getValue(session); } return row; } case OpTypes.ARRAY : { Object[] array = new Object[nodes.length]; for (int i = 0; i < nodes.length; i++) { array[i] = nodes[i].getValue(session); } return array; } case OpTypes.ARRAY_SUBQUERY : { subQuery.materialiseCorrelated(session); RowSetNavigatorData nav = subQuery.getNavigator(session); int size = nav.getSize(); Object[] array = new Object[size]; nav.beforeFirst(); for (int i = 0; nav.hasNext(); i++) { Object[] data = nav.getNextRowData(); array[i] = data[0]; } return array; } case OpTypes.TABLE_SUBQUERY : case OpTypes.ROW_SUBQUERY : { subQuery.materialiseCorrelated(session); Object[] value = subQuery.getValues(session); if (value.length == 1) { return ((Object[]) value)[0]; } return value; } default : throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } } public Result getResult(Session session) { switch (opType) { case OpTypes.ARRAY : { RowSetNavigatorData navigator = subQuery.getNavigator(session); Object[] array = new Object[navigator.getSize()]; navigator.beforeFirst(); for (int i = 0; navigator.hasNext(); i++) { Object[] data = navigator.getNext(); array[i] = data[0]; } return Result.newPSMResult(array); } case OpTypes.TABLE_SUBQUERY : { subQuery.materialiseCorrelated(session); RowSetNavigatorData navigator = subQuery.getNavigator(session); Result result = Result.newResult(navigator); result.metaData = subQuery.queryExpression.getMetaData(); return result; } default : { Object value = getValue(session); return Result.newPSMResult(value); } } } boolean testCondition(Session session) { return Boolean.TRUE.equals(getValue(session)); } static int countNulls(Object[] a) { int nulls = 0; for (int i = 0; i < a.length; i++) { if (a[i] == null) { nulls++; } } return nulls; } public boolean isIndexable(RangeVariable range) { return false; } static void convertToType(Session session, Object[] data, Type[] dataType, Type[] newType) { for (int i = 0; i < data.length; i++) { data[i] = newType[i].convertToType(session, data[i], dataType[i]); } } /** * Returns a Select object that can be used for checking the contents * of an existing table against the given CHECK search condition. */ static QuerySpecification getCheckSelect(Session session, Table t, Expression e) { CompileContext compileContext = new CompileContext(session, null); QuerySpecification s = new QuerySpecification(compileContext); RangeVariable[] ranges = new RangeVariable[]{ new RangeVariable(t, null, null, null, compileContext) }; e.resolveCheckOrGenExpression(session, ranges, true); s.exprColumns = new Expression[1]; s.exprColumns[0] = EXPR_TRUE; s.rangeVariables = ranges; if (Type.SQL_BOOLEAN != e.getDataType()) { throw Error.error(ErrorCode.X_42568); } Expression condition = new ExpressionLogical(OpTypes.NOT, e); s.queryCondition = condition; s.resolveReferences(session); s.resolveTypes(session); return s; } public void resolveCheckOrGenExpression(Session session, RangeVariable[] ranges, boolean isCheck) { boolean nonDeterministic = false; OrderedHashSet set = new OrderedHashSet(); HsqlList unresolved = resolveColumnReferences(ranges, null); ExpressionColumn.checkColumnsResolved(unresolved); resolveTypes(session, null); collectAllExpressions(set, Expression.subqueryAggregateExpressionSet, Expression.emptyExpressionSet); if (!set.isEmpty()) { throw Error.error(ErrorCode.X_42512); } collectAllExpressions(set, Expression.functionExpressionSet, Expression.emptyExpressionSet); for (int i = 0; i < set.size(); i++) { Expression current = (Expression) set.get(i); if (current.opType == OpTypes.FUNCTION) { if (!((FunctionSQLInvoked) current).isDeterministic()) { throw Error.error(ErrorCode.X_42512); } } if (current.opType == OpTypes.SQL_FUNCTION) { if (!((FunctionSQL) current).isDeterministic()) { if (isCheck) { nonDeterministic = true; continue; } throw Error.error(ErrorCode.X_42512); } } } if (isCheck && nonDeterministic) { HsqlArrayList list = new HsqlArrayList(); RangeVariableResolver.decomposeAndConditions(this, list); for (int i = 0; i < list.size(); i++) { nonDeterministic = true; Expression e = (Expression) list.get(i); Expression e1; if (e instanceof ExpressionLogical) { boolean b = ((ExpressionLogical) e).convertToSmaller(); if (!b) { break; } e1 = e.getRightNode(); e = e.getLeftNode(); if (!e.dataType.isDateTimeType()) { nonDeterministic = true; break; } if (e.hasNonDeterministicFunction()) { nonDeterministic = true; break; } // both sides are actually consistent regarding timeZone // e.dataType.isDateTimeTypeWithZone(); if (e1 instanceof ExpressionArithmetic) { if (opType == OpTypes.ADD) { if (e1.getRightNode() .hasNonDeterministicFunction()) { e1.swapLeftAndRightNodes(); } } else if (opType == OpTypes.SUBTRACT) {} else { break; } if (e1.getRightNode().hasNonDeterministicFunction()) { break; } e1 = e1.getLeftNode(); } if (e1.opType == OpTypes.SQL_FUNCTION) { FunctionSQL function = (FunctionSQL) e1; switch (function.funcType) { case FunctionSQL.FUNC_CURRENT_DATE : case FunctionSQL.FUNC_CURRENT_TIMESTAMP : case FunctionSQL.FUNC_LOCALTIMESTAMP : nonDeterministic = false; continue; default : break; } break; } break; } else { break; } } if (nonDeterministic) { throw Error.error(ErrorCode.X_42512); } } set.clear(); collectObjectNames(set); for (int i = 0; i < set.size(); i++) { HsqlName name = (HsqlName) set.get(i); switch (name.type) { case SchemaObject.COLUMN : { if (isCheck) { break; } int colIndex = ranges[0].rangeTable.findColumn(name.name); ColumnSchema column = ranges[0].rangeTable.getColumn(colIndex); if (column.isGenerated()) { throw Error.error(ErrorCode.X_42512); } break; } case SchemaObject.SEQUENCE : { throw Error.error(ErrorCode.X_42512); } case SchemaObject.SPECIFIC_ROUTINE : { Routine routine = (Routine) session.database.schemaManager .getSchemaObject(name); if (!routine.isDeterministic()) { throw Error.error(ErrorCode.X_42512); } int impact = routine.getDataImpact(); if (impact == Routine.READS_SQL || impact == Routine.MODIFIES_SQL) { throw Error.error(ErrorCode.X_42512); } } } } set.clear(); } boolean isUnresolvedParam() { return false; } boolean isDynamicParam() { return false; } boolean hasNonDeterministicFunction() { OrderedHashSet list = null; list = collectAllExpressions(list, Expression.functionExpressionSet, Expression.emptyExpressionSet); if (list == null) { return false; } for (int j = 0; j < list.size(); j++) { Expression current = (Expression) list.get(j); if (current.opType == OpTypes.FUNCTION) { if (!((FunctionSQLInvoked) current).isDeterministic()) { return true; } } else if (current.opType == OpTypes.SQL_FUNCTION) { if (!((FunctionSQL) current).isDeterministic()) { return true; } } } return false; } void swapLeftAndRightNodes() { Expression temp = nodes[LEFT]; nodes[LEFT] = nodes[RIGHT]; nodes[RIGHT] = temp; } void setAttributesAsColumn(ColumnSchema column, boolean isWritable) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } String getValueClassName() { Type type = dataType == null ? NullType.getNullType() : dataType; return type.getJDBCClassName(); } /** * collect all expressions of a set of expression types appearing anywhere * in a select statement and its subselects, etc. */ OrderedHashSet collectAllExpressions(OrderedHashSet set, OrderedIntHashSet typeSet, OrderedIntHashSet stopAtTypeSet) { if (stopAtTypeSet.contains(opType)) { return set; } for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { set = nodes[i].collectAllExpressions(set, typeSet, stopAtTypeSet); } } if (typeSet.contains(opType)) { if (set == null) { set = new OrderedHashSet(); } set.add(this); } if (subQuery != null && subQuery.queryExpression != null) { set = subQuery.queryExpression.collectAllExpressions(set, typeSet, stopAtTypeSet); } return set; } public OrderedHashSet getSubqueries() { return collectAllSubqueries(null); } OrderedHashSet collectAllSubqueries(OrderedHashSet set) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { set = nodes[i].collectAllSubqueries(set); } } if (subQuery != null) { if (set == null) { set = new OrderedHashSet(); } set.add(subQuery); if (subQuery.queryExpression != null) { OrderedHashSet tempSet = subQuery.queryExpression.getSubqueries(); set = OrderedHashSet.addAll(set, tempSet); } } return set; } /** * isCorrelated */ public boolean isCorrelated() { if (subQuery == null) { return false; } return subQuery.isCorrelated(); } /** * checkValidCheckConstraint */ public void checkValidCheckConstraint() { OrderedHashSet set = null; set = collectAllExpressions(set, subqueryAggregateExpressionSet, emptyExpressionSet); if (set != null && !set.isEmpty()) { throw Error.error(ErrorCode.X_0A000, "subquery in check constraint"); } } static HsqlList resolveColumnSet(RangeVariable[] rangeVars, int rangeCount, HsqlList sourceSet, HsqlList targetSet) { if (sourceSet == null) { return targetSet; } for (int i = 0; i < sourceSet.size(); i++) { Expression e = (Expression) sourceSet.get(i); targetSet = e.resolveColumnReferences(rangeVars, rangeCount, targetSet, true); } return targetSet; } Expression getIndexableExpression(RangeVariable rangeVar) { return null; } public Expression duplicate() { Expression e = null; try { e = (Expression) super.clone(); e.nodes = nodes.clone(); for (int i = 0; i < nodes.length; i++) { if (nodes[i] != null) { e.nodes[i] = nodes[i].duplicate(); } } } catch (CloneNotSupportedException ex) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } return e; } void replaceNode(Expression existing, Expression replacement) { for (int i = 0; i < nodes.length; i++) { if (nodes[i] == existing) { replacement.alias = nodes[i].alias; nodes[i] = replacement; return; } } throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } public Object updateAggregatingValue(Session session, Object currValue) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } public Object getAggregatedValue(Session session, Object currValue) { throw Error.runtimeError(ErrorCode.U_S0500, "Expression"); } }