blob: 20b3abafdee4b66758f745b5e451b461a0716527 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 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:
// 06/26/2018 - Will Dazey
// - 532160 : Add support for non-extension OracleXPlatform classes
// 05/06/2019 - Jody Grassel
// - 547023 : Add LOB Locator support for core Oracle platform.
package org.eclipse.persistence.platform.database;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.databaseaccess.SimpleAppendCallCustomParameter;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.queries.Call;
/**
* <p><b>Purpose:</b>
* Provides Oracle version specific behavior when
* org.eclipse.persistence.oracle bundle is not available.
*/
public class Oracle8Platform extends OraclePlatform {
/**
* Locator is required for Oracle thin driver to write LOB value exceeds the
* limits
*/
protected boolean usesLocatorForLOBWrite = true;
/** The LOB value limits when the Locator is required for the writing */
protected int lobValueLimits = 0;
/**
* Default constructor.
*/
public Oracle8Platform() {
super();
}
/**
* INTERNAL:
*/
@Override
protected Hashtable<Class<?>, FieldTypeDefinition> buildFieldTypes() {
Hashtable<Class<?>, FieldTypeDefinition> fieldTypeMapping = super.buildFieldTypes();
fieldTypeMapping.put(Byte[].class, new FieldTypeDefinition("BLOB", false));
fieldTypeMapping.put(Character[].class, new FieldTypeDefinition("CLOB", false));
return fieldTypeMapping;
}
/**
* INTERNAL: Allow for conversion from the Oracle type to the Java type.
*/
@Override
public void copyInto(Platform platform) {
super.copyInto(platform);
if (!(platform instanceof Oracle8Platform)) {
return;
}
Oracle8Platform oracle8Platform = (Oracle8Platform) platform;
oracle8Platform.setShouldUseLocatorForLOBWrite(shouldUseLocatorForLOBWrite());
oracle8Platform.setLobValueLimits(getLobValueLimits());
}
/**
* INTERNAL Used by SQLCall.appendModify(..) If the field should be passed
* to customModifyInDatabaseCall, retun true, otherwise false. Methods
* shouldCustomModifyInDatabaseCall and customModifyInDatabaseCall should be
* kept in sync: shouldCustomModifyInDatabaseCall should return true if and
* only if the field is handled by customModifyInDatabaseCall.
*/
@Override
public boolean shouldUseCustomModifyForCall(DatabaseField field) {
if (shouldUseLocatorForLOBWrite()) {
Class type = field.getType();
if (ClassConstants.BLOB.equals(type) || ClassConstants.CLOB.equals(type)) {
return true;
}
}
return super.shouldUseCustomModifyForCall(field);
}
/**
* INTERNAL: Return if the LOB value size is larger than the limit, i.e. 4k.
*/
protected boolean lobValueExceedsLimit(Object value) {
if (value == null) {
return false;
}
int limit = getLobValueLimits();
if (value instanceof byte[]) {// blob
return ((byte[]) value).length >= limit;
} else if (value instanceof String) {// clob
return ((String) value).length() >= limit;
} else {
return false;
}
}
/**
* INTERNAL: This method is used to unwrap the oracle connection wrapped by
* the application server. TopLink needs this unwrapped connection for
* certain Oracle Specific support. (ie TIMESTAMPTZ, LOB) This is added as a
* workaround for bug 4565190
*/
@Override
public Connection getConnection(AbstractSession session, Connection connection) {
if (session.getServerPlatform() != null && (session.getLogin()).shouldUseExternalConnectionPooling()) {
// This is added as a workaround for bug 4460996
return session.getServerPlatform().unwrapConnection(connection);
}
return connection;
}
/**
* INTERNAL Used by SQLCall.translate(..) Typically there is no field
* translation (and this is default implementation). However on different
* platforms (Oracle) there are cases such that the values for binding and
* appending may be different (BLOB, CLOB). In these special cases the
* method returns a wrapper object which knows whether it should be bound or
* appended and knows how to do that.
*/
@Override
public Object getCustomModifyValueForCall(Call call, Object value, DatabaseField field, boolean shouldBind) {
Class type = field.getType();
if (ClassConstants.BLOB.equals(type) || ClassConstants.CLOB.equals(type)) {
if (value == null) {
return null;
}
Object lobValue = convertToDatabaseType(value);
if (shouldUseLocatorForLOBWrite() & lobValueExceedsLimit(lobValue)) {
((DatabaseCall) call).addContext(field, lobValue);
if (ClassConstants.BLOB.equals(type)) {
if (shouldBind) {
lobValue = new byte[1];
} else {
lobValue = new SimpleAppendCallCustomParameter("empty_blob()");
}
} else {
if (shouldBind) {
lobValue = new String(" ");
} else {
lobValue = new SimpleAppendCallCustomParameter("empty_clob()");
}
}
}
return lobValue;
}
return super.getCustomModifyValueForCall(call, value, field, shouldBind);
}
/**
* INTERNAL: Write LOB value - only on Oracle8 and up
*/
@Override
public void writeLOB(DatabaseField field, Object value, ResultSet resultSet, AbstractSession session)
throws SQLException {
if (isBlob(field.getType())) {
// change for 338585 to use getName instead of getNameDelimited
java.sql.Blob blob = (java.sql.Blob) resultSet.getObject(field.getName());
blob.setBytes(1, (byte[]) value);
//impose the localization
session.log(SessionLog.FINEST, SessionLog.SQL, "write_BLOB", blob.length(), field.getName());
} else if (isClob(field.getType())) {
// change for 338585 to use getName instead of getNameDelimited
java.sql.Clob clob = (java.sql.Clob) resultSet.getObject(field.getName());
clob.setString(1, (String) value);
//impose the localization
session.log(SessionLog.FINEST, SessionLog.SQL, "write_CLOB", clob.length(), field.getName());
} else {
// do nothing for now, open to BFILE or NCLOB types
}
}
/**
* INTERNAL: Used in writeLOB method only to identify a BLOB
*/
protected boolean isBlob(Class type) {
return ClassConstants.BLOB.equals(type);
}
/**
* INTERNAL: Used in writeLOB method only to identify a CLOB
*/
protected boolean isClob(Class type) {
return ClassConstants.CLOB.equals(type);
}
/**
* INTERNAL: Indicates whether app. server should unwrap connection to use
* lob locator.
*/
@Override
public boolean isNativeConnectionRequiredForLobLocator() {
return true;
}
/**
* PUBLIC: Set if the locator is required for the LOB write. The default is
* true. For Oracle thin driver, the locator is recommended for large size (
* &gt;4k for Oracle8, &gt;5.9K for Oracle9) BLOB/CLOB value write.
*/
public void setShouldUseLocatorForLOBWrite(boolean usesLocatorForLOBWrite) {
this.usesLocatorForLOBWrite = usesLocatorForLOBWrite;
}
/**
* PUBLIC: Return if the locator is required for the LOB write. The default
* is true. For Oracle thin driver, the locator is recommended for large
* size ( &gt;4k for Oracle8, &gt;5.9K for Oracle9) BLOB/CLOB value write.
*/
public boolean shouldUseLocatorForLOBWrite() {
return usesLocatorForLOBWrite;
}
/**
* PUBLIC: Return the BLOB/CLOB value limits on thin driver. The default
* value is 0. If usesLocatorForLOBWrite is true, locator will be used in
* case the lob's size is larger than lobValueLimit.
*/
public int getLobValueLimits() {
return lobValueLimits;
}
/**
* PUBLIC: Set the BLOB/CLOB value limits on thin driver. The default value
* is 0. If usesLocatorForLOBWrite is true, locator will be used in case the
* lob's size is larger than lobValueLimit.
*/
public void setLobValueLimits(int lobValueLimits) {
this.lobValueLimits = lobValueLimits;
}
}