Bug 529602: Add CLOB compare for Oracle WHERE clause

Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
Reviewed-by: Lukas Jungmann <lukas.jungmann@oracle.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
index c7337aa..301a018 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/databaseaccess/DatabasePlatform.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2017 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -24,6 +24,8 @@
  *       - 458877 : Add national character support
  *     02/23/2015-2.6 Dalia Abo Sheasha
  *       - 460607: Change DatabasePlatform StoredProcedureTerminationToken to be configurable
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
  ******************************************************************************/
 package org.eclipse.persistence.internal.databaseaccess;
 
@@ -3517,4 +3519,14 @@ public String getConnectionUserName() {
          throw new UnsupportedOperationException("Connection user name is not supported.");
      }
 
+    /**
+     * INTERNAL:
+     * Override this method if the platform needs to use a custom function based on the DatabaseField
+     * @return An expression for the given field set equal to a parameter matching the field
+     */
+    public Expression createExpressionFor(DatabaseField field, Expression builder) {
+        Expression subExp1 = builder.getField(field);
+        Expression subExp2 = builder.getParameter(field);
+        return subExp1.equal(subExp2);
+    }
 }
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
index 413914f..c8e6c1a 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/internal/descriptors/ObjectBuilder.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -21,6 +21,8 @@
  *       - 485984: Retrieve FetchGroup info along with getReference() from cache
  *     08/07/2016-2.7 Dalia Abo Sheasha
  *       - 499335: Multiple embeddable fields can't reference same object
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
  ******************************************************************************/
 package org.eclipse.persistence.internal.descriptors;
 
@@ -2916,9 +2918,7 @@ public void createPrimaryKeyExpression(AbstractSession session) {
         if(null != primaryKeyFields) {
             for (int index = 0; index < primaryKeyFields.size(); index++) {
                 DatabaseField primaryKeyField = (DatabaseField)primaryKeyFields.get(index);
-                subExp1 = builder.getField(primaryKeyField);
-                subExp2 = builder.getParameter(primaryKeyField);
-                subExpression = subExp1.equal(subExp2);
+                subExpression = session.getPlatform().createExpressionFor(primaryKeyField, builder);
 
                 if (expression == null) {
                     expression = subExpression;
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
index c4d4df8..38ed64c 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/OraclePlatform.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 1998, 2016 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
+ * Copyright (c) 1998, 2018 Oracle and/or its affiliates, IBM Corporation. All rights reserved.
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
  * which accompanies this distribution.
@@ -20,6 +20,8 @@
  *       - 458877 : Add national character support
  *     02/23/2015-2.6 Dalia Abo Sheasha
  *       - 460607: Change DatabasePlatform StoredProcedureTerminationToken to be configurable
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
  *****************************************************************************/
 package org.eclipse.persistence.platform.database;
 
@@ -40,6 +42,7 @@
 
 import org.eclipse.persistence.exceptions.DatabaseException;
 import org.eclipse.persistence.exceptions.ValidationException;
+import org.eclipse.persistence.expressions.Expression;
 import org.eclipse.persistence.expressions.ExpressionOperator;
 import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
 import org.eclipse.persistence.internal.databaseaccess.FieldTypeDefinition;
@@ -1167,4 +1170,15 @@ public boolean shouldPrintForUpdateClause() {
         return shouldPrintForUpdateClause;
     }
 
+    @Override
+    public Expression createExpressionFor(DatabaseField field, Expression builder) {
+        if (field.getType() == java.sql.Clob.class || 
+                field.getType() == java.sql.Blob.class) {
+            Expression subExp1 = builder.getField(field);
+            Expression subExp2 = builder.getParameter(field);
+            subExp1 = subExp1.getFunction("dbms_lob.compare", subExp2);
+            return subExp1.equal(0);
+        }
+        return super.createExpressionFor(field, builder);
+    }
 }
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java
new file mode 100644
index 0000000..df17c1b
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/TestLobMerge.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob;
+
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+
+import org.eclipse.persistence.jpa.test.framework.DDLGen;
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.lob.model.CollectedEntity;
+import org.eclipse.persistence.jpa.test.lob.model.ParentEntity;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(EmfRunner.class)
+public class TestLobMerge {
+
+    @Emf(createTables = DDLGen.DROP_CREATE, classes = { CollectedEntity.class, ParentEntity.class })
+    private EntityManagerFactory emf;
+
+    /**
+     * Merging ElementCollections on Oracle fails when EclipseLink generates 
+     * a DELETE SQL statement with a WHERE clause containing a CLOB.
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void testLobMerge() throws Exception {
+        EntityManager em = null;
+        try {
+            em = emf.createEntityManager();
+
+            final Set<CollectedEntity> col1 = new HashSet<CollectedEntity>(
+                    Arrays.asList(new CollectedEntity[] { 
+                            new CollectedEntity("label1", "content1"),
+                            new CollectedEntity("label2", "content2"),
+                            new CollectedEntity("label3", "content3") }));
+
+            final ParentEntity pdo = new ParentEntity(9, Collections.unmodifiableSet(col1));
+            System.out.println("EM CREATED!!");
+            em.getTransaction().begin();
+            em.persist(pdo);
+            em.getTransaction().commit();
+
+            final Set<CollectedEntity> col2 = new HashSet<CollectedEntity>(
+                    Arrays.asList(new CollectedEntity[] { 
+                            new CollectedEntity("label1", "content1"),
+                            new CollectedEntity("label2", "content2") }));
+            final ParentEntity newEntity = new ParentEntity(pdo.getId(), col2);
+
+            try {
+                em.getTransaction().begin();
+                em.merge(newEntity);
+                //Failure would occur on merge, if it passed merge, test passed
+                em.getTransaction().commit();
+            } catch (final Exception e) {
+                System.err.println("Exception: " + e);
+                em.getTransaction().rollback();
+            }
+        } catch (Exception e) {
+            Assert.fail(e.getLocalizedMessage());
+        } finally {
+            if (em != null) {
+                em.close();
+            }
+        }
+    }
+
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java
new file mode 100644
index 0000000..b740000
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/CollectedEntity.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob.model;
+
+import javax.persistence.Column;
+import javax.persistence.Embeddable;
+import javax.persistence.Lob;
+
+@Embeddable
+public class CollectedEntity {
+
+    @Column(name = "label", nullable = false, unique = true)
+    private String label;
+
+    @Lob
+    @Column(name = "content", columnDefinition = "CLOB", nullable = false)
+    private String content;
+
+    public CollectedEntity() {}
+
+    public CollectedEntity(final String label, final String content) {
+      this.label = label;
+      this.content = content;
+    }
+
+    public String getLabel() {
+      return label;
+    }
+
+    public void setLabel(final String label) {
+      this.label = label;
+    }
+
+    public String getContent() {
+      return content;
+    }
+
+    public void setContent(final String content) {
+      this.content = content;
+    }
+
+    @Override
+    public int hashCode() {
+      return label.hashCode() + content.hashCode();
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+      if (object instanceof CollectedEntity) {
+        final CollectedEntity that = (CollectedEntity) object;
+        return this.label.equals(that.label)
+            && this.content.equals(that.content);
+      }
+      return false;
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java
new file mode 100644
index 0000000..068e1ca
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/lob/model/ParentEntity.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * This program and the accompanying materials are made available under the 
+ * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 
+ * which accompanies this distribution. 
+ * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at 
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *     02/14/2018-2.7 Will Dazey
+ *       - 529602: Added support for CLOBs in DELETE statements for Oracle
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.lob.model;
+
+import java.util.Collections;
+import java.util.Set;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+
+@Entity
+public class ParentEntity {
+
+    @Id private int id;
+
+    @ElementCollection
+    @CollectionTable(
+        joinColumns = {
+            @JoinColumn(name = "parent_id", referencedColumnName = "id")
+        })
+    private Set<CollectedEntity> subs;
+
+    public ParentEntity() {}
+
+    public ParentEntity(final int id, final Set<CollectedEntity> subs) {
+        this.id = id;
+        this.subs = Collections.unmodifiableSet(subs);
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(final int id) {
+        this.id = id;
+    }
+
+    public Set<CollectedEntity> getSubs() {
+        return subs;
+    }
+
+    public void setSubs(final Set<CollectedEntity> subs) {
+        this.subs = subs;
+    }
+
+    @Override
+    public int hashCode() {
+        return id;
+    }
+
+    @Override
+    public boolean equals(final Object object) {
+        if (object instanceof ParentEntity) {
+            final ParentEntity that = (ParentEntity) object;
+            return (this.id == that.id);
+        }
+        return false;
+    }
+}