Skip to content

Commit

Permalink
Bug 392762: Add Oracle support for IN Clause parameter limit
Browse files Browse the repository at this point in the history
Signed-off-by: Will Dazey <[email protected]>
  • Loading branch information
dazey3 committed Mar 25, 2020
1 parent a4ab8f3 commit 6c1d2c4
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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(",");
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -1235,4 +1235,8 @@ public boolean checkTableExists(final DatabaseSessionImpl session,
}
}

@Override
public int getINClauseLimit() {
return 1000;
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -16,17 +16,20 @@
// - #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;
import javax.persistence.criteria.CriteriaQuery;
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_;
Expand All @@ -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;
Expand Down Expand Up @@ -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<L1> query = builder.createQuery(L1.class);
Root<L1> 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<String> parameterList = new ArrayList<String>();
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<L1> query = builder.createQuery(L1.class);
Root<L1> 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<String> parameterList = new ArrayList<String>();
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();
}
}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -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 {
Expand All @@ -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<String> parameterList = new ArrayList<String>();
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<String> parameterList = new ArrayList<String>();
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();
Expand Down

0 comments on commit 6c1d2c4

Please sign in to comment.