--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.InListOperatorNode\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+/**\r
+ * An InListOperatorNode represents an IN list.\r
+ *\r
+ */\r
+\r
+public final class InListOperatorNode extends BinaryListOperatorNode\r
+{\r
+ private boolean isOrdered;\r
+ private boolean sortDescending;\r
+\r
+ /**\r
+ * Initializer for a InListOperatorNode\r
+ *\r
+ * @param leftOperand The left operand of the node\r
+ * @param rightOperandList The right operand list of the node\r
+ */\r
+\r
+ public void init(Object leftOperand, Object rightOperandList)\r
+ {\r
+ init(leftOperand, rightOperandList, "IN", "in");\r
+ }\r
+\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ return "isOrdered: " + isOrdered + "\n" +\r
+ super.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Create a shallow copy of this InListOperatorNode whose operands are\r
+ * the same as this node's operands. Copy over all other necessary\r
+ * state, as well.\r
+ */\r
+ protected InListOperatorNode shallowCopy() throws StandardException\r
+ {\r
+ InListOperatorNode ilon =\r
+ (InListOperatorNode)getNodeFactory().getNode(\r
+ C_NodeTypes.IN_LIST_OPERATOR_NODE,\r
+ leftOperand,\r
+ rightOperandList,\r
+ getContextManager());\r
+\r
+ ilon.copyFields(this);\r
+ if (isOrdered)\r
+ ilon.markAsOrdered();\r
+\r
+ if (sortDescending)\r
+ ilon.markSortDescending();\r
+\r
+ return ilon;\r
+ }\r
+\r
+ /**\r
+ * Preprocess an expression tree. We do a number of transformations\r
+ * here (including subqueries, IN lists, LIKE and BETWEEN) plus\r
+ * subquery flattening.\r
+ * NOTE: This is done before the outer ResultSetNode is preprocessed.\r
+ *\r
+ * @param numTables Number of tables in the DML Statement\r
+ * @param outerFromList FromList from outer query block\r
+ * @param outerSubqueryList SubqueryList from outer query block\r
+ * @param outerPredicateList PredicateList from outer query block\r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ValueNode preprocess(int numTables,\r
+ FromList outerFromList,\r
+ SubqueryList outerSubqueryList,\r
+ PredicateList outerPredicateList) \r
+ throws StandardException\r
+ {\r
+ super.preprocess(numTables,\r
+ outerFromList, outerSubqueryList,\r
+ outerPredicateList);\r
+\r
+ /* Check for the degenerate case of a single element in the IN list.\r
+ * If found, then convert to "=".\r
+ */\r
+ if (rightOperandList.size() == 1)\r
+ {\r
+ BinaryComparisonOperatorNode equal = \r
+ (BinaryComparisonOperatorNode) getNodeFactory().getNode(\r
+ C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+ leftOperand, \r
+ (ValueNode) rightOperandList.elementAt(0),\r
+ getContextManager());\r
+ /* Set type info for the operator node */\r
+ equal.bindComparisonOperator();\r
+ return equal;\r
+ }\r
+ else if ((leftOperand instanceof ColumnReference) &&\r
+ rightOperandList.containsOnlyConstantAndParamNodes())\r
+ {\r
+ /* At this point we have an IN-list made up of constant and/or\r
+ * parameter values. Ex.:\r
+ *\r
+ * select id, name from emp where id in (34, 28, ?)\r
+ *\r
+ * Since the optimizer does not recognize InListOperatorNodes\r
+ * as potential start/stop keys for indexes, it (the optimizer)\r
+ * may estimate that the cost of using any of the indexes would\r
+ * be too high. So we could--and probably would--end up doing\r
+ * a table scan on the underlying base table. But if the number\r
+ * of rows in the base table is significantly greater than the\r
+ * number of values in the IN-list, scanning the base table can\r
+ * be overkill and can lead to poor performance. And further,\r
+ * choosing to use an index but then scanning the entire index\r
+ * can be slow, too. DERBY-47.\r
+ *\r
+ * What we do, then, is create an "IN-list probe predicate",\r
+ * which is an internally generated equality predicate with a\r
+ * parameter value on the right. So for the query shown above\r
+ * the probe predicate would be "id = ?". We then replace\r
+ * this InListOperatorNode with the probe predicate during\r
+ * optimization. The optimizer in turn recognizes the probe\r
+ * predicate, which is disguised to look like a typical binary\r
+ * equality, as a potential start/stop key for any indexes.\r
+ * This start/stop key potential then factors into the estimated\r
+ * cost of probing the indexes, which leads to a more reasonable\r
+ * estimate and thus makes it more likely that the optimizer\r
+ * will choose to use an index vs a table scan. That done, we\r
+ * then use the probe predicate to perform multiple execution-\r
+ * time "probes" on the index--instead of doing a range index\r
+ * scan--which eliminates unnecessary scanning. For more see\r
+ * execute/MultiProbeTableScanResultSet.java.\r
+ *\r
+ * With this approach we know that regardless of how large the\r
+ * base table is, we'll only have to probe the index a max of\r
+ * N times, where "N" is the size of the IN-list. If N is\r
+ * significantly less than the number of rows in the table, or\r
+ * is significantly less than the number of rows between the\r
+ * min value and the max value in the IN-list, this selective\r
+ * probing can save us a lot of time.\r
+ *\r
+ * Note: We will do fewer than N probes if there are duplicates\r
+ * in the list.\r
+ *\r
+ * Note also that, depending on the relative size of the IN-list\r
+ * verses the number of rows in the table, it may actually be\r
+ * better to just do a table scan--especially if there are fewer\r
+ * rows in the table than there are in the IN-list. So even though\r
+ * we create a "probe predicate" and pass it to the optimizer, it\r
+ * (the optimizer) may still choose to do a table scan. If that\r
+ * happens then we'll "revert" the probe predicate back to its\r
+ * original form (i.e. to this InListOperatorNode) during code\r
+ * generation, and then we'll use it as a regular IN-list\r
+ * restriction when it comes time to execute.\r
+ */\r
+\r
+ boolean allConstants = rightOperandList.containsAllConstantNodes();\r
+\r
+ /* If we have all constants then sort them now. This allows us to\r
+ * skip the sort at execution time (we have to sort them so that\r
+ * we can eliminate duplicate IN-list values). If we have one\r
+ * or more parameter nodes then we do *not* sort the values here\r
+ * because we do not (and cannot) know what values the parameter(s)\r
+ * will have. In that case we'll sort the values at execution\r
+ * time. \r
+ */\r
+ if (allConstants)\r
+ {\r
+ /* When sorting or choosing min/max in the list, if types\r
+ * are not an exact match then we have to use the *dominant*\r
+ * type across all values, where "all values" includes the\r
+ * left operand. Otherwise we can end up with incorrect\r
+ * results.\r
+ *\r
+ * Note that it is *not* enough to just use the left operand's\r
+ * type as the judge because we have no guarantee that the\r
+ * left operand has the dominant type. If, for example, the\r
+ * left operand has type INTEGER and all (or any) values in\r
+ * the IN list have type DECIMAL, use of the left op's type\r
+ * would lead to comparisons with truncated values and could\r
+ * therefore lead to an incorrect sort order. DERBY-2256.\r
+ */\r
+ DataTypeDescriptor targetType = leftOperand.getTypeServices();\r
+ TypeId judgeTypeId = targetType.getTypeId();\r
+\r
+ if (!rightOperandList.allSamePrecendence(\r
+ judgeTypeId.typePrecedence()))\r
+ {\r
+ /* Iterate through the entire list of values to find out\r
+ * what the dominant type is.\r
+ */\r
+ ClassFactory cf = getClassFactory();\r
+ int sz = rightOperandList.size();\r
+ for (int i = 0; i < sz; i++)\r
+ {\r
+ ValueNode vn = (ValueNode)rightOperandList.elementAt(i);\r
+ targetType =\r
+ targetType.getDominantType(\r
+ vn.getTypeServices(), cf);\r
+ }\r
+ }\r
+ \r
+ /* Now wort the list in ascending order using the dominant\r
+ * type found above.\r
+ */\r
+ DataValueDescriptor judgeODV = targetType.getNull();\r
+\r
+ rightOperandList.sortInAscendingOrder(judgeODV);\r
+ isOrdered = true;\r
+\r
+ ValueNode minValue = (ValueNode)rightOperandList.elementAt(0);\r
+ ValueNode maxValue =\r
+ (ValueNode)rightOperandList.elementAt(\r
+ rightOperandList.size() - 1);\r
+\r
+ /* Handle the degenerate case where the min and the max\r
+ * are the same value. Note (again) that we need to do\r
+ * this comparison using the dominant type found above.\r
+ */\r
+ DataValueDescriptor minODV =\r
+ ((ConstantNode) minValue).getValue();\r
+ DataValueDescriptor maxODV =\r
+ ((ConstantNode) maxValue).getValue();\r
+\r
+ if (judgeODV.equals(minODV, maxODV).equals(true))\r
+ {\r
+ BinaryComparisonOperatorNode equal = \r
+ (BinaryComparisonOperatorNode)getNodeFactory().getNode(\r
+ C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+ leftOperand, \r
+ minValue,\r
+ getContextManager());\r
+ /* Set type info for the operator node */\r
+ equal.bindComparisonOperator();\r
+ return equal;\r
+ }\r
+ }\r
+\r
+ /* Create a parameter node to serve as the right operand of\r
+ * the probe predicate. We intentionally use a parameter node\r
+ * instead of a constant node because the IN-list has more than\r
+ * one value (some of which may be unknown at compile time, i.e.\r
+ * if they are parameters), so we don't want an estimate based\r
+ * on any single literal. Instead we want a generic estimate\r
+ * of the cost to retrieve the rows matching some _unspecified_\r
+ * value (namely, one of the values in the IN-list, but we\r
+ * don't know which one). That's exactly what a parameter\r
+ * node gives us.\r
+ *\r
+ * Note: If the IN-list only had a single value then we would\r
+ * have taken the "if (rightOperandList.size() == 1)" branch\r
+ * above and thus would not be here.\r
+ *\r
+ * We create the parameter node based on the first value in\r
+ * the list. This is arbitrary and should not matter in the\r
+ * big picture.\r
+ */\r
+ ValueNode srcVal = (ValueNode) rightOperandList.elementAt(0);\r
+ ParameterNode pNode =\r
+ (ParameterNode) getNodeFactory().getNode(\r
+ C_NodeTypes.PARAMETER_NODE,\r
+ new Integer(0),\r
+ null, // default value\r
+ getContextManager());\r
+\r
+ DataTypeDescriptor pType = srcVal.getTypeServices();\r
+ pNode.setType(pType);\r
+\r
+ /* If we choose to use the new predicate for execution-time\r
+ * probing then the right operand will function as a start-key\r
+ * "place-holder" into which we'll store the different IN-list\r
+ * values as we iterate through them. This means we have to\r
+ * generate a valid value for the parameter node--i.e. for the\r
+ * right side of the probe predicate--in order to have a valid\r
+ * execution-time placeholder. To do that we pass the source\r
+ * value from which we found the type down to the new, "fake"\r
+ * parameter node. Then, when it comes time to generate the\r
+ * parameter node, we'll just generate the source value as our\r
+ * place-holder. See ParameterNode.generateExpression().\r
+ *\r
+ * Note: the actual value of the "place-holder" does not matter\r
+ * because it will be clobbered by the various IN-list values\r
+ * (which includes "srcVal" itself) as we iterate through them\r
+ * during execution.\r
+ */\r
+ pNode.setValueToGenerate(srcVal);\r
+\r
+ /* Finally, create the "column = ?" equality that serves as the\r
+ * basis for the probe predicate. We store a reference to "this"\r
+ * node inside the probe predicate so that, if we later decide\r
+ * *not* to use the probe predicate for execution time index\r
+ * probing, we can revert it back to its original form (i.e.\r
+ * to "this").\r
+ */\r
+ BinaryComparisonOperatorNode equal = \r
+ (BinaryComparisonOperatorNode) getNodeFactory().getNode(\r
+ C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+ leftOperand, \r
+ pNode,\r
+ this,\r
+ getContextManager());\r
+\r
+ /* Set type info for the operator node */\r
+ equal.bindComparisonOperator();\r
+ return equal;\r
+ }\r
+ else\r
+ {\r
+ return this;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Eliminate NotNodes in the current query block. We traverse the tree, \r
+ * inverting ANDs and ORs and eliminating NOTs as we go. We stop at \r
+ * ComparisonOperators and boolean expressions. We invert \r
+ * ComparisonOperators and replace boolean expressions with \r
+ * boolean expression = false.\r
+ * NOTE: Since we do not recurse under ComparisonOperators, there\r
+ * still could be NotNodes left in the tree.\r
+ *\r
+ * @param underNotNode Whether or not we are under a NotNode.\r
+ * \r
+ *\r
+ * @return The modified expression\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ ValueNode eliminateNots(boolean underNotNode) \r
+ throws StandardException\r
+ {\r
+ AndNode newAnd = null;\r
+ BinaryComparisonOperatorNode leftBCO;\r
+ BinaryComparisonOperatorNode rightBCO;\r
+ int listSize = rightOperandList.size();\r
+ ValueNode leftSide;\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(listSize > 0,\r
+ "rightOperandList.size() is expected to be > 0");\r
+\r
+ if (! underNotNode)\r
+ {\r
+ return this;\r
+ }\r
+\r
+ /* we want to convert the IN List into = OR = ... as\r
+ * described below. \r
+ */\r
+\r
+ /* Convert:\r
+ * leftO IN rightOList.elementAt(0) , rightOList.elementAt(1) ...\r
+ * to:\r
+ * leftO <> rightOList.elementAt(0) AND leftO <> rightOList.elementAt(1) ...\r
+ * NOTE - We do the conversion here since the single table clauses\r
+ * can be pushed down and the optimizer may eventually have a filter factor\r
+ * for <>.\r
+ */\r
+\r
+ /* leftO <> rightOList.at(0) */\r
+ /* If leftOperand is a ColumnReference, it may be remapped during optimization, and that\r
+ * requires each <> node to have a separate object.\r
+ */\r
+ ValueNode leftClone = (leftOperand instanceof ColumnReference) ? leftOperand.getClone() : leftOperand;\r
+ leftBCO = (BinaryComparisonOperatorNode) \r
+ getNodeFactory().getNode(\r
+ C_NodeTypes.BINARY_NOT_EQUALS_OPERATOR_NODE,\r
+ leftClone,\r
+ (ValueNode) rightOperandList.elementAt(0),\r
+ getContextManager());\r
+ /* Set type info for the operator node */\r
+ leftBCO.bindComparisonOperator();\r
+\r
+ leftSide = leftBCO;\r
+\r
+ for (int elemsDone = 1; elemsDone < listSize; elemsDone++)\r
+ {\r
+\r
+ /* leftO <> rightOList.elementAt(elemsDone) */\r
+ leftClone = (leftOperand instanceof ColumnReference) ? leftOperand.getClone() : leftOperand;\r
+ rightBCO = (BinaryComparisonOperatorNode) \r
+ getNodeFactory().getNode(\r
+ C_NodeTypes.BINARY_NOT_EQUALS_OPERATOR_NODE,\r
+ leftClone,\r
+ (ValueNode) rightOperandList.elementAt(elemsDone),\r
+ getContextManager());\r
+ /* Set type info for the operator node */\r
+ rightBCO.bindComparisonOperator();\r
+\r
+ /* Create the AND */\r
+ newAnd = (AndNode) getNodeFactory().getNode(\r
+ C_NodeTypes.AND_NODE,\r
+ leftSide,\r
+ rightBCO,\r
+ getContextManager());\r
+ newAnd.postBindFixup();\r
+\r
+ leftSide = newAnd;\r
+ }\r
+\r
+ return leftSide;\r
+ }\r
+\r
+ /**\r
+ * See if this IN list operator is referencing the same table.\r
+ *\r
+ * @param cr The column reference.\r
+ *\r
+ * @return true if in list references the same table as in cr.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean selfReference(ColumnReference cr)\r
+ throws StandardException\r
+ {\r
+ int size = rightOperandList.size();\r
+ for (int i = 0; i < size; i++)\r
+ {\r
+ ValueNode vn = (ValueNode) rightOperandList.elementAt(i);\r
+ if (vn.getTablesReferenced().get(cr.getTableNumber()))\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * The selectivity for an "IN" predicate is generally very small.\r
+ * This is an estimate applicable when in list are not all constants.\r
+ */\r
+ public double selectivity(Optimizable optTable)\r
+ {\r
+ return 0.3d;\r
+ }\r
+ \r
+ /**\r
+ * Do code generation for this IN list operator.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class we're generating\r
+ * @param mb The MethodBuilder the expression will go into\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void generateExpression(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ int listSize = rightOperandList.size();\r
+ String resultTypeName;\r
+ String receiverType = ClassName.DataValueDescriptor;\r
+ \r
+ String leftInterfaceType = ClassName.DataValueDescriptor;\r
+ String rightInterfaceType = ClassName.DataValueDescriptor + "[]";\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(listSize > 0,\r
+ "listSize is expected to be > 0");\r
+ }\r
+\r
+ /*\r
+ ** There are 2 parts to the code generation for an IN list -\r
+ ** the code in the constructor and the code for the expression evaluation.\r
+ ** The code that gets generated for the constructor is:\r
+ ** DataValueDescriptor[] field = new DataValueDescriptor[size];\r
+ ** For each element in the IN list that is a constant, we also generate:\r
+ ** field[i] = rightOperandList[i];\r
+ ** \r
+ ** If the IN list is composed entirely of constants, then we generate the\r
+ ** the following:\r
+ ** leftOperand.in(rightOperandList, leftOperand, isNullable(), ordered, result);\r
+ **\r
+ ** Otherwise, we create a new method. This method contains the \r
+ ** assignment of the non-constant elements into the array and the call to the in()\r
+ ** method, which is in the new method's return statement. We then return a call\r
+ ** to the new method.\r
+ */\r
+\r
+ receiver = leftOperand;\r
+\r
+ /* Figure out the result type name */\r
+ resultTypeName = getTypeCompiler().interfaceName();\r
+\r
+ // Generate the code to build the array\r
+ LocalField arrayField = generateListAsArray(acb, mb);\r
+\r
+ /*\r
+ ** Call the method for this operator.\r
+ */\r
+ /*\r
+ ** Generate (field = <left expression>). This assignment is\r
+ ** used as the receiver of the method call for this operator,\r
+ ** and the field is used as the left operand:\r
+ **\r
+ ** (field = <left expression>).method(field, <right expression>...)\r
+ */\r
+\r
+ //LocalField receiverField =\r
+ // acb.newFieldDeclaration(Modifier.PRIVATE, receiverType);\r
+\r
+ leftOperand.generateExpression(acb, mb);\r
+ mb.dup();\r
+ //mb.putField(receiverField); // instance for method call\r
+ /*mb.getField(receiverField);*/ mb.upCast(leftInterfaceType); // first arg\r
+ mb.getField(arrayField); // second arg\r
+ mb.push(isOrdered); // third arg\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, receiverType, methodName, resultTypeName, 3);\r
+ }\r
+\r
+ /**\r
+ * Generate the code to create an array of DataValueDescriptors that\r
+ * will hold the IN-list values at execution time. The array gets\r
+ * created in the constructor. All constant elements in the array\r
+ * are initialized in the constructor. All non-constant elements,\r
+ * if any, are initialized each time the IN list is evaluated.\r
+ *\r
+ * @param acb The ExpressionClassBuilder for the class we're generating\r
+ * @param mb The MethodBuilder the expression will go into\r
+ */\r
+ protected LocalField generateListAsArray(ExpressionClassBuilder acb,\r
+ MethodBuilder mb) throws StandardException\r
+ {\r
+ int listSize = rightOperandList.size();\r
+ LocalField arrayField = acb.newFieldDeclaration(\r
+ Modifier.PRIVATE, ClassName.DataValueDescriptor + "[]");\r
+\r
+ /* Assign the initializer to the DataValueDescriptor[] field */\r
+ MethodBuilder cb = acb.getConstructor();\r
+ cb.pushNewArray(ClassName.DataValueDescriptor, listSize);\r
+ cb.setField(arrayField);\r
+\r
+ /* Set the array elements that are constant */\r
+ int numConstants = 0;\r
+ MethodBuilder nonConstantMethod = null;\r
+ MethodBuilder currentConstMethod = cb;\r
+ for (int index = 0; index < listSize; index++)\r
+ {\r
+ MethodBuilder setArrayMethod;\r
+ \r
+ if (rightOperandList.elementAt(index) instanceof ConstantNode)\r
+ {\r
+ numConstants++;\r
+ \r
+ /*if too many statements are added to a method, \r
+ *size of method can hit 65k limit, which will\r
+ *lead to the class format errors at load time.\r
+ *To avoid this problem, when number of statements added \r
+ *to a method is > 2048, remaing statements are added to a new function\r
+ *and called from the function which created the function.\r
+ *See Beetle 5135 or 4293 for further details on this type of problem.\r
+ */\r
+ if(currentConstMethod.statementNumHitLimit(1))\r
+ {\r
+ MethodBuilder genConstantMethod = acb.newGeneratedFun("void", Modifier.PRIVATE);\r
+ currentConstMethod.pushThis();\r
+ currentConstMethod.callMethod(VMOpcode.INVOKEVIRTUAL,\r
+ (String) null, \r
+ genConstantMethod.getName(),\r
+ "void", 0);\r
+ //if it is a generate function, close the metod.\r
+ if(currentConstMethod != cb){\r
+ currentConstMethod.methodReturn();\r
+ currentConstMethod.complete();\r
+ }\r
+ currentConstMethod = genConstantMethod;\r
+ }\r
+ setArrayMethod = currentConstMethod;\r
+ } else {\r
+ if (nonConstantMethod == null)\r
+ nonConstantMethod = acb.newGeneratedFun("void", Modifier.PROTECTED);\r
+ setArrayMethod = nonConstantMethod;\r
+\r
+ }\r
+\r
+ setArrayMethod.getField(arrayField); // first arg\r
+ ((ValueNode) rightOperandList.elementAt(index)).generateExpression(acb, setArrayMethod);\r
+ setArrayMethod.upCast(ClassName.DataValueDescriptor); // second arg\r
+ setArrayMethod.setArrayElement(index);\r
+ }\r
+\r
+ //if a generated function was created to reduce the size of the methods close the functions.\r
+ if(currentConstMethod != cb){\r
+ currentConstMethod.methodReturn();\r
+ currentConstMethod.complete();\r
+ }\r
+\r
+ if (nonConstantMethod != null) {\r
+ nonConstantMethod.methodReturn();\r
+ nonConstantMethod.complete();\r
+ mb.pushThis();\r
+ mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, nonConstantMethod.getName(), "void", 0);\r
+ }\r
+\r
+ return arrayField;\r
+ }\r
+\r
+\r
+ /**\r
+ * Generate start/stop key for this IN list operator. Bug 3858.\r
+ *\r
+ * @param isAsc is the index ascending on the column in question\r
+ * @param isStartKey are we generating start key or not\r
+ * @param acb The ExpressionClassBuilder for the class we're generating\r
+ * @param mb The MethodBuilder the expression will go into\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generateStartStopKey(boolean isAsc, boolean isStartKey,\r
+ ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ /* left side of the "in" operator is our "judge" when we try to get\r
+ * the min/max value of the operands on the right side. Judge's type\r
+ * is important for us, and is input parameter to min/maxValue.\r
+ */\r
+ int leftTypeFormatId = leftOperand.getTypeId().getTypeFormatId();\r
+ int leftJDBCTypeId = leftOperand.getTypeId().isUserDefinedTypeId() ?\r
+ leftOperand.getTypeId().getJDBCTypeId() : -1;\r
+\r
+ int listSize = rightOperandList.size();\r
+ int numLoops, numValInLastLoop, currentOpnd = 0;\r
+\r
+ /* We first calculate how many times (loops) we generate a call to\r
+ * min/maxValue function accumulatively, since each time it at most\r
+ * takes 4 input values. An example of the calls generated will be:\r
+ * minVal(minVal(...minVal(minVal(v1,v2,v3,v4,judge), v5,v6,v7,judge),\r
+ * ...), vn-1, vn, NULL, judge)\r
+ * Unused value parameters in the last call are filled with NULLs.\r
+ */\r
+ if (listSize < 5)\r
+ {\r
+ numLoops = 1;\r
+ numValInLastLoop = (listSize - 1) % 4 + 1;\r
+ }\r
+ else\r
+ {\r
+ numLoops = (listSize - 5) / 3 + 2;\r
+ numValInLastLoop = (listSize - 5) % 3 + 1;\r
+ }\r
+\r
+ for (int i = 0; i < numLoops; i++)\r
+ {\r
+ /* generate value parameters of min/maxValue\r
+ */\r
+ int numVals = (i == numLoops - 1) ? numValInLastLoop :\r
+ ((i == 0) ? 4 : 3);\r
+ for (int j = 0; j < numVals; j++)\r
+ {\r
+ ValueNode vn = (ValueNode) rightOperandList.elementAt(currentOpnd++);\r
+ vn.generateExpression(acb, mb);\r
+ mb.upCast(ClassName.DataValueDescriptor);\r
+ }\r
+\r
+ /* since we have fixed number of input values (4), unused ones\r
+ * in the last loop are filled with NULLs\r
+ */\r
+ int numNulls = (i < numLoops - 1) ? 0 :\r
+ ((i == 0) ? 4 - numValInLastLoop : 3 - numValInLastLoop);\r
+ for (int j = 0; j < numNulls; j++)\r
+ mb.pushNull(ClassName.DataValueDescriptor);\r
+\r
+ /* have to put judge's types in the end\r
+ */\r
+ mb.push(leftTypeFormatId);\r
+ mb.push(leftJDBCTypeId);\r
+\r
+ /* decide to get min or max value\r
+ */\r
+ String methodName;\r
+ if ((isAsc && isStartKey) || (! isAsc && ! isStartKey))\r
+ methodName = "minValue";\r
+ else\r
+ methodName = "maxValue";\r
+ \r
+ mb.callMethod(VMOpcode.INVOKESTATIC, ClassName.BaseExpressionActivation, methodName, ClassName.DataValueDescriptor, 6);\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Indicate that the IN-list values for this node are ordered (i.e. they\r
+ * are all constants and they have been sorted).\r
+ */\r
+ protected void markAsOrdered()\r
+ {\r
+ isOrdered = true;\r
+ }\r
+\r
+ /**\r
+ * Indicate that the IN-list values for this node must be sorted\r
+ * in DESCENDING order. This only applies to in-list "multi-probing",\r
+ * where the rows are processed in the order of the IN list elements\r
+ * themselves. In that case, any requirement to sort the rows in\r
+ * descending order means that the values in the IN list have to\r
+ * be sorted in descending order, as well.\r
+ */\r
+ protected void markSortDescending()\r
+ {\r
+ sortDescending = true;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the IN-list values for this node are ordered.\r
+ * This is used for determining whether or not we need to do an execution-\r
+ * time sort.\r
+ */\r
+ protected boolean isOrdered()\r
+ {\r
+ return isOrdered;\r
+ } \r
+\r
+ /**\r
+ * Return whether or not the IN-list values for this node must be\r
+ * sorted in DESCENDING order.\r
+ */\r
+ protected boolean sortDescending()\r
+ {\r
+ return sortDescending;\r
+ } \r
+}\r