| /* |
| |
| Derby - Class org.apache.derby.impl.sql.execute.TemporaryRowHolderResultSet |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to you under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| |
| package org.apache.derby.impl.sql.execute; |
| |
| import java.sql.SQLWarning; |
| import java.sql.Timestamp; |
| import org.w3c.dom.Element; |
| |
| import org.apache.derby.iapi.error.StandardException; |
| import org.apache.derby.iapi.services.io.FormatableBitSet; |
| import org.apache.derby.shared.common.sanity.SanityManager; |
| import org.apache.derby.iapi.sql.Activation; |
| import org.apache.derby.iapi.sql.ResultDescription; |
| import org.apache.derby.iapi.sql.ResultSet; |
| import org.apache.derby.iapi.sql.Row; |
| import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor; |
| import org.apache.derby.iapi.sql.execute.CursorResultSet; |
| import org.apache.derby.iapi.sql.execute.ExecRow; |
| import org.apache.derby.iapi.sql.execute.NoPutResultSet; |
| import org.apache.derby.iapi.sql.execute.RowChanger; |
| import org.apache.derby.iapi.sql.execute.TargetResultSet; |
| import org.apache.derby.iapi.store.access.ConglomerateController; |
| import org.apache.derby.iapi.store.access.ScanController; |
| import org.apache.derby.iapi.store.access.TransactionController; |
| import org.apache.derby.iapi.types.DataValueDescriptor; |
| import org.apache.derby.iapi.types.RowLocation; |
| import org.apache.derby.iapi.types.SQLLongint; |
| import org.apache.derby.iapi.sql.dictionary.DataDictionary; |
| |
| /** |
| * A result set to scan temporary row holders. Ultimately, this |
| * may be returned to users, hence the extra junk from the ResultSet |
| * interface. |
| * |
| */ |
| class TemporaryRowHolderResultSet implements CursorResultSet, NoPutResultSet, Cloneable |
| { |
| private ExecRow[] rowArray; |
| private int numRowsOut; |
| private ScanController scan; |
| private TransactionController tc; |
| private boolean isOpen; |
| private boolean finished; |
| private ExecRow currentRow; |
| private ResultDescription resultDescription; |
| private boolean isAppendable = false; |
| private long positionIndexConglomId; |
| private boolean isVirtualMemHeap; |
| private boolean currRowFromMem; |
| private TemporaryRowHolderImpl holder; |
| |
| // the following is used by position based scan, as well as virtual memory style heap |
| ConglomerateController heapCC; |
| private RowLocation baseRowLocation; |
| |
| /** |
| * Constructor |
| * |
| * @param tc the xact controller |
| * @param rowArray the row array |
| * @param resultDescription value returned by getResultDescription() |
| */ |
| public TemporaryRowHolderResultSet |
| ( |
| TransactionController tc, |
| ExecRow[] rowArray, |
| ResultDescription resultDescription, |
| boolean isVirtualMemHeap, |
| TemporaryRowHolderImpl holder |
| ) |
| { |
| |
| this(tc, rowArray, resultDescription, isVirtualMemHeap, false, 0, holder); |
| |
| |
| } |
| |
| /** |
| * Constructor |
| * |
| * @param tc the xact controller |
| * @param rowArray the row array |
| * @param resultDescription value returned by getResultDescription() |
| * @param isAppendable true,if we can insert rows after this result is created |
| * @param positionIndexConglomId conglomId of the index which has order rows |
| * are inserted and their row location |
| */ |
| public TemporaryRowHolderResultSet |
| ( |
| TransactionController tc, |
| ExecRow[] rowArray, |
| ResultDescription resultDescription, |
| boolean isVirtualMemHeap, |
| boolean isAppendable, |
| long positionIndexConglomId, |
| TemporaryRowHolderImpl holder |
| ) |
| { |
| this.tc = tc; |
| this.rowArray = rowArray; |
| this.resultDescription = resultDescription; |
| this.numRowsOut = 0; |
| isOpen = false; |
| finished = false; |
| this.isVirtualMemHeap = isVirtualMemHeap; |
| this.isAppendable = isAppendable; |
| this.positionIndexConglomId = positionIndexConglomId; |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(rowArray != null, "rowArray is null"); |
| SanityManager.ASSERT(rowArray.length > 0, "rowArray has no elements, need at least one"); |
| } |
| |
| this.holder = holder; |
| } |
| |
| /** |
| * Reset the exec row array and reinitialize |
| * |
| * @param rowArray the row array |
| */ |
| public void reset(ExecRow[] rowArray) |
| { |
| this.rowArray = rowArray; |
| this.numRowsOut = 0; |
| isOpen = false; |
| finished = false; |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(rowArray != null, "rowArray is null"); |
| SanityManager.ASSERT(rowArray.length > 0, "rowArray has no elements, need at least one"); |
| } |
| } |
| |
| |
| /** |
| * postion scan to start from after where we stopped earlier |
| */ |
| public void reStartScan(long currentConglomId, long pconglomId) throws StandardException |
| { |
| if(isAppendable) |
| { |
| if (SanityManager.DEBUG) { |
| SanityManager.ASSERT(currentConglomId == holder.getTemporaryConglomId(), |
| "currentConglomId(" + currentConglomId + |
| ") == holder.getTemporaryConglomeateId (" + |
| holder.getTemporaryConglomId() + ")"); |
| } |
| positionIndexConglomId = pconglomId; |
| setupPositionBasedScan(numRowsOut); |
| }else |
| { |
| numRowsOut--; |
| } |
| } |
| |
| //Make an array which is a superset of the 2 passed column arrays. |
| //The superset will not have any duplicates |
| private static int[] supersetofAllColumns(int[] columnsArray1, int[] columnsArray2) |
| { |
| int maxLength = columnsArray1.length + columnsArray2.length; |
| int[] maxArray = new int[maxLength]; |
| for (int i=0; i<maxLength; i++) maxArray[i]=-1; |
| |
| //First simply copy the first array into superset |
| for (int i=0; i<columnsArray1.length; i++) { |
| maxArray[i] = columnsArray1[i]; |
| } |
| |
| //Now copy only new values from second array into superset |
| int validColsPosition=columnsArray1.length; |
| for (int i=0; i<columnsArray2.length; i++) { |
| boolean found = false; |
| for (int j=0;j<validColsPosition;j++) { |
| if (maxArray[j] == columnsArray2[i]) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| maxArray[validColsPosition] = columnsArray2[i]; |
| validColsPosition++; |
| } |
| } |
| maxArray = shrinkArray(maxArray); |
| java.util.Arrays.sort(maxArray); |
| return maxArray; |
| } |
| |
| //The passed array can have some -1 elements and some +ve elements |
| // Return an array containing just the +ve elements |
| private static int[] shrinkArray(int[] columnsArrary) { |
| int countOfColsRefedInArray = 0; |
| int numberOfColsInTriggerTable = columnsArrary.length; |
| |
| //Count number of non -1 entries |
| for (int i=0; i < numberOfColsInTriggerTable; i++) { |
| if (columnsArrary[i] != -1) |
| countOfColsRefedInArray++; |
| } |
| |
| if (countOfColsRefedInArray > 0){ |
| int[] tempArrayOfNeededColumns = new int[countOfColsRefedInArray]; |
| int j=0; |
| for (int i=0; i < numberOfColsInTriggerTable; i++) { |
| if (columnsArrary[i] != -1) |
| tempArrayOfNeededColumns[j++] = columnsArrary[i]; |
| } |
| return tempArrayOfNeededColumns; |
| } else |
| return null; |
| } |
| |
| //Return an array which contains the column positions of all the |
| // +ve columns in the passed array |
| private static int[] justTheRequiredColumnsPositions(int[] columnsArrary) { |
| int countOfColsRefedInArray = 0; |
| int numberOfColsInTriggerTable = columnsArrary.length; |
| |
| //Count number of non -1 entries |
| for (int i=0; i < numberOfColsInTriggerTable; i++) { |
| if (columnsArrary[i] != -1) |
| countOfColsRefedInArray++; |
| } |
| |
| if (countOfColsRefedInArray > 0){ |
| int[] tempArrayOfNeededColumns = new int[countOfColsRefedInArray]; |
| int j=0; |
| for (int i=0; i < numberOfColsInTriggerTable; i++) { |
| if (columnsArrary[i] != -1) |
| tempArrayOfNeededColumns[j++] = i+1; |
| } |
| return tempArrayOfNeededColumns; |
| } else |
| return null; |
| } |
| |
| /** |
| * Whip up a new Temp ResultSet that has a single |
| * row. This row will either have all the columns from |
| * the current row of the passed resultset or a subset |
| * of the columns from the passed resulset. It all depends |
| * on what columns are needed by the passed trigger and what |
| * columns exist in the resulset. The Temp resulset |
| * should only have the columns required by the trigger. |
| * |
| * @param triggerd We are building Temp resultset for this trigger |
| * @param activation the activation |
| * @param rs the result set |
| * @param colsReadFromTable The passed resultset is composed of |
| * these columns. We will create a temp resultset which |
| * will have either all these columns or only a subset of |
| * these columns. It all depends on what columns are needed |
| * by the trigger. If this param is null, then that means that |
| * all the columns from the trigger table have been read into |
| * the passed resultset. |
| * |
| * @return a single row result set |
| * |
| * @exception StandardException on error |
| */ |
| public static TemporaryRowHolderResultSet getNewRSOnCurrentRow |
| ( |
| TriggerDescriptor triggerd, |
| Activation activation, |
| CursorResultSet rs, |
| int[] colsReadFromTable |
| ) throws StandardException |
| { |
| TemporaryRowHolderImpl singleRow; |
| DataDictionary dd = activation.getLanguageConnectionContext().getDataDictionary(); |
| // In soft upgrade mode, we could be dealing with databases created |
| // with 10.8 or prior and for such databases, we do not want to do |
| // any column reading optimization to maintain backward compatibility |
| if (!dd.checkVersion(DataDictionary.DD_VERSION_DERBY_10_9,null)) { |
| singleRow = |
| new TemporaryRowHolderImpl(activation, null, |
| rs.getResultDescription()); |
| singleRow.insert(rs.getCurrentRow()); |
| return (TemporaryRowHolderResultSet) singleRow.getResultSet(); |
| } |
| |
| //Get columns referenced in trigger action through REFERENCING clause |
| int[] referencedColsInTriggerAction = triggerd.getReferencedColsInTriggerAction(); |
| // Get trigger column. If null, then it means that all the columns |
| // have been read because this trigger can be fired for any of the |
| // columns in the table |
| int[] referencedColsInTrigger = triggerd.getReferencedCols(); |
| |
| if ((referencedColsInTrigger != null) && //this means not all the columns are being read |
| (triggerd.isRowTrigger() && referencedColsInTriggerAction!=null && |
| referencedColsInTriggerAction.length != 0)) { |
| //If we are here, then trigger is defined on specific columns and |
| // it has trigger action columns used through REFERENCING clause |
| |
| //Make an array which is a superset of trigger columns and |
| // trigger action columns referenced through REFERENCING clause. |
| //This superset is what the trigger is looking for in it's |
| // resulset. |
| int[] colsInTrigger = supersetofAllColumns(referencedColsInTrigger,referencedColsInTriggerAction); |
| int colsCountInTrigger = colsInTrigger.length; |
| int[] colsReallyNeeded = new int[colsCountInTrigger]; |
| |
| //Here, we find out what columns make up the passed resulset |
| int[] actualColsReadFromTable; |
| if (colsReadFromTable != null) //this means not all the columns are being read |
| actualColsReadFromTable = justTheRequiredColumnsPositions(colsReadFromTable); |
| else { |
| int colsInTriggerTable = triggerd.getTableDescriptor().getNumberOfColumns(); |
| actualColsReadFromTable = new int[colsInTriggerTable]; |
| for (int i=1; i<=colsInTriggerTable; i++) |
| actualColsReadFromTable[i-1] = i; |
| } |
| |
| //Now we have what columns make up the passed resulset and what |
| // columns are needed by the trigger. We will map a temporary |
| // resultset for the trigger out of the above information using |
| // the passed resultset |
| int indexInActualColsReadFromTable = 0; |
| for (int i=0; i<colsCountInTrigger; i++) { |
| |
| for (;indexInActualColsReadFromTable < actualColsReadFromTable.length; indexInActualColsReadFromTable++) |
| { |
| /* Return 1-based key column position if column is in the key */ |
| if (actualColsReadFromTable[indexInActualColsReadFromTable] |
| == colsInTrigger[i]) |
| { |
| colsReallyNeeded[i] = indexInActualColsReadFromTable+1; |
| break; |
| } |
| } |
| } |
| singleRow = |
| new TemporaryRowHolderImpl(activation, null, |
| activation.getLanguageConnectionContext().getLanguageFactory(). |
| getResultDescription(rs.getResultDescription(),colsReallyNeeded)); |
| ExecRow row = activation.getExecutionFactory().getValueRow( colsCountInTrigger ); |
| for (int i=0; i<colsCountInTrigger; i++) |
| row.setColumn(i+1, rs.getCurrentRow().getColumn(colsReallyNeeded[i])); |
| singleRow.insert(row); |
| } else { |
| singleRow = |
| new TemporaryRowHolderImpl(activation, null, |
| rs.getResultDescription()); |
| singleRow.insert(rs.getCurrentRow()); |
| } |
| |
| return (TemporaryRowHolderResultSet) singleRow.getResultSet(); |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // NoPutResultSet |
| // |
| ///////////////////////////////////////////////////////// |
| /** |
| * Mark the ResultSet as the topmost one in the ResultSet tree. |
| * Useful for closing down the ResultSet on an error. |
| */ |
| public void markAsTopResultSet() |
| { } |
| |
| /** |
| * Open the scan and evaluate qualifiers and the like. |
| * For us, there are no qualifiers, this is really a |
| * noop. |
| */ |
| public void openCore() throws StandardException |
| { |
| this.numRowsOut = 0; |
| isOpen = true; |
| currentRow = null; |
| |
| if(isAppendable) |
| setupPositionBasedScan(numRowsOut); |
| } |
| |
| /** |
| * Reopen the scan. Typically faster than open()/close() |
| * |
| * @exception StandardException on error |
| */ |
| public void reopenCore() throws StandardException |
| { |
| numRowsOut = 0; |
| isOpen = true; |
| currentRow = null; |
| |
| if(isAppendable) |
| { |
| setupPositionBasedScan(numRowsOut); |
| return; |
| } |
| |
| if (scan != null) |
| { |
| scan.reopenScan( |
| (DataValueDescriptor[]) null, // start key value |
| 0, // start operator |
| null, // qualifier |
| (DataValueDescriptor[]) null, // stop key value |
| 0); // stop operator |
| } |
| } |
| |
| /** |
| * Get the next row. |
| * |
| * @return the next row, or null if none |
| * |
| * @exception StandardException on error |
| */ |
| public ExecRow getNextRowCore() |
| throws StandardException |
| { |
| |
| if (!isOpen) |
| { |
| return (ExecRow)null; |
| } |
| |
| if(isAppendable) |
| { |
| return getNextAppendedRow() ; |
| } |
| |
| if (isVirtualMemHeap && holder.lastArraySlot >= 0) |
| { |
| numRowsOut++; |
| currentRow = rowArray[holder.lastArraySlot]; |
| currRowFromMem = true; |
| return currentRow; |
| } |
| else if (numRowsOut++ <= holder.lastArraySlot) |
| { |
| currentRow = rowArray[numRowsOut-1]; |
| return currentRow; |
| } |
| |
| if (holder.getTemporaryConglomId() == 0) |
| { |
| return (ExecRow)null; |
| } |
| |
| /* |
| ** Advance in the temporary conglomerate |
| */ |
| if (scan == null) |
| { |
| scan = |
| tc.openScan( |
| holder.getTemporaryConglomId(), |
| false, // hold |
| 0, // open read only |
| TransactionController.MODE_TABLE, |
| TransactionController.ISOLATION_SERIALIZABLE, |
| (FormatableBitSet) null, |
| (DataValueDescriptor[]) null, // start key value |
| 0, // start operator |
| null, // qualifier |
| (DataValueDescriptor[]) null, // stop key value |
| 0); // stop operator |
| } |
| else if (isVirtualMemHeap && holder.state == TemporaryRowHolderImpl.STATE_INSERT) |
| { |
| holder.state = TemporaryRowHolderImpl.STATE_DRAIN; |
| scan.reopenScan( |
| (DataValueDescriptor[]) null, // start key value |
| 0, // start operator |
| null, // qualifier |
| (DataValueDescriptor[]) null, // stop key value |
| 0); // stop operator |
| } |
| |
| if (scan.next()) |
| { |
| currentRow = rowArray[0].getNewNullRow(); |
| scan.fetch(currentRow.getRowArray()); |
| currRowFromMem = false; |
| return currentRow; |
| } |
| return (ExecRow)null; |
| } |
| |
| public void deleteCurrentRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(isVirtualMemHeap, "deleteCurrentRow is not implemented"); |
| } |
| if (currRowFromMem) |
| { |
| if (holder.lastArraySlot > 0) // 0 is kept for template |
| rowArray[holder.lastArraySlot] = null; // erase reference |
| holder.lastArraySlot--; |
| } |
| else |
| { |
| if (baseRowLocation == null) |
| baseRowLocation = scan.newRowLocationTemplate(); |
| scan.fetchLocation(baseRowLocation); |
| if(heapCC == null) |
| { |
| heapCC = tc.openConglomerate(holder.getTemporaryConglomId(), |
| false, |
| TransactionController.OPENMODE_FORUPDATE, |
| TransactionController.MODE_TABLE, |
| TransactionController.ISOLATION_SERIALIZABLE); |
| } |
| heapCC.delete(baseRowLocation); |
| } |
| } |
| |
| |
| //following variables are specific to the position based scans. |
| DataValueDescriptor[] indexRow; |
| ScanController indexsc; |
| |
| //open the scan of the temporary heap and the position index |
| private void setupPositionBasedScan(long position) throws StandardException |
| { |
| |
| //incase nothing is inserted yet into the temporary row holder |
| if (holder.getTemporaryConglomId() == 0) |
| return; |
| if(heapCC == null) |
| { |
| heapCC = tc.openConglomerate( holder.getTemporaryConglomId(), |
| false, |
| 0, |
| TransactionController.MODE_TABLE, |
| TransactionController.ISOLATION_SERIALIZABLE); |
| |
| } |
| |
| currentRow = rowArray[0].getNewNullRow(); |
| indexRow = new DataValueDescriptor[2]; |
| indexRow[0] = new SQLLongint(position); |
| indexRow[1] = heapCC.newRowLocationTemplate(); |
| |
| DataValueDescriptor[] searchRow = new DataValueDescriptor[1]; |
| searchRow[0] = new SQLLongint(position); |
| |
| if(indexsc == null) |
| { |
| indexsc = tc.openScan(positionIndexConglomId, |
| false, // don't hold open across commit |
| 0, // for read |
| TransactionController.MODE_TABLE, |
| TransactionController.ISOLATION_SERIALIZABLE, |
| (FormatableBitSet) null, // all fields as objects |
| searchRow, // start position - first row |
| ScanController.GE, // startSearchOperation |
| null, //scanQualifier, |
| null, // stop position - through last row |
| ScanController.GT); // stopSearchOperation |
| }else |
| { |
| |
| indexsc.reopenScan( |
| searchRow, // startKeyValue |
| ScanController.GE, // startSearchOp |
| null, // qualifier |
| null, // stopKeyValue |
| ScanController.GT // stopSearchOp |
| ); |
| } |
| |
| } |
| |
| |
| //get the next row inserted into the temporary holder |
| private ExecRow getNextAppendedRow() throws StandardException |
| { |
| if (indexsc == null) return null; |
| if (!indexsc.fetchNext(indexRow)) |
| { |
| return null; |
| } |
| |
| RowLocation baseRowLocation = (RowLocation) indexRow[1]; |
| boolean base_row_exists = |
| heapCC.fetch( |
| baseRowLocation, currentRow.getRowArray(), (FormatableBitSet) null); |
| |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(base_row_exists, "base row disappeared."); |
| } |
| numRowsOut++; |
| return currentRow; |
| } |
| |
| |
| |
| /** |
| * Return the point of attachment for this subquery. |
| * (Only meaningful for Any and Once ResultSets, which can and will only |
| * be at the top of a ResultSet for a subquery.) |
| * |
| * @return int Point of attachment (result set number) for this |
| * subquery. (-1 if not a subquery - also Sanity violation) |
| */ |
| public int getPointOfAttachment() |
| { |
| return -1; |
| } |
| |
| /** |
| * Return the isolation level of the scan in the result set. |
| * Only expected to be called for those ResultSets that |
| * contain a scan. |
| * |
| * @return The isolation level of the scan (in TransactionController constants). |
| */ |
| public int getScanIsolationLevel() |
| { |
| return TransactionController.ISOLATION_SERIALIZABLE; |
| } |
| |
| /** |
| * Notify a NPRS that it is the source for the specified |
| * TargetResultSet. This is useful when doing bulk insert. |
| * |
| * @param trs The TargetResultSet. |
| */ |
| public void setTargetResultSet(TargetResultSet trs) |
| { |
| } |
| |
| /** |
| * Set whether or not the NPRS need the row location when acting |
| * as a row source. (The target result set determines this.) |
| * |
| */ |
| public void setNeedsRowLocation(boolean needsRowLocation) |
| { |
| } |
| |
| /** |
| * Get the estimated row count from this result set. |
| * |
| * @return The estimated row count (as a double) from this result set. |
| */ |
| public double getEstimatedRowCount() |
| { |
| return 0d; |
| } |
| |
| /** |
| * Get the number of this ResultSet, which is guaranteed to be unique |
| * within a statement. |
| */ |
| public int resultSetNumber() |
| { |
| return TEMPORARY_RESULT_SET_NUMBER; |
| } |
| |
| /** |
| * Set the current row to the row passed in. |
| * |
| * @param row the new current row |
| * |
| */ |
| public void setCurrentRow(ExecRow row) |
| { |
| currentRow = row; |
| } |
| |
| /** |
| * Clear the current row |
| * |
| */ |
| public void clearCurrentRow() |
| { |
| currentRow = null; |
| } |
| |
| /** |
| * This result set has its row from the last fetch done. |
| * If the cursor is closed, a null is returned. |
| * |
| * @see CursorResultSet |
| * |
| * @return the last row returned; |
| * @exception StandardException thrown on failure. |
| */ |
| public ExecRow getCurrentRow() throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(isOpen, "resultSet expected to be open"); |
| } |
| |
| return currentRow; |
| } |
| |
| /** |
| * Returns the row location of the current base table row of the cursor. |
| * If this cursor's row is composed of multiple base tables' rows, |
| * i.e. due to a join, then a null is returned. For |
| * a temporary row holder, we always return null. |
| * |
| * @return the row location of the current cursor row. |
| */ |
| public RowLocation getRowLocation() |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.ASSERT(isOpen, "resultSet expected to be open"); |
| } |
| return (RowLocation)null; |
| } |
| |
| |
| /** |
| * Clean up |
| * |
| * @exception StandardException thrown on error |
| */ |
| public void close() throws StandardException |
| { |
| isOpen = false; |
| numRowsOut = 0; |
| currentRow = null; |
| if (scan != null) |
| { |
| scan.close(); |
| scan = null; |
| } |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////// |
| // |
| // MISC FROM RESULT SET |
| // |
| ///////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Returns TRUE if the statement returns rows (i.e. is a SELECT |
| * or FETCH statement), FALSE if it returns no rows. |
| * |
| * @return TRUE if the statement returns rows, FALSE if not. |
| */ |
| public boolean returnsRows() |
| { |
| return true; |
| } |
| |
| public long modifiedRowCount() { return 0L;}; |
| |
| /** |
| * Returns a ResultDescription object, which describes the results |
| * of the statement this ResultSet is in. This will *not* be a |
| * description of this particular ResultSet, if this is not the |
| * outermost ResultSet. |
| * |
| * @return A ResultDescription describing the results of the |
| * statement. |
| */ |
| public ResultDescription getResultDescription() |
| { |
| return resultDescription; |
| } |
| |
| /** |
| * Tells the system that there will be calls to getNextRow(). |
| * |
| * @exception StandardException Thrown on failure |
| */ |
| public void open() throws StandardException |
| { |
| openCore(); |
| } |
| |
| /** |
| * Returns the row at the absolute position from the query, |
| * and returns NULL when there is no such position. |
| * (Negative position means from the end of the result set.) |
| * Moving the cursor to an invalid position leaves the cursor |
| * positioned either before the first row (negative position) |
| * or after the last row (positive position). |
| * NOTE: An exception will be thrown on 0. |
| * |
| * @param row The position. |
| * @return The row at the absolute position, or NULL if no such position. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getAbsoluteRow(int row) throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getAbsoluteRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the row at the relative position from the current |
| * cursor position, and returns NULL when there is no such position. |
| * (Negative position means toward the beginning of the result set.) |
| * Moving the cursor to an invalid position leaves the cursor |
| * positioned either before the first row (negative position) |
| * or after the last row (positive position). |
| * NOTE: 0 is valid. |
| * NOTE: An exception is thrown if the cursor is not currently |
| * positioned on a row. |
| * |
| * @param row The position. |
| * @return The row at the relative position, or NULL if no such position. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getRelativeRow(int row) throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getRelativeRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Sets the current position to before the first row and returns NULL |
| * because there is no current row. |
| * |
| * @return NULL. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow setBeforeFirstRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "setBeforeFirstRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the first row from the query, and returns NULL when there |
| * are no rows. |
| * |
| * @return The first row, or NULL if no rows. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getFirstRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getFirstRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the next row from the query, and returns NULL when there |
| * are no more rows. |
| * |
| * @return The next row, or NULL if no more rows. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getNextRow() throws StandardException |
| { |
| return getNextRowCore(); |
| } |
| |
| /** |
| * Returns the previous row from the query, and returns NULL when there |
| * are no more previous rows. |
| * |
| * @return The previous row, or NULL if no more previous rows. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getPreviousRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getPreviousRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the last row from the query, and returns NULL when there |
| * are no rows. |
| * |
| * @return The last row, or NULL if no rows. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow getLastRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getLastRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Sets the current position to after the last row and returns NULL |
| * because there is no current row. |
| * |
| * @return NULL. |
| * |
| * @exception StandardException Thrown on failure |
| * @see Row |
| */ |
| public ExecRow setAfterLastRow() |
| throws StandardException |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "getLastRow() not expected to be called yet."); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Determine if the cursor is before the first row in the result |
| * set. |
| * |
| * @return true if before the first row, false otherwise. Returns |
| * false when the result set contains no rows. |
| */ |
| public boolean checkRowPosition(int isType) |
| { |
| return false; |
| } |
| |
| /** |
| * Returns the row number of the current row. Row |
| * numbers start from 1 and go to 'n'. Corresponds |
| * to row numbering used to position current row |
| * in the result set (as per JDBC). |
| * |
| * @return the row number, or 0 if not on a row |
| * |
| */ |
| public int getRowNumber() |
| { |
| return 0; |
| } |
| |
| /** |
| * Tells the system to clean up on an error. |
| * |
| * @exception StandardException Thrown on error. |
| */ |
| public void cleanUp() throws StandardException |
| { |
| close(); |
| } |
| |
| |
| /** |
| Find out if the ResultSet is closed or not. |
| Will report true for result sets that do not return rows. |
| |
| @return true if the ResultSet has been closed. |
| */ |
| public boolean isClosed() |
| { |
| return !isOpen; |
| } |
| |
| /** |
| * Tells the system that there will be no more access |
| * to any database information via this result set; |
| * in particular, no more calls to open(). |
| * Will close the result set if it is not already closed. |
| * |
| * @exception StandardException on error |
| */ |
| public void finish() throws StandardException |
| { |
| finished = true; |
| close(); |
| } |
| |
| |
| /** |
| * Get the execution time in milliseconds. |
| * |
| * @return long The execution time in milliseconds. |
| */ |
| public long getExecuteTime() |
| { |
| return 0L; |
| } |
| |
| /** |
| * @see ResultSet#getAutoGeneratedKeysResultset |
| */ |
| public ResultSet getAutoGeneratedKeysResultset() |
| { |
| //A non-null resultset would be returned only for an insert statement |
| return (ResultSet)null; |
| } |
| |
| /** |
| * Get the Timestamp for the beginning of execution. |
| * |
| * @return Timestamp The Timestamp for the beginning of execution. |
| */ |
| public Timestamp getBeginExecutionTimestamp() |
| { |
| return (Timestamp)null; |
| } |
| |
| /** |
| * Get the Timestamp for the end of execution. |
| * |
| * @return Timestamp The Timestamp for the end of execution. |
| */ |
| public Timestamp getEndExecutionTimestamp() |
| { |
| return (Timestamp)null; |
| } |
| |
| /** |
| * Return the total amount of time spent in this ResultSet |
| * |
| * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet |
| * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below. |
| * |
| * @return long The total amount of time spent (in milliseconds). |
| */ |
| public long getTimeSpent(int type) |
| { |
| return 0L; |
| } |
| |
| |
| /** |
| * Get the subquery ResultSet tracking array from the top ResultSet. |
| * (Used for tracking open subqueries when closing down on an error.) |
| * |
| * @param numSubqueries The size of the array (For allocation on demand.) |
| * |
| * @return NoPutResultSet[] Array of NoPutResultSets for subqueries. |
| */ |
| public NoPutResultSet[] getSubqueryTrackingArray(int numSubqueries) |
| { |
| return (NoPutResultSet[])null; |
| } |
| |
| /** |
| * Returns the name of the cursor, if this is cursor statement of some |
| * type (declare, open, fetch, positioned update, positioned delete, |
| * close). |
| * |
| * @return A String with the name of the cursor, if any. Returns |
| * NULL if this is not a cursor statement. |
| */ |
| public String getCursorName() |
| { |
| return (String) null; |
| } |
| |
| /** |
| * @see NoPutResultSet#requiresRelocking |
| */ |
| public boolean requiresRelocking() |
| { |
| if (SanityManager.DEBUG) |
| { |
| SanityManager.THROWASSERT( |
| "requiresRelocking() not expected to be called for " + |
| getClass().getName()); |
| } |
| return false; |
| } |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // Access/RowSource -- not implemented |
| // |
| ///////////////////////////////////////////////////////// |
| /** |
| Get the next row as an array of column objects. The column objects can |
| be a JBMS Storable or any |
| Serializable/Externalizable/Formattable/Streaming type. |
| <BR> |
| A return of null indicates that the complete set of rows has been read. |
| |
| <p> |
| A null column can be specified by leaving the object null, or indicated |
| by returning a non-null getValidColumns. On streaming columns, it can |
| be indicated by returning a non-null get FieldStates. |
| |
| <p> |
| If RowSource.needToClone() is true then the returned row (the |
| DataValueDescriptor[]) is guaranteed not to be modified by drainer of |
| the RowSource (except that the input stream will be read, of course) |
| and drainer will keep no reference to it before making the subsequent |
| nextRow call. So it is safe to return the same DataValueDescriptor[] |
| in subsequent nextRow calls if that is desirable for performance |
| reasons. |
| <p> |
| If RowSource.needToClone() is false then the returned row (the |
| DataValueDescriptor[]) may be be modified by drainer of the RowSource, |
| and the drainer may keep a reference to it after making the subsequent |
| nextRow call. In this case the client should severe all references to |
| the row after returning it from getNextRowFromRowSource(). |
| |
| @exception StandardException Standard Derby Error Policy |
| */ |
| public DataValueDescriptor[] getNextRowFromRowSource() throws StandardException |
| { |
| return null; |
| } |
| |
| /** |
| Does the caller of getNextRowFromRowSource() need to clone the row |
| in order to keep a reference to the row past the |
| getNextRowFromRowSource() call which returned the row. This call |
| must always return the same for all rows in a RowSource (ie. the |
| caller will call this once per scan from a RowSource and assume the |
| behavior is true for all rows in the RowSource). |
| |
| */ |
| public boolean needsToClone() |
| { |
| return false; |
| } |
| |
| |
| /** |
| getValidColumns describes the DataValueDescriptor[] returned by all |
| calls to the getNextRowFromRowSource() call. |
| |
| If getValidColumns returns null, the number of columns is given by the |
| DataValueDescriptor.length where DataValueDescriptor[] is returned by the |
| preceeding getNextRowFromRowSource() call. Column N maps to |
| DataValueDescriptor[N], where column numbers start at zero. |
| |
| If getValidColumns return a non null validColumns FormatableBitSet the number of |
| columns is given by the number of bits set in validColumns. Column N is |
| not in the partial row if validColumns.get(N) returns false. Column N is |
| in the partial row if validColumns.get(N) returns true. If column N is |
| in the partial row then it maps to DataValueDescriptor[M] where M is the |
| count of calls to validColumns.get(i) that return true where i < N. If |
| DataValueDescriptor.length is greater than the number of columns |
| indicated by validColumns the extra entries are ignored. |
| */ |
| public FormatableBitSet getValidColumns() |
| { |
| return null; |
| } |
| |
| /** |
| closeRowSource tells the RowSource that it will no longer need to |
| return any rows and it can release any resource it may have. |
| Subsequent call to any method on the RowSource will result in undefined |
| behavior. A closed rowSource can be closed again. |
| */ |
| public void closeRowSource() |
| { } |
| |
| |
| ///////////////////////////////////////////////////////// |
| // |
| // Access/RowLocationRetRowSource -- not implemented |
| // |
| ///////////////////////////////////////////////////////// |
| /** |
| needsRowLocation returns true iff this the row source expects the |
| drainer of the row source to call rowLocation after getting a row from |
| getNextRowFromRowSource. |
| |
| @return true iff this row source expects some row location to be |
| returned |
| @see #rowLocation |
| */ |
| public boolean needsRowLocation() |
| { |
| return false; |
| } |
| |
| public void setHasDeferrableChecks() { |
| if (SanityManager.DEBUG) { |
| SanityManager.NOTREACHED(); |
| } |
| } |
| |
| public boolean needsRowLocationForDeferredCheckConstraints() |
| { |
| return false; |
| } |
| |
| |
| /** |
| rowLocation is a callback for the drainer of the row source to return |
| the rowLocation of the current row, i.e, the row that is being returned |
| by getNextRowFromRowSource. This interface is for the purpose of |
| loading a base table with index. In that case, the indices can be |
| built at the same time the base table is laid down once the row |
| location of the base row is known. This is an example pseudo code on |
| how this call is expected to be used: |
| |
| <BR><pre> |
| boolean needsRL = rowSource.needsRowLocation(); |
| DataValueDescriptor[] row; |
| while((row = rowSource.getNextRowFromRowSource()) != null) |
| { |
| RowLocation rl = heapConglomerate.insertRow(row); |
| if (needsRL) |
| rowSource.rowLocation(rl); |
| } |
| </pre><BR> |
| |
| NeedsRowLocation and rowLocation will ONLY be called by a drainer of |
| the row source which CAN return a row location. Drainer of row source |
| which cannot return rowLocation will guarentee to not call either |
| callbacks. Conversely, if NeedsRowLocation is called and it returns |
| true, then for every row return by getNextRowFromRowSource, a |
| rowLocation callback must also be issued with the row location of the |
| row. Implementor of both the source and the drain of the row source |
| must be aware of this protocol. |
| |
| <BR> |
| The RowLocation object is own by the caller of rowLocation, in other |
| words, the drainer of the RowSource. This is so that we don't need to |
| new a row location for every row. If the Row Source wants to keep the |
| row location, it needs to clone it (RowLocation is a ClonableObject). |
| @exception StandardException on error |
| */ |
| public void rowLocation(RowLocation rl) throws StandardException |
| { } |
| |
| public void offendingRowLocation( |
| RowLocation rl, long containdId) throws StandardException { |
| if (SanityManager.DEBUG) { |
| SanityManager.NOTREACHED(); |
| } |
| } |
| |
| /** |
| * @see NoPutResultSet#positionScanAtRowLocation |
| * |
| * This method is result sets used for scroll insensitive updatable |
| * result sets for other result set it is a no-op. |
| */ |
| public void positionScanAtRowLocation(RowLocation rl) |
| throws StandardException |
| { |
| // Only used for Scrollable insensitive result sets otherwise no-op |
| } |
| |
| // Class implementation |
| |
| /** |
| * Is this ResultSet or it's source result set for update |
| * This method will be overriden in the inherited Classes |
| * if it is true |
| * @return Whether or not the result set is for update. |
| */ |
| public boolean isForUpdate() |
| { |
| return false; |
| } |
| |
| /** |
| * Shallow clone this result set. Used in trigger reference. |
| * beetle 4373. |
| */ |
| public Object clone() |
| { |
| Object clo = null; |
| try { |
| clo = super.clone(); |
| } |
| catch (CloneNotSupportedException e) {} |
| return clo; |
| } |
| |
| public void addWarning(SQLWarning w) { |
| getActivation().addWarning(w); |
| } |
| |
| public SQLWarning getWarnings() { |
| return null; |
| } |
| |
| /** |
| * @see NoPutResultSet#updateRow |
| * |
| * This method is result sets used for scroll insensitive updatable |
| * result sets for other result set it is a no-op. |
| */ |
| public void updateRow(ExecRow row, RowChanger rowChanger) |
| throws StandardException { |
| // Only ResultSets of type Scroll Insensitive implement |
| // detectability, so for other result sets this method |
| // is a no-op |
| } |
| |
| /** |
| * @see NoPutResultSet#markRowAsDeleted |
| * |
| * This method is result sets used for scroll insensitive updatable |
| * result sets for other result set it is a no-op. |
| */ |
| public void markRowAsDeleted() throws StandardException { |
| // Only ResultSets of type Scroll Insensitive implement |
| // detectability, so for other result sets this method |
| // is a no-op |
| } |
| |
| /** |
| * Return the <code>Activation</code> for this result set. |
| * |
| * @return activation |
| */ |
| public final Activation getActivation() { |
| return holder.activation; |
| } |
| |
| public Element toXML( Element parentNode, String tag ) throws Exception |
| { |
| return BasicNoPutResultSetImpl.childrenToXML( BasicNoPutResultSetImpl.toXML( parentNode, tag, this ), this ); |
| } |
| |
| } |