Fix for bug 460740. SQLSyntaxException when trying to use
 PESSIMISTIC_READ with setFirst/MaxResults against DB2.

Signed-off-by: Rick Curtis <curtisr7@gmail.com>
diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2Platform.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2Platform.java
index b426043..091aae6 100644
--- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2Platform.java
+++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/platform/database/DB2Platform.java
@@ -13,6 +13,8 @@
  *       - 357533: Allow DDL queries to execute even when Multitenant entities are part of the PU

  *     02/19/2015 - Rick Curtis  

  *       - 458877 : Add national character support

+ *     02/24/2016-2.6.0 Rick Curtis

+ *       - 460740: Fix pessimistic locking with setFirst/Max results on DB2

  *****************************************************************************/

 package org.eclipse.persistence.platform.database;

 

@@ -789,6 +791,10 @@
         return true;

     }

     

+    @Override

+    public boolean shouldPrintForUpdateClause() {

+        return false;

+    }

     /**

      * INTERNAL:

      * Print the SQL representation of the statement on a stream, storing the fields

@@ -807,22 +813,26 @@
         

         if ( !(this.shouldUseRownumFiltering()) || ( !(max>0) && !(firstRow>0) ) ){

             super.printSQLSelectStatement(call, printer, statement);

+            statement.appendForUpdateClause(printer);

             return;

         } else if ( max > 0 ){

             statement.setUseUniqueFieldAliases(true);

             printer.printString("SELECT * FROM (SELECT * FROM (SELECT ");

-            printer.printString("EL_TEMP.*, ROWNUMBER() OVER() AS EL_ROWNM FROM (");            

+            printer.printString("EL_TEMP.*, ROWNUMBER() OVER() AS EL_ROWNM FROM (");

             call.setFields(statement.printSQL(printer));

             printer.printString(") AS EL_TEMP) AS EL_TEMP2 WHERE EL_ROWNM <= ");

             printer.printParameter(DatabaseCall.MAXROW_FIELD);

             printer.printString(") AS EL_TEMP3 WHERE EL_ROWNM > ");

             printer.printParameter(DatabaseCall.FIRSTRESULT_FIELD);

+            // If we have a ForUpdate clause, it must be on the outermost query

+             statement.appendForUpdateClause(printer);

         } else {// firstRow>0

             statement.setUseUniqueFieldAliases(true);

-            printer.printString("SELECT * FROM (SELECT EL_TEMP.*, ROWNUMBER() OVER() AS EL_ROWNM FROM (");            

+            printer.printString("SELECT * FROM (SELECT EL_TEMP.*, ROWNUMBER() OVER() AS EL_ROWNM FROM (");

             call.setFields(statement.printSQL(printer));

             printer.printString(") AS EL_TEMP) AS EL_TEMP2 WHERE EL_ROWNM > ");

             printer.printParameter(DatabaseCall.FIRSTRESULT_FIELD);

+            statement.appendForUpdateClause(printer);

         }

         call.setIgnoreFirstRowSetting(true);

         call.setIgnoreMaxResultsSetting(true);

diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/TestPessimisticLocking.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/TestPessimisticLocking.java
new file mode 100644
index 0000000..99e5cbb
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/TestPessimisticLocking.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) 2015 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/24/2016-2.6.0 Rick Curtis
+ *       - 460740: Fix pessimistic locking with setFirst/Max results on DB2
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.locking;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.LockModeType;
+
+import org.eclipse.persistence.jpa.test.framework.Emf;
+import org.eclipse.persistence.jpa.test.framework.EmfRunner;
+import org.eclipse.persistence.jpa.test.framework.Property;
+import org.eclipse.persistence.jpa.test.locking.model.LockingDog;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(EmfRunner.class)
+public class TestPessimisticLocking {
+    @Emf(/* createTables = DDLGen.DROP_CREATE,**/ classes = { LockingDog.class, }, properties = { @Property(
+            name = "eclipselink.cache.shared.default", value = "false")})
+    private EntityManagerFactory emf;
+
+    static ExecutorService executor = null;
+
+    @BeforeClass
+    public static void beforeClass() {
+        executor = Executors.newFixedThreadPool(5);
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        executor.shutdownNow();
+    }
+
+    List<LockingDog> dogs;
+
+    @Before
+    public void before() {
+        EntityManager em = emf.createEntityManager();
+        dogs = new ArrayList<LockingDog>();
+        try {
+            em.getTransaction().begin();
+            for (int i = 0; i < 10; i++) {
+                LockingDog ld = new LockingDog();
+                dogs.add(ld);
+                em.persist(ld);
+            }
+            em.getTransaction().commit();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+        }
+    }
+
+    @Test
+    public void testPessimisticFind() throws Exception {
+        EntityManager em = emf.createEntityManager();
+        final EntityManager em2 = emf.createEntityManager();
+        final CountDownLatch cdl = new CountDownLatch(2);
+        try {
+            em.getTransaction().begin();
+            em2.getTransaction().begin();
+
+            LockingDog locked = em.find(LockingDog.class, dogs.get(0).getId(), LockModeType.PESSIMISTIC_READ);
+            Assert.assertNotNull(locked);
+            Callable<LockingDog> blocked = new Callable<LockingDog>() {
+                @Override
+                public LockingDog call() {
+                    cdl.countDown();
+                    return em2.find(LockingDog.class, dogs.get(0).getId(), LockModeType.PESSIMISTIC_READ);
+                }
+            };
+            Future<LockingDog> future = executor.submit(blocked);
+            cdl.countDown();
+            // Make sure worker is blocked
+            Assert.assertFalse(future.isDone());
+            // Rolling back of tran should allow worker to complete
+            em.getTransaction().rollback();
+            Assert.assertEquals(locked.getId(), future.get().getId());
+
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            if (em2.getTransaction().isActive()) {
+                em2.getTransaction().rollback();
+            }
+        }
+    }
+
+    @Test
+    public void testFirstResultPessimisticRead() throws Exception {
+        EntityManager em = emf.createEntityManager();
+        final EntityManager em2 = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            em2.getTransaction().begin();
+            List<LockingDog> dogs = em.createNamedQuery("find.lockingdogs", LockingDog.class).setLockMode(LockModeType.PESSIMISTIC_READ).setMaxResults(1).setFirstResult(4).getResultList();
+            // This worker should block as he is trying to lock already locked
+            // rows
+            Future<Boolean> result = executor.submit(new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception {
+                    List<LockingDog> dogs2 = em2.createNamedQuery("find.lockingdogs", LockingDog.class).setLockMode(LockModeType.PESSIMISTIC_READ).setMaxResults(1).setFirstResult(5).getResultList();
+                    return true;
+                }
+            });
+            // Make sure the worker isn't done
+            Assert.assertFalse(result.isDone());
+            em.getTransaction().rollback();
+            // Make sure that the worker obtained the locks and completed
+            Assert.assertTrue(result.get());
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+            if (em2.getTransaction().isActive()) {
+                em2.getTransaction().rollback();
+            }
+            em.close();
+            em2.close();
+        }
+    }
+
+    @Test
+    public void testMaxResultPessimisticRead() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            List<LockingDog> dogs = em.createNamedQuery("find.lockingdogs", LockingDog.class).setLockMode(LockModeType.PESSIMISTIC_READ).setMaxResults(5).getResultList();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+        }
+    }
+
+    @Test
+    public void testFirstResultMaxResultPessimisticRead() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            em.getTransaction().begin();
+            List<LockingDog> dogs = em.createNamedQuery("find.lockingdogs", LockingDog.class).setLockMode(LockModeType.PESSIMISTIC_READ).setFirstResult(5).setMaxResults(5).getResultList();
+        } finally {
+            if (em.getTransaction().isActive()) {
+                em.getTransaction().rollback();
+            }
+        }
+    }
+}
diff --git a/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/model/LockingDog.java b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/model/LockingDog.java
new file mode 100644
index 0000000..5da07db
--- /dev/null
+++ b/jpa/eclipselink.jpa.test.jse/src/org/eclipse/persistence/jpa/test/locking/model/LockingDog.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2015 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/24/2016-2.6.0 Rick Curtis
+ *       - 460740: Fix pessimistic locking with setFirst/Max results on DB2
+ ******************************************************************************/
+package org.eclipse.persistence.jpa.test.locking.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Version;
+
+@Entity
+@NamedQueries({@NamedQuery(name="find.lockingdogs", query="SELECT d FROM LockingDog d")})
+public class LockingDog {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    int id;
+
+    @Version
+    int version;
+
+    String name;
+    
+    public int getId() {
+        return id;
+    }
+
+}