📄 subquerynode.java
字号:
* @param dataDictionary The DataDictionary to use for optimization * @param outerRows The optimizer's estimate of the number of * times this subquery will be executed. * * @return Nothing * * @exception StandardException Thrown on error */ public void optimize(DataDictionary dataDictionary, double outerRows) throws StandardException { /* RESOLVE - is there anything else that we need to do for this * node. */ /* Optimize the underlying result set */ resultSet = resultSet.optimize(dataDictionary, null, outerRows); } /** * Make any changes to the access paths, as decided by the optimizer. * * @exception StandardException Thrown on error */ public void modifyAccessPaths() throws StandardException { resultSet = resultSet.modifyAccessPaths(); } /** * Return the variant type for the underlying expression. * The variant type can be: * VARIANT - variant within a scan * (method calls and non-static field access) * SCAN_INVARIANT - invariant within a scan * (column references from outer tables) * QUERY_INVARIANT - invariant within the life of a query * (constant expressions) * * @return The variant type for the underlying expression. * * @exception StandardException Thrown on error */ protected int getOrderableVariantType() throws StandardException { /* * If the subquery is variant, than return * VARIANT. Otherwise, if we have an expression * subquery and no correlated CRs we are going * to materialize it, so it is QUERY_INVARIANT. * Otherwise, SCAN_INVARIANT. */ if (isInvariant()) { if (!hasCorrelatedCRs() && (subqueryType == EXPRESSION_SUBQUERY)) { return Qualifier.QUERY_INVARIANT; } else { return Qualifier.SCAN_INVARIANT; } } else { return Qualifier.VARIANT; } } /** * Do code generation for this subquery. * * @param expressionBuilder The ExpressionClassBuilder for the class being built * @param mbex The method the expression will go into * * * @exception StandardException Thrown on error */ public void generateExpression( ExpressionClassBuilder expressionBuilder, MethodBuilder mbex) throws StandardException { CompilerContext cc = getCompilerContext(); String resultSetString; /////////////////////////////////////////////////////////////////////////// // // Subqueries should not appear in Filter expressions. We should get here // only if we're compiling a query. That means that our class builder // is an activation builder. If we ever allow subqueries in filters, we'll // have to revisit this code. // /////////////////////////////////////////////////////////////////////////// if (SanityManager.DEBUG) { SanityManager.ASSERT(expressionBuilder instanceof ActivationClassBuilder, "Expecting an ActivationClassBuilder"); } ActivationClassBuilder acb = (ActivationClassBuilder) expressionBuilder; /* Reuse generated code, where possible */ /* Generate the appropriate (Any or Once) ResultSet */ if (subqueryType == EXPRESSION_SUBQUERY) { resultSetString = "getOnceResultSet"; } else { resultSetString = "getAnyResultSet"; } // Get cost estimate for underlying subquery CostEstimate costEstimate = resultSet.getFinalCostEstimate(); /* Generate a new method. It's only used within the other * exprFuns, so it could be private. but since we don't * generate the right bytecodes to invoke private methods, * we just make it protected. This generated class won't * have any subclasses, certainly! (nat 12/97) */ 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); acb.pushThisAsActivation(mb); /* 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 = 9; } else { nargs = 8; } 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(); mbex.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mb.getName(), subqueryTypeString, 0); } } /* ** Materialize the subquery in question. Given the expression ** that represents the subquery, this returns fieldX where ** fieldX is set up as follows: ** ** private ... fieldX ** ** execute() ** { ** fieldX = <subqueryExpression> ** ... ** } ** ** So we wind up evaluating the subquery when we start ** execution. Obviously, it is absolutely necessary that ** the subquery is invariant and has no correlations ** for this to work. ** ** Ideally we wouldn't evaluate the expression subquery ** until we know we need to, but because we are marking ** this expression subquery as pushable, we must evaluate ** it up front because it might wind up as a qualification, ** and we cannot execute a subquery in the store as a ** qualification because the store executes qualifications ** while holding a latch. ** ** @param acb ** @param type ** @param subqueryExpression */ private LocalField generateMaterialization( ActivationClassBuilder acb, MethodBuilder mbsq, String type) { MethodBuilder mb = acb.executeMethod; // declare field LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE, type); /* Generate the call to the new method */ mb.pushThis(); mb.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mbsq.getName(), type, 0); // generate: field = value (value is on stack) mb.setField(field); return
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -