📄 cursornode.java
字号:
* <LI>if it has a set operation such as UNION or INTERSECT, i.e. * does not have a single outermost SELECT * <LI>if it does not have exactly 1 table in its FROM list; * 0 tables would occur if we ever support a SELECT without a * FROM e.g., for generating a row without an underlying table * (like what we do for an INSERT of a VALUES list); >1 tables * occurs when joins are in the tree. * <LI>if the table in its FROM list is not a base table (REMIND * when views/from subqueries are added, this should be relaxed to * be that the table is not updatable) * <LI>if it has a GROUP BY or HAVING (NOTE I am assuming that if * and aggregate is detected in a SELECT w/o a GROUP BY, one * has been added to show that the whole table is a group) * <LI> NOTE that cursors are updatable even if none of the columns * in the select are updatable -- what they care about is the * updatability of the columns of the target table. * </UL> * </UL> * * @return the known update mode for the cursor. * * @exception StandardException Thrown on error */ private int determineUpdateMode(DataDictionary dataDictionary) throws StandardException { SelectNode selectNode; FromList tables; FromTable targetTable; if (updateMode == READ_ONLY) { return READ_ONLY; } if (orderByList != null) { if (SanityManager.DEBUG) SanityManager.DEBUG("DumpUpdateCheck","cursor has order by"); return READ_ONLY; } // get the ResultSet to tell us what it thinks it is // and the target table if (! resultSet.isUpdatableCursor(dataDictionary)) { return READ_ONLY; } // The FOR UPDATE clause has two uses: // // for positioned cursor updates // // to change locking behaviour of the select // to reduce deadlocks on subsequent updates // in the same transaction. // // We now support this latter case, without requiring // that the source of the rows be able to implement // a positioned update. updateTable = resultSet.getCursorTargetTable(); /* Tell the table that it is the cursor target */ if (updateTable.markAsCursorTargetTable()) { /* Cursor is updatable - remember to generate the position code */ needTarget = true; /* We must generate the target column list at bind time * because the optimizer may transform the FromBaseTable from * a table scan into an index scan. */ genTargetResultColList(); } return UPDATE; } /** * Optimize a DML statement (which is the only type of statement that * should need optimizing, I think). This method over-rides the one * in QueryTreeNode. * * This method takes a bound tree, and returns an optimized tree. * It annotates the bound tree rather than creating an entirely * new tree. * * Throws an exception if the tree is not bound, or if the binding * is out of date. * * @return An optimized QueryTree * * @exception StandardException Thrown on error */ public QueryTreeNode optimize() 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(); } resultSet.pushOrderByList(orderByList); orderByList = null; } return super.optimize(); } /** * Returns the type of activation this class * generates. * * @return either (NEED_CURSOR_ACTIVATION * * @exception StandardException Thrown on error */ int activationKind() { return NEED_CURSOR_ACTIVATION; } /** * Do code generation for this CursorNode * * @param acb The ActivationClassBuilder for the class being built * @param mb The method the generated code is to go into * * @exception StandardException Thrown on error */ public void generate(ActivationClassBuilder acb, MethodBuilder mb) throws StandardException { if (indexOfSessionTableNamesInSavedObjects != -1 ) //if this cursor references session schema tables, do following { MethodBuilder constructor = acb.getConstructor(); constructor.pushThis(); constructor.push(indexOfSessionTableNamesInSavedObjects); constructor.putField(org.apache.derby.iapi.reference.ClassName.BaseActivation, "indexOfSessionTableNamesInSavedObjects", "int"); constructor.endStatement(); } // generate the parameters generateParameterValueSet(acb); // tell the outermost result set that it is the outer // result set of the statement. resultSet.markStatementResultSet(); // this will generate an expression that will be a ResultSet super.generate(acb, mb); /* ** Generate the position code if this cursor is updatable. This ** involves generating methods to get the cursor result set, and ** the target result set (which is for the base row). Also, ** generate code to store the cursor result set in a generated ** field. */ if (needTarget) { // PUSHCOMPILE - could be put into a single method acb.rememberCursor(mb); acb.addCursorPositionCode(); } /* ** ensure all parameters have been generated */ generateParameterHolders(acb); } // class interface public String getUpdateBaseTableName() { return (updateTable == null) ? null : updateTable.getBaseTableName(); } public String getUpdateExposedTableName() throws StandardException { return (updateTable == null) ? null : updateTable.getExposedName(); } public String getUpdateSchemaName() throws StandardException { //we need to use the base table for the schema name return (updateTable == null) ? null : ((FromBaseTable)updateTable).getTableNameField().getSchemaName(); } public int getUpdateMode() { return updateMode; } /** * Return String[] of names from the FOR UPDATE OF List * * @return String[] of names from the FOR UPDATE OF list. */ private String[] getUpdatableColumns() { return (updatableColumns == null) ? (String[])null : getUpdateColumnNames(); } /** Positioned update needs to know what the target result set looks like. This is generated from the UpdateColumnList available for the cursor, to describe the rows coming from the target result set under the cursor. This result set contains a superset of the updatable columns; the caller must verify that only those listed in the FOR UPDATE clause are used. @return a result column list containing a description of the target table (this may contain non-updatable columns). * @exception StandardException Thrown on error */ public ResultColumnDescriptor[] genTargetResultColList() throws StandardException { ResultColumnList newList; /* updateTable holds the FromTable that is the target. copy its ResultColumnList, making BaseColumn references for use in the CurrentOfNode, which behaves as if it had base columns for the statement it is in. updateTable is null if the cursor is not updatable. */ if (updateTable == null) return null; if (targetColumnDescriptors != null) return targetColumnDescriptors; newList = (ResultColumnList) getNodeFactory().getNode( C_NodeTypes.RESULT_COLUMN_LIST, getContextManager()); ResultColumnList rcl = updateTable.getResultColumns(); int rclSize = rcl.size(); for (int index = 0; index < rclSize; index++) { ResultColumn origCol, newCol; ValueNode newNode; origCol = (ResultColumn) rcl.elementAt(index); // Build a ResultColumn/BaseColumnNode pair for the column newNode = (ValueNode) getNodeFactory().getNode( C_NodeTypes.BASE_COLUMN_NODE, origCol.getName(), makeTableName(origCol.getSchemaName(), origCol.getTableName()), origCol.getTypeServices(), getContextManager()); newCol = (ResultColumn) getNodeFactory().getNode( C_NodeTypes.RESULT_COLUMN, origCol.columnDescriptor, newNode, getContextManager()); /* Build the ResultColumnList to return */ newList.addResultColumn(newCol); } // we save the result so we only do this once targetColumns = newList; targetColumnDescriptors = newList.makeResultDescriptors(); return targetColumnDescriptors; } /** * Returns whether or not this Statement requires a set/clear savepoint * around its execution. The following statement "types" do not require them: * Cursor - unnecessary and won't work in a read only environment * Xact - savepoint will get blown away underneath us during commit/rollback * * @return boolean Whether or not this Statement requires a set/clear savepoint */ public boolean needsSavepoint() { return false; } /** * Get information about this cursor. For sps, * this is info saved off of the original query * tree (the one for the underlying query). * * @return the cursor info * @exception StandardException thrown if generation fails */ public Object getCursorInfo() throws StandardException { if (!needTarget) return null; return new CursorInfo(updateMode, new CursorTableReference( getUpdateExposedTableName(), getUpdateBaseTableName(), getUpdateSchemaName()), genTargetResultColList(), getUpdatableColumns()); } /** Bind the update columns by their names to the target table of the cursor specification. Doesn't check for duplicates in the list, although it could... REVISIT: If the list is empty, should it expand it out? at present, it leaves it empty. @param dataDictionary The DataDictionary to use for binding @param targetTable The underlying target table @exception StandardException Thrown on error */ private void bindUpdateColumns(FromTable targetTable) throws StandardException { int size = updatableColumns.size(); TableDescriptor tableDescriptor; String columnName; ResultColumnList rcls = resultSet.getResultColumns(); for (int index = 0; index < size; index++) { columnName = (String) updatableColumns.elementAt(index); tableDescriptor = targetTable.getTableDescriptor(); if ( tableDescriptor.getColumnDescriptor(columnName) == null) { throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND, columnName); } ResultColumn rc; //make sure that we are not using correlation names for updatable columns. //eg select c11 as col1, 2, c13 as col3 from t1 for update of c11, c12 //In the eg above, correlation name for c11 will cause exception because Derby does not support correlation name for updatable columns //But correlation name for c13 is ok because it is a read only column for (int rclsIndex = 0; rclsIndex < rcls.size(); rclsIndex++) {//look through each column in the resultset for cursor rc = ((ResultColumn) rcls.elementAt(rclsIndex)); if (rc.getSourceTableName() == null) //continue to look at the next column because this is derived column in the select list continue; if (rc.getExpression() != null && rc.getExpression().getColumnName().equals(columnName) && !rc.getName().equals(columnName)) { throw StandardException.newException(SQLState.LANG_CORRELATION_NAME_FOR_UPDATABLE_COLUMN_DISALLOWED_IN_CURSOR, columnName); } } } } /** * Get an array of strings for each updatable column * in this list. * * @return an array of strings */ private String[] getUpdateColumnNames() { int size = updatableColumns.size(); if (size == 0) { return (String[])null; } String[] names = new String[size]; updatableColumns.copyInto(names); return names; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -