All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 1998, 2022 IBM Corporation. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     07/13/2012-2.5 Guy Pelletier
//       - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
//     08/24/2012-2.5 Guy Pelletier
//       - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
//     01/31/2017-2.6 Will Dazey
//       - 511426: Adding cloning support
//     04/11/2018 - Will Dazey
//       - 533148 : Add the eclipselink.jpa.sql-call-deferral property
package org.eclipse.persistence.internal.queries;

import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.databaseaccess.Accessor;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.DatabaseMapping.WriteType;
import org.eclipse.persistence.queries.ConstructorReportItem;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.DeleteAllQuery;
import org.eclipse.persistence.queries.ReportQuery;
import org.eclipse.persistence.queries.UpdateAllQuery;
import org.eclipse.persistence.queries.WriteObjectQuery;

/**
 * 

Purpose: * Mechanism used for call queries. *

*

Responsibilities: * Executes the appropriate call. * * @author James Sutherland * @since OracleAS TopLink 10g (10.0.3) */ public class DatasourceCallQueryMechanism extends DatabaseQueryMechanism { protected DatasourceCall call; /** Normally only a single call is used, however multiple table may require multiple calls on write. */ protected Vector calls; public DatasourceCallQueryMechanism() { } /** * Initialize the state of the query * @param query - owner of mechanism */ public DatasourceCallQueryMechanism(DatabaseQuery query) { super(query); } /** * Initialize the state of the query * @param query - owner of mechanism */ public DatasourceCallQueryMechanism(DatabaseQuery query, DatasourceCall call) { super(query); this.call = call; call.setQuery(query); } /** * Add the call. */ public void addCall(DatasourceCall call) { getCalls().addElement(call); call.setQuery(getQuery()); } /** * Clone the DatasourceCall and Vector. */ public DatabaseQueryMechanism clone(DatabaseQuery queryClone) { DatasourceCallQueryMechanism clone = (DatasourceCallQueryMechanism)super.clone(queryClone); if(this.call != null) { DatasourceCall callclone = (DatasourceCall)this.call.clone(); clone.setCall(callclone); } if(this.calls != null) { Vector callsclone = (Vector)this.calls.clone(); clone.setCalls(callsclone); } return clone; } /** * Read all rows from the database using a cursored stream. * @exception DatabaseException - an error has occurred on the database */ public DatabaseCall cursorSelectAllRows() throws DatabaseException { try { return (DatabaseCall)executeCall(); } catch (java.lang.ClassCastException e) { throw QueryException.mustUseCursorStreamPolicy(); } } /** * Read all rows from the database, return ResultSet * @exception DatabaseException - an error has occurred on the database */ public DatabaseCall selectResultSet() throws DatabaseException { try { // For CR 2923 must move to session we will execute call on now // so correct DatasourcePlatform used by translate. AbstractSession sessionToUse = this.query.getExecutionSession(); DatabaseCall clonedCall = (DatabaseCall)this.call.clone(); clonedCall.setQuery(this.query); clonedCall.translate(this.query.getTranslationRow(), getModifyRow(), sessionToUse); clonedCall.returnCursor(); return (DatabaseCall)sessionToUse.executeCall(clonedCall, this.query.getTranslationRow(), this.query); } catch (java.lang.ClassCastException e) { throw QueryException.invalidDatabaseCall(this.call); } } /** * INTERNAL: * Delete a collection of objects. Assume call is correct. * @exception DatabaseException - an error has occurred on the database */ public Integer deleteAll() throws DatabaseException { if(((DeleteAllQuery)this.query).isPreparedUsingTempStorage()) { return deleteAllUsingTempTables(); } else { if (hasMultipleCalls()) { Integer returnedRowCount = null; // Deletion must occur in reverse order. for (int index = getCalls().size() - 1; index >= 0; index--) { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); returnedRowCount = (Integer)executeCall(databseCall); } // returns the number of rows removed from the first table in insert order return returnedRowCount; } else { return (Integer)executeCall(); } } } /** * Execute deleteAll using temp tables * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer deleteAllUsingTempTables() throws DatabaseException { DatabaseException ex = null; Integer returnedRowCount = null; // Deletion must occur in reverse order. // first call - crete temp table. // may fail in case global temp table already exists. try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(getCalls().size() - 1); executeCall(databseCall); } catch (DatabaseException databaseEx) { // ignore } // second call - populate temp table. // if that fails save the exception and untill cleanup try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(getCalls().size() - 2); executeCall(databseCall); } catch (DatabaseException databaseEx) { ex = databaseEx; } // third (a call per table) - delete from original tables calls. // if that fails save the exception untill cleanup for (int index = getCalls().size() - 3; index >= 1 && ex == null; index--) { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); try { // returns the number of rows removed from the first table in insert order returnedRowCount = (Integer)executeCall(databseCall); } catch (DatabaseException databaseEx) { ex = databaseEx; } } // last call - cleanup temp table. // ignore exceptions here. try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(0); executeCall(databseCall); } catch (DatabaseException databaseEx) { // ignore } if(ex != null) { throw ex; } return returnedRowCount; } /** * INTERNAL: * Delete an object. Assume call is correct * @exception DatabaseException - an error has occurred on the database */ public Integer deleteObject() throws DatabaseException { if (hasMultipleCalls()) { Integer returnedRowCount = null; // Deletion must occur in reverse order. for (int index = getCalls().size() - 1; index >= 0; index--) { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); Integer rowCount = (Integer)executeCall(databseCall); if ((index == (getCalls().size() - 1)) || (rowCount.intValue() <= 0)) {// Row count returned must be from first table or zero if any are zero. returnedRowCount = rowCount; } } return returnedRowCount; } else { return (Integer)executeCall(); } } /** * INTERNAL: * Execute a call. * @exception DatabaseException - an error has occurred on the database */ public Object execute() throws DatabaseException { return executeCall(); } /** * Execute the call. It is assumed the call has been fully prepared. * @exception DatabaseException - an error has occurred on the database. */ protected Object executeCall() throws DatabaseException { return executeCall(this.call); } /** * Execute the call. It is assumed the call has been fully prepared. * @exception DatabaseException - an error has occurred on the database. */ protected Object executeCall(DatasourceCall databaseCall) throws DatabaseException { // For CR 2923 must move to session we will execute call on now // so correct DatasourcePlatform used by translate. AbstractSession sessionToUse = this.query.getExecutionSession(); DatasourceCall clonedCall = (DatasourceCall)databaseCall.clone(); clonedCall.setQuery(this.query); clonedCall.translate(this.query.getTranslationRow(), getModifyRow(), sessionToUse); return sessionToUse.executeCall(clonedCall, this.query.getTranslationRow(), this.query); } /** * Execute a non selecting call. * @exception DatabaseException - an error has occurred on the database. * @return Returns either a {@link DatabaseCall} or Integer value, * depending on if this INSERT call needs to return generated keys */ public Object executeNoSelect() throws DatabaseException { if(((DatabaseCall)this.call).shouldReturnGeneratedKeys()) { return generateKeysExecuteNoSelect(); } return executeNoSelectCall(); } /** * Execute a non selecting call. * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public DatabaseCall generateKeysExecuteNoSelect() throws DatabaseException { return (DatabaseCall)executeCall(); } /** * Execute a non selecting call. * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer executeNoSelectCall() throws DatabaseException { if (hasMultipleCalls()) { Integer returnedRowCount = null; for (int index = 0; index < getCalls().size(); index++) { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); Integer rowCount = (Integer)executeCall(databseCall); if ((index == 0) || (rowCount.intValue() <= 0)) {// Row count returned must be from first table or zero if any are zero. returnedRowCount = rowCount; } } return returnedRowCount; } else { return (Integer)executeCall(); } } /** * INTERNAL: * Execute a selecting call. * @exception DatabaseException - an error has occurred on the database */ public Vector executeSelect() throws DatabaseException { return executeSelectCall(); } /** * INTERNAL: * Execute a selecting call. * @exception DatabaseException - an error has occurred on the database */ public Vector executeSelectCall() throws DatabaseException { if (hasMultipleCalls()) { Vector results = new Vector(); for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databseCall = (DatasourceCall)callsEnum.nextElement(); Helper.addAllToVector(results, (Vector)executeCall(databseCall)); } return results; } else { return (Vector)executeCall(); } } /** * Return the call. */ public DatasourceCall getCall() { return call; } /** * Normally only a single call is used, however multiple table may require multiple calls on write. * This is lazy initialised to conserve space. */ public Vector getCalls() { if (calls == null) { calls = org.eclipse.persistence.internal.helper.NonSynchronizedVector.newInstance(3); } return calls; } /** * Normally only a single call is used, however multiple table may require multiple calls on write. * This is lazy initialised to conserve space. */ public boolean hasMultipleCalls() { return (this.calls != null) && (!this.calls.isEmpty()); } /** * Insert the object. Assume the call is correct. * @exception DatabaseException - an error has occurred on the database */ public void insertObject() throws DatabaseException { ClassDescriptor descriptor = getDescriptor(); boolean usesSequencing = descriptor.usesSequenceNumbers(); boolean shouldAcquireValueAfterInsert = false; if (usesSequencing) { shouldAcquireValueAfterInsert = descriptor.getSequence().shouldAcquireValueAfterInsert(); } Collection returnFields = null; if (descriptor.getReturnFieldsToMergeInsert() != null) { returnFields = descriptor.getReturnFieldsToMergeInsert(); } // Check to see if sequence number should be retrieved after insert if (usesSequencing && !shouldAcquireValueAfterInsert) { // PERF: Unit of work always assigns sequence, so only need to check it here for non unit of work/change set query. if (getWriteObjectQuery().getObjectChangeSet() == null) { // This is the normal case. Update object with sequence number before insert. updateObjectAndRowWithSequenceNumber(); } } if (hasMultipleCalls()) { int size = this.calls.size(); for (int index = 0; index < size; index++) { DatasourceCall databseCall = (DatasourceCall)this.calls.get(index); if ((index > 0) && isExpressionQueryMechanism() && this.query.shouldCascadeOnlyDependentParts() && !descriptor.hasMultipleTableConstraintDependecy() && this.query.getSession().getProject().allowSQLDeferral()) { DatabaseTable table = descriptor.getMultipleTableInsertOrder().get(index); this.query.getSession().getCommitManager().addDeferredCall(table, databseCall, this); } else { Object result = executeCall(databseCall); // Set the return row if one was returned (Postgres). if (result instanceof AbstractRecord) { this.query.setProperty("output", result); } if (returnFields != null) { updateObjectAndRowWithReturnRow(returnFields, index == 0); } if ((index == 0) && usesSequencing && shouldAcquireValueAfterInsert) { if(result instanceof DatabaseCall) { updateObjectAndRowWithSequenceNumber((DatabaseCall) result); } else { updateObjectAndRowWithSequenceNumber(); } } } } } else { Object result = executeCall(); // Set the return row if one was returned (Postgres). if (result instanceof AbstractRecord) { this.query.setProperty("output", result); } if (returnFields != null) { updateObjectAndRowWithReturnRow(returnFields, true); } if (usesSequencing && shouldAcquireValueAfterInsert) { if(result instanceof DatabaseCall) { updateObjectAndRowWithSequenceNumber((DatabaseCall) result); } else { updateObjectAndRowWithSequenceNumber(); } } } // Bug 3110860: RETURNINGPOLICY-OBTAINED PK CAUSES LOB TO BE INSERTED INCORRECTLY // The deferred locator SELECT calls should be generated and executed after ReturningPolicy // merges PK obtained from the db into the object held by the query. // //Oracle thin driver handles LOB differently. During the insert, empty lob would be //insert first, and then the LOb locator is retrieved and LOB data are written through //the locator. // // Bug 2804663 - LOBValueWriter is no longer a singleton, so we execute any deferred // select calls through the DatabaseAccessor which holds the writer instance AbstractSession executionSession = this.query.getExecutionSession(); for (Accessor accessor : executionSession.getAccessors()) { accessor.flushSelectCalls(executionSession); } } /** * Execute the call that was deferred to the commit manager. * This is used to allow multiple table batching and deadlock avoidance. */ public void executeDeferredCall(DatasourceCall call) { Object result = executeCall(call); // Set the return row if one was returned (Postgres). if (result instanceof AbstractRecord) { this.query.setProperty("output", result); } Collection returnFields = null; if (this.query.getDescriptor().hasReturningPolicy()) { returnFields = this.query.getDescriptor().getReturningPolicy().getFieldsToMergeInsert(); } if (returnFields != null) { updateObjectAndRowWithReturnRow(returnFields, false); } } /** * Return true if this is a call query mechanism */ public boolean isCallQueryMechanism() { return true; } /** * INTERNAL: * This is different from 'prepareForExecution' in that this is called on the original query, * and the other is called on the copy of the query. * This query is copied for concurrency so this prepare can only setup things that * will apply to any future execution of this query. */ public void prepare() { if ((!hasMultipleCalls()) && (getCall() == null)) { throw QueryException.sqlStatementNotSetProperly(getQuery()); } } /** * INTERNAL: * This is different from 'prepareForExecution' in that this is called on the original query, * and the other is called on the copy of the query. * This query is copied for concurrency so this prepare can only setup things that * will apply to any future execution of this query. */ public void prepareCall() throws QueryException { DatabaseQuery query = getQuery(); AbstractSession executionSession = query.getExecutionSession(); if (hasMultipleCalls()) { for (DatasourceCall call : (List)getCalls()) { call.prepare(executionSession); } } else if (getCall() != null) { getCall().prepare(executionSession); } } /** * Pre-build configure the call. */ public void prepareCursorSelectAllRows() throws QueryException { getCall().returnCursor(); prepareCall(); } /** * Pre-build configure the call. */ public void prepareDeleteAll() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall call = (DatasourceCall)callsEnum.nextElement(); call.returnNothing(); } } else { getCall().returnNothing(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareDeleteObject() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall call = (DatasourceCall)callsEnum.nextElement(); call.returnNothing(); } } else { getCall().returnNothing(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareDoesExist(DatabaseField field) { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { ((DatasourceCall)callsEnum.nextElement()).returnOneRow(); } } else { getCall().returnOneRow(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareExecuteNoSelect() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { ((DatasourceCall)callsEnum.nextElement()).returnNothing(); } } else { getCall().returnNothing(); } prepareCall(); } /** * Pre-build configure the call. This method assumes the query was built * using a stored procedure query which is a single call. * * The return type on the call will already be set and */ public void prepareExecute() { getCall().setExecuteUpdate(); prepareCall(); } /** * Pre-build configure the call. */ public void prepareExecuteSelect() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databseCall = (DatasourceCall)callsEnum.nextElement(); databseCall.returnManyRows(); } } else { getCall().returnManyRows(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareInsertObject() { if (hasMultipleCalls()) { int size = this.calls.size(); for (int index = 0; index < size; index++) { DatabaseCall call = (DatabaseCall)this.calls.get(index); if (!call.isReturnSet()) { call.returnNothing(); } } } else { if (!this.call.isReturnSet()) { this.call.returnNothing(); } } prepareCall(); } /** * Prepare the report items. * Indexes of results need to be calculated. */ protected void prepareReportQueryItems(){ //calculate indexes after normalize to insure expressions are set up correctly //take into account any field expressions added to the ReportQuery ReportQuery query = (ReportQuery)getQuery(); computeAndSetItemOffset(query, query.getItems(), query.getQueryExpressions().size()); } /** * calculate indexes for given items, given the current Offset */ protected int computeAndSetItemOffset(ReportQuery query, List items, int itemOffset) { for(ReportItem item : items) { if (item.isConstructorItem()) { List reportItems = ((ConstructorReportItem) item).getReportItems(); itemOffset = computeAndSetItemOffset(query, reportItems, itemOffset); } else { //Don't set the offset on the ConstructorItem item.setResultIndex(itemOffset); if (item.getAttributeExpression() != null) { if (item.hasJoining()){ itemOffset = item.getJoinedAttributeManager().computeJoiningMappingIndexes(true, getSession(), itemOffset); } else { if (item.getDescriptor() != null) { itemOffset += item.getDescriptor().getAllSelectionFields(query).size(); } else { if (item.getMapping() != null && item.getMapping().isAggregateObjectMapping()) { itemOffset += item.getMapping().getFields().size(); // Aggregate object may consist out of 1..n fields } else { ++itemOffset; //only a single attribute can be selected } } } } } } return itemOffset; } /** * Pre-build configure the call. */ public void prepareReportQuerySelectAllRows() { prepareReportQueryItems(); prepareExecuteSelect(); } /** * Prepare for a sub select using a call. */ public void prepareReportQuerySubSelect() { prepareReportQueryItems(); prepareCall(); } /** * Pre-build configure the call. */ public void prepareSelectAllRows() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databseCall = (DatasourceCall)callsEnum.nextElement(); databseCall.returnManyRows(); } } else { getCall().returnManyRows(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareSelectOneRow() { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databseCall = (DatasourceCall)callsEnum.nextElement(); databseCall.returnOneRow(); } } else { getCall().returnOneRow(); } prepareCall(); } /** * Pre-build configure the call. */ public void prepareUpdateObject() { if (hasMultipleCalls()) { int size = this.calls.size(); for (int index = 0; index < size; index++) { DatabaseCall call = (DatabaseCall)this.calls.get(index); if (!call.isReturnSet()) { call.returnNothing(); } } } else if (getCall() != null) { if (!call.isReturnSet()) { this.call.returnNothing(); } } prepareCall(); } /** * Pre-build configure the call. */ public void prepareUpdateAll() { if (getCall() != null) { getCall().returnNothing(); } prepareCall(); } /** * Read all rows from the database. Assume call is correct returns the required fields. * @return Vector containing the database rows * @exception DatabaseException - an error has occurred on the database */ public Vector selectAllReportQueryRows() throws DatabaseException { return executeSelect(); } /** * Read all rows from the database. Assume call is correct returns the required fields. * @return Vector containing the database rows * @exception DatabaseException - an error has occurred on the database */ public Vector selectAllRows() throws DatabaseException { return executeSelectCall(); } /** * Read a single row from the database. Assume call is correct. * @return row containing data * @exception DatabaseException - an error has occurred on the database */ public AbstractRecord selectOneRow() throws DatabaseException { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databaseCall = (DatasourceCall)callsEnum.nextElement(); AbstractRecord result = (AbstractRecord)executeCall(databaseCall); if (result != null) { return result; } } return null; } else { return (AbstractRecord)executeCall(); } } /** * Perform a does exist check * @param field - the field used for does exist check * @return the associated row from the database * @exception DatabaseException - an error has occurred on the database */ public AbstractRecord selectRowForDoesExist(DatabaseField field) throws DatabaseException { if (hasMultipleCalls()) { for (Enumeration callsEnum = getCalls().elements(); callsEnum.hasMoreElements();) { DatasourceCall databaseCall = (DatasourceCall)callsEnum.nextElement(); AbstractRecord result = (AbstractRecord)executeCall(databaseCall); if (result != null) { return result; } } return null; } else { return (AbstractRecord)executeCall(); } } /** * Set the call. */ public void setCall(DatasourceCall call) { this.call = call; if (call != null) { call.setQuery(getQuery()); } } /** * Normally only a single call is used, however multiple table may require multiple calls on write. * This is lazy initialised to conserve space. */ protected void setCalls(Vector calls) { this.calls = calls; } /** * Update the object. Assume the call is correct. * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer updateObject() throws DatabaseException { ClassDescriptor descriptor = getDescriptor(); Collection returnFields = null; if (descriptor.getReturnFieldsToMergeUpdate() != null) { returnFields = descriptor.getReturnFieldsToMergeUpdate(); } Integer returnedRowCount = null; if (hasMultipleCalls()) { int size = this.calls.size(); for (int index = 0; index < size; index++) { DatasourceCall databseCall = (DatasourceCall)this.calls.get(index); if ((index > 0) && isExpressionQueryMechanism() && this.query.shouldCascadeOnlyDependentParts() && !descriptor.hasMultipleTableConstraintDependecy() && this.query.getSession().getProject().allowSQLDeferral()) { DatabaseTable table = descriptor.getMultipleTableInsertOrder().get(index); this.query.getSession().getCommitManager().addDeferredCall(table, databseCall, this); } else { Object result = executeCall(databseCall); // Set the return row if one was returned (Postgres). Integer rowCount; if (result instanceof AbstractRecord) { this.query.setProperty("output", result); rowCount = Integer.valueOf(1); } else { rowCount = (Integer)result; } if ((index == 0) || (rowCount.intValue() <= 0)) {// Row count returned must be from first table or zero if any are zero. returnedRowCount = rowCount; } if (returnFields != null) { updateObjectAndRowWithReturnRow(returnFields, false); } } } } else { Object result = executeCall(); // Set the return row if one was returned (Postgres). if (result instanceof AbstractRecord) { this.query.setProperty("output", result); returnedRowCount = Integer.valueOf(1); } else { returnedRowCount = (Integer)result; } if (returnFields != null) { updateObjectAndRowWithReturnRow(returnFields, false); } } //Oracle thin driver handles LOB differently. During the insert, empty lob would be //insert first, and then the LOb locator is retrieved and LOB data are written through //the locator. // // Bug 2804663 - LOBValueWriter is no longer a singleton, so we execute any deferred // select calls through the DatabaseAccessor which holds the writer instance // // Building of SELECT statements is no longer done in DatabaseAccessor.basicExecuteCall // because DatabaseCall.isUpdateCall() can't recognize update in case StoredProcedureCall // is used. AbstractSession executionSession = this.query.getExecutionSession(); for (Accessor accessor : executionSession.getAccessors()) { accessor.flushSelectCalls(executionSession); } return returnedRowCount; } /** * Update the rows on the database. Assume the call is correct. * @exception DatabaseException - an error has occurred on the database. */ public Integer updateAll() throws DatabaseException { if(((UpdateAllQuery)this.query).isPreparedUsingTempStorage() && getExecutionSession().getPlatform().supportsTempTables()) { return updateAllUsingTempTables(); } else { Integer rowCount = executeNoSelectCall(); if(((UpdateAllQuery)this.query).isPreparedUsingTempStorage()) { // the query was prepared using Oracle anonymous block AbstractRecord outputRow = (AbstractRecord)this.query.getProperty("output"); rowCount = (Integer)outputRow.get("ROW_COUNT"); } return rowCount; } } /** * Execute updateAll using temp tables * @exception DatabaseException - an error has occurred on the database. * @return the row count. */ public Integer updateAllUsingTempTables() throws DatabaseException { int nTables = getCalls().size() / 4; DatabaseException ex = null; Integer returnedRowCount = null; // first quarter - crete temp tables calls. // may fail in case global temp table already exists. for (int index = 0; index < nTables; index++) { try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); executeCall(databseCall); } catch (DatabaseException databaseEx) { // ignore } } // second quarter - populate temp tables calls. // if that fails save the exception and until cleanup for (int index = nTables; index < nTables*2 && ex == null; index++) { try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); executeCall(databseCall); } catch (DatabaseException databaseEx) { ex = databaseEx; } } // third quarter - update original tables calls. // if that fails save the exception and until cleanup for (int index = nTables*2; index < nTables*3 && ex == null; index++) { try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); Integer rowCount = (Integer)executeCall(databseCall); if ((index == nTables*2) || (rowCount.intValue() <= 0)) {// Row count returned must be from first table or zero if any are zero. returnedRowCount = rowCount; } } catch (DatabaseException databaseEx) { ex = databaseEx; } } // last quarter - cleanup temp tables calls. // ignore exceptions here. for (int index = nTables*3; index < nTables*4; index++) { try { DatasourceCall databseCall = (DatasourceCall)getCalls().elementAt(index); executeCall(databseCall); } catch (DatabaseException databaseEx) { // ignore } } if(ex != null) { throw ex; } return returnedRowCount; } /** * Update the foreign key fields when resolving a bi-directional reference in a UOW. * This is rare to occur for non-relational, however if it does each of the calls must be re-executed. */ protected void updateForeignKeyFieldAfterInsert(WriteObjectQuery writeQuery) { writeQuery.setModifyRow(this.getDescriptor().getObjectBuilder().buildRow(writeQuery.getObject(), this.getSession(), WriteType.INSERT)); // For CR 2923 must move to session we will execute call on now // so correct DatasourcePlatform used by translate. AbstractSession sessionToUse = this.query.getExecutionSession(); // yes - this is a bit ugly... Vector calls = ((DatasourceCallQueryMechanism)this.getDescriptor().getQueryManager().getUpdateQuery().getQueryMechanism()).getCalls(); for (Enumeration stream = calls.elements(); stream.hasMoreElements();) { DatasourceCall call = (DatasourceCall)((DatasourceCall)stream.nextElement()).clone(); call.setQuery(writeQuery); sessionToUse.executeCall(call, this.getTranslationRow(), writeQuery); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy