/** * This overload variant of optimizeStatement is used by subclass CursorNode (as well as a minion * for the no-arg variant). * * @param offset Any OFFSET row count, or null * @param fetchFirst Any FETCH FIRST row count or null * @exception StandardException Thrown on error * @see DMLStatementNode#optimizeStatement() */ protected void optimizeStatement(ValueNode offset, ValueNode fetchFirst) throws StandardException { resultSet = resultSet.preprocess(getCompilerContext().getNumTables(), null, (FromList) null); resultSet = resultSet.optimize(getDataDictionary(), null, 1.0d); resultSet = resultSet.modifyAccessPaths(); // Any OFFSET/FETCH FIRST narrowing must be done *after* any rewrite of // the query tree (if not, underlying GROUP BY fails), but *before* the // final scroll insensitive result node set is added - that one needs // to sit on top - so now is the time. // // This example statement fails if we wrap *before* the optimization // above: // select max(a) from t1 group by b fetch first row only // // A java.sql.ResultSet#previous on a scrollable result set will fail // if we don't wrap *after* the ScrollInsensitiveResultSetNode below. // // We need only wrap the RowCountNode set if at least one of the // clauses is present. if (offset != null || fetchFirst != null) { resultSet = wrapRowCountNode(resultSet, offset, fetchFirst); } /* If this is a cursor, then we * need to generate a new ResultSetNode to enable the scrolling * on top of the tree before modifying the access paths. */ if (this instanceof CursorNode) { ResultColumnList siRCList; ResultColumnList childRCList; ResultSetNode siChild = resultSet; /* We get a shallow copy of the ResultColumnList and its * ResultColumns. (Copy maintains ResultColumn.expression for now.) */ siRCList = resultSet.getResultColumns(); childRCList = siRCList.copyListAndObjects(); resultSet.setResultColumns(childRCList); /* Replace ResultColumn.expression with new VirtualColumnNodes * in the ScrollInsensitiveResultSetNode's ResultColumnList. (VirtualColumnNodes include * pointers to source ResultSetNode, this, and source ResultColumn.) */ siRCList.genVirtualColumnNodes(resultSet, childRCList); /* Finally, we create the new ScrollInsensitiveResultSetNode */ resultSet = (ResultSetNode) getNodeFactory() .getNode( C_NodeTypes.SCROLL_INSENSITIVE_RESULT_SET_NODE, resultSet, siRCList, null, getContextManager()); // Propagate the referenced table map if it's already been created if (siChild.getReferencedTableMap() != null) { resultSet.setReferencedTableMap((JBitSet) siChild.getReferencedTableMap().clone()); } } }
/** * Preprocess a ResultSetNode - this currently means: o Generating a referenced table map for each * ResultSetNode. o Putting the WHERE and HAVING clauses in conjunctive normal form (CNF). o * Converting the WHERE and HAVING clauses into PredicateLists and classifying them. o Ensuring * that a ProjectRestrictNode is generated on top of every FromBaseTable and generated in place of * every FromSubquery. o Pushing single table predicates down to the new ProjectRestrictNodes. * * @param numTables The number of tables in the DML Statement * @param gbl The group by list, if any * @param fromList The from list, if any * @return ResultSetNode at top of preprocessed tree. * @exception StandardException Thrown on error */ public ResultSetNode preprocess(int numTables, GroupByList gbl, FromList fromList) throws StandardException { // Push the order by list down to the ResultSet if (orderByList != null) { // If we have more than 1 ORDERBY columns, we may be able to // remove duplicate columns, e.g., "ORDER BY 1, 1, 2". if (orderByList.size() > 1) { orderByList.removeDupColumns(); } subquery.pushOrderByList(orderByList); orderByList = null; } subquery.pushOffsetFetchFirst(offset, fetchFirst, hasJDBClimitClause); /* We want to chop out the FromSubquery from the tree and replace it * with a ProjectRestrictNode. One complication is that there may be * ColumnReferences above us which point to the FromSubquery's RCL. * What we want to return is a tree with a PRN with the * FromSubquery's RCL on top. (In addition, we don't want to be * introducing any redundant ProjectRestrictNodes.) * Another complication is that we want to be able to only push * projections and restrictions down to this ProjectRestrict, but * we want to be able to push them through as well. * So, we: * o call subquery.preprocess() which returns a tree with * a SelectNode or a RowResultSetNode on top. * o If the FSqry is flattenable(), then we return (so that the * caller can then call flatten()), otherwise we: * o generate a PRN, whose RCL is the FSqry's RCL, on top of the result. * o create a referencedTableMap for the PRN which represents * the FSqry's tableNumber, since ColumnReferences in the outer * query block would be referring to that one. * (This will allow us to push restrictions down to the PRN.) */ subquery = subquery.preprocess(numTables, gbl, fromList); /* Return if the FSqry is flattenable() * NOTE: We can't flatten a FromSubquery if there is a group by list * because the group by list must be ColumnReferences. For: * select c1 from v1 group by c1, * where v1 is select 1 from t1 * The expression under the last redundant ResultColumn is an IntConstantNode, * not a ColumnReference. * We also do not flatten a subquery if tableProperties is non-null, * as the user is specifying 1 or more properties for the derived table, * which could potentially be lost on the flattening. * RESOLVE - this is too restrictive. */ if ((gbl == null || gbl.size() == 0) && tableProperties == null && subquery.flattenableInFromSubquery(fromList)) { /* Set our table map to the subquery's table map. */ setReferencedTableMap(subquery.getReferencedTableMap()); return this; } return extractSubquery(numTables); }