--- /dev/null
+/*\r
+ Derby - Class org.apache.derby.impl.sql.compile.ResultColumnList\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
+\r
+import java.lang.reflect.Modifier;\r
+import java.sql.ResultSetMetaData;\r
+import java.sql.Types;\r
+import java.util.Hashtable;\r
+import java.util.Vector;\r
+\r
+import org.apache.derby.catalog.types.DefaultInfoImpl;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.ResultColumnDescriptor;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.compile.NodeFactory;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.StoreCostController;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+\r
+/**\r
+ * A ResultColumnList is the target list of a SELECT, INSERT, or UPDATE.\r
+ *\r
+ * @see ResultColumn\r
+ */\r
+\r
+public class ResultColumnList extends QueryTreeNodeVector\r
+{\r
+ /* Is this the ResultColumnList for an index row? */\r
+ protected boolean indexRow;\r
+ protected long conglomerateId;\r
+\r
+ int orderBySelect = 0; // the number of result columns pulled up\r
+ // from ORDERBY list\r
+ /*\r
+ * A comment on 'orderBySelect'. When we encounter a SELECT .. ORDER BY\r
+ * statement, the columns (or expressions) in the ORDER BY clause may\r
+ * or may not have been explicitly mentioned in the SELECT column list.\r
+ * If the columns were NOT explicitly mentioned in the SELECT column\r
+ * list, then the parsing of the ORDER BY clause implicitly generates\r
+ * them into the result column list, because we'll need to have those\r
+ * columns present at execution time in order to sort by them. Those\r
+ * generated columns are added to the *end* of the ResultColumnList, and\r
+ * we keep track of the *number* of those columns in 'orderBySelect',\r
+ * so we can tell whether we are looking at a generated column by seeing\r
+ * whether its position in the ResultColumnList is in the last\r
+ * 'orderBySelect' number of columns. If the SELECT .. ORDER BY\r
+ * statement uses the "*" token to select all the columns from a table,\r
+ * then during ORDER BY parsing we redundantly generate the columns\r
+ * mentioned in the ORDER BY clause into the ResultColumnlist, but then\r
+ * later in getOrderByColumnToBind we determine that these are\r
+ * duplicates and we take them back out again.\r
+ */\r
+\r
+ /*\r
+ ** Is this ResultColumnList for a FromBaseTable for an index\r
+ ** that is to be updated?\r
+ */\r
+ protected boolean forUpdate;\r
+\r
+ // Is a count mismatch allowed - see set/get methods for details.\r
+ private boolean countMismatchAllowed;\r
+\r
+ // Number of RCs in this RCL at "init" time, before additional\r
+ // ones were added internally.\r
+ private int initialListSize = 0;\r
+\r
+ public ResultColumnList()\r
+ {\r
+ }\r
+\r
+ /**\r
+ * Add a ResultColumn (at this point, ResultColumn or\r
+ * AllResultColumn) to the list\r
+ *\r
+ * @param resultColumn The ResultColumn to add to the list\r
+ */\r
+\r
+ public void addResultColumn(ResultColumn resultColumn)\r
+ {\r
+ /* Vectors are 0-based, ResultColumns are 1-based */\r
+ resultColumn.setVirtualColumnId(size() + 1);\r
+ addElement(resultColumn);\r
+ }\r
+\r
+ /**\r
+ * Append a given ResultColumnList to this one, resetting the virtual\r
+ * column ids in the appended portion.\r
+ *\r
+ * @param resultColumns The ResultColumnList to be appended\r
+ * @param destructiveCopy Whether or not this is a descructive copy\r
+ * from resultColumns\r
+ */\r
+ public void appendResultColumns(ResultColumnList resultColumns,\r
+ boolean destructiveCopy)\r
+ {\r
+ int oldSize = size();\r
+ int newID = oldSize + 1;\r
+\r
+ /*\r
+ ** Set the virtual column ids in the list being appended.\r
+ ** Vectors are zero-based, and virtual column ids are one-based,\r
+ ** so the new virtual column ids start at the original size\r
+ ** of this list, plus one.\r
+ */\r
+ int otherSize = resultColumns.size();\r
+ for (int index = 0; index < otherSize; index++)\r
+ {\r
+ /* ResultColumns are 1-based */\r
+ ((ResultColumn) resultColumns.elementAt(index)).setVirtualColumnId(newID);\r
+ newID++;\r
+ }\r
+\r
+ if (destructiveCopy)\r
+ {\r
+ destructiveAppend(resultColumns);\r
+ }\r
+ else\r
+ {\r
+ nondestructiveAppend(resultColumns);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get a ResultColumn from a column position (1-based) in the list\r
+ *\r
+ * @param position The ResultColumn to get from the list (1-based)\r
+ *\r
+ * @return the column at that position.\r
+ */\r
+\r
+ public ResultColumn getResultColumn(int position)\r
+ {\r
+ /*\r
+ ** First see if it falls in position x. If not,\r
+ ** search the whole shebang\r
+ */\r
+ if (position <= size()) \r
+ {\r
+ // this wraps the cast needed, \r
+ // and the 0-based nature of the Vectors.\r
+ ResultColumn rc = (ResultColumn)elementAt(position-1);\r
+ if (rc.getColumnPosition() == position)\r
+ {\r
+ return rc;\r
+ }\r
+ }\r
+ \r
+ /*\r
+ ** Check each column\r
+ */\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ if (rc.getColumnPosition() == position)\r
+ {\r
+ return rc;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Take a column position and a ResultSetNode and find the ResultColumn\r
+ * in this RCL whose source result set is the same as the received\r
+ * RSN and whose column position is the same as the received column\r
+ * position.\r
+ *\r
+ * @param colNum The column position (w.r.t rsn) for which we're searching\r
+ * @param rsn The result set node for which we're searching.\r
+ * @return The ResultColumn in this RCL whose source is column colNum\r
+ * in result set rsn. That ResultColumn's position w.r.t to this RCL\r
+ * is also returned via the whichRC parameter. If no match is found,\r
+ * return null and leave whichRC untouched.\r
+ */\r
+ public ResultColumn getResultColumn(int colNum, ResultSetNode rsn,\r
+ int [] whichRC) throws StandardException\r
+ {\r
+ if (colNum == -1)\r
+ return null;\r
+\r
+ ResultColumn rc = null;\r
+ ColumnReference colRef = null;\r
+ int [] crColNum = new int[] { -1 };\r
+\r
+ for (int index = size() - 1; index >= 0; index--)\r
+ {\r
+ rc = (ResultColumn) elementAt(index);\r
+ if (!(rc.getExpression() instanceof ColumnReference))\r
+ {\r
+ // If the rc's expression isn't a column reference then\r
+ // it can't be pointing to rsn, so just skip it.\r
+ continue;\r
+ }\r
+\r
+ colRef = (ColumnReference)rc.getExpression();\r
+ if ((rsn == colRef.getSourceResultSet(crColNum)) &&\r
+ (crColNum[0] == colNum))\r
+ {\r
+ // Found a match.\r
+ whichRC[0] = index+1;\r
+ return rc;\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Get a ResultColumn from a column position (1-based) in the list,\r
+ * null if out of range (for order by).\r
+ *\r
+ * @param position The ResultColumn to get from the list (1-based)\r
+ *\r
+ * @return the column at that position, null if out of range\r
+ */\r
+ public ResultColumn getOrderByColumn(int position)\r
+ {\r
+ // this wraps the cast needed, and the 0-based nature of the Vectors.\r
+ if (position == 0) \r
+ return null;\r
+\r
+ return getResultColumn(position);\r
+ }\r
+\r
+ /**\r
+ * Get a ResultColumn that matches the specified columnName and\r
+ * mark the ResultColumn as being referenced.\r
+ *\r
+ * @param columnName The ResultColumn to get from the list\r
+ *\r
+ * @return the column that matches that name.\r
+ */\r
+\r
+ public ResultColumn getResultColumn(String columnName)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ if (columnName.equals( resultColumn.getName()) )\r
+ {\r
+ /* Mark ResultColumn as referenced and return it */\r
+ resultColumn.setReferenced();\r
+ return resultColumn;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Get a ResultColumn that matches the specified columnName and\r
+ * mark the ResultColumn as being referenced.\r
+ *\r
+ * @param columnsTableName Qualifying name for the column\r
+ * @param columnName The ResultColumn to get from the list\r
+ *\r
+ * @return the column that matches that name.\r
+ */\r
+\r
+ public ResultColumn getResultColumn(String columnsTableName, String columnName)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ /* If the column's table name is non-null, then we have found a match\r
+ * only if the RC's table name is non-null and the same as the\r
+ * the CR's table name.\r
+ */\r
+ if (columnsTableName != null)\r
+ {\r
+ if (resultColumn.getTableName() == null)\r
+ {\r
+ continue;\r
+ }\r
+ \r
+ if (! columnsTableName.equals(resultColumn.getTableName()))\r
+ {\r
+ continue;\r
+ }\r
+ }\r
+ if (columnName.equals( resultColumn.getName()) )\r
+ {\r
+ /* Mark ResultColumn as referenced and return it */\r
+ resultColumn.setReferenced();\r
+ return resultColumn;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Get a ResultColumn that matches the specified columnName and\r
+ * mark the ResultColumn as being referenced.\r
+ * NOTE - this flavor enforces no ambiguity (at most 1 match)\r
+ * Only FromSubquery needs to call this flavor since\r
+ * it can have ambiguous references in its own list.\r
+ *\r
+ * @param cr The ColumnReference to resolve\r
+ * @param exposedTableName Exposed table name for FromTable\r
+ * @param considerGeneratedColumns Also consider columns that are generated.\r
+ * One example of this is group by where columns are added to the select list\r
+ * if they are referenced in the group by but are not present in the select\r
+ * list.\r
+ * @return the column that matches that name.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public ResultColumn getAtMostOneResultColumn(\r
+ ColumnReference cr,\r
+ String exposedTableName,\r
+ boolean considerGeneratedColumns)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ ResultColumn retRC = null;\r
+ String columnName = cr.getColumnName();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ if (columnName.equals( resultColumn.getName()))\r
+ {\r
+ if (resultColumn.isGenerated() && !considerGeneratedColumns) {\r
+ continue;\r
+ }\r
+ /* We should get at most 1 match */\r
+ if (retRC != null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_AMBIGUOUS_COLUMN_NAME_IN_TABLE, \r
+ columnName, exposedTableName);\r
+ }\r
+ /* Mark ResultColumn as referenced and return it */\r
+ resultColumn.setReferenced();\r
+ retRC = resultColumn;\r
+ }\r
+ }\r
+ return retRC;\r
+ }\r
+\r
+ /**\r
+ * For order by column bind, get a ResultColumn that matches the specified \r
+ * columnName.\r
+ *\r
+ * This method is called during bind processing, in the special\r
+ * "bind the order by" call that is made by CursorNode.bindStatement().\r
+ * The OrderByList has a special set of bind processing routines\r
+ * that analyzes the columns in the ORDER BY list and verifies that\r
+ * each column is one of:\r
+ * - a direct reference to a column explicitly mentioned in\r
+ * the SELECT list\r
+ * - a direct reference to a column implicitly mentioned as "SELECT *"\r
+ * - a direct reference to a column "pulled up" into the result\r
+ * column list\r
+ * - or a valid and fully-bound expression ("c+2", "YEAR(hire_date)", etc.)\r
+ *\r
+ * At this point in the processing, it is possible that we'll find\r
+ * the column present in the RCL twice: once because it was pulled\r
+ * up during statement compilation, and once because it was added\r
+ * when "SELECT *" was expanded into the table's actual column list.\r
+ * If we find such a duplicated column, we can, and do, remove the\r
+ * pulled-up copy of the column and point the OrderByColumn\r
+ * to the actual ResultColumn from the *-expansion.\r
+ *\r
+ * Note that the association of the OrderByColumn with the\r
+ * corresponding ResultColumn in the RCL occurs in\r
+ * OrderByColumn.resolveAddedColumn.\r
+ *\r
+ * @param columnName The ResultColumn to get from the list\r
+ * @param tableName The table name on the OrderByColumn, if any\r
+ * @param tableNumber The tableNumber corresponding to the FromTable with the\r
+ * exposed name of tableName, if tableName != null.\r
+ * @param obc The OrderByColumn we're binding.\r
+ *\r
+ * @return the column that matches that name.\r
+ * @exception StandardException thrown on ambiguity\r
+ */\r
+ public ResultColumn getOrderByColumnToBind(\r
+ String columnName,\r
+ TableName tableName,\r
+ int tableNumber,\r
+ OrderByColumn obc)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ ResultColumn retVal = null, resultColumn;\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ /* The order by column is qualified, then it is okay to consider\r
+ * this RC if:\r
+ * o The RC is qualified and the qualifiers on the order by column\r
+ * and the RC are equal().\r
+ * o The RC is not qualified, but its expression is a ColumnReference\r
+ * from the same table (as determined by the tableNumbers).\r
+ */\r
+ boolean columnNameMatches;\r
+ if (tableName != null)\r
+ {\r
+ ValueNode rcExpr = resultColumn.getExpression();\r
+ if (! (rcExpr instanceof ColumnReference))\r
+ continue;\r
+\r
+ ColumnReference cr = (ColumnReference) rcExpr;\r
+ if( (! tableName.equals( cr.getTableNameNode())) && tableNumber != cr.getTableNumber())\r
+ continue;\r
+ columnNameMatches =\r
+ columnName.equals( resultColumn.getSourceColumnName() );\r
+ }\r
+ else\r
+ columnNameMatches =\r
+ resultColumn.columnNameMatches(columnName);\r
+\r
+\r
+ /* We finally got past the qualifiers, now see if the column\r
+ * names are equal. If they are, then we appear to have found\r
+ * our order by column. If we find our order by column multiple\r
+ * times, make sure that they are truly duplicates, otherwise\r
+ * we have an ambiguous situation. For example, the query\r
+ * SELECT b+c AS a, d+e AS a FROM t ORDER BY a\r
+ * is ambiguous because we don't know which "a" is meant. But\r
+ * SELECT t.a, t.* FROM t ORDER BY a\r
+ * is not ambiguous, even though column "a" is selected twice.\r
+ * If we find our ORDER BY column at the end of the\r
+ * SELECT column list, in the last 'orderBySelect' number\r
+ * of columns, then this column was not explicitly mentioned\r
+ * by the user in their SELECT column list, but was implicitly \r
+ * added by the parsing of the ORDER BY clause, and it\r
+ * should be removed from the ResultColumnList and returned\r
+ * to the caller.\r
+ */\r
+ if (columnNameMatches)\r
+ {\r
+ if (retVal == null)\r
+ {\r
+ retVal = resultColumn;\r
+ }\r
+ else if (! retVal.isEquivalent(resultColumn))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_FOR_ORDER_BY, columnName);\r
+ }\r
+ else if (index >= size - orderBySelect)\r
+ {// remove the column due to pullup of orderby item\r
+ removeElement(resultColumn);\r
+ decOrderBySelect();\r
+ obc.clearAddedColumnOffset();\r
+ collapseVirtualColumnIdGap(\r
+ resultColumn.getColumnPosition());\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ return retVal;\r
+ }\r
+\r
+ /**\r
+ * Adjust virtualColumnId values due to result column removal\r
+ *\r
+ * This method is called when a duplicate column has been detected and\r
+ * removed from the list. We iterate through each of the other columns\r
+ * in the list and notify them of the column removal so they can adjust\r
+ * their virtual column id if necessary.\r
+ *\r
+ * @param gap id of the column which was just removed.\r
+ */\r
+ private void collapseVirtualColumnIdGap(int gap)\r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ ((ResultColumn) elementAt(index)).collapseVirtualColumnIdGap(gap);\r
+ }\r
+\r
+\r
+ /**\r
+ * For order by, get a ResultColumn that matches the specified \r
+ * columnName.\r
+ *\r
+ * This method is called during pull-up processing, at the very\r
+ * start of bind processing, as part of\r
+ * OrderByList.pullUpOrderByColumns. Its job is to figure out\r
+ * whether the provided column (from the ORDER BY list) already\r
+ * exists in the ResultColumnList or not. If the column does\r
+ * not exist in the RCL, we return NULL, which signifies that\r
+ * a new ResultColumn should be generated and added ("pulled up")\r
+ * to the RCL by our caller.\r
+ *\r
+ * Note that at this point in the processing, we should never\r
+ * find this column present in the RCL multiple times; if the\r
+ * column is already present in the RCL, then we don't need to,\r
+ * and won't, pull a new ResultColumn up into the RCL.\r
+ *\r
+ * If the caller specified "SELECT *", then the RCL at this\r
+ * point contains a special AllResultColumn object. This object\r
+ * will later be expanded and replaced by the actual set of\r
+ * columns in the table, but at this point we don't know what\r
+ * those columns are, so we may pull up an OrderByColumn\r
+ * which duplicates a column in the *-expansion; such\r
+ * duplicates will be removed at the end of bind processing\r
+ * by OrderByList.bindOrderByColumns.\r
+ *\r
+ * @param columnName The ResultColumn to get from the list\r
+ * @param tableName The table name on the OrderByColumn, if any\r
+ *\r
+ * @return the column that matches that name, or NULL if pull-up needed\r
+ * @exception StandardException thrown on ambiguity\r
+ */\r
+ public ResultColumn findResultColumnForOrderBy(\r
+ String columnName, TableName tableName)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ ResultColumn retVal = null, resultColumn;\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ // We may be checking on "ORDER BY T.A" against "SELECT *".\r
+ // exposedName will not be null and "*" will not have an expression\r
+ // or tablename.\r
+ // We may be checking on "ORDER BY T.A" against "SELECT T.B, T.A".\r
+ boolean columnNameMatches;\r
+ if (tableName != null)\r
+ {\r
+ ValueNode rcExpr = resultColumn.getExpression();\r
+ if (rcExpr == null || ! (rcExpr instanceof ColumnReference))\r
+ {\r
+ continue;\r
+ }\r
+ ColumnReference cr = (ColumnReference) rcExpr;\r
+ if( ! tableName.equals( cr.getTableNameNode()))\r
+ continue;\r
+ columnNameMatches =\r
+ columnName.equals( resultColumn.getSourceColumnName() );\r
+ }\r
+ else\r
+ columnNameMatches =\r
+ resultColumn.columnNameMatches(columnName);\r
+\r
+ /* We finally got past the qualifiers, now see if the column\r
+ * names are equal.\r
+ */\r
+ if (columnNameMatches)\r
+ {\r
+ if (retVal == null)\r
+ {\r
+ retVal = resultColumn;\r
+ }\r
+ else if (! retVal.isEquivalent(resultColumn))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_FOR_ORDER_BY, columnName);\r
+ }\r
+ else if (index >= size - orderBySelect)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.THROWASSERT(\r
+ "Unexpectedly found ORDER BY column '" +\r
+ columnName + "' pulled up at position " +index);\r
+ }\r
+ }\r
+ }\r
+ return retVal;\r
+ }\r
+\r
+\r
+ /**\r
+ * Copy the result column names from the given ResultColumnList\r
+ * to this ResultColumnList. This is useful for insert-select,\r
+ * where the columns being inserted into may be different from\r
+ * the columns being selected from. The result column list for\r
+ * an insert is supposed to have the column names being inserted\r
+ * into.\r
+ *\r
+ * @param nameList The ResultColumnList from which to copy\r
+ * the column names\r
+ */\r
+\r
+ void copyResultColumnNames(ResultColumnList nameList)\r
+ {\r
+ /* List checking is done during bind(). Lists should be the\r
+ * same size when we are called.\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ((! countMismatchAllowed) && size() != nameList.size())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "The size of the 2 lists is expected to be the same. size() = " +\r
+ size() + ", nameList.size() = " + nameList.size());\r
+ }\r
+ }\r
+\r
+ int size = (countMismatchAllowed) ? nameList.size() : size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn thisResultColumn = (ResultColumn) elementAt(index);\r
+ ResultColumn nameListResultColumn =\r
+ (ResultColumn) nameList.elementAt(index);\r
+ thisResultColumn.setName(nameListResultColumn.getName());\r
+ thisResultColumn.setNameGenerated(nameListResultColumn.isNameGenerated());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * This class needs a treePrint method, even though it is not a\r
+ * descendant of QueryTreeNode, because its members contain tree\r
+ * nodes, and these have to be printed and indented properly.\r
+ *\r
+ * @param depth The depth at which to indent the sub-nodes\r
+ */\r
+\r
+ public void treePrint(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ ((ResultColumn) elementAt(index) ).treePrint(depth);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind the expressions in this ResultColumnList. This means binding\r
+ * the expression under each ResultColumn node.\r
+ *\r
+ * @param fromList The FROM list for the query this\r
+ * expression is in, for binding columns.\r
+ * @param subqueryList The subquery list being built as we find SubqueryNodes\r
+ * @param aggregateVector The aggregate vector being built as we find AggregateNodes\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindExpressions(\r
+ FromList fromList, SubqueryList subqueryList,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ /* First we expand the *'s in the result column list */\r
+ expandAllsAndNameColumns(fromList);\r
+\r
+ /* Now we bind each result column */\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ValueNode vn = (ValueNode) elementAt(index);\r
+ vn = ((ResultColumn) vn ).bindExpression(\r
+ fromList, subqueryList, \r
+ aggregateVector);\r
+ setElementAt(vn, index);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind the result columns to the expressions that live under them.\r
+ * All this does is copy the datatype information to from each expression\r
+ * to each result column. This is useful for SELECT statements, where\r
+ * the result type of each column is the type of the column's expression.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindResultColumnsToExpressions()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ((ResultColumn) elementAt(index) ).bindResultColumnToExpression();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind the result columns by their names. This is useful for GRANT and REVOKE statements\r
+ * like "GRANT SELECT ON t(c1,c1,c3) TO george", where the user specified a column list.\r
+ * This method does not check for duplicate column names.\r
+ *\r
+ * @param targetTableDescriptor The descriptor for the table\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindResultColumnsByName(TableDescriptor targetTableDescriptor)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ rc.bindResultColumnByName(\r
+ targetTableDescriptor,\r
+ index + 1\r
+ );\r
+ }\r
+ } // end of bindResultColumnsByName( TableDescriptor)\r
+\r
+ /**\r
+ * Bind the result columns by their names. This is useful for update, grant, and revoke\r
+ * statements, and for INSERT statements like "insert into t (a, b, c)\r
+ * values (1, 2, 3)" where the user specified a column list.\r
+ * If the statment is an insert or update verify that the result column list does not contain any duplicates.\r
+ * NOTE: We pass the ResultColumns position in the ResultColumnList so\r
+ * that the VirtualColumnId gets set.\r
+ *\r
+ * @param targetTableDescriptor The descriptor for the table being\r
+ * updated or inserted into\r
+ * @param statement DMLStatementNode containing this list, null if no duplicate checking is to be done\r
+ *\r
+ * @return A FormatableBitSet representing the set of columns with respect to the table\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public FormatableBitSet bindResultColumnsByName(TableDescriptor targetTableDescriptor,\r
+ DMLStatementNode statement)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ FormatableBitSet columnBitSet = new FormatableBitSet( targetTableDescriptor.getNumberOfColumns());\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ rc.bindResultColumnByName(\r
+ targetTableDescriptor,\r
+ index + 1\r
+ );\r
+ int colIdx = rc.getColumnPosition() - 1;\r
+ if( SanityManager.DEBUG)\r
+ SanityManager.ASSERT( colIdx >= 0 && colIdx < targetTableDescriptor.getNumberOfColumns(),\r
+ "Invalid column position found for " + rc.getName());\r
+ /* Verify that this column's name is unique within the list if requested */\r
+ if( statement != null && columnBitSet.isSet( colIdx))\r
+ {\r
+ String colName = rc.getName();\r
+\r
+ if (statement instanceof UpdateNode)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_UPDATE, colName);\r
+ }\r
+ else\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_INSERT, colName);\r
+ }\r
+ }\r
+ columnBitSet.set( colIdx);\r
+ }\r
+ return columnBitSet;\r
+ }\r
+\r
+ /**\r
+ * Bind the result columns by their names. This is useful for update\r
+ * VTI statements, and for INSERT statements like "insert into new t() (a, b, c)\r
+ * values (1, 2, 3)" where the user specified a column list.\r
+ * Also, verify that the result column list does not contain any duplicates.\r
+ * NOTE: We pass the ResultColumns position in the ResultColumnList so\r
+ * that the VirtualColumnId gets set.\r
+ *\r
+ * @param fullRCL The full RCL for the target table\r
+ * @param statement DMLStatementNode containing this list\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindResultColumnsByName(ResultColumnList fullRCL,\r
+ FromVTI targetVTI,\r
+ DMLStatementNode statement)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ Hashtable ht = new Hashtable(size + 2, (float) .999);\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn matchRC;\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ /* Verify that this column's name is unique within the list */\r
+ String colName = rc.getName();\r
+\r
+ Object object = ht.put(colName, colName);\r
+\r
+ if (object != null &&\r
+ ((String) object).equals(colName))\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT((statement instanceof UpdateNode) ||\r
+ (statement instanceof InsertNode),\r
+ "statement is expected to be instanceof UpdateNode or InsertNode");\r
+ }\r
+ if (statement instanceof UpdateNode)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_UPDATE, colName);\r
+ }\r
+ else\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_INSERT, colName);\r
+ }\r
+ }\r
+\r
+ matchRC = fullRCL.getResultColumn(null, rc.getName());\r
+ if (matchRC == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, \r
+ rc.getName(), \r
+ targetVTI.getNewInvocation().getJavaClassName());\r
+ }\r
+\r
+ /* We have a match. We need to create a dummy ColumnDescriptor\r
+ * since calling code expects one to get column info.\r
+ */\r
+ ColumnDescriptor cd = new ColumnDescriptor(\r
+ rc.getName(),\r
+ matchRC.getVirtualColumnId(),\r
+ matchRC.getType(),\r
+ null,\r
+ null,\r
+ (TableDescriptor) null,\r
+ null,\r
+ 0, 0);\r
+ rc.setColumnDescriptor(null, cd);\r
+ rc.setVirtualColumnId(index + 1);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind the result columns by ordinal position. This is useful for\r
+ * INSERT statements like "insert into t values (1, 2, 3)", where the\r
+ * user did not specify a column list.\r
+ *\r
+ * @param targetTableDescriptor The descriptor for the table being\r
+ * inserted into\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindResultColumnsByPosition(TableDescriptor targetTableDescriptor)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ /*\r
+ ** Add one to the iterator index, because iterator indexes start at zero,\r
+ ** and column numbers start at one.\r
+ */\r
+ ((ResultColumn) elementAt(index) ).bindResultColumnByPosition(\r
+ targetTableDescriptor,\r
+ index + 1);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Preprocess the expression trees under the RCL.\r
+ * 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
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void preprocess(int numTables,\r
+ FromList outerFromList,\r
+ SubqueryList outerSubqueryList,\r
+ PredicateList outerPredicateList) \r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ setElementAt(resultColumn.preprocess(numTables,\r
+ outerFromList,\r
+ outerSubqueryList,\r
+ outerPredicateList),\r
+ index);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Verify that all the result columns have expressions that\r
+ are storable for them. Check versus the given ResultColumnList.\r
+\r
+ @exception StandardException Thrown on error\r
+ */\r
+ void checkStorableExpressions(ResultColumnList toStore)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn otherRC = (ResultColumn) toStore.elementAt(index);\r
+\r
+ ((ResultColumn) elementAt(index) ).checkStorableExpression(otherRC);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Return an array holding the 0 based heap offsets of \r
+ the StreamStorable columns in this ResultColumnList.\r
+ This returns null if this list does not contain any\r
+ StreamStorableColumns. The list this returns does not\r
+ contain duplicates. This should only be used for\r
+ a resultColumnList the refers to a single heap\r
+ such as the target for an Insert, Update or Delete.\r
+ @param heapColCount the number of heap columns\r
+ @exception StandardException Thrown on error\r
+ */\r
+ public int[] getStreamStorableColIds(int heapColCount) throws StandardException\r
+ {\r
+ //@#$\r
+ //System.out.println("getStreamStorableColids");\r
+\r
+ int ssCount = 0;\r
+ boolean[] isSS = new boolean[heapColCount];//Should be table length.\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.getTypeId().streamStorable())\r
+ {\r
+ //System.out.println(" streamStorable=true");\r
+ ColumnDescriptor cd = rc.getTableColumnDescriptor();\r
+ isSS[cd.getPosition()-1] = true;\r
+ }\r
+ }\r
+\r
+ for (int ix=0;ix<isSS.length;ix++) if (isSS[ix]) ssCount++;\r
+\r
+ if (ssCount==0)return null;\r
+\r
+ int[] result = new int[ssCount];\r
+ int resultOffset=0;\r
+ for (int heapOffset=0;heapOffset<isSS.length;heapOffset++)\r
+ {\r
+ if (isSS[heapOffset])\r
+ result[resultOffset++]=heapOffset;\r
+ }\r
+\r
+ return result;\r
+ }\r
+ \r
+ /**\r
+ Verify that all the result columns have expressions that\r
+ are storable for them. Check versus the expressions under the\r
+ ResultColumns.\r
+\r
+ @exception StandardException Thrown on error\r
+ */\r
+ void checkStorableExpressions()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ((ResultColumn) elementAt(index) ).checkStorableExpression();\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Generate the code to place the columns' values into\r
+ * a row variable named "r". This wrapper is here\r
+ * rather than in ResultColumn, because that class does\r
+ * not know about the position of the columns in the list.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generate(ActivationClassBuilder acb, MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ generateCore(acb, mb, false);\r
+ }\r
+\r
+ /**\r
+ * Generate the code to place the columns' values into\r
+ * a row variable named "r". This wrapper is here\r
+ * rather than in ResultColumn, because that class does\r
+ * not know about the position of the columns in the list.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void generateNulls(ActivationClassBuilder acb,\r
+ MethodBuilder mb) \r
+ throws StandardException\r
+ {\r
+ generateCore(acb, mb, true);\r
+ }\r
+\r
+ /**\r
+ * Generate the code to place the columns' values into\r
+ * a row variable named "r". This wrapper is here\r
+ * rather than in ResultColumn, because that class does\r
+ * not know about the position of the columns in the list.\r
+ *\r
+ * This is the method that does the work.\r
+ */\r
+ void generateCore(ExpressionClassBuilder acb,\r
+ MethodBuilder mb,\r
+ boolean genNulls) \r
+ throws StandardException\r
+ {\r
+ // generate the function and initializer:\r
+ // private ExecRow fieldX;\r
+ // In the constructor:\r
+ // fieldX = getExecutionFactory().getValueRow(# cols);\r
+ // private ExecRow exprN()\r
+ // { \r
+ // fieldX.setColumn(1, col(1).generateColumn(ps)));\r
+ // ... and so on for each column ...\r
+ // return fieldX;\r
+ // }\r
+ // static Method exprN = method pointer to exprN;\r
+\r
+ // this sets up the method and the static field.\r
+ MethodBuilder userExprFun = acb.newUserExprFun();\r
+\r
+ /* Declare the field */\r
+ LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.ExecRow);\r
+\r
+ // Generate the code to create the row in the constructor\r
+ genCreateRow(acb, field, "getValueRow", ClassName.ExecRow, size());\r
+\r
+ ResultColumn rc; \r
+ int size = size();\r
+\r
+ MethodBuilder cb = acb.getConstructor();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ // generate statements of the form\r
+ // fieldX.setColumn(columnNumber, (DataValueDescriptor) columnExpr);\r
+ // and add them to exprFun.\r
+ rc = (ResultColumn) elementAt(index);\r
+\r
+ /* If we are not generating nulls, then we can skip this RC if\r
+ * it is simply propagating a column from the source result set.\r
+ */\r
+ if (!genNulls)\r
+ {\r
+ ValueNode sourceExpr = rc.getExpression();\r
+\r
+ if (sourceExpr instanceof VirtualColumnNode && ! ( ((VirtualColumnNode) sourceExpr).getCorrelated()))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (sourceExpr instanceof ColumnReference && ! ( ((ColumnReference) sourceExpr).getCorrelated()))\r
+ {\r
+ continue;\r
+ }\r
+ }\r
+\r
+\r
+ // row add is 1-based, and iterator index is 0-based\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (index + 1 != rc.getVirtualColumnId())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "VirtualColumnId (" +\r
+ rc.getVirtualColumnId() +\r
+ ") does not agree with position within Vector (" +\r
+ (index + 1) +\r
+ ")");\r
+ }\r
+ }\r
+\r
+ // we need the expressions to be Columns exactly.\r
+\r
+ /* SPECIAL CASE: Expression is a non-null constant.\r
+ * Generate the setColumn() call in the constructor\r
+ * so that it will only be executed once per instantiation.\r
+ *\r
+ * Increase the statement counter in constructor. Code size in\r
+ * constructor can become too big (more than 64K) for Java compiler\r
+ * to handle (beetle 4293). We set constant columns in other\r
+ * methods if constructor has too many statements already.\r
+ */\r
+ if ( (! genNulls) &&\r
+ (rc.getExpression() instanceof ConstantNode) &&\r
+ ! ((ConstantNode) rc.getExpression()).isNull() &&\r
+ ! cb.statementNumHitLimit(1))\r
+ {\r
+\r
+\r
+ cb.getField(field); // instance\r
+ cb.push(index + 1); // first arg;\r
+\r
+ rc.generateExpression(acb, cb);\r
+ cb.cast(ClassName.DataValueDescriptor); // second arg\r
+ cb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);\r
+ continue;\r
+ }\r
+\r
+ userExprFun.getField(field); // instance\r
+ userExprFun.push(index + 1); // arg1\r
+\r
+ /* We want to reuse the null values instead of doing a new each time\r
+ * if the caller said to generate nulls or the underlying expression\r
+ * is a typed null value.\r
+ */\r
+ boolean needDVDCast = true;\r
+ if (rc.isAutoincrementGenerated())\r
+ {\r
+ // (com.ibm.db2j.impl... DataValueDescriptor)\r
+ // this.getSetAutoincValue(column_number)\r
+\r
+ userExprFun.pushThis();\r
+\r
+ userExprFun.push(rc.getColumnPosition());\r
+ userExprFun.push(rc.getTableColumnDescriptor().getAutoincInc());\r
+\r
+ userExprFun.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation,\r
+ "getSetAutoincrementValue", ClassName.DataValueDescriptor, 2);\r
+ needDVDCast = false;\r
+ \r
+ }\r
+ else if (genNulls ||\r
+ ((rc.getExpression() instanceof ConstantNode) &&\r
+ ((ConstantNode) rc.getExpression()).isNull()))\r
+ {\r
+ userExprFun.getField(field);\r
+ userExprFun.push(index + 1);\r
+ userExprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "getColumn",\r
+ ClassName.DataValueDescriptor, 1); // the express\r
+\r
+ acb.generateNullWithExpress(userExprFun, rc.getTypeCompiler(),\r
+ rc.getTypeServices().getCollationType());\r
+ }\r
+ else\r
+ {\r
+ rc.generateExpression(acb, userExprFun);\r
+ }\r
+ if (needDVDCast)\r
+ userExprFun.cast(ClassName.DataValueDescriptor);\r
+\r
+ userExprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);\r
+ }\r
+ userExprFun.getField(field);\r
+ userExprFun.methodReturn();\r
+\r
+ // we are now done modifying userExprFun\r
+ userExprFun.complete();\r
+\r
+ // what we return is the access of the field, i.e. the pointer to the method.\r
+ acb.pushMethodReference(mb, userExprFun);\r
+ }\r
+\r
+ /**\r
+ * Build an empty row with the size and shape of the ResultColumnList.\r
+ *\r
+ * @return an empty row of the correct size and shape.\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ExecRow buildEmptyRow()\r
+ throws StandardException\r
+ {\r
+ int columnCount = size();\r
+ ExecRow row = getExecutionFactory().getValueRow( columnCount );\r
+ int position = 1;\r
+\r
+ for (int index = 0; index < columnCount; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ DataTypeDescriptor dataType = rc.getTypeServices();\r
+ DataValueDescriptor dataValue = dataType.getNull();\r
+\r
+ row.setColumn( position++, dataValue );\r
+ }\r
+\r
+ return row;\r
+ }\r
+\r
+ /**\r
+ * Build an empty index row for the given conglomerate.\r
+ *\r
+ * @return an empty row of the correct size and shape.\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ExecRow buildEmptyIndexRow(TableDescriptor td,\r
+ ConglomerateDescriptor cd,\r
+ StoreCostController scc,\r
+ DataDictionary dd)\r
+ throws StandardException\r
+ {\r
+ ResultColumn rc;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (! cd.isIndex())\r
+ {\r
+ SanityManager.THROWASSERT("ConglomerateDescriptor expected to be for index: " + cd);\r
+ }\r
+ }\r
+\r
+ int[] baseCols = cd.getIndexDescriptor().baseColumnPositions();\r
+ ExecRow row = getExecutionFactory().getValueRow(baseCols.length + 1);\r
+\r
+ for (int i = 0; i < baseCols.length; i++)\r
+ {\r
+ ColumnDescriptor coldes = td.getColumnDescriptor(baseCols[i]);\r
+ DataTypeDescriptor dataType = coldes.getType();\r
+\r
+ // rc = getResultColumn(baseCols[i]);\r
+ // rc = (ResultColumn) at(baseCols[i] - 1);\r
+ // dataType = rc.getTypeServices();\r
+ DataValueDescriptor dataValue = dataType.getNull();\r
+\r
+ row.setColumn(i + 1, dataValue );\r
+ }\r
+\r
+ RowLocation rlTemplate = scc.newRowLocationTemplate();\r
+\r
+ row.setColumn(baseCols.length + 1, rlTemplate);\r
+\r
+ return row;\r
+ }\r
+\r
+\r
+ /**\r
+ Generates a row with the size and shape of the ResultColumnList.\r
+\r
+ Some structures, like FromBaseTable and DistinctNode,\r
+ need to generate rowAllocator functions to get a row\r
+ the size and shape of their ResultColumnList. \r
+\r
+ We return the method pointer, which is a field access\r
+ in the generated class.\r
+\r
+ @exception StandardException\r
+ */\r
+ void generateHolder(ExpressionClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException \r
+ {\r
+ generateHolder(acb, mb, (FormatableBitSet) null, (FormatableBitSet) null);\r
+ }\r
+\r
+ /**\r
+ Generates a row with the size and shape of the ResultColumnList.\r
+\r
+ Some structures, like FromBaseTable and DistinctNode,\r
+ need to generate rowAllocator functions to get a row\r
+ the size and shape of their ResultColumnList. \r
+\r
+ We return the method pointer, which is a field access\r
+ in the generated class.\r
+\r
+ @exception StandardException\r
+ */\r
+ void generateHolder(ExpressionClassBuilder acb,\r
+ MethodBuilder mb,\r
+ FormatableBitSet referencedCols,\r
+ FormatableBitSet propagatedCols)\r
+ throws StandardException {\r
+\r
+ // what we return is a pointer to the method.\r
+ acb.pushMethodReference(mb, generateHolderMethod(acb, referencedCols, propagatedCols));\r
+ }\r
+\r
+ MethodBuilder generateHolderMethod(ExpressionClassBuilder acb,\r
+ FormatableBitSet referencedCols,\r
+ FormatableBitSet propagatedCols)\r
+ throws StandardException {\r
+ int numCols;\r
+ String rowAllocatorMethod;\r
+ String rowAllocatorType;\r
+ int highestColumnNumber = -1;\r
+\r
+ if (referencedCols != null)\r
+ {\r
+ // Find the number of the last column referenced in the table\r
+ for (int i = referencedCols.anySetBit();\r
+ i != -1;\r
+ i = referencedCols.anySetBit(i))\r
+ {\r
+ highestColumnNumber = i;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ highestColumnNumber = size() - 1;\r
+ }\r
+\r
+ // Within the constructor:\r
+ // fieldX = getExecutionFactory().getValueRow(# cols);\r
+ // The body of the new method:\r
+ // { \r
+ // fieldX.setColumn(1, col(1).generateColumn(ps)));\r
+ // ... and so on for each column ...\r
+ // return fieldX;\r
+ // }\r
+ // static Method exprN = method pointer to exprN;\r
+\r
+ // this sets up the method and the static field\r
+ MethodBuilder exprFun = acb.newExprFun();\r
+\r
+ // Allocate the right type of row, depending on\r
+ // whether we're scanning an index or a heap.\r
+ if (indexRow)\r
+ {\r
+ rowAllocatorMethod = "getIndexableRow";\r
+ rowAllocatorType = ClassName.ExecIndexRow;\r
+ }\r
+ else\r
+ {\r
+ rowAllocatorMethod = "getValueRow";\r
+ rowAllocatorType = ClassName.ExecRow;\r
+ }\r
+ numCols = size();\r
+\r
+ /* Declare the field */\r
+ LocalField lf = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.ExecRow);\r
+ // Generate the code to create the row in the constructor\r
+ genCreateRow(acb, lf, rowAllocatorMethod, rowAllocatorType, highestColumnNumber + 1);\r
+\r
+ // now we fill in the body of the function\r
+\r
+ int colNum;\r
+\r
+ // If there is a referenced column map, the first column to fill\r
+ // in is the first one in the bit map - otherwise, it is\r
+ // column 0.\r
+ if (referencedCols != null)\r
+ colNum = referencedCols.anySetBit();\r
+ else\r
+ colNum = 0;\r
+\r
+ for (int index = 0; index < numCols; index++)\r
+ {\r
+ ResultColumn rc = ((ResultColumn) elementAt(index));\r
+\r
+ /* Special code generation for RID since expression is CurrentRowLocationNode.\r
+ * Really need yet another node type that does its own code generation.\r
+ */\r
+ if (rc.getExpression() instanceof CurrentRowLocationNode)\r
+ {\r
+ ConglomerateController cc = null;\r
+ int savedItem;\r
+ RowLocation rl;\r
+ \r
+ cc = getLanguageConnectionContext().\r
+ getTransactionCompile().openConglomerate(\r
+ conglomerateId,\r
+ false,\r
+ 0,\r
+ TransactionController.MODE_RECORD,\r
+ TransactionController.ISOLATION_READ_COMMITTED);\r
+ try\r
+ {\r
+ rl = cc.newRowLocationTemplate();\r
+ }\r
+ finally\r
+ {\r
+ if (cc != null)\r
+ {\r
+ cc.close();\r
+ }\r
+ }\r
+\r
+ savedItem = acb.addItem(rl);\r
+ \r
+ // get the RowLocation template\r
+ exprFun.getField(lf); // instance for setColumn\r
+ exprFun.push(highestColumnNumber + 1); // first arg\r
+\r
+ exprFun.pushThis(); // instance for getRowLocationTemplate\r
+ exprFun.push(savedItem); // first arg\r
+ exprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Activation, "getRowLocationTemplate",\r
+ ClassName.RowLocation, 1);\r
+\r
+ exprFun.upCast(ClassName.DataValueDescriptor);\r
+ exprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn",\r
+ "void", 2);\r
+ continue;\r
+ }\r
+\r
+ /* Skip over those columns whose source is the immediate\r
+ * child result set. (No need to generate a wrapper\r
+ * for a SQL NULL when we are smart enough not to pass\r
+ * that wrapper to the store.)\r
+ * NOTE: Believe it or not, we have to check for the case\r
+ * where referencedCols is not null, but no bits are set.\r
+ * This can happen when we need to get all of the columns\r
+ * from the heap due to a check constraint.\r
+ */\r
+ if (propagatedCols != null &&\r
+ propagatedCols.getNumBitsSet() != 0)\r
+ {\r
+ /* We can skip this RC if it is simply propagating \r
+ * a column from the source result set.\r
+ */\r
+ ValueNode sourceExpr = rc.getExpression();\r
+\r
+ if (sourceExpr instanceof VirtualColumnNode)\r
+ {\r
+ // There is a referenced columns bit set, so use\r
+ // it to figure out what the next column number is.\r
+ // colNum = referencedCols.anySetBit(colNum);\r
+ continue;\r
+ }\r
+ }\r
+\r
+ // generate the column space creation call\r
+ // generate statements of the form\r
+ // r.setColumn(columnNumber, columnShape);\r
+ //\r
+ // This assumes that there are no "holes" in the column positions,\r
+ // and that column positions reflect the stored format/order\r
+ exprFun.getField(lf); // instance\r
+ exprFun.push(colNum + 1); // first arg\r
+ rc.generateHolder(acb, exprFun);\r
+\r
+ exprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);\r
+\r
+ // If there is a bit map of referenced columns, use it to\r
+ // figure out what the next column is, otherwise just go\r
+ // to the next column.\r
+ if (referencedCols != null)\r
+ colNum = referencedCols.anySetBit(colNum);\r
+ else\r
+ colNum++;\r
+ }\r
+ \r
+ // generate:\r
+ // return fieldX;\r
+ // and add to the end of exprFun's body.\r
+ exprFun.getField(lf);\r
+ exprFun.methodReturn();\r
+\r
+ // we are done putting stuff in exprFun:\r
+ exprFun.complete();\r
+\r
+ return exprFun;\r
+ }\r
+\r
+ /**\r
+ * Generate the code to create an empty row in the constructor.\r
+ *\r
+ * @param acb The ACB.\r
+ * @param field The field for the new row.\r
+ * @param rowAllocatorMethod The method to call.\r
+ * @param rowAllocatorType The row type.\r
+ * @param numCols The number of columns in the row.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void genCreateRow(ExpressionClassBuilder acb,\r
+ LocalField field,\r
+ String rowAllocatorMethod, \r
+ String rowAllocatorType, \r
+ int numCols)\r
+ throws StandardException\r
+ {\r
+ // Create the row in the constructor\r
+ // fieldX = getExecutionFactory().getValueRow(# cols);\r
+\r
+ MethodBuilder cb = acb.getConstructor();\r
+\r
+ acb.pushGetExecutionFactoryExpression(cb); // instance\r
+ cb.push(numCols);\r
+ cb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,\r
+ rowAllocatorMethod, rowAllocatorType, 1);\r
+ cb.setField(field);\r
+ /* Increase the statement counter in constructor. Code size in\r
+ * constructor can become too big (more than 64K) for Java compiler\r
+ * to handle (beetle 4293). We set constant columns in other\r
+ * methods if constructor has too many statements already.\r
+ */\r
+ cb.statementNumHitLimit(1); // ignore return value\r
+ }\r
+\r
+ /**\r
+ * Make a ResultDescription for use in a ResultSet.\r
+ * This is useful when generating/executing a NormalizeResultSet, since\r
+ * it can appear anywhere in the tree.\r
+ *\r
+ * @return A ResultDescription for this ResultSetNode.\r
+ */\r
+ public ResultColumnDescriptor[] makeResultDescriptors()\r
+ {\r
+ ResultColumnDescriptor colDescs[] = new ResultColumnDescriptor[size()];\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ // the ResultColumn nodes are descriptors, so take 'em...\r
+ colDescs[index] = getExecutionFactory().getResultColumnDescriptor(((ResultColumnDescriptor) elementAt(index)));\r
+ }\r
+\r
+ return colDescs;\r
+ }\r
+\r
+ /**\r
+ * Expand any *'s in the ResultColumnList. In addition, we will guarantee that\r
+ * each ResultColumn has a name. (All generated names will be unique across the\r
+ * entire statement.)\r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void expandAllsAndNameColumns(FromList fromList)\r
+ throws StandardException\r
+ {\r
+ boolean expanded = false;\r
+ ResultColumnList allExpansion;\r
+ TableName fullTableName;\r
+\r
+ /* First walk result column list looking for *'s to expand */\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ if (rc instanceof AllResultColumn)\r
+ {\r
+ expanded = true;\r
+\r
+ //fullTableName = ((AllResultColumn) rc).getFullTableName();\r
+ TableName temp = rc.getTableNameObject();\r
+ if(temp != null) {\r
+ String sName = temp.getSchemaName();\r
+ String tName = temp.getTableName();\r
+ fullTableName = makeTableName(sName,tName);\r
+ }\r
+ else\r
+ fullTableName = null;\r
+ allExpansion = fromList.expandAll(fullTableName);\r
+\r
+ /* Make sure that every column has a name */\r
+ allExpansion.nameAllResultColumns();\r
+\r
+ /* Replace the AllResultColumn with the expanded list. \r
+ * We will update the VirtualColumnIds once below.\r
+ */\r
+ removeElementAt(index);\r
+ for (int inner = 0; inner < allExpansion.size(); inner++)\r
+ {\r
+ insertElementAt(allExpansion.elementAt(inner), index + inner);\r
+ }\r
+\r
+ // If the rc was a "*", we need to set the initial list size\r
+ // to the number of columns that are actually returned to\r
+ // the user.\r
+ markInitialSize();\r
+ }\r
+ else\r
+ {\r
+ /* Make sure that every column has a name */\r
+ rc.guaranteeColumnName();\r
+ }\r
+ }\r
+\r
+ /* Go back and update the VirtualColumnIds if we expanded any *'s */\r
+ if (expanded)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ /* Vectors are 0-based, VirtualColumnIds are 1-based. */\r
+ ((ResultColumn) elementAt(index)).setVirtualColumnId(index + 1);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Generate (unique across the entire statement) column names for those\r
+ * ResultColumns in this list which are not named.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void nameAllResultColumns()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ resultColumn.guaranteeColumnName();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Copy the types and lengths for this RCL (the target)\r
+ * to another RCL (the source). \r
+ * This is useful when adding a NormalizeResultSetNode.\r
+ *\r
+ * @param sourceRCL The source RCL\r
+ */\r
+ public void copyTypesAndLengthsToSource(ResultColumnList sourceRCL) throws StandardException\r
+ {\r
+ /* Source and target can have different lengths. */\r
+ int size = Math.min(size(), sourceRCL.size());\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn sourceRC = (ResultColumn) sourceRCL.elementAt(index);\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ sourceRC.setType(resultColumn.getTypeServices());\r
+ sourceRC.getExpression().setType(resultColumn.getTypeServices());\r
+ }\r
+ }\r
+ \r
+\r
+ /*\r
+ ** Check whether the column lengths and types of the result columns\r
+ ** match the expressions under those columns. This is useful for\r
+ ** INSERT and UPDATE statements. For SELECT statements this method\r
+ ** should always return true. There is no need to call this for a\r
+ ** DELETE statement.\r
+ ** NOTE: We skip over generated columns since they won't have a\r
+ ** column descriptor.\r
+ **\r
+ ** @return true means all the columns match their expressions,\r
+ ** false means at least one column does not match its\r
+ ** expression\r
+ */\r
+\r
+ boolean columnTypesAndLengthsMatch()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ /* Skip over generated columns */\r
+ if (resultColumn.isGenerated())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (! resultColumn.columnTypeAndLengthMatch())\r
+ return false;\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ boolean columnTypesAndLengthsMatch(ResultColumnList otherRCL)\r
+ throws StandardException\r
+ {\r
+ boolean retval = true;\r
+\r
+ /* We check every RC, even after finding 1 that requires\r
+ * normalization, because the conversion of constants to\r
+ * the appropriate type occurs under this loop.\r
+ */\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ ResultColumn otherResultColumn = (ResultColumn) otherRCL.elementAt(index);\r
+\r
+ /* Skip over generated columns */\r
+ if (resultColumn.isGenerated() || otherResultColumn.isGenerated())\r
+ {\r
+ continue;\r
+ }\r
+\r
+ if (! resultColumn.columnTypeAndLengthMatch(otherResultColumn))\r
+ {\r
+ retval = false;\r
+ }\r
+ }\r
+\r
+ return retval;\r
+ }\r
+\r
+ /**\r
+ * Determine whether this RCL is a No-Op projection of the given RCL.\r
+ * It only makes sense to do this if the given RCL is from the child\r
+ * result set of the ProjectRestrict that this RCL is from.\r
+ *\r
+ * @param childRCL The ResultColumnList of the child result set.\r
+ *\r
+ * @return true if this RCL is a No-Op projection of the given RCL.\r
+ */\r
+ public boolean nopProjection(ResultColumnList childRCL)\r
+ {\r
+ /*\r
+ ** This RCL is a useless projection if each column in the child\r
+ ** if the same as the column in this RCL. This is impossible\r
+ ** if the two RCLs have different numbers of columns.\r
+ */\r
+ if (this.size() != childRCL.size())\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ ** The two lists have the same numbers of elements. Are the lists\r
+ ** identical? In other words, is the expression in every ResultColumn\r
+ ** in the PRN's RCL a ColumnReference that points to the corresponding\r
+ ** column in the child?\r
+ */\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn thisColumn = (ResultColumn) elementAt(index);\r
+ ResultColumn referencedColumn = null;\r
+\r
+ /*\r
+ ** A No-Op projection can point to a VirtualColumnNode or a\r
+ ** ColumnReference.\r
+ */\r
+ if (thisColumn.getExpression() instanceof VirtualColumnNode)\r
+ {\r
+ referencedColumn =\r
+ ((VirtualColumnNode) (thisColumn.getExpression())).\r
+ getSourceColumn();\r
+ }\r
+ else if (thisColumn.getExpression() instanceof ColumnReference)\r
+ {\r
+ referencedColumn =\r
+ ((ColumnReference) (thisColumn.getExpression())).\r
+ getSource();\r
+ }\r
+ else\r
+ {\r
+ return false;\r
+ }\r
+\r
+ ResultColumn childColumn = (ResultColumn) childRCL.elementAt(index);\r
+\r
+ if (referencedColumn != childColumn)\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Create a shallow copy of a ResultColumnList and its ResultColumns.\r
+ * (All other pointers are preserved.)\r
+ * Useful for building new ResultSetNodes during preprocessing.\r
+ *\r
+ * @return None.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultColumnList copyListAndObjects()\r
+ throws StandardException\r
+ {\r
+ ResultColumn newResultColumn;\r
+ ResultColumn origResultColumn;\r
+ ResultColumnList newList;\r
+\r
+ /* Create the new ResultColumnList */\r
+ newList = (ResultColumnList) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN_LIST,\r
+ getContextManager());\r
+\r
+ /* Walk the current list - for each ResultColumn in the list, make a copy\r
+ * and add it to the new list.\r
+ */\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ origResultColumn = (ResultColumn) elementAt(index);\r
+\r
+ newResultColumn = origResultColumn.cloneMe();\r
+\r
+ newList.addResultColumn(newResultColumn);\r
+ }\r
+ newList.copyOrderBySelect(this);\r
+ return newList;\r
+ }\r
+\r
+ /**\r
+ * Remove any columns that may have been added for an order by clause.\r
+ * In a query like:\r
+ * <pre>select a from t order by b</pre> b is added to the select list\r
+ * However in the final projection, after the sort is complete, b will have\r
+ * to be removed. \r
+ *\r
+ */\r
+ public void removeOrderByColumns() \r
+ {\r
+ int idx = size() - 1;\r
+ for (int i = 0; i < orderBySelect; i++, idx--) {\r
+ removeElementAt(idx);\r
+ }\r
+ orderBySelect = 0;\r
+ }\r
+ \r
+ /**\r
+ * Walk the list and replace ResultColumn.expression with a new \r
+ * VirtualColumnNode. This is useful when propagating a ResultColumnList\r
+ * up the query tree.\r
+ * NOTE: This flavor marks all of the underlying RCs as referenced.\r
+ *\r
+ * @param sourceResultSet ResultSetNode that is source of value\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void genVirtualColumnNodes(ResultSetNode sourceResultSet, \r
+ ResultColumnList sourceResultColumnList)\r
+ throws StandardException\r
+ {\r
+ genVirtualColumnNodes(sourceResultSet, sourceResultColumnList, true);\r
+ }\r
+\r
+\r
+\r
+ /**\r
+ * Walk the list and replace ResultColumn.expression with a new \r
+ * VirtualColumnNode. This is useful when propagating a ResultColumnList\r
+ * up the query tree.\r
+ *\r
+ * @param sourceResultSet ResultSetNode that is source of value\r
+ * @param markReferenced Whether or not to mark the underlying RCs\r
+ * as referenced\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void genVirtualColumnNodes(ResultSetNode sourceResultSet, \r
+ ResultColumnList sourceResultColumnList,\r
+ boolean markReferenced)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ /* dts = resultColumn.getExpression().getTypeServices(); */\r
+ DataTypeDescriptor dts = resultColumn.getTypeServices();\r
+\r
+ /* Vectors are 0-based, VirtualColumnIds are 1-based */\r
+ resultColumn.expression = (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.VIRTUAL_COLUMN_NODE,\r
+ sourceResultSet,\r
+ sourceResultColumnList.elementAt(index),\r
+ ReuseFactory.getInteger(index + 1),\r
+ getContextManager());\r
+\r
+ /* Mark the ResultColumn as being referenced */\r
+ if (markReferenced)\r
+ {\r
+ resultColumn.setReferenced();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Walk the list and adjust the virtualColumnIds in the ResultColumns\r
+ * by the specified amount. If ResultColumn.expression is a VirtualColumnNode,\r
+ * then we adjust the columnId there as well.\r
+ *\r
+ * @param adjust The size of the increment.\r
+ */\r
+ public void adjustVirtualColumnIds(int adjust)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ resultColumn.adjustVirtualColumnId(adjust);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if ( !\r
+ (resultColumn.getExpression() instanceof VirtualColumnNode))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "resultColumn.getExpression() is expected to be " +\r
+ "instanceof VirtualColumnNode" +\r
+ " not " +\r
+ resultColumn.getExpression().getClass().getName());\r
+ }\r
+ }\r
+\r
+ ((VirtualColumnNode) resultColumn.getExpression()).columnId += adjust;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Project out any unreferenced ResultColumns from the list and \r
+ * reset the virtual column ids in the referenced ResultColumns.\r
+ * If all ResultColumns are projected out, then the list is not empty.\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void doProjection() throws StandardException\r
+ {\r
+ int numDeleted = 0;\r
+ int size = size();\r
+ ResultColumnList deletedRCL = new ResultColumnList();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ /* RC's for FromBaseTables are marked as referenced during binding.\r
+ * For other nodes, namely JoinNodes, we need to go 1 level\r
+ * down the RC/VCN chain to see if the RC is referenced. This is\r
+ * because we propagate the referencing info from the bottom up.\r
+ */\r
+ if ((! resultColumn.isReferenced()) &&\r
+ (resultColumn.getExpression() instanceof VirtualColumnNode) &&\r
+ !(((VirtualColumnNode) resultColumn.getExpression()).getSourceColumn().isReferenced()))\r
+ {\r
+ // Remember the RC to delete when done\r
+ deletedRCL.addElement(resultColumn);\r
+\r
+ /* Remember how many we have deleted and decrement the\r
+ * VirtualColumnIds for all nodes which appear after us\r
+ * in the list.\r
+ */\r
+ numDeleted++;\r
+ }\r
+ else\r
+ {\r
+ /* Decrement the VirtualColumnId for each node in the list\r
+ * after the 1st deleted one.\r
+ */\r
+ if (numDeleted >= 1)\r
+ resultColumn.adjustVirtualColumnId( - numDeleted);\r
+ /* Make sure that the RC is marked as referenced! */\r
+ resultColumn.setReferenced();\r
+ }\r
+ }\r
+\r
+ // Go back and delete the RCs to be delete from the list\r
+ for (int index = 0; index < deletedRCL.size(); index++)\r
+ {\r
+ removeElement((ResultColumn) deletedRCL.elementAt(index));\r
+ }\r
+ }\r
+\r
+ /** \r
+ * Check the uniqueness of the column names within a column list.\r
+ *\r
+ * @param errForGenCols Raise an error for any generated column names.\r
+ *\r
+ * @return String The first duplicate column name, if any.\r
+ */\r
+ public String verifyUniqueNames(boolean errForGenCols)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ Hashtable ht = new Hashtable(size + 2, (float) .999);\r
+ ResultColumn rc;\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ rc = (ResultColumn) elementAt(index);\r
+ if (errForGenCols && rc.isNameGenerated())\r
+ throw StandardException.newException(SQLState.LANG_DB2_VIEW_REQUIRES_COLUMN_NAMES);\r
+ /* Verify that this column's name is unique within the list */\r
+ String colName = ((ResultColumn) elementAt(index)).getName();\r
+\r
+ Object object = ht.put(colName, colName);\r
+\r
+ if (object != null &&\r
+ ((String) object).equals(colName))\r
+ {\r
+ return colName;\r
+ }\r
+ }\r
+\r
+ /* No duplicate column names */\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Validate the derived column list (DCL) and propagate the info\r
+ * from the list to the final ResultColumnList.\r
+ *\r
+ * @param derivedRCL The derived column list\r
+ * @param tableName The table name for the FromTable\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void propagateDCLInfo(ResultColumnList derivedRCL, String tableName)\r
+ throws StandardException\r
+ {\r
+ String duplicateColName;\r
+\r
+ /* Do both lists, if supplied by user, have the same degree? */\r
+ if (derivedRCL.size() != size() &&\r
+ ! derivedRCL.getCountMismatchAllowed())\r
+ {\r
+ if (visibleSize() != derivedRCL.size()) {\r
+ throw StandardException.newException(SQLState.LANG_DERIVED_COLUMN_LIST_MISMATCH, tableName);\r
+ }\r
+ }\r
+\r
+ /* Check the uniqueness of the column names within the derived list */\r
+ duplicateColName = derivedRCL.verifyUniqueNames(false);\r
+ if (duplicateColName != null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_DUPLICATE_COLUMN_NAME_DERIVED, duplicateColName);\r
+ }\r
+\r
+ /* We can finally copy the derived names into the final list */\r
+ copyResultColumnNames(derivedRCL);\r
+ }\r
+\r
+ /**\r
+ * Look for and reject ? parameters under ResultColumns. This is done for\r
+ * SELECT statements.\r
+ *\r
+ * @exception StandardException Thrown if a ? parameter found directly\r
+ * under a ResultColumn\r
+ */\r
+\r
+ void rejectParameters() throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ rc.rejectParameter();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Check for (and reject) XML values directly under the ResultColumns.\r
+ * This is done for SELECT/VALUES statements. We reject values\r
+ * in this case because JDBC does not define an XML type/binding\r
+ * and thus there's no standard way to pass such a type back\r
+ * to a JDBC application.\r
+ *\r
+ * Note that we DO allow an XML column in a top-level RCL\r
+ * IF that column was added to the RCL by _us_ instead of\r
+ * by the user. For example, if we have a table:\r
+ *\r
+ * create table t1 (i int, x xml)\r
+ *\r
+ * and the user query is:\r
+ *\r
+ * select i from t1 order by x\r
+ *\r
+ * the "x" column will be added (internally) to the RCL\r
+ * as part of ORDER BY processing--and so we need to\r
+ * allow that XML column to be bound without throwing\r
+ * an error. If, as in this case, the XML column reference\r
+ * is invalid (we can't use ORDER BY on an XML column because\r
+ * XML values aren't ordered), a more appropriate error\r
+ * message should be returned to the user in later processing.\r
+ * If we didn't allow for this, the user would get an\r
+ * error saying that XML columns are not valid as part\r
+ * of the result set--but as far as s/he knows, there\r
+ * isn't such a column: only "i" is supposed to be returned\r
+ * (the RC for "x" was added to the RCL by _us_ as part of\r
+ * ORDER BY processing).\r
+ *\r
+ * ASSUMPTION: Any RCs that are generated internally and\r
+ * added to this RCL (before this RCL is bound) are added\r
+ * at the _end_ of the list. If that's true, then any\r
+ * RC with an index greater than the size of the initial\r
+ * (user-specified) list must have been added internally\r
+ * and will not be returned to the user.\r
+ *\r
+ * @exception StandardException Thrown if an XML value found\r
+ * directly under a ResultColumn\r
+ */\r
+ void rejectXMLValues() throws StandardException\r
+ {\r
+ int sz = size();\r
+ ResultColumn rc = null;\r
+ for (int i = 1; i <= sz; i++) {\r
+\r
+ if (i > initialListSize)\r
+ // this RC was generated internally and will not\r
+ // be returned to the user, so don't throw error.\r
+ continue;\r
+\r
+ rc = getResultColumn(i);\r
+ if ((rc != null) && (rc.getType() != null) &&\r
+ rc.getType().getTypeId().isXMLTypeId())\r
+ { // Disallow it.\r
+ throw StandardException.newException(\r
+ SQLState.LANG_ATTEMPT_TO_SELECT_XML);\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Set the resultSetNumber in all of the ResultColumns.\r
+ *\r
+ * @param resultSetNumber The resultSetNumber\r
+ */\r
+ public void setResultSetNumber(int resultSetNumber)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ((ResultColumn) elementAt(index)).setResultSetNumber(resultSetNumber);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark all of the ResultColumns as redundant.\r
+ * Useful when chopping a ResultSetNode out of a tree when there are\r
+ * still references to its RCL.\r
+ */\r
+ public void setRedundant()\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ((ResultColumn) elementAt(index)).setRedundant();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Verify that all of the columns in the SET clause of a positioned update\r
+ * appear in the cursor's FOR UPDATE OF list.\r
+ *\r
+ * @param ucl The cursor's FOR UPDATE OF list. (May be null.)\r
+ * @param cursorName The cursor's name.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void checkColumnUpdateability(String[] ucl, String cursorName) \r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+\r
+ if (resultColumn.updated() &&\r
+ ! resultColumn.foundInList(ucl))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_COLUMN_NOT_UPDATABLE_IN_CURSOR, \r
+ resultColumn.getName(),\r
+ cursorName);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Set up the result expressions for a UNION, INTERSECT, or EXCEPT:\r
+ * o Verify union type compatiblity\r
+ * o Get dominant type for result (type + max length + nullability)\r
+ * o Create a new ColumnReference with dominant type and name of from this\r
+ * RCL and make that the new expression.\r
+ * o Set the type info for in the ResultColumn to the dominant type\r
+ *\r
+ * NOTE - We are assuming that caller has generated a new RCL for the UNION\r
+ * with the same names as the left side's RCL and copies of the expressions.\r
+ *\r
+ * @param otherRCL RCL from other side of the UNION.\r
+ * @param tableNumber The tableNumber for the UNION.\r
+ * @param level The nesting level for the UNION.\r
+ * @param operatorName "UNION", "INTERSECT", or "EXCEPT"\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void setUnionResultExpression(ResultColumnList otherRCL,\r
+ int tableNumber,\r
+ int level,\r
+ String operatorName)\r
+ throws StandardException\r
+ {\r
+ TableName dummyTN;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (visibleSize() != otherRCL.visibleSize())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "size() = (" +\r
+ size() +\r
+ ") is expected to equal otherRCL.size (" +\r
+ otherRCL.size() +\r
+ ")");\r
+ }\r
+ }\r
+\r
+ /* Make a dummy TableName to be shared by all new CRs */\r
+ dummyTN = (TableName) getNodeFactory().getNode(\r
+ C_NodeTypes.TABLE_NAME,\r
+ null,\r
+ null,\r
+ getContextManager());\r
+\r
+ ContextManager cm = getContextManager();\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ boolean nullableResult;\r
+ ColumnReference newCR;\r
+ ResultColumn thisRC = (ResultColumn) elementAt(index);\r
+ ResultColumn otherRC = (ResultColumn) otherRCL.elementAt(index);\r
+ ValueNode thisExpr = thisRC.getExpression();\r
+ ValueNode otherExpr = otherRC.getExpression();\r
+\r
+ // If there is one row that is not 'autoincrement', the Union should\r
+ // not be 'autoincrement'.\r
+ if (!otherRC.isAutoincrementGenerated() && thisRC.isAutoincrementGenerated())\r
+ {\r
+ thisRC.resetAutoincrementGenerated();\r
+ }\r
+ /*\r
+ ** If there are ? parameters in the ResultColumnList of a row\r
+ ** in a table constructor, their types will not be set. Just skip\r
+ ** these - their types will be set later. Each ? parameter will\r
+ ** get the type of the first non-? in its column, so it can't\r
+ ** affect the final dominant type. It's possible that all the\r
+ ** rows for a particular column will have ? parameters - this is\r
+ ** an error condition that will be caught later.\r
+ */\r
+ TypeId thisTypeId = thisExpr.getTypeId();\r
+ if (thisTypeId == null)\r
+ continue;\r
+\r
+ TypeId otherTypeId = otherExpr.getTypeId();\r
+ if (otherTypeId == null)\r
+ continue;\r
+\r
+ /* \r
+ ** Check type compatability. We want to make sure that\r
+ ** the types are assignable in either direction\r
+ ** and they are comparable.\r
+ */\r
+ ClassFactory cf = getClassFactory();\r
+ if (\r
+ !thisExpr.getTypeCompiler().storable(otherTypeId, cf) &&\r
+ !otherExpr.getTypeCompiler().storable(thisTypeId, cf))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NOT_UNION_COMPATIBLE, \r
+ thisTypeId.getSQLTypeName(),\r
+ otherTypeId.getSQLTypeName(),\r
+ operatorName);\r
+ }\r
+\r
+ DataTypeDescriptor resultType = thisExpr.getTypeServices().getDominantType(\r
+ otherExpr.getTypeServices(),\r
+ cf);\r
+\r
+ newCR = (ColumnReference) getNodeFactory().getNode(\r
+ C_NodeTypes.COLUMN_REFERENCE,\r
+ thisRC.getName(),\r
+ dummyTN,\r
+ getContextManager());\r
+ newCR.setType(resultType);\r
+ /* Set the tableNumber and nesting levels in newCR.\r
+ * If thisExpr is not a CR, then newCR cannot be\r
+ * correlated, hence source and nesting levels are\r
+ * the same.\r
+ */\r
+ if (thisExpr instanceof ColumnReference)\r
+ {\r
+ newCR.copyFields((ColumnReference) thisExpr);\r
+ }\r
+ else\r
+ {\r
+ newCR.setNestingLevel(level);\r
+ newCR.setSourceLevel(level);\r
+ }\r
+ newCR.setTableNumber(tableNumber);\r
+ thisRC.setExpression(newCR);\r
+ thisRC.setType(\r
+ thisRC.getTypeServices().getDominantType(\r
+ otherRC.getTypeServices(), cf));\r
+\r
+ /* DB2 requires both sides of union to have same name for the result to\r
+ * have that name. Otherwise, leave it or set it to a generated name */\r
+ if (thisRC.getName() != null && !thisRC.isNameGenerated() &&\r
+ otherRC.getName() != null)\r
+ {\r
+ /* Result name needs to be changed */\r
+ if (otherRC.isNameGenerated())\r
+ {\r
+ thisRC.setName(otherRC.getName());\r
+ thisRC.setNameGenerated(true);\r
+ }\r
+ else if (!thisRC.getName().equals(otherRC.getName()))\r
+ {\r
+ /* Both sides have user specified names that don't match */\r
+ thisRC.setName(null);\r
+ thisRC.guaranteeColumnName();\r
+ thisRC.setNameGenerated(true);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Do the 2 RCLs have the same type & length.\r
+ * This is useful for UNIONs when deciding whether a NormalizeResultSet is required.\r
+ *\r
+ * @param otherRCL The other RCL.\r
+ *\r
+ * @return boolean Whether or not there is an exact UNION type match on the 2 RCLs.\r
+ */\r
+ public boolean isExactTypeAndLengthMatch(ResultColumnList otherRCL) throws StandardException\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn thisRC = (ResultColumn) elementAt(index);\r
+ ResultColumn otherRC = (ResultColumn) otherRCL.elementAt(index);\r
+\r
+ if (! thisRC.getTypeServices().isExactTypeAndLengthMatch(\r
+ otherRC.getTypeServices() ))\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Does the column list contain any of the given column positions\r
+ * that are updated? Implements same named routine in UpdateList.\r
+ *\r
+ * @param columns An array of column positions\r
+ *\r
+ * @return True if this column list contains any of the given columns\r
+ */\r
+ public boolean updateOverlaps(int[] columns)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if ( ! rc.updated())\r
+ continue;\r
+\r
+ int column = rc.getColumnPosition();\r
+\r
+ for (int i = 0; i < columns.length; i++)\r
+ {\r
+ if (columns[i] == column)\r
+ return true;\r
+ }\r
+ }\r
+\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Return an array that contains references to the columns in this list\r
+ * sorted by position.\r
+ *\r
+ * @return The sorted array.\r
+ */\r
+ ResultColumn[] getSortedByPosition()\r
+ {\r
+ int size = size();\r
+ ResultColumn[] result;\r
+ \r
+ /*\r
+ ** Form an array of the original ResultColumns\r
+ */\r
+ result = new ResultColumn[size];\r
+\r
+ /*\r
+ ** Put the ResultColumns in the array\r
+ */\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ result[index] = (ResultColumn) elementAt(index);\r
+ }\r
+\r
+ /*\r
+ ** Sort the array by column position\r
+ */\r
+ java.util.Arrays.sort(result);\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Return an array of all my column positions, sorted in\r
+ * ascending order.\r
+ *\r
+ * @return a sorted array\r
+ */\r
+ public int[] sortMe()\r
+ {\r
+ ResultColumn[] sortedResultColumns = getSortedByPosition();\r
+ int[] sortedColumnIds = new int[sortedResultColumns.length];\r
+ for (int ix = 0; ix < sortedResultColumns.length; ix++)\r
+ {\r
+ sortedColumnIds[ix] = sortedResultColumns[ix].getColumnPosition();\r
+ }\r
+ return sortedColumnIds;\r
+ }\r
+\r
+\r
+ /**\r
+ * Expand this ResultColumnList by adding all columns from the given\r
+ * table that are not in this list. The result is sorted by column\r
+ * position.\r
+ *\r
+ * @param td The TableDescriptor for the table in question\r
+ * @param tableName The name of the table as given in the query\r
+ *\r
+ * @return A new ResultColumnList expanded to include all columns in\r
+ * the given table.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public ResultColumnList expandToAll(TableDescriptor td,\r
+ TableName tableName)\r
+ throws StandardException\r
+ {\r
+ ResultColumn rc;\r
+ ColumnDescriptor cd;\r
+ ResultColumnList retval;\r
+ ResultColumn[] originalRCS;\r
+ int posn;\r
+\r
+ /* Get a new ResultColumnList */\r
+ retval = (ResultColumnList) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN_LIST,\r
+ getContextManager());\r
+\r
+ /*\r
+ ** Form a sorted array of the ResultColumns\r
+ */\r
+ originalRCS = getSortedByPosition();\r
+\r
+ posn = 0;\r
+ \r
+ /* Iterate through the ColumnDescriptors for the given table */\r
+ ColumnDescriptorList cdl = td.getColumnDescriptorList();\r
+ int cdlSize = cdl.size();\r
+\r
+ for (int index = 0; index < cdlSize; index++)\r
+ {\r
+ cd = (ColumnDescriptor) cdl.elementAt(index);\r
+\r
+ if ((posn < originalRCS.length) &&\r
+ (cd.getPosition() == originalRCS[posn].getColumnPosition()))\r
+ {\r
+ rc = originalRCS[posn];\r
+ posn++;\r
+ }\r
+ else\r
+ {\r
+ /* Build a ResultColumn/ColumnReference pair for the column */\r
+ rc = makeColumnReferenceFromName( tableName, cd.getColumnName() );\r
+\r
+ /* Bind the new ResultColumn */\r
+ rc.bindResultColumnByPosition(td, cd.getPosition());\r
+ }\r
+\r
+ /* Add the ResultColumn to the list */\r
+ retval.addResultColumn(rc);\r
+ }\r
+\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(posn == originalRCS.length,\r
+ "ResultColumns in original list not added to expanded ResultColumnList");\r
+\r
+ return retval;\r
+ }\r
+\r
+ /**\r
+ * Bind any untyped null nodes to the types in the given ResultColumnList.\r
+ * Nodes that don't know their type may pass down nulls to\r
+ * children nodes. In the case of something like a union, it knows\r
+ * to try its right and left result sets against each other. \r
+ * But if a null reaches us, it means we have a null type that\r
+ * we don't know how to handle.\r
+ *\r
+ * @param bindingRCL The ResultColumnList with the types to bind to.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindUntypedNullsToResultColumns(ResultColumnList bindingRCL)\r
+ throws StandardException\r
+ {\r
+ if (bindingRCL == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_NULL_IN_VALUES_CLAUSE);\r
+ }\r
+ \r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(bindingRCL.size() >= this.size(),\r
+ "More columns in result column list than in base table");\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn bindingRC = (ResultColumn) bindingRCL.elementAt(index);\r
+ ResultColumn thisRC = (ResultColumn) elementAt(index);\r
+\r
+ thisRC.typeUntypedNullExpression(bindingRC);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark all the columns in this list as updated by an update statement.\r
+ */\r
+ void markUpdated()\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ((ResultColumn) elementAt(index)).markUpdated();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark all the (base) columns in this list as updatable by a positioned update\r
+ * statement. This is necessary\r
+ * for positioned update statements, because we expand the column list\r
+ * to include all the columns in the base table, and we need to be able\r
+ * to tell which ones the user is really trying to update so we can\r
+ * determine correctly whether all the updated columns are in the\r
+ * "for update" list.\r
+ */\r
+ void markUpdatableByCursor()\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ //determine if the column is a base column and not a derived column\r
+ if (((ResultColumn) elementAt(index)).getSourceTableName() != null)\r
+ ((ResultColumn) elementAt(index)).markUpdatableByCursor();\r
+ }\r
+ }\r
+\r
+ /** \r
+ * @see QueryTreeNode#disablePrivilegeCollection\r
+ */\r
+ public void disablePrivilegeCollection()\r
+ {\r
+ super.disablePrivilegeCollection();\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ ((ResultColumn) elementAt(index)).disablePrivilegeCollection(); \r
+ }\r
+ \r
+ /**\r
+ * Verify that all of the column names in this list are contained\r
+ * within the ColumnDefinitionNodes within the TableElementList.\r
+ *\r
+ * \r
+ * @return String The 1st column name, if any, that is not in the list.\r
+ */\r
+ public String verifyCreateConstraintColumnList(TableElementList tel)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ String colName = ((ResultColumn) elementAt(index)).getName();\r
+\r
+ if (! tel.containsColumnName(colName))\r
+ {\r
+ return colName;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Export the result column names to the passed in String[].\r
+ *\r
+ * @param columnNames String[] to hold the column names.\r
+ */\r
+ public void exportNames(String[] columnNames)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (size() != columnNames.length)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "size() (" +\r
+ size() + \r
+ ") is expected to equal columnNames.length (" +\r
+ columnNames.length +\r
+ ")");\r
+ }\r
+ }\r
+\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ columnNames[index] = ((ResultColumn) elementAt(index)).getName();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Given a ResultColumn at the next deepest level in the tree, \r
+ * search this RCL for its parent ResultColumn.\r
+ *\r
+ * @param childRC The child ResultColumn\r
+ *\r
+ * @return ResultColumn The parent ResultColumn\r
+ */\r
+ public ResultColumn findParentResultColumn(ResultColumn childRC)\r
+ {\r
+ ResultColumn parentRC = null;\r
+\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.getExpression() instanceof ColumnReference)\r
+ {\r
+ ColumnReference cr = (ColumnReference) rc.getExpression();\r
+\r
+ if (cr.getSource() == childRC)\r
+ {\r
+ parentRC = rc;\r
+ break;\r
+ }\r
+ }\r
+ else if (rc.getExpression() instanceof VirtualColumnNode)\r
+ {\r
+ VirtualColumnNode vcn = (VirtualColumnNode) rc.getExpression();\r
+\r
+ if (vcn.getSourceColumn() == childRC)\r
+ {\r
+ parentRC = rc;\r
+ break;\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ return parentRC;\r
+ }\r
+\r
+ /**\r
+ * Mark as updatable all the columns in this result column list\r
+ * that match the columns in the given update column list.\r
+ *\r
+ * @param updateColumns A ResultColumnList representing the columns\r
+ * to be updated.\r
+ */\r
+ void markUpdated(ResultColumnList updateColumns)\r
+ {\r
+ ResultColumn updateColumn;\r
+ ResultColumn resultColumn;\r
+\r
+ int size = updateColumns.size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ updateColumn = (ResultColumn) updateColumns.elementAt(index);\r
+\r
+ resultColumn = getResultColumn(updateColumn.getName());\r
+\r
+ /*\r
+ ** This ResultColumnList may not be bound yet - for update\r
+ ** statements, we mark the updated columns *before* we bind\r
+ ** the RCL. This ordering is important because we add columns\r
+ ** to the RCL after marking the update columns and before\r
+ ** binding.\r
+ **\r
+ ** So, it can happen that there is an invalid column name in\r
+ ** the list. This condition will cause an exception when the\r
+ ** RCL is bound. Just ignore it for now.\r
+ */\r
+ if (resultColumn != null)\r
+ {\r
+ resultColumn.markUpdated();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark all the columns in the select sql that this result column list represents\r
+ * as updatable if they match the columns in the given update column list.\r
+ *\r
+ * @param updateColumns A Vector representing the columns\r
+ * to be updated.\r
+ */\r
+ void markColumnsInSelectListUpdatableByCursor(Vector updateColumns)\r
+ {\r
+ commonCodeForUpdatableByCursor(updateColumns, true);\r
+ }\r
+\r
+ /**\r
+ * dealingWithSelectResultColumnList true means we are dealing with\r
+ * ResultColumnList for a select sql. When dealing with ResultColumnList for\r
+ * select sql, it is possible that not all the updatable columns are\r
+ * projected in the select column list and hence it is possible that we may\r
+ * not find the column to be updated in the ResultColumnList and that is why\r
+ * special handling is required when dealingWithSelectResultColumnList is true.\r
+ * eg select c11, c13 from t1 for update of c11, c12\r
+ * In the eg above, we will find updatable column c11 in the select column\r
+ * list but we will not find updatable column c12 in the select column list\r
+ */\r
+ private void commonCodeForUpdatableByCursor(Vector updateColumns, boolean dealingWithSelectResultColumnList)\r
+ {\r
+ /*\r
+ ** If there is no update column list, or the list is empty, then it means that\r
+ ** all the columns which have a base table associated with them are updatable.\r
+ */\r
+ if ( (updateColumns == null) || (updateColumns.size() == 0) )\r
+ {\r
+ markUpdatableByCursor();\r
+ }\r
+ else\r
+ {\r
+ int ucSize = updateColumns.size();\r
+ ResultColumn resultColumn;\r
+ String columnName;\r
+\r
+ for (int index = 0; index < ucSize; index++)\r
+ {\r
+ columnName = (String) updateColumns.elementAt(index);\r
+\r
+ resultColumn = getResultColumn(columnName);\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (resultColumn == null && !dealingWithSelectResultColumnList)\r
+ {\r
+ SanityManager.THROWASSERT("No result column found with name " +\r
+ columnName);\r
+ }\r
+ }\r
+ //Following if means the column specified in FOR UPDATE clause is not\r
+ //part of the select list\r
+ if (resultColumn == null && dealingWithSelectResultColumnList)\r
+ continue;\r
+ resultColumn.markUpdatableByCursor();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Mark as updatable all the columns in this result column list\r
+ * that match the columns in the given update column list\r
+ *\r
+ * @param updateColumns A Vector representing the columns\r
+ * to be updated.\r
+ */\r
+ void markUpdatableByCursor(Vector updateColumns)\r
+ {\r
+ commonCodeForUpdatableByCursor(updateColumns, false);\r
+ }\r
+\r
+ /**\r
+ * Returns true if the given column position is for a column that will\r
+ * be or could be updated by the positioned update of a cursor.\r
+ *\r
+ * @param columnPosition The position of the column in question\r
+ *\r
+ * @return true if the column is updatable\r
+ */\r
+ boolean updatableByCursor(int columnPosition)\r
+ {\r
+ return getResultColumn(columnPosition).updatableByCursor();\r
+ }\r
+\r
+\r
+ /** \r
+ * Return whether or not this RCL can be flattened out of a tree.\r
+ * It can only be flattened if the expressions are all cloneable.\r
+ *\r
+ * @return boolean Whether or not this RCL can be flattened out of a tree.\r
+ */\r
+ public boolean isCloneable()\r
+ {\r
+ boolean retcode = true;\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (! rc.getExpression().isCloneable())\r
+ {\r
+ retcode = false;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return retcode;\r
+ }\r
+\r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void remapColumnReferencesToExpressions() throws StandardException\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ // The expression may be null if this column is an identity\r
+ // column generated always. If the expression is not null, it\r
+ // is a ColumnReference; we call through to the ColumnReference\r
+ // to give it a chance to remap itself from the outer query\r
+ // node to this one.\r
+ if (rc.getExpression() != null)\r
+ rc.setExpression(\r
+ rc.getExpression().remapColumnReferencesToExpressions());\r
+ }\r
+ }\r
+\r
+ /*\r
+ ** Indicate that the conglomerate is an index, so we need to generate a\r
+ ** RowLocation as the last column of the result set.\r
+ **\r
+ ** @param cid The conglomerate id of the index\r
+ */\r
+ void setIndexRow(long cid, boolean forUpdate)\r
+ {\r
+ indexRow = true;\r
+ conglomerateId = cid;\r
+ this.forUpdate = forUpdate;\r
+ }\r
+\r
+ /* Debugging methods */\r
+\r
+ /**\r
+ * Verify that all ResultColumns and their expressions have type information\r
+ * and that the type information between the respective RCs and\r
+ * expressions matches.\r
+ *\r
+ * @return boolean Whether or not the type information is consistent\r
+ */\r
+ public boolean hasConsistentTypeInfo() throws StandardException\r
+ {\r
+ boolean isConsistent = true;\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ ValueNode expr = rc.getExpression();\r
+ DataTypeDescriptor rcDTS = rc.getTypeServices();\r
+ DataTypeDescriptor exDTS = expr.getTypeServices();\r
+\r
+ if (rcDTS == null || exDTS == null)\r
+ {\r
+ isConsistent = false;\r
+ break;\r
+ }\r
+\r
+ if (rcDTS.getClass().getName() !=\r
+ exDTS.getClass().getName())\r
+ {\r
+ isConsistent = false;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ return isConsistent;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not this RCL contains an AllResultColumn.\r
+ * This is useful when dealing with SELECT * views which \r
+ * reference tables that may have had columns added to them via \r
+ * ALTER TABLE since the view was created.\r
+ *\r
+ * @return Whether or not this RCL contains an AllResultColumn.\r
+ */\r
+ public boolean containsAllResultColumn()\r
+ {\r
+ boolean containsAllResultColumn = false;\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ if (elementAt(index) instanceof AllResultColumn)\r
+ {\r
+ containsAllResultColumn = true;\r
+ break;\r
+ }\r
+ }\r
+\r
+ return containsAllResultColumn;\r
+ }\r
+\r
+ /**\r
+ * Count the number of RCs in the list that are referenced.\r
+ *\r
+ * @return The number of RCs in the list that are referenced.\r
+ */\r
+ public int countReferencedColumns()\r
+ {\r
+ int numReferenced = 0;\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ if (rc.isReferenced())\r
+ {\r
+ numReferenced++;\r
+ }\r
+ }\r
+ return numReferenced;\r
+ }\r
+\r
+ /**\r
+ * Record the column ids of the referenced columns in the specified array.\r
+ *\r
+ * @param idArray int[] for column ids\r
+ * @param basis 0 (for 0-based ids) or 1 (for 1-based ids)\r
+ */\r
+ public void recordColumnReferences(int[] idArray, int basis)\r
+ {\r
+ int currArrayElement = 0;\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.isReferenced())\r
+ {\r
+ idArray[currArrayElement++] = index + basis;\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Record the top level ColumnReferences in the specified array\r
+ * and table map\r
+ * This is useful when checking for uniqueness conditions.\r
+ * NOTE: All top level CRs assumed to be from the same table.\r
+ * The size of the array is expected to be the # of columns\r
+ * in the table of interest + 1, so we use 1-base column #s.\r
+ *\r
+ * @param colArray1 boolean[] for columns\r
+ * @param tableColMap JBitSet[] for tables\r
+ * @param tableNumber Table number of column references\r
+ */\r
+ public void recordColumnReferences(boolean[] colArray1, JBitSet[] tableColMap,\r
+ int tableNumber)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ int columnNumber;\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (! (rc.getExpression() instanceof ColumnReference))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ columnNumber = ((ColumnReference) rc.getExpression()).getColumnNumber();\r
+ colArray1[columnNumber] = true;\r
+ tableColMap[tableNumber].set(columnNumber);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return whether or not all of the RCs in the list whose\r
+ * expressions are ColumnReferences are \r
+ * from the same table. One place this\r
+ * is useful for distinct elimination based on the existence\r
+ * of a uniqueness condition.\r
+ *\r
+ * @return -1 if all of the top level CRs in the RCL\r
+ * are not ColumnReferences from the same table,\r
+ * else the tableNumber\r
+ */\r
+ int allTopCRsFromSameTable()\r
+ {\r
+ int tableNumber = -1;\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ ValueNode vn = rc.getExpression();\r
+ if (! (vn instanceof ColumnReference))\r
+ {\r
+ continue;\r
+ }\r
+\r
+ // Remember the tableNumber from the first CR\r
+ ColumnReference cr = (ColumnReference) vn;\r
+ if (tableNumber == -1)\r
+ {\r
+ tableNumber = cr.getTableNumber();\r
+ }\r
+ else if (tableNumber != cr.getTableNumber())\r
+ {\r
+ return -1;\r
+ }\r
+ }\r
+ return tableNumber;\r
+ }\r
+\r
+ /**\r
+ * Clear the column references from the RCL. (Restore RCL back to a state\r
+ * where none of the RCs are marked as referenced.)\r
+ */\r
+ public void clearColumnReferences()\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.isReferenced())\r
+ {\r
+ rc.setUnreferenced();\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Copy the referenced RCs from this list to the supplied target list.\r
+ *\r
+ * @param targetList The list to copy to\r
+ */\r
+ public void copyReferencedColumnsToNewList(ResultColumnList targetList)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.isReferenced())\r
+ {\r
+ targetList.addElement(rc);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Copy the RCs from this list to the supplied target list.\r
+ *\r
+ * @param targetList The list to copy to,\r
+ * @param copyList 1 based bitMap we copy columns associated with set bits.\r
+ */\r
+ public void copyColumnsToNewList(ResultColumnList targetList, FormatableBitSet copyList)\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ if (copyList.isSet(rc.getColumnPosition())) \r
+ {\r
+ targetList.addElement(rc);\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ /**\r
+ * Get a FormatableBitSet of the columns referenced in this rcl\r
+ *\r
+ * @return the FormatableBitSet\r
+ */\r
+ public FormatableBitSet getColumnReferenceMap()\r
+ {\r
+ FormatableBitSet colMap = new FormatableBitSet(size());\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ if (rc.isReferenced())\r
+ {\r
+ colMap.set(index);\r
+ } \r
+ }\r
+ return colMap;\r
+ }\r
+\r
+ /**\r
+ * Or in any isReferenced booleans from the virtual column chain. That is the isReferenced bits on each\r
+ * ResultColumn on the list will be set if the ResultColumn is referenced or if any VirtualColumnNode in its\r
+ * expression chain refers to a referenced column.\r
+ */\r
+ void pullVirtualIsReferenced()\r
+ {\r
+ int size = size();\r
+ for( int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ rc.pullVirtualIsReferenced();\r
+ }\r
+ } // end of pullVirtualIsReferenced\r
+\r
+ public void clearTableNames()\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ rc.clearTableName();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Set the value of whether or not a count mismatch is allowed between\r
+ * this RCL, as a derived column list, and an underlying RCL. This is allowed\r
+ * for SELECT * views when an underlying table has had columns added to it\r
+ * via ALTER TABLE.\r
+ *\r
+ * @param allowed Whether or not a mismatch is allowed.\r
+ */\r
+ protected void setCountMismatchAllowed(boolean allowed)\r
+ {\r
+ countMismatchAllowed = allowed;\r
+ }\r
+\r
+ /**\r
+ * Return whether or not a count mismatch is allowed between this RCL,\r
+ * as a derived column list, and an underlying RCL. This is allowed\r
+ * for SELECT * views when an underlying table has had columns added to it\r
+ * via ALTER TABLE.\r
+ *\r
+ * return Whether or not a mismatch is allowed.\r
+ */\r
+\r
+ protected boolean getCountMismatchAllowed()\r
+ {\r
+ return countMismatchAllowed;\r
+ }\r
+\r
+ /**\r
+ * Get the size of all the columns added\r
+ * together. Does <B>NOT</B> include the\r
+ * column overhead that the store requires.\r
+ * Also, will be a very rough estimate for\r
+ * user types.\r
+ *\r
+ * @return the size\r
+ */\r
+ public int getTotalColumnSize()\r
+ {\r
+ int colSize = 0;\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ colSize += ((ResultColumn) elementAt(index)).getMaximumColumnSize();\r
+ }\r
+ return colSize;\r
+ }\r
+\r
+ /** \r
+ * Generate an RCL to match the contents of a ResultSetMetaData.\r
+ * This is useful when dealing with VTIs.\r
+ *\r
+ * @param rsmd The ResultSetMetaData.\r
+ * @param tableName The TableName for the BCNs.\r
+ * @param javaClassName The name of the VTI\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void createListFromResultSetMetaData(ResultSetMetaData rsmd,\r
+ TableName tableName,\r
+ String javaClassName)\r
+ throws StandardException\r
+ {\r
+ try\r
+ {\r
+ // JDBC columns #s are 1-based\r
+ // Check to make sure # of columns >= 1\r
+ int numColumns = rsmd.getColumnCount();\r
+\r
+ if (numColumns <= 0)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_V_T_I_COLUMN_COUNT, \r
+ javaClassName, String.valueOf(numColumns));\r
+ }\r
+\r
+ for (int index = 1; index <= numColumns; index++)\r
+ {\r
+ boolean nullableResult = \r
+ (rsmd.isNullable(index) != ResultSetMetaData.columnNoNulls);\r
+\r
+ TypeId cti;\r
+\r
+ int jdbcColumnType = rsmd.getColumnType(index);\r
+\r
+ switch (jdbcColumnType) {\r
+ case org.apache.derby.iapi.reference.JDBC20Translation.SQL_TYPES_JAVA_OBJECT:\r
+ case Types.OTHER:\r
+ {\r
+ cti = TypeId.getUserDefinedTypeId(rsmd.getColumnTypeName(index), false);\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ cti = TypeId.getBuiltInTypeId(jdbcColumnType);\r
+ break;\r
+ }\r
+ }\r
+\r
+ // Handle the case where a VTI returns a bad column type\r
+ if (cti == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_BAD_J_D_B_C_TYPE_INFO, Integer.toString(index));\r
+ }\r
+\r
+ // Get the maximum byte storage for this column\r
+ int maxWidth;\r
+\r
+ /* Get maximum byte storage from rsmd for variable\r
+ * width types, set it to MAXINT for the long types,\r
+ * otherwise get it from the TypeId\r
+ */\r
+ if (cti.variableLength())\r
+ {\r
+ maxWidth = rsmd.getColumnDisplaySize(index);\r
+ }\r
+ else if (jdbcColumnType == Types.LONGVARCHAR ||\r
+ jdbcColumnType == Types.LONGVARBINARY)\r
+ {\r
+ maxWidth = Integer.MAX_VALUE;\r
+ }\r
+ else\r
+ {\r
+ maxWidth = 0;\r
+ }\r
+\r
+ int precision = cti.isDecimalTypeId() ? rsmd.getPrecision(index) : 0;\r
+ int scale = cti.isDecimalTypeId() ? rsmd.getScale(index) : 0;\r
+ DataTypeDescriptor dts = new DataTypeDescriptor(cti, \r
+ precision,\r
+ scale, \r
+ nullableResult, \r
+ maxWidth);\r
+ ValueNode bcn = (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.BASE_COLUMN_NODE,\r
+ rsmd.getColumnName(index),\r
+ tableName,\r
+ dts,\r
+ getContextManager());\r
+ ResultColumn rc = (ResultColumn) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ rsmd.getColumnName(index),\r
+ bcn,\r
+ getContextManager());\r
+ rc.setType(dts);\r
+ addResultColumn(rc);\r
+ }\r
+ }\r
+ catch (Throwable t)\r
+ {\r
+ if (t instanceof StandardException)\r
+ {\r
+ throw (StandardException) t;\r
+ }\r
+ else\r
+ {\r
+ throw StandardException.unexpectedUserException(t);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Add an RC to the end of the list for the RID from an index.\r
+ * NOTE: RC.expression is a CurrentRowLocationNode. This was previously only used\r
+ * for non-select DML. We test for this node when generating the holder above\r
+ * and generate the expected code. (We really should create yet another new node\r
+ * type with its own code generation.)\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void addRCForRID()\r
+ throws StandardException\r
+ {\r
+ ResultColumn rowLocationColumn;\r
+ CurrentRowLocationNode rowLocationNode;\r
+\r
+ /* Generate the RowLocation column */\r
+ rowLocationNode = (CurrentRowLocationNode) getNodeFactory().getNode(\r
+ C_NodeTypes.CURRENT_ROW_LOCATION_NODE,\r
+ getContextManager());\r
+ rowLocationColumn =\r
+ (ResultColumn) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ "",\r
+ rowLocationNode,\r
+ getContextManager());\r
+ rowLocationColumn.markGenerated();\r
+\r
+ /* Append to the ResultColumnList */\r
+ addResultColumn(rowLocationColumn);\r
+ }\r
+\r
+ /**\r
+ * Walk the list and mark all RCs as unreferenced. This is useful\r
+ * when recalculating which RCs are referenced at what level like\r
+ * when deciding which columns need to be returned from a non-matching\r
+ * index scan (as opposed to those returned from the base table).\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void markAllUnreferenced()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ resultColumn.setUnreferenced();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Determine if all of the RC.expressions are columns in the source result set.\r
+ * This is useful for determining if we need to do reflection\r
+ * at execution time.\r
+ *\r
+ * @param sourceRS The source ResultSet.\r
+ *\r
+ * @return Whether or not all of the RC.expressions are columns in the source result set.\r
+ */\r
+ boolean allExpressionsAreColumns(ResultSetNode sourceRS)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn;\r
+ ValueNode expr;\r
+\r
+ resultColumn = (ResultColumn) elementAt(index);\r
+ expr = resultColumn.getExpression();\r
+ if (! (expr instanceof VirtualColumnNode) &&\r
+ ! (expr instanceof ColumnReference))\r
+ {\r
+ return false;\r
+ }\r
+\r
+ /* If the expression is a VirtualColumnNode, make sure that the column\r
+ * is coming from the source result set, ie, that it is not a correlated\r
+ * column.\r
+ */\r
+ if (expr instanceof VirtualColumnNode)\r
+ {\r
+ VirtualColumnNode vcn = (VirtualColumnNode) expr;\r
+ if (vcn.getSourceResultSet() != sourceRS)\r
+ {\r
+ vcn.setCorrelated();\r
+ return false;\r
+ }\r
+ }\r
+\r
+ /* Make sure this is not a correlated CR */\r
+ if (expr instanceof ColumnReference)\r
+ {\r
+ ColumnReference cr = (ColumnReference) expr;\r
+ if (cr.getCorrelated())\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Map the source columns to these columns. Build an array to represent the mapping.\r
+ * For each RC, if the expression is simply a VCN or a CR then set the array element to be\r
+ * the virtual column number of the source RC. Otherwise, set the array element to\r
+ * -1.\r
+ * This is useful for determining if we need to do reflection\r
+ * at execution time.\r
+ *\r
+ * @return Array representiong mapping of RCs to source RCs.\r
+ */\r
+ int[] mapSourceColumns()\r
+ {\r
+ int[] mapArray = new int[size()];\r
+ ResultColumn resultColumn;\r
+\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ resultColumn = (ResultColumn) elementAt(index);\r
+ if (resultColumn.getExpression() instanceof VirtualColumnNode)\r
+ {\r
+ VirtualColumnNode vcn = (VirtualColumnNode) resultColumn.getExpression();\r
+\r
+ // Can't deal with correlated VCNs\r
+ if (vcn.getCorrelated())\r
+ {\r
+ mapArray[index] = -1;\r
+ }\r
+ else\r
+ {\r
+ // Virtual column #s are 1-based\r
+ mapArray[index] = vcn.getSourceColumn().getVirtualColumnId();\r
+ }\r
+ }\r
+ else if (resultColumn.getExpression() instanceof ColumnReference)\r
+ {\r
+ ColumnReference cr = (ColumnReference) resultColumn.getExpression();\r
+\r
+ // Can't deal with correlated CRs\r
+ if (cr.getCorrelated())\r
+ {\r
+ mapArray[index] = -1;\r
+ }\r
+ else\r
+ {\r
+ // Virtual column #s are 1-based\r
+ mapArray[index] = cr.getSource().getVirtualColumnId();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ mapArray[index] = -1;\r
+ }\r
+ }\r
+\r
+ return mapArray;\r
+ }\r
+\r
+ /** Set the nullability of every ResultColumn in this list */\r
+ public void setNullability(boolean nullability)\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ resultColumn.setNullability(nullability);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Generate a FormatableBitSet representing the columns that are referenced in this RCL.\r
+ * The caller decides if they want this FormatableBitSet if every RC is referenced.\r
+ * \r
+ * @param positionedUpdate Whether or not the scan that the RCL \r
+ * belongs to is for update w/o a column list\r
+ * @param always Whether or not caller always wants a non-null FormatableBitSet if\r
+ * all RCs are referenced.\r
+ * @param onlyBCNs If true, only set bit if expression is a BaseColumnNode,\r
+ * otherwise set bit for all referenced RCs.\r
+ *\r
+ * @return The FormatableBitSet representing the referenced RCs.\r
+ */\r
+\r
+ FormatableBitSet getReferencedFormatableBitSet(boolean positionedUpdate, boolean always, boolean onlyBCNs) \r
+ {\r
+ int index;\r
+ int colsAdded = 0;\r
+ int size = size();\r
+\r
+ FormatableBitSet newReferencedCols = new FormatableBitSet(size);\r
+\r
+ /*\r
+ ** For an updatable cursor, we need\r
+ ** all columns.\r
+ */\r
+ if (positionedUpdate)\r
+ {\r
+ if (always)\r
+ {\r
+ /* Set all bits in the bit map */\r
+ for (index = 0; index < size; index++)\r
+ {\r
+ newReferencedCols.set(index);\r
+ }\r
+\r
+ return newReferencedCols;\r
+ }\r
+ else\r
+ {\r
+ return null;\r
+ }\r
+ }\r
+ \r
+ for (index = 0; index < size; index++)\r
+ {\r
+ ResultColumn oldCol = (ResultColumn) elementAt(index);\r
+ if (oldCol.isReferenced())\r
+ {\r
+ /* Skip RCs whose expression is not a BCN\r
+ * when requested to do so.\r
+ */\r
+ if (onlyBCNs && ! (oldCol.getExpression() instanceof BaseColumnNode))\r
+ {\r
+ continue;\r
+ }\r
+ newReferencedCols.set(index);\r
+ colsAdded++;\r
+ }\r
+ }\r
+\r
+ /* Return the FormatableBitSet if not all RCs are referenced or if\r
+ * the caller always wants the FormatableBitSet returned.\r
+ */\r
+ if (colsAdded != index || always)\r
+ {\r
+ return newReferencedCols;\r
+ }\r
+ else\r
+ {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Create a new, compacted RCL based on the referenced RCs\r
+ * in this list. If the RCL being compacted is for an\r
+ * updatable scan, then we simply return this.\r
+ *\r
+ * The caller tells us whether or not they want a new list\r
+ * if there is no compaction because all RCs are referenced.\r
+ * This is useful in the case where the caller needs a new\r
+ * RCL for existing RCs so that it can augment the new list.\r
+ * \r
+ * @param positionedUpdate Whether or not the scan that the RCL \r
+ * belongs to is for update w/o a column list\r
+ * @param always Whether or not caller always wants a new RCL\r
+ *\r
+ * @return The compacted RCL if compaction occurred, otherwise return this RCL.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ ResultColumnList compactColumns(boolean positionedUpdate, boolean always) \r
+ throws StandardException\r
+ {\r
+ int index;\r
+ int colsAdded = 0;\r
+\r
+ /*\r
+ ** For an updatable cursor, we need\r
+ ** all columns.\r
+ */\r
+ if (positionedUpdate)\r
+ {\r
+ return this;\r
+ }\r
+ \r
+ ResultColumnList newCols = (ResultColumnList) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN_LIST,\r
+ getContextManager());\r
+\r
+ int size = size();\r
+ for (index = 0; index < size; index++)\r
+ {\r
+ ResultColumn oldCol = (ResultColumn) elementAt(index);\r
+ if (oldCol.isReferenced())\r
+ {\r
+ newCols.addResultColumn(oldCol);\r
+ colsAdded++;\r
+ }\r
+ }\r
+\r
+ /* Return new RCL if we found unreferenced columns or if\r
+ * the caller always wants a new list. \r
+ */\r
+ if (colsAdded != index || always)\r
+ {\r
+ return newCols;\r
+ }\r
+ else\r
+ {\r
+ return this;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Remove the columns which are join columns (in the\r
+ * joinColumns RCL) from this list. This is useful\r
+ * for a JOIN with a USING clause.\r
+ * \r
+ * @param joinColumns The list of join columns\r
+ */\r
+ void removeJoinColumns(ResultColumnList joinColumns)\r
+ {\r
+ int jcSize = joinColumns.size();\r
+ for (int index = 0; index < jcSize; index++)\r
+ {\r
+ ResultColumn joinRC = (ResultColumn) joinColumns.elementAt(index);\r
+ String columnName = joinRC.getName();\r
+\r
+ // columnName should always be non-null\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(columnName != null,\r
+ "columnName should be non-null");\r
+ }\r
+\r
+ ResultColumn rightRC = getResultColumn(columnName);\r
+\r
+ // Remove the RC from this list.\r
+ if (rightRC != null)\r
+ {\r
+ removeElement(rightRC);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the join columns from this list.\r
+ * This is useful for a join with a USING clause. \r
+ * (ANSI specifies that the join columns appear 1st.) \r
+ *\r
+ * @param joinColumns A list of the join columns.\r
+ *\r
+ * @return A list of the join columns from this list\r
+ */\r
+ ResultColumnList getJoinColumns(ResultColumnList joinColumns)\r
+ {\r
+ ResultColumnList newRCL = new ResultColumnList();\r
+\r
+ /* Find all of the join columns and put them 1st on the\r
+ * new RCL.\r
+ */\r
+ int jcSize = joinColumns.size();\r
+ for (int index = 0; index < jcSize; index++)\r
+ {\r
+ ResultColumn joinRC = (ResultColumn) joinColumns.elementAt(index);\r
+ String columnName = joinRC.getName();\r
+\r
+ // columnName should always be non-null\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(columnName != null,\r
+ "columnName should be non-null");\r
+ }\r
+\r
+ ResultColumn xferRC = getResultColumn(columnName);\r
+\r
+ // Add the RC to the new list.\r
+ newRCL.addElement(xferRC);\r
+ }\r
+ return newRCL;\r
+ }\r
+\r
+ /**\r
+ * Reset the virtual column ids for all of the\r
+ * underlying RCs. (Virtual column ids are 1-based.)\r
+ */\r
+ void resetVirtualColumnIds()\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ /* ResultColumns are 1-based */\r
+ ((ResultColumn) elementAt(index)).setVirtualColumnId(index + 1);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return whether or not the same result row can be used for all\r
+ * rows returned by the associated ResultSet. This is possible\r
+ * if all entries in the list are constants or AggregateNodes.\r
+ *\r
+ * @return Whether or not the same result row can be used for all\r
+ * rows returned by the associated ResultSet. \r
+ */\r
+ boolean reusableResult()\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if ((rc.getExpression() instanceof ConstantNode) ||\r
+ (rc.getExpression() instanceof AggregateNode))\r
+ {\r
+ continue;\r
+ }\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Get an array of column positions (1-based) for all the columns\r
+ * in this RCL. Assumes that all the columns are in the passed-in\r
+ * table\r
+ *\r
+ * @return the array of strings\r
+ *\r
+ * @exception throws StandardException on error\r
+ */\r
+ public int[] getColumnPositions( TableDescriptor td )\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+ int[] myPositions = new int[ size ];\r
+ String columnName;\r
+ ColumnDescriptor cd;\r
+\r
+ for ( int index = 0; index < size; index++ )\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt( index );\r
+ columnName = resultColumn.getName();\r
+ cd = td.getColumnDescriptor( columnName );\r
+\r
+ if ( cd == null )\r
+ {\r
+ throw StandardException.newException\r
+ ( SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, columnName, td.getQualifiedName() );\r
+ }\r
+\r
+ myPositions[ index ] = cd.getPosition();\r
+ }\r
+\r
+ return myPositions;\r
+ }\r
+\r
+ /**\r
+ * Get an array of strings for all the columns\r
+ * in this RCL.\r
+ *\r
+ * @return the array of strings\r
+ */\r
+ public String[] getColumnNames()\r
+ {\r
+ String strings[] = new String[size()];\r
+\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) elementAt(index);\r
+ strings[index] = resultColumn.getName();\r
+ }\r
+ return strings;\r
+ }\r
+\r
+ /**\r
+ * Replace any DEFAULTs with the associated tree for the default.\r
+ *\r
+ * @param ttd The TableDescriptor for the target table.\r
+ * @param tcl The RCL for the target table.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl) \r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.isDefaultColumn())\r
+ {\r
+ // DefaultNode defaultNode = (DefaultNode) rc.getExpression();\r
+ // Get ColumnDescriptor by name or by position?\r
+ ColumnDescriptor cd;\r
+ if (tcl == null)\r
+ {\r
+ cd = ttd.getColumnDescriptor(index + 1);\r
+ }\r
+ else\r
+ {\r
+ ResultColumn trc = (ResultColumn) tcl.elementAt(index);\r
+ cd = ttd.getColumnDescriptor(trc.getName());\r
+ }\r
+\r
+ // Too many RCs if no ColumnDescriptor\r
+ if (cd == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TOO_MANY_RESULT_COLUMNS, \r
+ ttd.getQualifiedName());\r
+ }\r
+\r
+ if (cd.isAutoincrement())\r
+ {\r
+ rc.setAutoincrementGenerated();\r
+ } // end of if ()\r
+ \r
+ DefaultInfoImpl defaultInfo = (DefaultInfoImpl) cd.getDefaultInfo();\r
+ if (defaultInfo != null)\r
+ {\r
+ /* Query is dependent on the DefaultDescriptor */\r
+ DefaultDescriptor defaultDescriptor = cd.getDefaultDescriptor(getDataDictionary());\r
+ getCompilerContext().createDependency(defaultDescriptor);\r
+\r
+ rc.setExpression(\r
+ DefaultNode.parseDefault(\r
+ defaultInfo.getDefaultText(),\r
+ getLanguageConnectionContext(),\r
+ getCompilerContext()));\r
+\r
+ }\r
+ else\r
+ {\r
+ rc.setExpression(\r
+ (ValueNode) getNodeFactory().getNode(\r
+ C_NodeTypes.UNTYPED_NULL_CONSTANT_NODE,\r
+ getContextManager()));\r
+ }\r
+ rc.setDefaultColumn(false);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Walk the RCL and check for DEFAULTs. DEFAULTs\r
+ * are invalid at the time that this method is called,\r
+ * so we throw an exception if found.\r
+ * NOTE: The grammar allows:\r
+ * VALUES DEFAULT;\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void checkForInvalidDefaults()\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+\r
+ if (rc.isAutoincrementGenerated())\r
+ continue;\r
+\r
+ if (rc.isDefaultColumn())\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_USE_OF_DEFAULT);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Verify that all of the RCs in this list are comparable.\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void verifyAllOrderable() \r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ rc.verifyOrderable();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Build this ResultColumnList from a table description and\r
+ * an array of column IDs.\r
+ *\r
+ * @param table describes the table\r
+ * @param columnIDs column positions in that table (1-based)\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void populate\r
+ (\r
+ TableDescriptor table,\r
+ int[] columnIDs\r
+ )\r
+ throws StandardException\r
+ {\r
+ if ( columnIDs == null ) { return; }\r
+\r
+ int count = columnIDs.length;\r
+ TableName tableName = makeTableName( table.getSchemaName(), table.getName() );\r
+ String columnName;\r
+ int columnPosition;\r
+ ResultColumn rc;\r
+\r
+ for ( int i = 0; i < count; i++ )\r
+ {\r
+ columnPosition = columnIDs[ i ];\r
+ columnName = table.getColumnDescriptor( columnPosition ).getColumnName();\r
+\r
+ rc = makeColumnFromName( columnName );\r
+\r
+ addResultColumn( rc );\r
+ }\r
+\r
+ }\r
+\r
+ private ResultColumn makeColumnFromName( String columnName )\r
+ throws StandardException\r
+ {\r
+ ResultColumn resultColumn = (ResultColumn) getNodeFactory().getNode\r
+ (\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ columnName,\r
+ null,\r
+ getContextManager()\r
+ );\r
+\r
+ return resultColumn;\r
+ }\r
+\r
+ private ResultColumn makeColumnReferenceFromName\r
+ (\r
+ TableName tableName,\r
+ String columnName\r
+ )\r
+ throws StandardException\r
+ {\r
+ ContextManager cm = getContextManager();\r
+ NodeFactory nodeFactory = getNodeFactory();\r
+\r
+ ResultColumn rc = (ResultColumn) nodeFactory.getNode\r
+ (\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ null,\r
+ nodeFactory.getNode\r
+ (\r
+ C_NodeTypes.COLUMN_REFERENCE,\r
+ columnName,\r
+ tableName,\r
+ cm\r
+ ),\r
+ cm\r
+ );\r
+\r
+ return rc;\r
+ }\r
+ \r
+ /**\r
+ * check if any autoincrement columns exist in the result column list.\r
+ * called from insert or update where you cannot insert/update the value\r
+ * of an autoincrement column.\r
+ *\r
+ * @exception StandardException If the column is an ai column\r
+ */\r
+ public void checkAutoincrement(ResultColumnList sourceRSRCL)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(index);\r
+ ResultColumn sourceRC = \r
+ (ResultColumn)((sourceRSRCL == null) ? null : sourceRSRCL.elementAt(index));\r
+ ColumnDescriptor cd = rc.getTableColumnDescriptor();\r
+ \r
+ if ((cd != null) && (cd.isAutoincrement()))\r
+ {\r
+ if ((sourceRC != null) && \r
+ (sourceRC.isAutoincrementGenerated()))\r
+ {\r
+ sourceRC.setColumnDescriptor(cd.getTableDescriptor(), cd);\r
+\r
+ }else{\r
+ if(cd.isAutoincAlways())\r
+ throw StandardException.newException(SQLState.LANG_AI_CANNOT_MODIFY_AI,\r
+ rc.getName());\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public void incOrderBySelect()\r
+ {\r
+ orderBySelect++;\r
+ }\r
+\r
+ private void decOrderBySelect()\r
+ {\r
+ orderBySelect--;\r
+ }\r
+\r
+ public int getOrderBySelect()\r
+ {\r
+ return orderBySelect;\r
+ }\r
+\r
+ public void copyOrderBySelect( ResultColumnList src)\r
+ {\r
+ orderBySelect = src.orderBySelect;\r
+ }\r
+\r
+ /* ****\r
+ * Take note of the size of this RCL _before_ we start\r
+ * processing/binding it. This is so that, at bind time,\r
+ * we can tell if any columns in the RCL were added\r
+ * internally by us (i.e. they were not specified by the\r
+ * user and thus will not be returned to the user).\r
+ */\r
+ protected void markInitialSize() {\r
+ initialListSize = size();\r
+ }\r
+\r
+ private int numGeneratedColumns() \r
+ {\r
+ int numGenerated = 0;\r
+ int sz = size();\r
+ for (int i = sz - 1; i >= 0; i--) \r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(i);\r
+ if (rc.isGenerated()) \r
+ {\r
+ numGenerated++;\r
+ }\r
+ }\r
+ return numGenerated;\r
+ }\r
+ \r
+ /**\r
+ * @return the number of generated columns in this RCL.\r
+ */\r
+ int numGeneratedColumnsForGroupBy()\r
+ {\r
+ int numGenerated = 0;\r
+ int sz = size();\r
+ for (int i = sz - 1; i >= 0; i--) {\r
+ ResultColumn rc = (ResultColumn) elementAt(i);\r
+ if (rc.isGenerated() && rc.isGroupingColumn())\r
+ {\r
+ numGenerated++;\r
+ }\r
+ }\r
+ return numGenerated;\r
+ }\r
+ \r
+ /**\r
+ * Remove any generated columns from this RCL.\r
+ */\r
+ void removeGeneratedGroupingColumns()\r
+ {\r
+ int sz = size();\r
+ for (int i = sz - 1; i >= 0; i--) \r
+ {\r
+ ResultColumn rc = (ResultColumn) elementAt(i);\r
+ if (rc.isGenerated() && rc.isGroupingColumn()) \r
+ {\r
+ removeElementAt(i);\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * @return the number of columns that will be visible during execution. \r
+ * During compilation we can add columns for a group by/order by but these\r
+ * to an RCL but these are projected out during query execution. \r
+ */\r
+ public int visibleSize() \r
+ {\r
+ return size() - orderBySelect - numGeneratedColumns();\r
+ }\r
+}\r