/* Copyright (c) 2001-2019, 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.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.OrderedIntHashSet;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.types.ArrayType;
import org.hsqldb.types.DTIType;
import org.hsqldb.types.DateTimeType;
import org.hsqldb.types.Type;
import org.hsqldb.types.Types;

Author:Campbell Burnet (campbell-burnet@users dot sourceforge.net), Fred Toussi (fredt@users dot sourceforge.net)
Version:2.5.0
Since:1.9.0
/** * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net) * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.9.0 */
public class ExpressionLogical extends Expression { boolean noOptimisation; boolean isQuantified; boolean isTerminal;
For LIKE
/** * For LIKE */
ExpressionLogical(int type) { super(type); dataType = Type.SQL_BOOLEAN; }
For boolean constants
/** * For boolean constants */
ExpressionLogical(boolean b) { super(OpTypes.VALUE); dataType = Type.SQL_BOOLEAN; valueData = b ? Boolean.TRUE : Boolean.FALSE; } /* * Create an equality expressions using existing columns and * range variables. The expression is fully resolved in constructor. */ ExpressionLogical(RangeVariable leftRangeVar, int colIndexLeft, RangeVariable rightRangeVar, int colIndexRight) { super(OpTypes.EQUAL); ExpressionColumn leftExpression = new ExpressionColumn(leftRangeVar, colIndexLeft); ExpressionColumn rightExpression = new ExpressionColumn(rightRangeVar, colIndexRight); nodes = new Expression[BINARY]; nodes[LEFT] = leftExpression; nodes[RIGHT] = rightExpression; setEqualityMode(); dataType = Type.SQL_BOOLEAN; }
Creates an equality expression
/** * Creates an equality expression */
ExpressionLogical(Expression left, Expression right) { super(OpTypes.EQUAL); nodes = new Expression[BINARY]; nodes[LEFT] = left; nodes[RIGHT] = right; setEqualityMode(); dataType = Type.SQL_BOOLEAN; }
Creates a binary operation expression
/** * Creates a binary operation expression */
ExpressionLogical(int type, Expression left, Expression right) { super(type); nodes = new Expression[BINARY]; nodes[LEFT] = left; nodes[RIGHT] = right; switch (opType) { case OpTypes.EQUAL : case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : case OpTypes.GREATER : case OpTypes.SMALLER : case OpTypes.SMALLER_EQUAL : setEqualityMode(); break; case OpTypes.NOT_EQUAL : case OpTypes.OVERLAPS : case OpTypes.RANGE_CONTAINS : case OpTypes.RANGE_EQUALS : case OpTypes.RANGE_OVERLAPS : case OpTypes.RANGE_IMMEDIATELY_PRECEDES : case OpTypes.RANGE_IMMEDIATELY_SUCCEEDS : case OpTypes.RANGE_PRECEDES : case OpTypes.RANGE_SUCCEEDS : case OpTypes.NOT_DISTINCT : case OpTypes.IN : 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.AND : case OpTypes.OR : break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } dataType = Type.SQL_BOOLEAN; }
Creates a modified LIKE comparison
/** * Creates a modified LIKE comparison */
ExpressionLogical(int type, Expression left, Expression right, Expression end) { super(type); nodes = new Expression[TERNARY]; nodes[LEFT] = left; nodes[RIGHT] = right; nodes[2] = end; }
Creates a unary operation expression
/** * Creates a unary operation expression */
ExpressionLogical(int type, Expression e) { super(type); nodes = new Expression[UNARY]; nodes[LEFT] = e; switch (opType) { case OpTypes.UNIQUE : case OpTypes.EXISTS : case OpTypes.IS_NULL : case OpTypes.IS_NOT_NULL : case OpTypes.NOT : dataType = Type.SQL_BOOLEAN; break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } if (opType == OpTypes.IS_NULL && nodes[LEFT].opType == OpTypes.COLUMN) { isSingleColumnNull = true; } if (opType == OpTypes.NOT && nodes[LEFT].isSingleColumnNull) { isSingleColumnNotNull = true; } }
Creates a column not null expression for check constraints
/** * Creates a column not null expression for check constraints */
ExpressionLogical(ColumnSchema column) { super(OpTypes.NOT); nodes = new Expression[UNARY]; dataType = Type.SQL_BOOLEAN; Expression e = new ExpressionColumn(column); e = new ExpressionLogical(OpTypes.IS_NULL, e); nodes[LEFT] = e; } void setEqualityMode() { if (nodes[LEFT].opType == OpTypes.COLUMN) { nodes[LEFT].nullability = SchemaObject.Nullability.NO_NULLS; switch (nodes[RIGHT].opType) { case OpTypes.COLUMN : isColumnCondition = true; if (opType == OpTypes.EQUAL) { isColumnEqual = true; } nodes[RIGHT].nullability = SchemaObject.Nullability.NO_NULLS; break; case OpTypes.VALUE : case OpTypes.DYNAMIC_PARAM : case OpTypes.PARAMETER : case OpTypes.VARIABLE : isSingleColumnCondition = true; if (opType == OpTypes.EQUAL) { isSingleColumnEqual = true; } break; default : } } else if (nodes[RIGHT].opType == OpTypes.COLUMN) { nodes[RIGHT].nullability = SchemaObject.Nullability.NO_NULLS; switch (nodes[LEFT].opType) { case OpTypes.VALUE : case OpTypes.DYNAMIC_PARAM : case OpTypes.PARAMETER : case OpTypes.VARIABLE : isSingleColumnCondition = true; if (opType == OpTypes.EQUAL) { isSingleColumnEqual = true; } break; default : } } }
Creates a NOT NULL condition
/** * Creates a NOT NULL condition */
static ExpressionLogical newNotNullCondition(Expression e) { e = new ExpressionLogical(OpTypes.IS_NULL, e); return new ExpressionLogical(OpTypes.NOT, e); } // logical ops static Expression andExpressions(Expression e1, Expression e2) { if (e1 == null) { return e2; } if (e2 == null) { return e1; } if (ExpressionLogical.EXPR_FALSE.equals(e1) || ExpressionLogical.EXPR_FALSE.equals(e2)) { return ExpressionLogical.EXPR_FALSE; } if (e1 == e2) { return e1; } return new ExpressionLogical(OpTypes.AND, e1, e2); } static Expression orExpressions(Expression e1, Expression e2) { if (e1 == null) { return e2; } if (e2 == null) { return e1; } if (e1 == e2) { return e1; } return new ExpressionLogical(OpTypes.OR, e1, e2); } public void addLeftColumnsForAllAny(RangeVariable range, OrderedIntHashSet set) { if (nodes.length == 0) { return; } for (int j = 0; j < nodes[LEFT].nodes.length; j++) { int index = nodes[LEFT].nodes[j].getColumnIndex(); if (index < 0 || nodes[LEFT].nodes[j].getRangeVariable() != range) { set.clear(); return; } set.add(index); } } public void setSubType(int type) { exprSubType = type; if (exprSubType == OpTypes.ALL_QUANTIFIED || exprSubType == OpTypes.ANY_QUANTIFIED) { isQuantified = true; } } public String getSQL() { StringBuilder sb = new StringBuilder(64); if (opType == OpTypes.VALUE) { return super.getSQL(); } String left = getContextSQL(nodes[LEFT]); String right = getContextSQL(nodes.length > 1 ? nodes[RIGHT] : null); switch (opType) { case OpTypes.NOT : if (nodes[LEFT].opType == OpTypes.IS_NULL) { sb.append(getContextSQL(nodes[LEFT].nodes[LEFT])).append( ' ').append(Tokens.T_IS).append(' ').append( Tokens.T_NOT).append(' ').append(Tokens.T_NULL); return sb.toString(); } if (nodes[LEFT].opType == OpTypes.NOT_DISTINCT) { sb.append(getContextSQL(nodes[LEFT].nodes[LEFT])).append( ' ').append(Tokens.T_IS).append(' ').append( Tokens.T_DISTINCT).append(' ').append( Tokens.T_FROM).append(' ').append( getContextSQL(nodes[LEFT].nodes[RIGHT])); return sb.toString(); } sb.append(Tokens.T_NOT).append(' ').append(left); return sb.toString(); case OpTypes.NOT_DISTINCT : sb.append(left).append(' ').append(Tokens.T_IS).append( ' ').append(Tokens.T_NOT).append(' ').append( Tokens.T_DISTINCT).append(' ').append( Tokens.T_FROM).append(' ').append(right); return sb.toString(); case OpTypes.IS_NULL : sb.append(left).append(' ').append(Tokens.T_IS).append( ' ').append(Tokens.T_NULL); return sb.toString(); case OpTypes.UNIQUE : sb.append(' ').append(Tokens.T_UNIQUE).append(' '); break; case OpTypes.EXISTS : sb.append(' ').append(Tokens.T_EXISTS).append(' '); break; case OpTypes.EQUAL : sb.append(left).append('=').append(right); return sb.toString(); case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : sb.append(left).append(">=").append(right); return sb.toString(); case OpTypes.GREATER : sb.append(left).append('>').append(right); return sb.toString(); case OpTypes.SMALLER : sb.append(left).append('<').append(right); return sb.toString(); case OpTypes.SMALLER_EQUAL : sb.append(left).append("<=").append(right); return sb.toString(); case OpTypes.NOT_EQUAL : if (Tokens.T_NULL.equals(right)) { sb.append(left).append(" IS NOT ").append(right); } else { sb.append(left).append("!=").append(right); } return sb.toString(); case OpTypes.AND : sb.append(left).append(' ').append(Tokens.T_AND).append( ' ').append(right); return sb.toString(); case OpTypes.OR : sb.append(left).append(' ').append(Tokens.T_OR).append( ' ').append(right); return sb.toString(); case OpTypes.IN : sb.append(left).append(' ').append(Tokens.T_IN).append( ' ').append(right); return sb.toString(); case OpTypes.MATCH_SIMPLE : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(right); return sb.toString(); case OpTypes.MATCH_PARTIAL : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(Tokens.PARTIAL).append(right); return sb.toString(); case OpTypes.MATCH_FULL : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(Tokens.FULL).append(right); return sb.toString(); case OpTypes.MATCH_UNIQUE_SIMPLE : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(Tokens.UNIQUE).append(right); return sb.toString(); case OpTypes.MATCH_UNIQUE_PARTIAL : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(Tokens.UNIQUE).append(' ').append( Tokens.PARTIAL).append(right); return sb.toString(); case OpTypes.MATCH_UNIQUE_FULL : sb.append(left).append(' ').append(Tokens.T_MATCH).append( ' ').append(Tokens.UNIQUE).append(' ').append( Tokens.FULL).append(right); return sb.toString(); default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } return sb.toString(); } protected String describe(Session session, int blanks) { StringBuilder sb = new StringBuilder(64); sb.append('\n'); for (int i = 0; i < blanks; i++) { sb.append(' '); } switch (opType) { case OpTypes.VALUE : sb.append("VALUE = ").append( dataType.convertToSQLString(valueData)); sb.append(", TYPE = ").append(dataType.getNameString()); return sb.toString(); case OpTypes.NOT : if (nodes[LEFT].opType == OpTypes.NOT_DISTINCT) { sb.append(Tokens.T_DISTINCT); return sb.toString(); } sb.append(Tokens.T_NOT); break; case OpTypes.NOT_DISTINCT : sb.append(Tokens.T_NOT).append(' ').append(Tokens.T_DISTINCT); break; case OpTypes.EQUAL : sb.append("EQUAL"); break; case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : sb.append("GREATER_EQUAL"); break; case OpTypes.GREATER : sb.append("GREATER"); break; case OpTypes.SMALLER : sb.append("SMALLER"); break; case OpTypes.SMALLER_EQUAL : sb.append("SMALLER_EQUAL"); break; case OpTypes.NOT_EQUAL : sb.append("NOT_EQUAL"); break; case OpTypes.AND : sb.append(Tokens.T_AND); break; case OpTypes.OR : sb.append(Tokens.T_OR); break; 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 : sb.append(Tokens.T_MATCH); break; case OpTypes.IS_NULL : sb.append(Tokens.T_IS).append(' ').append(Tokens.T_NULL); break; case OpTypes.UNIQUE : sb.append(Tokens.T_UNIQUE); break; case OpTypes.EXISTS : sb.append(Tokens.T_EXISTS); break; case OpTypes.OVERLAPS : sb.append(Tokens.T_OVERLAPS); break; case OpTypes.RANGE_CONTAINS : sb.append(Tokens.T_CONTAINS); break; case OpTypes.RANGE_EQUALS : sb.append(Tokens.T_EQUALS); break; case OpTypes.RANGE_OVERLAPS : sb.append(Tokens.T_OVERLAPS); break; case OpTypes.RANGE_PRECEDES : sb.append(Tokens.T_PRECEDES); break; case OpTypes.RANGE_SUCCEEDS : sb.append(Tokens.T_SUCCEEDS); break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } if (getLeftNode() != null) { sb.append(" arg_left=["); sb.append(nodes[LEFT].describe(session, blanks + 1)); sb.append(']'); } if (getRightNode() != null) { sb.append(" arg_right=["); sb.append(nodes[RIGHT].describe(session, blanks + 1)); sb.append(']'); } return sb.toString(); } public void resolveTypes(Session session, Expression parent) { // parametric ALL / ANY if (isQuantified) { if (nodes[RIGHT].opType == OpTypes.TABLE) { if (nodes[RIGHT] instanceof ExpressionTable) { if (nodes[RIGHT].nodes[LEFT].opType == OpTypes.DYNAMIC_PARAM) { nodes[LEFT].resolveTypes(session, this); nodes[RIGHT].nodes[LEFT].dataType = new ArrayType( nodes[LEFT].dataType, ArrayType.defaultLargeArrayCardinality); } } } } 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.NOT_DISTINCT : changeToRowExpression(LEFT); changeToRowExpression(RIGHT); resolveRowTypes(); checkRowComparison(); break; case OpTypes.EQUAL : case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : case OpTypes.GREATER : case OpTypes.SMALLER : case OpTypes.SMALLER_EQUAL : case OpTypes.NOT_EQUAL : resolveTypesForComparison(session, parent); break; case OpTypes.AND : { resolveTypesForLogicalOp(); if (nodes[LEFT].opType == OpTypes.VALUE) { if (nodes[RIGHT].opType == OpTypes.VALUE) { setAsConstantValue(session, parent); } else { Object value = nodes[LEFT].getValue(session); if (value == null || Boolean.FALSE.equals(value)) { setAsConstantValue(Boolean.FALSE, parent); } } } else if (nodes[RIGHT].opType == OpTypes.VALUE) { Object value = nodes[RIGHT].getValue(session); if (value == null || Boolean.FALSE.equals(value)) { setAsConstantValue(Boolean.FALSE, parent); } } break; } case OpTypes.OR : { resolveTypesForLogicalOp(); if (nodes[LEFT].opType == OpTypes.VALUE) { if (nodes[RIGHT].opType == OpTypes.VALUE) { setAsConstantValue(session, parent); } else { Object value = nodes[LEFT].getValue(session); if (Boolean.TRUE.equals(value)) { setAsConstantValue(Boolean.TRUE, parent); } } } else if (nodes[RIGHT].opType == OpTypes.VALUE) { Object value = nodes[RIGHT].getValue(session); if (Boolean.TRUE.equals(value)) { setAsConstantValue(Boolean.TRUE, parent); } } break; } case OpTypes.IS_NOT_NULL : case OpTypes.IS_NULL : { switch (nodes[LEFT].opType) { case OpTypes.ROW : { Expression[] sourceNodes = nodes[LEFT].nodes; Expression result = null; for (int i = 0; i < sourceNodes.length; i++) { Expression node; node = new ExpressionLogical(OpTypes.IS_NULL, sourceNodes[i]); if (opType == OpTypes.IS_NOT_NULL) { node = new ExpressionLogical(OpTypes.NOT, node); } result = andExpressions(result, node); } opType = OpTypes.AND; nodes = result.nodes; resolveTypes(session, parent); break; } case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : { break; } default : { if (nodes[LEFT].isUnresolvedParam()) { if (session.database.sqlEnforceTypes) { throw Error.error(ErrorCode.X_42563); } nodes[LEFT].dataType = Type.SQL_VARCHAR_DEFAULT; } if (opType == OpTypes.IS_NOT_NULL) { Expression node; node = new ExpressionLogical(OpTypes.IS_NULL, nodes[LEFT]); nodes[LEFT] = node; opType = OpTypes.NOT; resolveTypes(session, parent); break; } if (nodes[LEFT].opType == OpTypes.VALUE) { setAsConstantValue(session, parent); } break; } } break; } case OpTypes.NOT : { if (nodes[LEFT].isUnresolvedParam()) { nodes[LEFT].dataType = Type.SQL_BOOLEAN; break; } if (nodes[LEFT].opType == OpTypes.VALUE) { if (nodes[LEFT].dataType.isBooleanType()) { setAsConstantValue(session, parent); break; } else { throw Error.error(ErrorCode.X_42563); } } if (nodes[LEFT].dataType == null || !nodes[LEFT].dataType.isBooleanType()) { throw Error.error(ErrorCode.X_42563); } dataType = Type.SQL_BOOLEAN; break; } case OpTypes.OVERLAPS : resolveTypesForOverlaps(); break; case OpTypes.IN : resolveTypesForIn(session); break; 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 : resolveTypesForAllAny(session); break; case OpTypes.UNIQUE : // create the full index before meterialization // needed for VALUES expression nodes[LEFT].table.getFullIndex(session); break; case OpTypes.EXISTS : break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } } private void resolveTypesForLogicalOp() { if (nodes[LEFT].isUnresolvedParam()) { nodes[LEFT].dataType = Type.SQL_BOOLEAN; } if (nodes[RIGHT].isUnresolvedParam()) { nodes[RIGHT].dataType = Type.SQL_BOOLEAN; } if (nodes[LEFT].dataType == null || nodes[RIGHT].dataType == null) { throw Error.error(ErrorCode.X_42571); } if (nodes[LEFT].opType == OpTypes.ROW || nodes[RIGHT].opType == OpTypes.ROW) { throw Error.error(ErrorCode.X_42565); } if (Type.SQL_BOOLEAN != nodes[LEFT].dataType || Type.SQL_BOOLEAN != nodes[RIGHT].dataType) { throw Error.error(ErrorCode.X_42568); } } private void resolveTypesForComparison(Session session, Expression parent) { if (exprSubType == OpTypes.ALL_QUANTIFIED || exprSubType == OpTypes.ANY_QUANTIFIED) { resolveTypesForAllAny(session); checkRowComparison(); return; } int leftDegree = nodes[LEFT].getDegree(); int rightDegree = nodes[RIGHT].getDegree(); if (leftDegree > 1 || rightDegree > 1) { if (leftDegree != rightDegree) { throw Error.error(ErrorCode.X_42564); } resolveRowTypes(); checkRowComparison(); return; } else { if (nodes[LEFT].isUnresolvedParam()) { nodes[LEFT].dataType = nodes[RIGHT].dataType; } else if (nodes[RIGHT].isUnresolvedParam()) { nodes[RIGHT].dataType = nodes[LEFT].dataType; } if (nodes[LEFT].dataType == null) { nodes[LEFT].dataType = nodes[RIGHT].dataType; } else if (nodes[RIGHT].dataType == null) { nodes[RIGHT].dataType = nodes[LEFT].dataType; } if (nodes[LEFT].dataType == null || nodes[RIGHT].dataType == null) { throw Error.error(ErrorCode.X_42567); } if (!nodes[LEFT].dataType.canCompareDirect( nodes[RIGHT].dataType)) { if (convertDateTime(session)) { // compatibility for BIT with number and BOOLEAN - convert bit to other type } else if (nodes[LEFT].dataType.isBitType() || nodes[LEFT].dataType.isBooleanType()) { if (session.database.sqlEnforceTypes) { throw Error.error(ErrorCode.X_42562); } if (nodes[LEFT].dataType.canConvertFrom( nodes[RIGHT].dataType)) { nodes[RIGHT] = ExpressionOp.getCastExpression(session, nodes[RIGHT], nodes[LEFT].dataType); } } else if (nodes[RIGHT].dataType.isBitType() || nodes[RIGHT].dataType.isBooleanType()) { if (session.database.sqlEnforceTypes) { throw Error.error(ErrorCode.X_42562); } if (nodes[RIGHT].dataType.canConvertFrom( nodes[LEFT].dataType)) { nodes[LEFT] = ExpressionOp.getCastExpression(session, nodes[LEFT], nodes[RIGHT].dataType); } } else if (nodes[LEFT].dataType.isNumberType()) { if (session.database.sqlEnforceTypes) { throw Error.error(ErrorCode.X_42562); } if (nodes[LEFT].dataType.canConvertFrom( nodes[RIGHT].dataType)) { nodes[RIGHT] = ExpressionOp.getCastExpression(session, nodes[RIGHT], nodes[LEFT].dataType); } } else if (nodes[RIGHT].dataType.isNumberType()) { if (session.database.sqlEnforceTypes) { throw Error.error(ErrorCode.X_42562); } if (nodes[RIGHT].dataType.canConvertFrom( nodes[LEFT].dataType)) { nodes[LEFT] = ExpressionOp.getCastExpression(session, nodes[LEFT], nodes[RIGHT].dataType); } } else if (nodes[LEFT].dataType.isDateTimeType()) { if (nodes[LEFT].dataType.isDateTimeTypeWithZone() ^ nodes[RIGHT].dataType.isDateTimeTypeWithZone()) { nodes[LEFT] = new ExpressionOp(nodes[LEFT]); } } else if (nodes[LEFT].dataType.canConvertFrom( nodes[RIGHT].dataType)) { nodes[RIGHT] = ExpressionOp.getCastExpression(session, nodes[RIGHT], nodes[LEFT].dataType); } else { throw Error.error(ErrorCode.X_42562); } } if (opType == OpTypes.EQUAL || opType == OpTypes.NOT_EQUAL) { // } else { if (nodes[LEFT].dataType.isArrayType() || nodes[LEFT].dataType.isLobType() || nodes[RIGHT].dataType.isLobType()) { throw Error.error(ErrorCode.X_42534); } } if (nodes[LEFT].opType == OpTypes.ROWNUM && nodes[RIGHT].opType == OpTypes.VALUE) { isTerminal = true; } if (nodes[LEFT].dataType.typeComparisonGroup != nodes[RIGHT].dataType.typeComparisonGroup) { throw Error.error(ErrorCode.X_42562); } if (nodes[LEFT].opType == OpTypes.VALUE && nodes[RIGHT].opType == OpTypes.VALUE) { setAsConstantValue(session, parent); } else if (session.database.sqlSyntaxDb2) { if (nodes[LEFT].dataType.typeComparisonGroup == Types.SQL_VARCHAR) { if (nodes[LEFT].opType == OpTypes.VALUE) { nodes[RIGHT].dataType.convertToTypeLimits(session, nodes[LEFT].valueData); } if (nodes[RIGHT].opType == OpTypes.VALUE) { nodes[LEFT].dataType.convertToTypeLimits(session, nodes[RIGHT].valueData); } } } } } private void changeToRowExpression(int nodeIndex) { if (nodes[nodeIndex].opType != OpTypes.ROW) { nodes[nodeIndex] = new Expression(OpTypes.ROW, new Expression[]{ nodes[nodeIndex] }); nodes[nodeIndex].nodeDataTypes = new Type[]{ nodes[nodeIndex].nodes[0].dataType }; } } private void resolveRowTypes() { for (int i = 0; i < nodes[LEFT].nodeDataTypes.length; i++) { Type leftType = nodes[LEFT].nodeDataTypes[i]; Type rightType = nodes[RIGHT].nodeDataTypes[i]; if (leftType == null) { leftType = nodes[LEFT].nodeDataTypes[i] = rightType; } else if (nodes[RIGHT].dataType == null) { rightType = nodes[RIGHT].nodeDataTypes[i] = leftType; } if (leftType == null || rightType == null) { throw Error.error(ErrorCode.X_42567); } if (leftType.typeComparisonGroup != rightType.typeComparisonGroup) { throw Error.error(ErrorCode.X_42562); } else if (leftType.isDateTimeType()) { if (leftType.isDateTimeTypeWithZone() ^ rightType.isDateTimeTypeWithZone()) { nodes[LEFT].nodes[i] = new ExpressionOp(nodes[LEFT].nodes[i]); nodes[LEFT].nodeDataTypes[i] = nodes[LEFT].nodes[i].dataType; } } } } void checkRowComparison() { if (opType == OpTypes.EQUAL || opType == OpTypes.NOT_EQUAL) { return; } for (int i = 0; i < nodes[LEFT].nodeDataTypes.length; i++) { Type leftType = nodes[LEFT].nodeDataTypes[i]; Type rightType = nodes[RIGHT].nodeDataTypes[i]; if (leftType.isArrayType() || leftType.isLobType() || rightType.isLobType()) { throw Error.error(ErrorCode.X_42534); } } }
for compatibility, convert a datetime character string to a datetime value for comparison
/** * for compatibility, convert a datetime character string to a datetime * value for comparison */
private boolean convertDateTime(Session session) { int a = LEFT; int b = RIGHT; if (nodes[a].dataType.isDateTimeType()) { // } else if (nodes[b].dataType.isDateTimeType()) { a = RIGHT; b = LEFT; } else { return false; } if (nodes[a].dataType.isDateTimeTypeWithZone()) { return false; } if (nodes[b].dataType.isCharacterType()) { if (nodes[b].opType == OpTypes.VALUE) { try { nodes[b].valueData = nodes[a].dataType.castToType(session, nodes[b].valueData, nodes[b].dataType); nodes[b].dataType = nodes[a].dataType; } catch (HsqlException e) { if (nodes[a].dataType == Type.SQL_DATE) { nodes[b].valueData = Type.SQL_TIMESTAMP.castToType(session, nodes[b].valueData, nodes[b].dataType); nodes[b].dataType = Type.SQL_TIMESTAMP; } } return true; } else { nodes[b] = new ExpressionOp(nodes[b], nodes[a].dataType); nodes[b].resolveTypes(session, this); return true; } } return false; } void resolveTypesForOverlaps() { if (nodes[LEFT].nodes[0].isUnresolvedParam()) { nodes[LEFT].nodes[0].dataType = nodes[RIGHT].nodes[0].dataType; } if (nodes[RIGHT].nodes[0].isUnresolvedParam()) { nodes[RIGHT].nodes[0].dataType = nodes[LEFT].nodes[0].dataType; } if (nodes[LEFT].nodes[0].dataType == null) { nodes[LEFT].nodes[0].dataType = Type.SQL_TIMESTAMP; nodes[RIGHT].nodes[0].dataType = Type.SQL_TIMESTAMP; } if (nodes[LEFT].nodes[1].isUnresolvedParam()) { nodes[LEFT].nodes[1].dataType = nodes[RIGHT].nodes[0].dataType; } if (nodes[RIGHT].nodes[1].isUnresolvedParam()) { nodes[RIGHT].nodes[1].dataType = nodes[LEFT].nodes[0].dataType; } if (!DTIType.isValidDatetimeRange(nodes[LEFT].nodes[0].dataType, nodes[LEFT].nodes[1].dataType)) { throw Error.error(ErrorCode.X_42563); } if (!DTIType.isValidDatetimeRange(nodes[RIGHT].nodes[0].dataType, nodes[RIGHT].nodes[1].dataType)) { throw Error.error(ErrorCode.X_42563); } nodes[LEFT].nodeDataTypes[0] = nodes[LEFT].nodes[0].dataType; nodes[LEFT].nodeDataTypes[1] = nodes[LEFT].nodes[1].dataType; nodes[RIGHT].nodeDataTypes[0] = nodes[RIGHT].nodes[0].dataType; nodes[RIGHT].nodeDataTypes[1] = nodes[RIGHT].nodes[1].dataType; } void resolveTypesForAllAny(Session session) { int degree = nodes[LEFT].getDegree(); if (degree == 1 && nodes[LEFT].opType != OpTypes.ROW) { nodes[LEFT] = new Expression(OpTypes.ROW, new Expression[]{ nodes[LEFT] }); } if (nodes[RIGHT].opType == OpTypes.VALUELIST) { nodes[RIGHT].prepareTable(session, nodes[LEFT], degree); nodes[RIGHT].table.prepareTable(session); } // encounterd in system generated MATCH predicates if (nodes[RIGHT].nodeDataTypes == null) { nodes[RIGHT].prepareTable(session, nodes[LEFT], degree); } if (degree != nodes[RIGHT].nodeDataTypes.length) { throw Error.error(ErrorCode.X_42564); } if (nodes[RIGHT].opType == OpTypes.VALUELIST) {} if (nodes[LEFT].nodeDataTypes == null) { nodes[LEFT].nodeDataTypes = new Type[nodes[LEFT].nodes.length]; } for (int i = 0; i < nodes[LEFT].nodeDataTypes.length; i++) { Type type = nodes[LEFT].nodes[i].dataType; if (type == null) { type = nodes[RIGHT].nodeDataTypes[i]; } if (type == null) { throw Error.error(ErrorCode.X_42567); } if (type.typeComparisonGroup != nodes[RIGHT].nodeDataTypes[i].typeComparisonGroup) { throw Error.error(ErrorCode.X_42563); } nodes[LEFT].nodeDataTypes[i] = type; nodes[LEFT].nodes[i].dataType = type; } } void resolveTypesForIn(Session session) { resolveTypesForAllAny(session); } public Object getValue(Session session) { switch (opType) { case OpTypes.VALUE : return valueData; case OpTypes.NEGATE : return dataType.negate( nodes[LEFT].getValue(session, nodes[LEFT].dataType)); case OpTypes.IS_NOT_NULL : case OpTypes.IS_NULL : { switch (nodes[LEFT].opType) { case OpTypes.ROW : case OpTypes.ROW_SUBQUERY : case OpTypes.TABLE_SUBQUERY : { Object[] values = nodes[LEFT].getRowValue(session); for (int i = 0; i < values.length; i++) { if (values[i] == null) { if (opType == OpTypes.IS_NOT_NULL) { return Boolean.FALSE; } } else { if (opType == OpTypes.IS_NULL) { return Boolean.FALSE; } } } return Boolean.TRUE; } } return nodes[LEFT].getValue(session) == null ? Boolean.TRUE : Boolean.FALSE; } case OpTypes.OVERLAPS : { Object[] left = nodes[LEFT].getRowValue(session); Object[] right = nodes[RIGHT].getRowValue(session); return DateTimeType.overlaps(session, left, nodes[LEFT].nodeDataTypes, right, nodes[RIGHT].nodeDataTypes); } case OpTypes.IN : { return testInCondition(session); } 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 : { return testMatchCondition(session); } case OpTypes.NOT_DISTINCT : return testNotDistinctCondition(session); case OpTypes.UNIQUE : { nodes[LEFT].materialise(session); return nodes[LEFT].table.hasUniqueNotNullRows(session) ? Boolean.TRUE : Boolean.FALSE; } case OpTypes.EXISTS : { return testExistsCondition(session); } case OpTypes.NOT : { Boolean result = (Boolean) nodes[LEFT].getValue(session); return result == null ? null : result.booleanValue() ? Boolean.FALSE : Boolean.TRUE; } case OpTypes.AND : { Boolean r1 = (Boolean) nodes[LEFT].getValue(session); if (Boolean.FALSE.equals(r1)) { return Boolean.FALSE; } Boolean r2 = (Boolean) nodes[RIGHT].getValue(session); if (Boolean.FALSE.equals(r2)) { return Boolean.FALSE; } if (r1 == null || r2 == null) { return null; } return Boolean.TRUE; } case OpTypes.OR : { Boolean r1 = (Boolean) nodes[LEFT].getValue(session); if (Boolean.TRUE.equals(r1)) { return Boolean.TRUE; } Boolean r2 = (Boolean) nodes[RIGHT].getValue(session); if (Boolean.TRUE.equals(r2)) { return Boolean.TRUE; } if (r1 == null || r2 == null) { return null; } return Boolean.FALSE; } case OpTypes.EQUAL : case OpTypes.GREATER : case OpTypes.GREATER_EQUAL : case OpTypes.SMALLER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : case OpTypes.SMALLER : case OpTypes.NOT_EQUAL : { if (exprSubType == OpTypes.ANY_QUANTIFIED || exprSubType == OpTypes.ALL_QUANTIFIED) { return testAllAnyCondition(session); } Object o1 = nodes[LEFT].getValue(session); Object o2 = nodes[RIGHT].getValue(session); if (nodes[LEFT].dataType != null && nodes[LEFT].dataType.isArrayType()) { return compareValues(session, o1, o2); } if (o1 instanceof Object[]) { if (o2 != null && !(o2 instanceof Object[])) { throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } return compareValues(session, (Object[]) o1, (Object[]) o2); } else { if (o2 instanceof Object[]) { o2 = ((Object[]) o2)[0]; } return compareValues(session, o1, o2); } } default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } }
For MATCH SIMPLE and FULL expressions, nulls in left are handled prior to calling this method
/** * 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) { int result = 0; if (left == null || right == null) { return null; } result = nodes[LEFT].dataType.compare(session, left, right, opType); switch (opType) { case OpTypes.EQUAL : return result == 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.NOT_EQUAL : return result != 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.GREATER : return result > 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : return result >= 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.SMALLER_EQUAL : return result <= 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.SMALLER : return result < 0 ? Boolean.TRUE : Boolean.FALSE; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } }
For MATCH SIMPLE and FULL expressions, nulls in left are handled prior to calling this method
/** * For MATCH SIMPLE and FULL expressions, nulls in left are handled * prior to calling this method */
private Boolean compareValues(Session session, Object[] leftList, Object[] rightList) { int result = 0; boolean hasNull = false; if (leftList == null || rightList == null) { return null; } for (int i = 0; i < nodes[LEFT].nodes.length; i++) { if (leftList[i] == null) { if (opType == OpTypes.MATCH_PARTIAL || opType == OpTypes.MATCH_UNIQUE_PARTIAL) { continue; } hasNull = true; } if (rightList[i] == null) { hasNull = true; } Object leftValue = leftList[i]; Object rightValue = rightList[i]; Type[] types = nodes[LEFT].nodeDataTypes; result = types[i].compare(session, leftValue, rightValue); if (result != 0) { break; } } switch (opType) { case OpTypes.MATCH_SIMPLE : case OpTypes.MATCH_UNIQUE_SIMPLE : case OpTypes.MATCH_PARTIAL : case OpTypes.MATCH_UNIQUE_PARTIAL : case OpTypes.MATCH_FULL : case OpTypes.MATCH_UNIQUE_FULL : case OpTypes.NOT_DISTINCT : return result == 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.IN : case OpTypes.EQUAL : if (hasNull) { return null; } return result == 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.NOT_EQUAL : if (hasNull) { return null; } return result != 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.GREATER : if (hasNull) { return null; } return result > 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : if (hasNull) { return null; } return result >= 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.SMALLER_EQUAL : if (hasNull) { return null; } return result <= 0 ? Boolean.TRUE : Boolean.FALSE; case OpTypes.SMALLER : if (hasNull) { return null; } return result < 0 ? Boolean.TRUE : Boolean.FALSE; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } }
Returns the result of testing a VALUE_LIST expression
/** * Returns the result of testing a VALUE_LIST expression */
private Boolean testInCondition(Session session) { Object[] data = nodes[LEFT].getRowValue(session); if (data == null) { return null; } if (Expression.countNulls(data) != 0) { return null; } if (nodes[RIGHT].opType == OpTypes.VALUELIST) { final int length = nodes[RIGHT].nodes.length; for (int i = 0; i < length; i++) { Object[] rowData = nodes[RIGHT].nodes[i].getRowValue(session); if (Boolean.TRUE.equals(compareValues(session, data, rowData))) { return Boolean.TRUE; } } return Boolean.FALSE; } throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } private Boolean testNotDistinctCondition(Session session) { Object[] leftData = nodes[LEFT].getRowValue(session); Object[] rightData = nodes[RIGHT].getRowValue(session); if (leftData == null || rightData == null) { return leftData == rightData; } return compareValues(session, leftData, rightData); } private Boolean testMatchCondition(Session session) { Object[] data = nodes[LEFT].getRowValue(session); if (data == null) { return Boolean.TRUE; } final int nulls = countNulls(data); if (nulls != 0) { switch (opType) { case OpTypes.MATCH_SIMPLE : case OpTypes.MATCH_UNIQUE_SIMPLE : return Boolean.TRUE; case OpTypes.MATCH_PARTIAL : case OpTypes.MATCH_UNIQUE_PARTIAL : if (nulls == data.length) { return Boolean.TRUE; } break; case OpTypes.MATCH_FULL : case OpTypes.MATCH_UNIQUE_FULL : return nulls == data.length ? Boolean.TRUE : Boolean.FALSE; } } switch (nodes[RIGHT].opType) { case OpTypes.VALUELIST : { final int length = nodes[RIGHT].nodes.length; boolean hasMatch = false; for (int i = 0; i < length; i++) { Object[] rowData = nodes[RIGHT].nodes[i].getRowValue(session); Boolean result = compareValues(session, data, rowData); if (result == null || !result.booleanValue()) { continue; } switch (opType) { case OpTypes.MATCH_SIMPLE : case OpTypes.MATCH_PARTIAL : case OpTypes.MATCH_FULL : return Boolean.TRUE; case OpTypes.MATCH_UNIQUE_SIMPLE : case OpTypes.MATCH_UNIQUE_PARTIAL : case OpTypes.MATCH_UNIQUE_FULL : if (hasMatch) { return Boolean.FALSE; } hasMatch = true; break; default : } } return hasMatch ? Boolean.TRUE : Boolean.FALSE; } case OpTypes.TABLE_SUBQUERY : { PersistentStore store = nodes[RIGHT].getTable().getRowStore(session); nodes[RIGHT].materialise(session); convertToType(session, data, nodes[LEFT].nodeDataTypes, nodes[RIGHT].nodeDataTypes); if (nulls != 0 && (opType == OpTypes.MATCH_PARTIAL || opType == OpTypes.MATCH_UNIQUE_PARTIAL)) { boolean hasMatch = false; RowIterator it = nodes[RIGHT].getTable().rowIterator(session); while (it.next()) { Object[] rowData = it.getCurrent(); Boolean result = compareValues(session, data, rowData); if (result == null) { continue; } if (result.booleanValue()) { if (opType == OpTypes.MATCH_PARTIAL) { return Boolean.TRUE; } if (hasMatch) { return Boolean.FALSE; } hasMatch = true; } } return hasMatch ? Boolean.TRUE : Boolean.FALSE; } // now nulls == 0; RowIterator it = nodes[RIGHT].getTable().getFullIndex( session).findFirstRow(session, store, data); boolean result = it.next(); if (!result) { return Boolean.FALSE; } switch (opType) { case OpTypes.MATCH_SIMPLE : case OpTypes.MATCH_PARTIAL : case OpTypes.MATCH_FULL : return Boolean.TRUE; } // only one match allowed for MATCH UNIQUE xxxx while (true) { result = it.next(); if (!result) { break; } Object[] rowData = it.getCurrent(); if (Boolean.TRUE.equals(compareValues(session, data, rowData))) { return Boolean.FALSE; } } return Boolean.TRUE; } default : { throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } } } private Boolean testExistsCondition(Session session) { nodes[LEFT].materialise(session); return nodes[LEFT].getTable().isEmpty(session) ? Boolean.FALSE : Boolean.TRUE; } private Boolean testAllAnyCondition(Session session) { Object[] rowData = nodes[LEFT].getRowValue(session); TableDerived td = nodes[RIGHT].table; td.materialiseCorrelated(session); Boolean result = getAllAnyValue(session, rowData, td); return result; } private Boolean getAllAnyValue(Session session, Object[] data, TableDerived td) { Table table = td; boolean empty = table.isEmpty(session); Index index = table.getFullIndex(session); RowIterator it; PersistentStore store = table.getRowStore(session); Object[] firstdata; Object[] lastdata; boolean hasNullValue = false; for (int i = 0; i < table.columnCount; i++) { hasNullValue |= store.hasNull(i); } switch (exprSubType) { case OpTypes.ANY_QUANTIFIED : { if (empty) { return Boolean.FALSE; } if (countNulls(data) == data.length) { return null; } convertToType(session, data, nodes[LEFT].nodeDataTypes, nodes[RIGHT].nodeDataTypes); if (opType == OpTypes.EQUAL) { it = index.findFirstRow(session, store, data); if (it.next()) { return Boolean.TRUE; } else { if (hasNullValue) { return null; } else { return Boolean.FALSE; } } } if (opType == OpTypes.NOT_EQUAL) { it = index.firstRow(session, store, null, 0, null); } else { it = index.findFirstRowNotNull(session, store); } if (!it.next()) { return null; } firstdata = it.getCurrent(); RowIterator lastIt = index.lastRow(session, store, 0, null); lastIt.next(); lastdata = lastIt.getCurrent(); Boolean comparefirst = compareValues(session, data, firstdata); Boolean comparelast = compareValues(session, data, lastdata); switch (opType) { case OpTypes.NOT_EQUAL : if (Boolean.TRUE.equals(comparefirst) || Boolean.TRUE.equals(comparelast)) { return Boolean.TRUE; } else if (Boolean.FALSE.equals(comparefirst) && Boolean.FALSE.equals(comparelast)) { it = index.findFirstRow(session, store, data); return Boolean.FALSE; } else { return null; } case OpTypes.GREATER : return comparefirst; case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : return comparefirst; case OpTypes.SMALLER : return comparelast; case OpTypes.SMALLER_EQUAL : return comparelast; } break; } case OpTypes.ALL_QUANTIFIED : { if (empty) { return Boolean.TRUE; } if (countNulls(data) == data.length) { return null; } it = index.firstRow(session, store, null, 0, null); it.next(); firstdata = it.getCurrent(); if (countNulls(firstdata) == data.length) { return null; } convertToType(session, data, nodes[LEFT].nodeDataTypes, nodes[RIGHT].nodeDataTypes); it = index.findFirstRow(session, store, data); if (opType == OpTypes.EQUAL) { if (it.next()) { return store.elementCount() == 1 ? Boolean.TRUE : Boolean.FALSE; } else { return Boolean.FALSE; } } if (opType == OpTypes.NOT_EQUAL) { return it.next() ? Boolean.FALSE : Boolean.TRUE; } RowIterator lastIt = index.lastRow(session, store, 0, null); lastIt.next(); lastdata = lastIt.getCurrent(); Boolean comparefirst = compareValues(session, data, firstdata); Boolean comparelast = compareValues(session, data, lastdata); switch (opType) { case OpTypes.GREATER : return comparelast; case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : return comparelast; case OpTypes.SMALLER : return comparefirst; case OpTypes.SMALLER_EQUAL : return comparefirst; } break; } } return null; }
Converts an OR containing an AND to an AND
/** * Converts an OR containing an AND to an AND */
void distributeOr() { if (opType != OpTypes.OR) { return; } if (nodes[LEFT].opType == OpTypes.AND) { opType = OpTypes.AND; Expression temp = new ExpressionLogical(OpTypes.OR, nodes[LEFT].nodes[RIGHT], nodes[RIGHT]); nodes[LEFT].opType = OpTypes.OR; nodes[LEFT].nodes[RIGHT] = nodes[RIGHT]; nodes[RIGHT] = temp; } else if (nodes[RIGHT].opType == OpTypes.AND) { Expression temp = nodes[LEFT]; nodes[LEFT] = nodes[RIGHT]; nodes[RIGHT] = temp; distributeOr(); return; } ((ExpressionLogical) nodes[LEFT]).distributeOr(); ((ExpressionLogical) nodes[RIGHT]).distributeOr(); } /** * */ public boolean isIndexable(RangeVariable rangeVar) { boolean result; switch (opType) { case OpTypes.AND : { result = nodes[LEFT].isIndexable(rangeVar) || nodes[RIGHT].isIndexable(rangeVar); return result; } case OpTypes.OR : { result = nodes[LEFT].isIndexable(rangeVar) && nodes[RIGHT].isIndexable(rangeVar); return result; } default : { Expression temp = getIndexableExpression(rangeVar); return temp != null; } } } Expression getIndexableExpression(RangeVariable rangeVar) { switch (opType) { case OpTypes.IS_NULL : return nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].isIndexable(rangeVar) ? this : null; case OpTypes.NOT : return nodes[LEFT].opType == OpTypes.IS_NULL && nodes[LEFT].nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].nodes[LEFT].isIndexable(rangeVar) ? this : null; case OpTypes.EQUAL : if (exprSubType == OpTypes.ANY_QUANTIFIED) { if (nodes[RIGHT].isCorrelated()) { return null; } for (int node = 0; node < nodes[LEFT].nodes.length; node++) { if (nodes[LEFT].nodes[node].opType == OpTypes.COLUMN && nodes[LEFT].nodes[node].isIndexable( rangeVar)) { return this; } } return null; } // fall through case OpTypes.GREATER : case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : case OpTypes.SMALLER : case OpTypes.SMALLER_EQUAL : if (exprSubType != 0) { return null; } if (nodes[RIGHT].isCorrelated()) { return null; } if (nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].isIndexable(rangeVar)) { if (nodes[RIGHT].hasReference(rangeVar)) { return null; } return this; } if (nodes[LEFT].hasReference(rangeVar)) { return null; } if (nodes[RIGHT].opType == OpTypes.COLUMN && nodes[RIGHT].isIndexable(rangeVar)) { swapCondition(); return this; } return null; case OpTypes.OR : if (isIndexable(rangeVar)) { return this; } return null; default : return null; } }
Called only on comparison expressions after reordering which have a COLUMN left leaf
/** * Called only on comparison expressions after reordering which have * a COLUMN left leaf */
boolean isSimpleBound() { if (opType == OpTypes.IS_NULL) { return true; } if (nodes[RIGHT] != null) { if (nodes[RIGHT].opType == OpTypes.VALUE) { // also true for all parameters return true; } if (nodes[RIGHT].opType == OpTypes.SQL_FUNCTION) { if (((FunctionSQL) nodes[RIGHT]).isValueFunction()) { return true; } } } return false; } boolean convertToSmaller() { switch (opType) { case OpTypes.GREATER_EQUAL : case OpTypes.GREATER : swapCondition(); return true; case OpTypes.SMALLER_EQUAL : case OpTypes.SMALLER : return true; default : return false; } }
Swap the condition with its complement
/** * Swap the condition with its complement */
void swapCondition() { int i = OpTypes.EQUAL; switch (opType) { case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : i = OpTypes.SMALLER_EQUAL; break; case OpTypes.SMALLER_EQUAL : i = OpTypes.GREATER_EQUAL; break; case OpTypes.SMALLER : i = OpTypes.GREATER; break; case OpTypes.GREATER : i = OpTypes.SMALLER; break; case OpTypes.NOT_DISTINCT : i = OpTypes.NOT_DISTINCT; break; case OpTypes.EQUAL : break; default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } opType = i; Expression e = nodes[LEFT]; nodes[LEFT] = nodes[RIGHT]; nodes[RIGHT] = e; } boolean reorderComparison(Session session, Expression parent) { Expression colExpression = null; Expression nonColExpression = null; boolean left = false; boolean replaceColumn = false; int operation = 0; if (nodes[LEFT].opType == OpTypes.ADD) { operation = OpTypes.SUBTRACT; left = true; } else if (nodes[LEFT].opType == OpTypes.SUBTRACT) { operation = OpTypes.ADD; left = true; } else if (nodes[RIGHT].opType == OpTypes.ADD) { operation = OpTypes.SUBTRACT; } else if (nodes[RIGHT].opType == OpTypes.SUBTRACT) { operation = OpTypes.ADD; } if (operation == 0) { return false; } if (left) { if (nodes[LEFT].nodes[LEFT].opType == OpTypes.COLUMN) { colExpression = nodes[LEFT].nodes[LEFT]; nonColExpression = nodes[LEFT].nodes[RIGHT]; } else if (nodes[LEFT].nodes[RIGHT].opType == OpTypes.COLUMN) { replaceColumn = operation == OpTypes.ADD; colExpression = nodes[LEFT].nodes[RIGHT]; nonColExpression = nodes[LEFT].nodes[LEFT]; } } else { if (nodes[RIGHT].nodes[LEFT].opType == OpTypes.COLUMN) { colExpression = nodes[RIGHT].nodes[LEFT]; nonColExpression = nodes[RIGHT].nodes[RIGHT]; } else if (nodes[RIGHT].nodes[RIGHT].opType == OpTypes.COLUMN) { replaceColumn = operation == OpTypes.ADD; colExpression = nodes[RIGHT].nodes[RIGHT]; nonColExpression = nodes[RIGHT].nodes[LEFT]; } } if (colExpression == null) { return false; } Expression otherExpression = left ? nodes[RIGHT] : nodes[LEFT]; ExpressionArithmetic newArg = null; if (!replaceColumn) { newArg = new ExpressionArithmetic(operation, otherExpression, nonColExpression); newArg.resolveTypesForArithmetic(session, parent); } if (left) { if (replaceColumn) { nodes[RIGHT] = colExpression; nodes[LEFT].nodes[RIGHT] = otherExpression; ((ExpressionArithmetic) nodes[LEFT]).resolveTypesForArithmetic( session, parent); } else { nodes[LEFT] = colExpression; nodes[RIGHT] = newArg; } } else { if (replaceColumn) { nodes[LEFT] = colExpression; nodes[RIGHT].nodes[RIGHT] = otherExpression; ((ExpressionArithmetic) nodes[RIGHT]) .resolveTypesForArithmetic(session, parent); } else { nodes[RIGHT] = colExpression; nodes[LEFT] = newArg; } } return true; } boolean isConditionRangeVariable(RangeVariable range) { if (nodes[LEFT].getRangeVariable() == range) { return true; } if (nodes[RIGHT].getRangeVariable() == range) { return true; } return false; } void getJoinRangeVariables(RangeVariable[] ranges, HsqlList list) { for (int i = 0; i < nodes.length; i++) { nodes[i].getJoinRangeVariables(ranges, list); } } double costFactor(Session session, RangeVariable rangeVar, int operation) { double cost; switch (opType) { case OpTypes.OR : { return nodes[LEFT].costFactor(session, rangeVar, opType) + nodes[RIGHT].costFactor(session, rangeVar, opType); } case OpTypes.OVERLAPS : case OpTypes.IN : 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.NOT_DISTINCT : { PersistentStore store = rangeVar.rangeTable.getRowStore(session); cost = store.elementCount(); if (cost < Index.minimumSelectivity) { cost = Index.minimumSelectivity; } break; } case OpTypes.IS_NULL : case OpTypes.NOT : { cost = costFactorUnaryColumn(session, rangeVar); break; } case OpTypes.EQUAL : { switch (exprSubType) { case OpTypes.ANY_QUANTIFIED : { if (nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].getRangeVariable() == rangeVar) { cost = costFactorColumns(session, rangeVar); cost *= 1024; break; } } // fall through case OpTypes.ALL_QUANTIFIED : { PersistentStore store = rangeVar.rangeTable.getRowStore(session); cost = store.elementCount(); if (cost < Index.minimumSelectivity) { cost = Index.minimumSelectivity; } cost *= 1024; break; } default : cost = costFactorColumns(session, rangeVar); } break; } case OpTypes.GREATER : case OpTypes.GREATER_EQUAL : case OpTypes.GREATER_EQUAL_PRE : case OpTypes.SMALLER : case OpTypes.SMALLER_EQUAL : { cost = costFactorColumns(session, rangeVar); break; } default : throw Error.runtimeError(ErrorCode.U_S0500, "ExpressionLogical"); } return cost; } double costFactorUnaryColumn(Session session, RangeVariable rangeVar) { if (nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].getRangeVariable() == rangeVar) { return nodes[LEFT].costFactor(session, rangeVar, opType); } else { PersistentStore store = rangeVar.rangeTable.getRowStore(session); double cost = store.elementCount(); return cost < Index.minimumSelectivity ? Index.minimumSelectivity : cost; } } double costFactorColumns(Session session, RangeVariable rangeVar) { double cost = 0; if (nodes[LEFT].opType == OpTypes.COLUMN && nodes[LEFT].getRangeVariable() == rangeVar) { if (!nodes[RIGHT].hasReference(rangeVar)) { cost = nodes[LEFT].costFactor(session, rangeVar, opType); } } else if (nodes[RIGHT].opType == OpTypes.COLUMN && nodes[RIGHT].getRangeVariable() == rangeVar) { if (!nodes[LEFT].hasReference(rangeVar)) { cost = nodes[RIGHT].costFactor(session, rangeVar, opType); } } else { PersistentStore store = rangeVar.rangeTable.getRowStore(session); cost = store.elementCount(); } if (cost == 0) { PersistentStore store = rangeVar.rangeTable.getRowStore(session); cost = store.elementCount(); } if (cost < Index.minimumSelectivity) { cost = Index.minimumSelectivity; } return cost; } }