Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 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
// 05/24/2011-2.3 Guy Pelletier
// - 345962: Join fetch query when using tenant discriminator column fails.
// 02/08/2012-2.4 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 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
// 09/27/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 09/03/2015 - Will Dazey
// - 456067 : Added support for defining query timeout units
package org.eclipse.persistence.internal.databaseaccess;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.Array;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.expressions.ParameterExpression;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.queries.CallQueryMechanism;
import org.eclipse.persistence.internal.queries.DatabaseQueryMechanism;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor;
import org.eclipse.persistence.mappings.structures.ObjectRelationalDatabaseField;
import org.eclipse.persistence.queries.DatabaseQuery;
import org.eclipse.persistence.queries.ModifyQuery;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.sessions.DatabaseRecord;
/**
* INTERNAL:
* Purpose: Used as an abstraction of a database invocation.
* A call is an SQL string or procedure call with parameters.
*/
public abstract class DatabaseCall extends DatasourceCall {
/**
* JPA 2.1 NamedStoredProcedureQuery execute API implementation.
*/
protected boolean executeReturnValue;
/**
* Following fields are used to bind MaxResults and FirstRow settings into
* the query instead of using the values stored in the call.
*/
public static final DatabaseField MAXROW_FIELD = new DatabaseField("EclipseLink-MaxResults");
public static final DatabaseField FIRSTRESULT_FIELD = new DatabaseField("EclipseLink-FirstRow");
/**
* Indicates if the FirstRow value in this call object is to be ignored. If
* true, it should mean it has been built into the SQL statement directly
* ex: using Oracle Rownum support
*/
protected boolean ignoreFirstRowSetting;
/**
* Indicates if the MaxResults value in this call object is to be ignored.
* If true, it should mean it has been built into the SQL statement directly
* ex: using Oracle Rownum support
*/
protected boolean ignoreMaxResultsSetting;
// The result and statement are cached for cursor selects.
transient protected Statement statement;
transient protected ResultSet result;
// The generated keys are cached for lookup later
transient protected ResultSet generatedKeys;
// The call may specify that its parameters should be bound.
protected Boolean usesBinding;
// Bound calls can use prepared statement caching.
protected Boolean shouldCacheStatement;
/*
* Indicate this call should return generated keys. Only supported for INSERT calls.
*/
protected boolean shouldReturnGeneratedKeys;
// The returned fields.
transient protected Vector fields;
// PERF: fields array
transient protected DatabaseField[] fieldsArray;
// Field matching is required for custom SQL when the fields order is not known.
protected boolean isFieldMatchingRequired;
// optimistic locking determination is required for batch writing
protected boolean hasOptimisticLock;
protected boolean isResultSetScrollable;
// JDK 1.2 supports initial fetch size for the result set.
protected int resultSetFetchSize;
// JDK 1.2 supports various types of results set
protected int resultSetType;
// JDK 1.2 supports various types of concurrency on results set
protected int resultSetConcurrency;
//query timeout limit in seconds
protected int queryTimeout;
//query timeout unit
protected TimeUnit queryTimeoutUnit;
//max rows returned in the result set by the call
protected int maxRows;
//firstResult set into the result set by the call
protected int firstResult;
//contain field - value pairs for LOB fields used to the
//streaming operation during the writing (to the table)
private transient AbstractRecord contexts;
/** Allow for a single cursored output parameter. */
protected boolean isCursorOutputProcedure;
/** Allow for multiple cursored output parameter. */
protected boolean isMultipleCursorOutputProcedure;
// This parameter is here to determine if we should expect a ResultSet back from the call
// We need to know this information in order to call the correct JDBC API
protected Boolean returnsResultSet;
// Whether the call has to build output row
protected boolean shouldBuildOutputRow;
// Callable statement is required if there is an output parameter
protected boolean isCallableStatementRequired;
/** Support multiple result sets. */
protected boolean hasMultipleResultSets;
/**
* Support returning multiple results sets instead of just one list, i.e.
* support multiple results set mappings.
*/
protected boolean returnMultipleResultSetCollections;
/** The SQL string to execute. */
protected String sqlString;
/** Indicates whether the call has allocated connection. May be set if the call has not finished */
protected boolean hasAllocatedConnection;
/**
* Define if this query is compatible with batch writing.
* Some queries, such as DDL are not compatible.
*/
protected boolean isBatchExecutionSupported;
public DatabaseCall() {
super.shouldProcessTokenInQuotes = false;
this.shouldCacheStatement = null;
this.isFieldMatchingRequired = false;
this.queryTimeout = 0;
this.queryTimeoutUnit = null;
this.maxRows = 0;
this.resultSetFetchSize = 0;
this.isCursorOutputProcedure = false;
this.shouldBuildOutputRow = false;
this.returnsResultSet = null;
this.isBatchExecutionSupported = true;
}
/**
* Return if the call returns multiple result sets.
*/
public boolean hasMultipleResultSets() {
return hasMultipleResultSets;
}
/**
* Set if the call returns multiple result sets.
*/
public void setHasMultipleResultSets(boolean hasMultipleResultSets) {
this.hasMultipleResultSets = hasMultipleResultSets;
}
/**
* Add the parameter.
*
* If binding is enabled, then bind the parameter; otherwise let the platform print it.
* The platform may also decide to bind the value.
*/
@Override
public void appendParameter(Writer writer, Object parameter, boolean shouldBind, AbstractSession session) {
if (Boolean.TRUE.equals(shouldBind)) {
bindParameter(writer, parameter);
} else {
session.getPlatform().appendParameter(this, writer, parameter);
}
}
/**
* Bind the parameter. Binding is determined by the parameter and second the platform.
*/
public void bindParameter(Writer writer, Object parameter) {
if (parameter instanceof Collection) {
throw QueryException.inCannotBeParameterized(getQuery());
}
try {
writer.write("?");
} catch (IOException exception) {
throw ValidationException.fileError(exception);
}
getParameters().add(parameter);
}
/**
* Return the appropriate mechanism,
* with the call added as necessary.
*/
@Override
public DatabaseQueryMechanism buildNewQueryMechanism(DatabaseQuery query) {
return new CallQueryMechanism(query, this);
}
/**
* INTERNAL:
* Return Record containing output fields and values.
* Called only if shouldBuildOutputRow method returns true.
*/
public AbstractRecord buildOutputRow(CallableStatement statement, DatabaseAccessor accessor, AbstractSession session) throws SQLException {
AbstractRecord row = new DatabaseRecord();
int size = this.parameters.size();
for (int index = 0; index < size; index++) {
Object parameter = this.parameters.get(index);
if (parameter instanceof OutputParameterForCallableStatement) {
OutputParameterForCallableStatement outParameter = (OutputParameterForCallableStatement)parameter;
if (!outParameter.isCursor() || !isCursorOutputProcedure()) {
Object value = getOutputParameterValue(statement, index, session);
DatabaseField field = outParameter.getOutputField();
if (value instanceof Struct){
ClassDescriptor descriptor = session.getDescriptor(field.getType());
if ((descriptor != null) && descriptor.isObjectRelationalDataTypeDescriptor()) {
AbstractRecord nestedRow = ((ObjectRelationalDataTypeDescriptor)descriptor).buildRowFromStructure((Struct)value);
ReadObjectQuery query = new ReadObjectQuery();
query.setSession(session);
value = descriptor.getObjectBuilder().buildNewInstance();
descriptor.getObjectBuilder().buildAttributesIntoObject(value, null, nestedRow, query, null, null, false, this.getQuery().getSession());
}
} else if ((value instanceof Array) && (field.isObjectRelationalDatabaseField())) {
value = ObjectRelationalDataTypeDescriptor.buildContainerFromArray((Array)value, (ObjectRelationalDatabaseField)field, session);
} else if (value instanceof ResultSet) {
// Support multiple out cursors, put list of records in row.
ResultSet resultSet = (ResultSet)value;
setFields(null);
matchFieldOrder(resultSet, accessor, session);
value = accessor.processResultSet(resultSet, this, statement, session);
}
row.put(field, value);
}
}
}
return row;
}
/**
* Return the appropriate mechanism,
* with the call added as necessary.
*/
@Override
public DatabaseQueryMechanism buildQueryMechanism(DatabaseQuery query, DatabaseQueryMechanism mechanism) {
if (mechanism.isCallQueryMechanism() && (mechanism instanceof CallQueryMechanism)) {
// Must also add the call singleton...
CallQueryMechanism callMechanism = ((CallQueryMechanism)mechanism);
if (!callMechanism.hasMultipleCalls()) {
callMechanism.addCall(callMechanism.getCall());
callMechanism.setCall(null);
}
callMechanism.addCall(this);
return mechanism;
} else {
return buildNewQueryMechanism(query);
}
}
/**
* INTERNAL:
* Returns INOUT parameter. The first parameter is value to pass in, the second DatabaseField for out.
*/
@Override
protected Object createInOutParameter(Object inValue, Object outParameter, AbstractSession session) {
if (outParameter instanceof OutputParameterForCallableStatement) {
return new InOutputParameterForCallableStatement(inValue, (OutputParameterForCallableStatement)outParameter);
}
if (outParameter instanceof DatabaseField) {
return new InOutputParameterForCallableStatement(inValue, (DatabaseField)outParameter, session);
}
//should never happen
return null;
}
/**
* INTERNAL:
* Return the SQL string for the call.
*/
public String getCallString() {
return getSQLString();
}
/**
* The fields expected by the calls result set.
* null means that the fields are unknown and should be built from the result set.
*/
public Vector getFields() {
return fields;
}
/**
* INTERNAL:
* The array of fields returned by the call.
*/
public DatabaseField[] getFieldsArray() {
return fieldsArray;
}
/**
* INTERNAL:
* Unfortunately can't avoid referencing query and descriptor:
* the call should be performed after the translateCustomSQL (in SQLCall)
* in the middle of prepare method (no parameter available earlier).
*
*/
protected DatabaseField getFieldWithTypeFromDescriptor(DatabaseField outField) {
if (getQuery().getDescriptor() != null) {
return getQuery().getDescriptor().getTypedField(outField);
} else {
return null;
}
}
/**
* INTERNAL:
* Return 1-based index of out cursor parameter, or -1.
*/
public int getCursorOutIndex() {
int size = getParameters().size();
for (int i = 0; i < size; i++) {
Object parameter = this.parameters.get(i);
if (parameter instanceof OutputParameterForCallableStatement) {
if (((OutputParameterForCallableStatement)parameter).isCursor()) {
return i + 1;
}
}
}
return -1;
}
/**
* After an execute call the return value can be retrieved here.
*/
public boolean getExecuteReturnValue() {
return executeReturnValue;
}
/**
* get first result
*/
public int getFirstResult() {
return this.firstResult;
}
/**
* Return the SQL string for logging purposes.
*/
@Override
public String getLogString(Accessor accessor) {
if (hasParameters()) {
StringWriter writer = new StringWriter();
writer.write(getSQLString());
writer.write(Helper.cr());
if (hasParameters()) {
AbstractSession session = null;
if (getQuery() != null) {
session = getQuery().getSession();
}
appendLogParameters(getParameters(), accessor, writer, session);
}
return writer.toString();
} else {
return getSQLString();
}
}
/**
* Print the parameters to the write for logging purposes.
*/
public static void appendLogParameters(Collection parameters, Accessor accessor, StringWriter writer, AbstractSession session) {
writer.write("\tbind => [");
if (session == null || session.shouldDisplayData()) {
for (Iterator paramsEnum = parameters.iterator(); paramsEnum.hasNext();) {
Object parameter = paramsEnum.next();
if (parameter instanceof DatabaseField) {
writer.write("null");
} else {
if (session != null) {
parameter = session.getPlatform().convertToDatabaseType(parameter);
}
writer.write(String.valueOf(parameter));
}
if (paramsEnum.hasNext()) {
writer.write(", ");
} else {
writer.write("]");
}
}
} else {
String parameterString = parameters.size() == 1 ? " parameter" : " parameters";
writer.write(parameters.size() + parameterString + " bound]");
}
}
/**
* get max rows returned from the call
*/
public int getMaxRows() {
return this.maxRows;
}
/**
* INTERNAL
* Returns the fields to be used in output row.
*/
public Vector getOutputRowFields() {
Vector