From 6c1d2c485db43f7a86aa94f9125f1afbc26e9a1c Mon Sep 17 00:00:00 2001 From: Will Dazey Date: Thu, 23 Jan 2020 10:49:58 -0600 Subject: [PATCH] Bug 392762: Add Oracle support for IN Clause parameter limit Signed-off-by: Will Dazey --- .../internal/databaseaccess/DatabaseCall.java | 34 +++++++-- .../databaseaccess/DatasourcePlatform.java | 12 ++- .../platform/database/OraclePlatform.java | 8 +- .../test/criteria/TestCriteriaBuilder.java | 74 ++++++++++++++++++- .../jpa/test/jpql/TestComplexJPQL.java | 70 ++++++++++++++++-- 5 files changed, 179 insertions(+), 19 deletions(-) diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabaseCall.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabaseCall.java index 3a91ea8f6c7..fab262aa5df 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabaseCall.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatabaseCall.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020 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 @@ -1261,10 +1262,33 @@ public void translateQueryStringForParameterizedIN(AbstractRecord translationRow } else { parametersValues.addAll(values); int size = values.size(); - for (int index = 0; index < size; index++) { - writer.write("?"); - if ((index + 1) < size) { - writer.write(","); + + int limit = ((DatasourcePlatform)session.getDatasourcePlatform()).getINClauseLimit(); + //The database platform has a limit for the IN clause so we need to reformat the clause + if(limit > 0) { + boolean not = token.endsWith(" NOT IN "); + String subToken = token.substring(0, token.length() - (not ? " NOT IN " : " IN ").length()); + int spaceIndex = subToken.lastIndexOf(' '); + int braceIndex = subToken.lastIndexOf('('); + String fieldName = subToken.substring((spaceIndex > braceIndex ? spaceIndex : braceIndex) + 1); + String inToken = not ? ") AND " + fieldName + " NOT IN (" : ") OR " + fieldName + " IN ("; + + for (int index = 0; index < size; index++) { + writer.write("?"); + if ((index + 1) < size) { + if (index > 0 && (index + 1) % limit == 0) { + writer.write(inToken); + } else { + writer.write(","); + } + } + } + } else { + for (int index = 0; index < size; index++) { + writer.write("?"); + if ((index + 1) < size) { + writer.write(","); + } } } } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java index e3209243ab8..54ed67fa693 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/databaseaccess/DatasourcePlatform.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2019 IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020 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 @@ -1068,4 +1068,12 @@ public Expression createExpressionFor(DatabaseField field, Expression builder) { Expression subExp2 = builder.getParameter(field); return subExp1.equal(subExp2); } + + /** + * INTERNAL: + * Some database platforms have a limit for the number of parameters in an IN clause. + */ + public int getINClauseLimit() { + return 0; + } } diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java index 4be818ba3ec..7e1d6556c17 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 1998, 2019 IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2020 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 @@ -1235,4 +1235,8 @@ public boolean checkTableExists(final DatabaseSessionImpl session, } } + @Override + public int getINClauseLimit() { + return 1000; + } } diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestCriteriaBuilder.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestCriteriaBuilder.java index c4b96b6103d..d0860c1742a 100644 --- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestCriteriaBuilder.java +++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/criteria/TestCriteriaBuilder.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2019 IBM Corporation. All rights reserved. + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 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 @@ -16,10 +16,12 @@ // - #253: Add support for embedded constructor results with CriteriaBuilder package org.eclipse.persistence.jpa.test.criteria; +import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; +import javax.persistence.Query; import javax.persistence.TypedQuery; import javax.persistence.criteria.CompoundSelection; import javax.persistence.criteria.CriteriaBuilder; @@ -27,6 +29,7 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.Root; +import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl; import org.eclipse.persistence.jpa.test.criteria.model.L1; import org.eclipse.persistence.jpa.test.criteria.model.L1Model; import org.eclipse.persistence.jpa.test.criteria.model.L1_; @@ -36,6 +39,7 @@ 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.platform.database.DatabasePlatform; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @@ -107,4 +111,70 @@ public void testCriteriaCompoundSelectionModel() throws Exception { } } } + + @Test + public void testCriteriaBuilder_IN_ClauseLimit() throws Exception { + EntityManager em = emf.createEntityManager(); + try { + //"SELECT OBJECT(emp) FROM Employee emp WHERE emp.id IN :result" + final CriteriaBuilder builder = em.getCriteriaBuilder(); + final CriteriaQuery query = builder.createQuery(L1.class); + Root root = query.from(L1.class); + query.where(root.get("name").in(builder.parameter(List.class, "parameterList"))); + + Query q = em.createQuery(query); + + //Create a list longer than the limit + int limit = getPlatform(emf).getINClauseLimit() + 10; + List parameterList = new ArrayList(); + for(int p = 0; p < limit; p++) { + parameterList.add("" + p); + } + q.setParameter("parameterList", parameterList); + + q.getResultList(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void testCriteriaBuilder_NOTIN_ClauseLimit() throws Exception { + EntityManager em = emf.createEntityManager(); + try { + //"SELECT OBJECT(emp) FROM Employee emp WHERE emp.id IN :result" + final CriteriaBuilder builder = em.getCriteriaBuilder(); + final CriteriaQuery query = builder.createQuery(L1.class); + Root root = query.from(L1.class); + query.where(root.get("name").in(builder.parameter(List.class, "parameterList")).not()); + + Query q = em.createQuery(query); + + //Create a list longer than the limit + int limit = getPlatform(emf).getINClauseLimit() + 10; + List parameterList = new ArrayList(); + for(int p = 0; p < limit; p++) { + parameterList.add("" + p); + } + q.setParameter("parameterList", parameterList); + + q.getResultList(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + private DatabasePlatform getPlatform(EntityManagerFactory emf) { + return ((EntityManagerFactoryImpl)emf).getServerSession().getPlatform(); + } } diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/jpql/TestComplexJPQL.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/jpql/TestComplexJPQL.java index 23150d099bd..af2ba8632b0 100644 --- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/jpql/TestComplexJPQL.java +++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/jpql/TestComplexJPQL.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2018, 2019 IBM Corporation. All rights reserved. + * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020 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 @@ -18,6 +18,7 @@ // - 534515: Incorrect return type set for CASE functions package org.eclipse.persistence.jpa.test.jpql; +import java.util.ArrayList; import java.util.List; import javax.persistence.EntityManager; @@ -40,6 +41,7 @@ import org.eclipse.persistence.platform.database.DatabasePlatform; import org.eclipse.persistence.platform.database.DerbyPlatform; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; @@ -56,12 +58,10 @@ public class TestComplexJPQL { private EntityManagerFactory joinEMF; @Test - public void testComplexJPQLIN() { - if(getPlatform(inEMF) instanceof DerbyPlatform) { - Assert.assertTrue("Test will not run on DerbyPlatform. Derby does " - + "not support multiple IN clause for prepared statements.", true); - return; - } + public void testINWithSubquery() { + DatabasePlatform platform = getPlatform(inEMF); + Assume.assumeFalse("Test will not run on " + platform + ". " + platform + " does " + + "not support multiple IN clause for prepared statements.", platform instanceof DerbyPlatform); EntityManager em = inEMF.createEntityManager(); try { @@ -79,6 +79,60 @@ public void testComplexJPQLIN() { } } + @Test + public void test_IN_ClauseLimit() { + EntityManager em = inEMF.createEntityManager(); + try { + Query q = em.createQuery("select t0.id from JPQLEntity t0 " + + "where t0.id.value1.value <> :parameterString and t0.string1 in :parameterList"); + + //Create a list longer than the limit + int limit = getPlatform(inEMF).getINClauseLimit() + 10; + List parameterList = new ArrayList(); + for(int p = 0; p < limit; p++) { + parameterList.add("" + p); + } + q.setParameter("parameterList", parameterList); + q.setParameter("parameterString", "Test"); + + q.getResultList(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + + @Test + public void test_NOTIN_ClauseLimit() { + EntityManager em = inEMF.createEntityManager(); + try { + Query q = em.createQuery("select t0.id from JPQLEntity t0 " + + "where t0.id.value1.value <> :parameterString and t0.string1 not in :parameterList"); + + //Create a list longer than the limit + int limit = getPlatform(inEMF).getINClauseLimit() + 10; + List parameterList = new ArrayList(); + for(int p = 0; p < limit; p++) { + parameterList.add("" + p); + } + q.setParameter("parameterList", parameterList); + q.setParameter("parameterString", "Test"); + + q.getResultList(); + } finally { + if (em.getTransaction().isActive()) { + em.getTransaction().rollback(); + } + if(em.isOpen()) { + em.close(); + } + } + } + @Test public void testComplexJPQLCase() { EntityManager em = caseEMF.createEntityManager();