📄 joinnode.java
字号:
SanityManager.THROWASSERT( "allTableName (" + allTableName + ") expected to be null"); } } // Return a spliced copy of the 2 lists ResultColumnList tempList = (ResultColumnList) getNodeFactory().getNode( C_NodeTypes.RESULT_COLUMN_LIST, getContextManager()); tempList.nondestructiveAppend(leftRCL); tempList.nondestructiveAppend(rightRCL); return tempList; } } /** * Try to find a ResultColumn in the table represented by this FromTable * that matches the name in the given ColumnReference. * * @param columnReference The columnReference whose name we're looking * for in the given table. * * @return A ResultColumn whose expression is the ColumnNode * that matches the ColumnReference. * Returns null if there is no match. * * @exception StandardException Thrown on error */ public ResultColumn getMatchingColumn(ColumnReference columnReference) throws StandardException { /* Get the logical left and right sides of the join. * (For RIGHT OUTER JOIN, the left is the right * and the right is the left and the JOIN is the NIOJ). */ ResultSetNode logicalLeftRS = getLogicalLeftResultSet(); ResultSetNode logicalRightRS = getLogicalRightResultSet(); ResultColumn leftRC = null; ResultColumn resultColumn = null; ResultColumn rightRC = null; ResultColumn usingRC = null; leftRC = logicalLeftRS.getMatchingColumn(columnReference); if (leftRC != null) { resultColumn = leftRC; /* Find out if the column is in the using clause */ if (usingClause != null) { usingRC = usingClause.getResultColumn(leftRC.getName()); } } /* We only search on the right if the column isn't in the * USING clause. */ if (usingRC == null) { rightRC = logicalRightRS.getMatchingColumn(columnReference); } if (rightRC != null) { /* We must catch ambiguous column references for joins here, * since FromList only checks for ambiguous references between * nodes, not within a node. */ if (leftRC != null) { throw StandardException.newException(SQLState.LANG_AMBIGUOUS_COLUMN_NAME, columnReference.getSQLColumnName()); } resultColumn = rightRC; } /* Insert will bind the underlying result sets which have * tables twice. On the 2nd bind, resultColumns != null, * we must return the RC from the JoinNode's RCL which is above * the RC that we just found above. (Otherwise, the source * for the ColumnReference will be from the wrong ResultSet * at generate().) */ if (resultColumns != null) { int rclSize = resultColumns.size(); for (int index = 0; index < rclSize; index++) { ResultColumn rc = (ResultColumn) resultColumns.elementAt(index); VirtualColumnNode vcn = (VirtualColumnNode) rc.getExpression(); if (resultColumn == vcn.getSourceColumn()) { resultColumn = rc; break; } } } return resultColumn; } /** * Bind the result columns of this ResultSetNode when there is no * base table to bind them to. This is useful for SELECT statements, * where the result columns get their types from the expressions that * live under them. * * @param fromListParam FromList to use/append to. * * @return Nothing * * @exception StandardException Thrown on error */ public void bindResultColumns(FromList fromListParam) throws StandardException { super.bindResultColumns(fromListParam); /* Now we build our RCL */ buildRCL(); /* We cannot bind the join clause until after we've bound our * result columns. This is because the resultColumns from the * children are propagated and merged to create our resultColumns * in super.bindRCs(). If we bind the join clause prior to that * call, then the ColumnReferences in the join clause will point * to the children's RCLs at the time that they are bound, but * will end up pointing above themselves, to our resultColumns, * after the call to super.bindRCS(). */ deferredBindExpressions(fromListParam); } /** * Bind the result columns for this ResultSetNode to a base table. * This is useful for INSERT and UPDATE statements, where the * result columns get their types from the table being updated or * inserted into. * If a result column list is specified, then the verification that the * result column list does not contain any duplicates will be done when * binding them by name. * * @param targetTableDescriptor The TableDescriptor for the table being * updated or inserted into * @param targetColumnList For INSERT statements, the user * does not have to supply column * names (for example, "insert into t * values (1,2,3)". When this * parameter is null, it means that * the user did not supply column * names, and so the binding should * be done based on order. When it * is not null, it means do the binding * by name, not position. * @param statement Calling DMLStatementNode (Insert or Update) * @param fromListParam FromList to use/append to. * * @return Nothing * * @exception StandardException Thrown on error */ public void bindResultColumns(TableDescriptor targetTableDescriptor, FromVTI targetVTI, ResultColumnList targetColumnList, DMLStatementNode statement, FromList fromListParam) throws StandardException { super.bindResultColumns(targetTableDescriptor, targetVTI, targetColumnList, statement, fromListParam); /* Now we build our RCL */ buildRCL(); /* We cannot bind the join clause until after we've bound our * result columns. This is because the resultColumns from the * children are propagated and merged to create our resultColumns * in super.bindRCs(). If we bind the join clause prior to that * call, then the ColumnReferences in the join clause will point * to the children's RCLs at the time that they are bound, but * will end up pointing above themselves, to our resultColumns, * after the call to super.bindRCS(). */ deferredBindExpressions(fromListParam); } /** * Build the RCL for this node. We propagate the RCLs up from the * children and splice them to form this node's RCL. * * @return Nothing * * @exception StandardException Thrown on error */ private void buildRCL() throws StandardException { /* NOTE - we only need to build this list if it does not already * exist. This can happen in the degenerate case of an insert * select with a join expression in a derived table within the select. */ if (resultColumns != null) { return; } ResultColumnList leftRCL; ResultColumnList rightRCL; ResultColumnList tmpRCL; /* We get a shallow copy of the left's ResultColumnList and its * ResultColumns. (Copy maintains ResultColumn.expression for now.) */ resultColumns = leftResultSet.getResultColumns(); leftRCL = resultColumns.copyListAndObjects(); leftResultSet.setResultColumns(leftRCL); /* Replace ResultColumn.expression with new VirtualColumnNodes * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include * pointers to source ResultSetNode, this, and source ResultColumn.) */ resultColumns.genVirtualColumnNodes(leftResultSet, leftRCL, false); /* ** If this is a right outer join, we can get nulls on the left side, ** so change the types of the left result set to be nullable. */ if (this instanceof HalfOuterJoinNode && ((HalfOuterJoinNode)this).isRightOuterJoin()) { resultColumns.setNullability(true); } /* Now, repeat the process with the right's RCL */ tmpRCL = rightResultSet.getResultColumns(); rightRCL = tmpRCL.copyListAndObjects(); rightResultSet.setResultColumns(rightRCL); /* Replace ResultColumn.expression with new VirtualColumnNodes * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include * pointers to source ResultSetNode, this, and source ResultColumn.) */ tmpRCL.genVirtualColumnNodes(rightResultSet, rightRCL, false); tmpRCL.adjustVirtualColumnIds(resultColumns.size()); /* ** If this is a left outer join, we can get nulls on the right side, ** so change the types of the right result set to be nullable. */ if (this instanceof HalfOuterJoinNode && !((HalfOuterJoinNode)this).isRightOuterJoin()) { tmpRCL.setNullability(true); } /* Now we append the propagated RCL from the right to the one from * the left and call it our own. */ resultColumns.nondestructiveAppend(tmpRCL); } private void deferredBindExpressions(FromList fromListParam) throws StandardException { /* Bind the expressions in the join clause */ subqueryList = (SubqueryList) getNodeFactory().getNode( C_NodeTypes.SUBQUERY_LIST, getContextManager()); aggregateVector = new Vector(); /* ON clause */ if (joinClause != null) { /* Create a new fromList with only left and right children before * binding the join clause. Valid column references in the join clause * are limited to columns from the 2 tables being joined. This * algorithm enforces that. */ FromList fromList = (FromList) getNodeFactory().getNode( C_NodeTypes.FROM_LIST, getNodeFactory().doJoinOrderOptimization(), getContextManager()); fromList.addElement((FromTable) leftResultSet); fromList.addElement((FromTable) rightResultSet); /* First bind with all tables in the from clause, to detect ambiguous * references. Push the left and right children to the front of the * fromListParam before binding the join clause. (We will * remove it before returning.) Valid column references in * the join clause are limited to columns from the 2 tables being * joined */ fromListParam.insertElementAt(rightResultSet, 0); fromListParam.insertElementAt(leftResultSet, 0); joinClause = joinClause.bindExpression( fromListParam, subqueryList, aggregateVector); /* Now bind with two tables being joined. If this raises column not found exception, * then we have a reference to other tables in the from clause. Raise invalid * ON clause error to match DB2. */ try { joinClause = joinClause.bindExpression( fromList, subqueryList, aggregateVector); } catch (StandardException se) { if (se.getSQLState().equals(SQLState.LANG_COLUMN_NOT_FOUND)) throw StandardException.newException(SQLState.LANG_DB2_ON_CLAUSE_INVALID); throw se; } /* DB2 doesn't allow subquerries in the ON clause */ if (subqueryList.size() > 0) throw StandardException.newException(SQLState.LANG_DB2_ON_CLAUSE_INVALID); /* ** We cannot have aggregates in the ON clause. ** In the future, if we relax this, we'll need ** to be able to pass the aggregateVector up ** the tree. */ if (aggregateVector.size() > 0) { throw StandardException.newException(SQLState.LANG_NO_AGGREGATES_IN_ON_CLAUSE); } fromListParam.removeElementAt(0); fromListParam.removeElementAt(0); } /* USING clause */ else if (usingClause != null) { /* Build a join clause from the usingClause, using the * exposed names in the left and right RSNs. * For each column in the list, we generate 2 ColumnReferences, * 1 for the left and 1 for the right. We bind each of these * to the appropriate side and build an equality predicate * between the 2. We bind the = and AND nodes by hand because * we have to bind the ColumnReferences a side at a time. * We need to bind the CRs a side at a time to ensure that * we don't find an bogus ambiguous column reference. (Bug 377) */ joinClause = (AndNode) getNodeFactory().getNode( C_NodeTypes.AND_NODE, null, null, getContextManager()); AndNode currAnd = (AndNode) joinClause; ValueNode trueNode = (ValueNode) getNodeFactory().getNode( C_NodeTypes.BOOLEAN_CONSTANT_NODE, Boolean.TRUE, getContextManager()); int usingSize = usingClause.size(); for (int index = 0; index < usingSize; index++) { BinaryComparisonOperatorNode equalsNode; ColumnReference leftCR; ColumnReference rightCR; ResultColumn rc = (ResultColumn) usingClause.elementAt(index); /* currAnd starts as first point of insertion (leftOperand == null) * and becomes last point of insertion. */ if (currAnd.getLeftOperand() != null) { currAnd.setRightOperand( (AndNode) getNodeFactory().getNode( C_NodeTypes.AND_NODE, null, null, getContextManager())); currAnd = (AndNode) currAnd.getRightOperand(); } /* Create and bind the left CR */ fromListParam.insertElementAt(leftResultSet, 0); leftCR = (ColumnReference) getNodeFactory().getNode( C_NodeTypes.COLUMN_REFERENCE, rc.getName(), ((FromTable) leftResultSet).getTableName(), getContextManager()); leftCR = (ColumnReference) leftCR.bindExpression( fromListParam, subqueryList, aggregateVector); fromListParam.removeElementAt(0); /* Create and bind the right CR */ fromListParam.insertElementAt(rightResultSet, 0); rightCR = (ColumnReference) getNodeFactory().getNode( C_NodeTypes.COLUMN_REFERENCE, rc.getName(), ((FromTable) rightResultSet).getTableName(), getContextManager()); rightCR = (ColumnReference) rightCR.bindExpression( fromListParam, subqueryList, aggregateVector); fromListParam.removeElementAt(0); /* Create and insert the new = condition */ equalsNode = (BinaryComparisonOperatorNode) getNodeFactory().getNode( C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE, leftCR, rightCR, getContextManager()); equalsNode.bindComparisonOperator(); currAnd.setLeftOperand(equalsNode); /* The right deep chain of AndNodes ends in a BinaryTrueNode. * NOTE: We set it for every AndNode, even though we will * overwrite it if this is not the last column in the list, * because postBindFixup() expects both the AndNode to have * both the left and right operands. */ currAnd.setRightOperand(trueNode); currAnd.postBindFixup(); } } if (joinClause != null) { /* If joinClause is a parameter, (where ?), then we assume * it will be a nullable boolean. */ if (joinClause.isParameterNode()) { joinClause.setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID, true)); } /* ** Is the datatype of the JOIN clause BOOLEAN? ** ** NOTE: This test is not necessary in SQL92 entry level, because ** it is syntactically impossible to have a non-Boolean JOIN clause ** in that level of the standard. But we intend to extend the ** language to allow Boolean user functions in the JOIN clause, ** so we need to test for the error condition. */ TypeId joinTypeId = joinClause.getTypeId(); /* If the where clause is not a built-in type, then generate a bound * conversion tree to a built-in type. */ if (! joinTypeId.systemBuiltIn()) { joinClause = joinClause.genSQLJavaSQLTree(); } if (! joinClause.getTypeServices().getTypeId().equals( TypeId.BOOLEAN_ID)) { throw StandardException.newException(SQLState.LANG_NON_BOOLEAN_JOIN_CLAUSE, joinClause.getTypeServices().getTypeId().getSQLTypeName() ); } } } /** * Put a ProjectRestrictNode on top of each FromTable in the FromList. * ColumnReferences must continue to point to the same ResultColumn, so * that ResultColumn must percolate up to the new PRN. However, * that ResultColumn will point to a new expression, a VirtualColumnNode, * which points to the FromTable and the ResultColumn that is the source for * the ColumnReference. * (The new PRN will have the original of the ResultColumnList and * the ResultColumns from that list. The FromTable will get shallow copies * of the ResultColumnList and its ResultColumns. ResultColumn.expression * will remain at the FromTable, with the PRN getting a new * VirtualColumnNode for each ResultColumn.expression.) * We then project out the non-referenced columns. If there are no referenced * columns, then the PRN's ResultColumnList will consist of a single ResultColumn * whose expression is 1. * * @param numTables Number of tables in the DML Statement * @param gbl The group by list, if any * @param fromList The from list, if any * * @return The generated ProjectRestrictNode atop the original FromTable. * * @exception StandardException Thrown on error */ public ResultSetNode preprocess(int numTables, GroupByList gbl, FromList fromList) throws StandardException
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -