String subqueryTypeString =
getTypeCompiler().interfaceName();
MethodBuilder mb = acb.newGeneratedFun(subqueryTypeString, Modifier.PROTECTED);
/* Declare the field to hold the suquery's ResultSet tree */
LocalField rsFieldLF = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.NoPutResultSet);
ResultSetNode subNode = null;
if (!isMaterializable())
{
MethodBuilder executeMB = acb.getExecuteMethod();
if (pushedNewPredicate && (! hasCorrelatedCRs()))
{
/* We try to materialize the subquery if it can fit in the memory. We
* evaluate the subquery first. If the result set fits in the memory,
* we replace the resultset with in-memory unions of row result sets.
* We do this trick by replacing the child result with a new node --
* MaterializeSubqueryNode, which essentially generates the suitable
* code to materialize the subquery if possible. This may have big
* performance improvement. See beetle 4373.
*/
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(resultSet instanceof ProjectRestrictNode,
"resultSet expected to be a ProjectRestrictNode!");
}
subNode = ((ProjectRestrictNode) resultSet).getChildResult();
LocalField subRS = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.NoPutResultSet);
mb.getField(subRS);
mb.conditionalIfNull();
ResultSetNode materialSubNode = new MaterializeSubqueryNode(subRS);
// Propagate the resultSet's cost estimate to the new node.
materialSubNode.costEstimate = resultSet.getFinalCostEstimate();
((ProjectRestrictNode) resultSet).setChildResult(materialSubNode);
/* Evaluate subquery resultset here first. Next time when we come to
* this subquery it may be replaced by a bunch of unions of rows.
*/
subNode.generate(acb, mb);
mb.startElseCode();
mb.getField(subRS);
mb.completeConditional();
mb.setField(subRS);
executeMB.pushNull( ClassName.NoPutResultSet);
executeMB.setField(subRS);
}
executeMB.pushNull( ClassName.NoPutResultSet);
executeMB.setField(rsFieldLF);
// now we fill in the body of the conditional
mb.getField(rsFieldLF);
mb.conditionalIfNull();
}
acb.pushGetResultSetFactoryExpression(mb);
// start of args
int nargs;
/* Inside here is where subquery could already have been materialized. 4373
*/
resultSet.generate(acb, mb);
/* Get the next ResultSet #, so that we can number the subquery's
* empty row ResultColumnList and Once/Any ResultSet.
*/
int subqResultSetNumber = cc.getNextResultSetNumber();
/* We will be reusing the RCL from the subquery's ResultSet for the
* empty row function. We need to reset the resultSetNumber in the
* RCL, before we generate that function. Now that we've called
* generate() on the subquery's ResultSet, we can reset that
* resultSetNumber.
*/
resultSet.getResultColumns().setResultSetNumber(subqResultSetNumber);
/* Generate code for empty row */
resultSet.getResultColumns().generateNulls(acb, mb);
/*
* arg1: suqueryExpress - Expression for subquery's
* ResultSet
* arg2: Activation
* arg3: Method to generate Row with null(s) if subquery
* Result Set is empty
*/
if (subqueryType == EXPRESSION_SUBQUERY)
{
int cardinalityCheck;
/* No need to do sort if subquery began life as a distinct expression subquery.
* (We simply check for a single unique value at execution time.)
* No need for cardinality check if we know that underlying
* ResultSet can contain at most 1 row.
* RESOLVE - Not necessary if we know we
* are getting a single row because of a unique index.
*/
if (distinctExpression)
{
cardinalityCheck = OnceResultSet.UNIQUE_CARDINALITY_CHECK;
}
else if (resultSet.returnsAtMostOneRow())
{
cardinalityCheck = OnceResultSet.NO_CARDINALITY_CHECK;
}
else
{
cardinalityCheck = OnceResultSet.DO_CARDINALITY_CHECK;
}
/* arg4: int - whether or not cardinality check is required
* DO_CARDINALITY_CHECK - required
* NO_CARDINALITY_CHECK - not required
* UNIQUE_CARDINALITY_CHECK - verify single
* unique value
*/
mb.push(cardinalityCheck);
nargs = 8;
} else {
nargs = 7;
}
mb.push(subqResultSetNumber);
mb.push(subqueryNumber);
mb.push(pointOfAttachment);
mb.push(costEstimate.rowCount());
mb.push(costEstimate.getEstimatedCost());
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, resultSetString, ClassName.NoPutResultSet, nargs);
/* Fill in the body of the method
* generates the following.
* (NOTE: the close() method only generated for
* materialized subqueries. All other subqueries
* closed by top result set in the query.):
*
* NoPutResultSet rsFieldX;
* {
* <Datatype interface> col;
* ExecRow r;
* rsFieldX = (rsFieldX == null) ? outerRSCall() : rsFieldX; // <== NONmaterialized specific
* rsFieldX.openCore();
* r = rsFieldX.getNextRowCore();
* col = (<Datatype interface>) r.getColumn(1);
* return col;
* }
*
* MATERIALIZED:
* NoPutResultSet rsFieldX;
* {
* <Datatype interface> col;
* ExecRow r;
* rsFieldX = outerRSCall();
* rsFieldX.openCore();
* r = rsFieldX.getNextRowCore();
* col = (<Datatype interface>) r.getColumn(1);
* rsFieldX.close(); // <== materialized specific
* return col;
* }
* and adds it to exprFun
*/
/* Generate the declarations */ // PUSHCOMPILE
//VariableDeclaration colVar = mb.addVariableDeclaration(subqueryTypeString);
//VariableDeclaration rVar = mb.addVariableDeclaration(ClassName.ExecRow);
if (!isMaterializable())
{
/* put it back
*/
if (pushedNewPredicate && (! hasCorrelatedCRs()))
((ProjectRestrictNode) resultSet).setChildResult(subNode);
// now we fill in the body of the conditional
mb.startElseCode();
mb.getField(rsFieldLF);
mb.completeConditional();
}
mb.setField(rsFieldLF);
/* rs.openCore() */
mb.getField(rsFieldLF);
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "openCore", "void", 0);
/* r = rs.next() */
mb.getField(rsFieldLF);
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getNextRowCore", ClassName.ExecRow, 0);
//mb.putVariable(rVar);
//mb.endStatement();
/* col = (<Datatype interface>) r.getColumn(1) */
//mb.getVariable(rVar);
mb.push(1); // both the Row interface and columnId are 1-based
mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "getColumn", ClassName.DataValueDescriptor, 1);
mb.cast(subqueryTypeString);
//mb.putVariable(colVar);
//mb.endStatement();
/* Only generate the close() method for materialized
* subqueries. All others will be closed when the
* close() method is called on the top ResultSet.
*/
if (isMaterializable())
{
/* rs.close() */
mb.getField(rsFieldLF);
mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.ResultSet, "close", "void", 0);
}
/* return col */
//mb.getVariable(colVar);
mb.methodReturn();
mb.complete();
/*
** If we have an expression subquery, then we
** can materialize it if it has no correlated
** column references and is invariant.
*/
if (isMaterializable())
{
LocalField lf = generateMaterialization(acb, mb, subqueryTypeString);
mbex.getField(lf);
} else {
/* Generate the call to the new method */
mbex.pushThis();