| /* |
| * 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 ( |
| * >4k for Oracle8, >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 ( >4k for Oracle8, >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; |
| } |
| } |