Bug 463042: Concurrency issue with Case expression operator (#1359)

Signed-off-by: Will Dazey <dazeydev.3@gmail.com>
diff --git a/foundation/eclipselink.core.test/src/it/java/org/eclipse/persistence/testing/tests/expressions/ExpressionOperatorUnitTestSuite.java b/foundation/eclipselink.core.test/src/it/java/org/eclipse/persistence/testing/tests/expressions/ExpressionOperatorUnitTestSuite.java
index a553afc..aa90456 100644
--- a/foundation/eclipselink.core.test/src/it/java/org/eclipse/persistence/testing/tests/expressions/ExpressionOperatorUnitTestSuite.java
+++ b/foundation/eclipselink.core.test/src/it/java/org/eclipse/persistence/testing/tests/expressions/ExpressionOperatorUnitTestSuite.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -84,6 +85,158 @@
         }
     }
 
+    public void _testDefaultCaseOperatorDatabaseStringsTest() {
+        ExpressionOperator caseOp = ExpressionOperator.caseStatement();
+
+        String[] databaseStrings = caseOp.getDatabaseStrings(0);
+        String[] expectedStrings = new String[] {"CASE ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(1);
+        expectedStrings = new String[] {"CASE ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(2);
+        expectedStrings = new String[] {"CASE ", " ELSE ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(3);
+        expectedStrings = new String[] {"CASE ", " WHEN ", " THEN ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(4);
+        expectedStrings = new String[] {"CASE ", " WHEN ", " THEN ", " ELSE ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(5);
+        expectedStrings = new String[] {"CASE ", " WHEN ", " THEN ", " WHEN ", " THEN ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseOp.getDatabaseStrings(6);
+        expectedStrings = new String[] {"CASE ", " WHEN ", " THEN ", " WHEN ", " THEN ", " ELSE ", " END"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+    }
+
+    public void _testDefaultCaseConditionOperatorDatabaseStringsTest() {
+        ExpressionOperator caseConditionOp = ExpressionOperator.caseConditionStatement();
+
+        String[] databaseStrings = caseConditionOp.getDatabaseStrings(0);
+        String[] expectedStrings = new String[] {"CASE WHEN ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(1);
+        expectedStrings = new String[] {"CASE WHEN ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(2);
+        expectedStrings = new String[] {"CASE WHEN ", " THEN ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(3);
+        expectedStrings = new String[] {"CASE WHEN ", " THEN ", " ELSE ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(4);
+        expectedStrings = new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(5);
+        expectedStrings = new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " ELSE ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(6);
+        expectedStrings = new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " WHEN ", " THEN ", " END "};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+    }
+
+    public void _testDefaultCoalesceOperatorDatabaseStringsTest() {
+        ExpressionOperator coalesceOp = ExpressionOperator.coalesce();
+
+        String[] databaseStrings = coalesceOp.getDatabaseStrings(0);
+        String[] expectedStrings = new String[] {"COALESCE(", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = coalesceOp.getDatabaseStrings(1);
+        expectedStrings = new String[] {"COALESCE(", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = coalesceOp.getDatabaseStrings(2);
+        expectedStrings = new String[] {"COALESCE(", ", ", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = coalesceOp.getDatabaseStrings(3);
+        expectedStrings = new String[] {"COALESCE(", ", ", ", ", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = coalesceOp.getDatabaseStrings(4);
+        expectedStrings = new String[] {"COALESCE(", ", ", ", ", ", ", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+
+        databaseStrings = coalesceOp.getDatabaseStrings(5);
+        expectedStrings = new String[] {"COALESCE(", ", ", ", ", ", ", ", ", ")"};
+        if(!(Arrays.equals(expectedStrings, databaseStrings))) {
+            throw new TestErrorException("Expected " + Arrays.toString(expectedStrings) + 
+                    " but was " + Arrays.toString(databaseStrings));
+        }
+    }
+
     @Override
     public void addTests() {
         setManager(PopulationManager.getDefaultManager());
@@ -95,5 +248,9 @@
         addTest(new UnitTestCase("IsComparisonOperatorTest"));
         addTest(new UnitTestCase("IsFunctionOperatorTest"));
         addTest(new UnitTestCase("IsLogicalOperatorTest"));
+
+        addTest(new UnitTestCase("DefaultCaseOperatorDatabaseStringsTest"));
+        addTest(new UnitTestCase("DefaultCaseConditionOperatorDatabaseStringsTest"));
+        addTest(new UnitTestCase("DefaultCoalesceOperatorDatabaseStringsTest"));
     }
 }
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
index 09c1f4e..be93a0c 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ExpressionOperator.java
@@ -56,7 +56,10 @@
     static final long serialVersionUID = -7066100204792043980L;
     protected int selector;
     protected String name;
-    protected String[] databaseStrings;
+
+    // ListExpressionOperator uses its own start/separator/terminator strings
+    private String[] databaseStrings;
+
     protected boolean isPrefix = false;
     protected boolean isRepeating = false;
     protected Class<?> nodeClass;
@@ -317,7 +320,7 @@
         }
         ExpressionOperator operator = (ExpressionOperator) object;
         if (getSelector() == 0) {
-            return Arrays.equals(getDatabaseStrings(), operator.getDatabaseStrings());
+            return Arrays.equals(getDatabaseStrings(0), operator.getDatabaseStrings(0));
         } else {
             return getSelector() == operator.getSelector();
         }
@@ -1232,6 +1235,13 @@
     /**
      * INTERNAL:
      */
+    public String[] getDatabaseStrings(int arguments) {
+        return databaseStrings;
+    }
+
+    /**
+     * INTERNAL:
+     */
     public String[] getJavaStrings() {
         return javaStrings;
     }
@@ -2166,16 +2176,11 @@
         if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
             printer.getCall().setUsesBinding(false);
         }
+
         int dbStringIndex = 0;
-        try {
-            if (isPrefix()) {
-                printer.getWriter().write(getDatabaseStrings()[0]);
-                dbStringIndex = 1;
-            } else {
-                dbStringIndex = 0;
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
+        if (isPrefix()) {
+            printer.printString(getDatabaseStrings()[0]);
+            dbStringIndex = 1;
         }
 
         if (argumentIndices == null) {
@@ -2185,6 +2190,7 @@
             }
         }
 
+        String[] dbStrings = getDatabaseStrings(items.size());
         for (final int index : argumentIndices) {
             Expression item = (Expression)items.get(index);
             if ((this.selector == Ref) || ((this.selector == Deref) && (item.isObjectExpression()))) {
@@ -2195,8 +2201,8 @@
             } else {
                 item.printSQL(printer);
             }
-            if (dbStringIndex < getDatabaseStrings().length) {
-                printer.printString(getDatabaseStrings()[dbStringIndex++]);
+            if (dbStringIndex < dbStrings.length) {
+                printer.printString(dbStrings[dbStringIndex++]);
             }
         }
     }
@@ -2225,12 +2231,11 @@
         if (printer.getPlatform().isDynamicSQLRequiredForFunctions() && !isBindingSupported()) {
             printer.getCall().setUsesBinding(false);
         }
-        int dbStringIndex;
+
+        int dbStringIndex = 0;
         if (isPrefix()) {
             printer.printString(getDatabaseStrings()[0]);
             dbStringIndex = 1;
-        } else {
-            dbStringIndex = 0;
         }
 
         first.printSQL(printer);
@@ -2996,11 +3001,12 @@
      */
     @Override
     public String toString() {
-        if ((getDatabaseStrings() == null) || (getDatabaseStrings().length == 0)) {
+        String[] dbStrings = getDatabaseStrings();
+        if ((dbStrings == null) || (dbStrings.length == 0)) {
             //CR#... Print a useful name for the missing platform operator.
             return "platform operator - " + getPlatformOperatorName(this.selector);
         } else {
-            return "operator " + Arrays.asList(getDatabaseStrings());
+            return "operator " + Arrays.asList(dbStrings);
         }
     }
 
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ListExpressionOperator.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ListExpressionOperator.java
index ba32a46..6093a93 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ListExpressionOperator.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/expressions/ListExpressionOperator.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -14,6 +15,8 @@
 //     tware - initial API and implementation from for JPA 2.0 criteria API
 package org.eclipse.persistence.expressions;
 
+import java.util.List;
+
 import org.eclipse.persistence.internal.helper.Helper;
 
 /**
@@ -39,7 +42,6 @@
     protected String[] startStrings = null;
     protected String[] separators = null;
     protected String[] terminationStrings = null;
-    protected int numberOfItems = 0;
     protected boolean isComplete = false;
 
     @Override
@@ -61,35 +63,50 @@
      */
     @Override
     public String[] getDatabaseStrings() {
-        databaseStrings = new String[numberOfItems + 1];
+        return getDatabaseStrings(0);
+    }
+
+    /**
+     * Returns an array of Strings that expects a query argument between each String in the array to form the Expression.
+     * The array is built from the defined startStrings, separators, and terminationStrings.
+     * Start strings and termination strings take precedence over separator strings.
+     * 
+     * The first defined start string will be added to the array.
+     * All subsequent start strings are optional, meaning they will only be added to the array if there are argument spaces available.
+     * 
+     * The defined set of separator strings will be repeated, as a complete set, as long as there are argument spaces available.
+     * 
+     * The last defined termination string will be added to the array.
+     * All antecedent termination strings are optional, meaning they will only be added to the array if there are argument spaces available.
+     */
+    @Override
+    public String[] getDatabaseStrings(int arguments) {
         int i = 0;
-        while (i < startStrings.length){
-            databaseStrings[i] = startStrings[i];
+        String[] databaseStrings = new String[(arguments == 0) ? 2 : arguments + 1];
+
+        int start = (arguments < (startStrings.length)) ? databaseStrings.length - 1 : startStrings.length;
+        for (int j = 0; j < start; j++) {
+            databaseStrings[i] = startStrings[j];
             i++;
         }
-        while  (i < numberOfItems - (terminationStrings.length - 1)){
-            for (int j=0;j<separators.length;j++){
-                databaseStrings[i] = separators[j];
+
+        // '- 1' to save a spot for the guaranteed 1 terminator
+        int separ = ((databaseStrings.length - start - 1) / separators.length);
+        for (int j = 0; j < separ; j++) {
+            for (int k = 0; k < separators.length; k++) {
+                databaseStrings[i] = separators[k];
                 i++;
             }
         }
-        while (i <= numberOfItems){
-            for (int j=0;j<terminationStrings.length;j++){
-                databaseStrings[i] = terminationStrings[j];
-                    i++;
-            }
+
+        int termi = databaseStrings.length - (start + (separ * separators.length));
+        for (int j = (terminationStrings.length - termi); j < terminationStrings.length; j++) {
+            databaseStrings[i] = terminationStrings[j];
+            i++;
         }
         return databaseStrings;
     }
 
-    public int getNumberOfItems(){
-        return numberOfItems;
-    }
-
-    public void setNumberOfItems(int numberOfItems){
-        this.numberOfItems = numberOfItems;
-    }
-
     public String[] getStartStrings() {
         return startStrings;
     }
@@ -126,10 +143,6 @@
         this.terminationStrings = terminationStrings;
     }
 
-    public void incrementNumberOfItems(){
-        numberOfItems++;
-    }
-
     public void setIsComplete(boolean isComplete){
         this.isComplete = isComplete;
     }
@@ -138,6 +151,4 @@
     public boolean isComplete() {
         return isComplete;
     }
-
-
 }
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ArgumentListFunctionExpression.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ArgumentListFunctionExpression.java
index e91fff9..2bf2c2a 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ArgumentListFunctionExpression.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ArgumentListFunctionExpression.java
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -56,7 +57,6 @@
             super.addChild(argument);
         }
         setBaseExpression(getChildren().firstElement());
-        ((ListExpressionOperator)operator).incrementNumberOfItems();
     }
 
     /**
@@ -86,7 +86,6 @@
     public void setOperator(ExpressionOperator theOperator) {
         assert(theOperator instanceof ListExpressionOperator);
         super.setOperator(theOperator);
-        ((ListExpressionOperator)theOperator).setNumberOfItems(0);
     }
 
     /**
@@ -104,7 +103,6 @@
 
     @Override
     protected void postCopyIn(Map alreadyDone) {
-        ((ListExpressionOperator)operator).setNumberOfItems(0);
         Boolean hasLastChildCopy = hasLastChild;
         hasLastChild = null;
         super.postCopyIn(alreadyDone);
@@ -117,7 +115,6 @@
     @Override
     public void initializePlatformOperator(DatabasePlatform platform) {
         super.initializePlatformOperator(platform);
-        ((ListExpressionOperator)platformOperator).setNumberOfItems(((ListExpressionOperator)operator).getNumberOfItems());
     }
 
 
diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ExpressionOperatorConverter.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ExpressionOperatorConverter.java
index 72d4734..b268ad5 100644
--- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ExpressionOperatorConverter.java
+++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/internal/expressions/ExpressionOperatorConverter.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 1998, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021 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
@@ -43,10 +44,11 @@
         } else {
             fieldValue = getAttributeToFieldValues().get(attributeValue);
             if (fieldValue == null) {
-                //Custom function.  Remove "(".
-                if (((ExpressionOperator)attributeValue).getDatabaseStrings() != null) {
-                    String databaseString = ((ExpressionOperator)attributeValue).getDatabaseStrings()[0];
-                    fieldValue = databaseString.substring(0, databaseString.length()-1);
+                // Custom function. Remove "(".
+                String[] dbStrings = ((ExpressionOperator)attributeValue).getDatabaseStrings();
+                if (dbStrings != null) {
+                    String databaseString = dbStrings[0];
+                    fieldValue = databaseString.substring(0, databaseString.length() - 1);
                 } else {
                     throw DescriptorException.noAttributeValueConversionToFieldValueProvided(attributeValue, getMapping());
                 }
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/TestConcurrencyPersistence.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/TestConcurrencyPersistence.java
index 11070fd..e22a34e 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/TestConcurrencyPersistence.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/TestConcurrencyPersistence.java
@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * Copyright (c) 2018, 2021 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
@@ -19,7 +19,10 @@
 import java.util.Queue;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import jakarta.persistence.EntityManager;
 import jakarta.persistence.EntityManagerFactory;
@@ -75,7 +78,51 @@
         }
 
         Assert.assertTrue(errors.toString(), errors.isEmpty());
+    }
 
+    /**
+     * Bug 463042: Executing the same query simultaneously on separate threads has the possibility of
+     * causing an ArrayOutOfBoundsException to be thrown. This test spins up multiple threads, executes
+     * the same query on each and validates that none of the threads failed.
+     * 
+     * @throws Exception
+     */
+    @Test
+    public void testCaseExpressionOperatorConcurrency() throws Exception {
+        final AtomicInteger count = new AtomicInteger();
+        final AtomicInteger error = new AtomicInteger();
+
+        final int threads = 100;
+        final ExecutorService taskExecutor = Executors.newFixedThreadPool(threads);
+
+        // Spawn 100 threads
+        for (int i = 0; i < threads; i++) {
+            taskExecutor.execute(new Runnable() {
+                public void run() {
+                    count.incrementAndGet();
+
+                    final EntityManager em = emf.createEntityManager();
+                    try {
+                        // Executing the Query
+                        em.createNamedQuery("CONCURR_CASE_QUERY", Integer.class).setParameter("id", 1).getSingleResult();
+                    } catch (Exception e) {
+                        error.incrementAndGet();
+                        System.out.println(e.getMessage());
+                    } finally {
+                        if (em != null) {
+                            if (em.getTransaction().isActive()) {
+                                em.getTransaction().rollback();
+                            }
+                            em.close();
+                        }
+                    }
+                }
+            });
+        }
+        taskExecutor.shutdown();
+        taskExecutor.awaitTermination(5, TimeUnit.SECONDS);
+
+        Assert.assertEquals("Expected no failures, but " + error.intValue() + "/" + count.intValue() + " threads failed", 0, error.intValue());
     }
 
     /**
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/model/User.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/model/User.java
index d349b12..bbf775a 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/model/User.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/concurrency/model/User.java
@@ -1,6 +1,6 @@
 /*
- * Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018 IBM Corporation. All rights reserved.
+ * Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2021 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
@@ -25,10 +25,12 @@
 import jakarta.persistence.FetchType;
 import jakarta.persistence.Id;
 import jakarta.persistence.JoinColumn;
+import jakarta.persistence.NamedQuery;
 import jakarta.persistence.Table;
 
 @Entity
 @Table(name="CONCURR_USER")
+@NamedQuery(name = "CONCURR_CASE_QUERY", query = "SELECT CASE WHEN (COUNT(e) > 0) THEN true ELSE false END FROM User e WHERE e.id = :id")
 public class User {
 
     @Id private int id;
diff --git a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQueryCase.java b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQueryCase.java
index 3971db9..33dc427 100644
--- a/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQueryCase.java
+++ b/jpa/eclipselink.jpa.test.jse/src/it/java/org/eclipse/persistence/jpa/test/query/TestQueryCase.java
@@ -28,6 +28,7 @@
 import jakarta.persistence.criteria.ParameterExpression;
 import jakarta.persistence.criteria.Root;
 
+import org.eclipse.persistence.expressions.ExpressionOperator;
 import org.eclipse.persistence.jpa.test.framework.DDLGen;
 import org.eclipse.persistence.jpa.test.framework.Emf;
 import org.eclipse.persistence.jpa.test.framework.EmfRunner;
@@ -35,6 +36,7 @@
 import org.eclipse.persistence.jpa.test.query.model.Dto01;
 import org.eclipse.persistence.jpa.test.query.model.EntityTbl01;
 import org.eclipse.persistence.jpa.test.query.model.EntityTbl01_;
+import org.junit.Assert;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,6 +50,32 @@
     private static boolean POPULATED = false;
 
     @Test
+    public void testDefaultCaseConditionOperatorDatabaseStrings() {
+        ExpressionOperator caseConditionOp = ExpressionOperator.caseConditionStatement();
+
+        String[] databaseStrings = caseConditionOp.getDatabaseStrings(0);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(1);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(2);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " THEN ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(3);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " THEN ", " ELSE ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(4);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(5);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " ELSE ", " END "}, databaseStrings);
+
+        databaseStrings = caseConditionOp.getDatabaseStrings(6);
+        Assert.assertArrayEquals(new String[] {"CASE WHEN ", " THEN ", " WHEN ", " THEN ", " WHEN ", " THEN ", " END "}, databaseStrings);
+    }
+
+    @Test
     public void testQuery_JPQL_Case_Literals_1() {
         if (emf == null)
             return;