/*******************************************************************************
 * Copyright (c) 1998, 2015 Oracle and/or its affiliates. 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:
 *     Oracle - initial API and implementation from Oracle TopLink
 ******************************************************************************/
package org.eclipse.persistence.testing.tests.jpa.jpql;

import java.io.EOFException;
import java.util.List;

import javax.persistence.NoResultException;
import javax.persistence.OptimisticLockException;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import javax.persistence.RollbackException;
import javax.persistence.TransactionRequiredException;
import javax.persistence.Query;
import javax.persistence.EntityManager;

import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;

import org.eclipse.persistence.config.QueryHints;
import org.eclipse.persistence.exceptions.*;
import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform;
import org.eclipse.persistence.internal.jpa.EntityManagerImpl;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.testing.models.jpa.advanced.Employee;
import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCaseHelper;

import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.testing.models.jpa.advanced.AdvancedTableCreator;

/**
 * <p>
 * <b>Purpose</b>: Test EJBQL exceptions.
 * <p>
 * <b>Description</b>: This class creates a test suite, initializes the database
 * and adds tests to the suite.
 * <p>
 * <b>Responsibilities</b>:
 * <ul>
 * <li> Run tests for expected EJBQL exceptions thrown
 * </ul>
 * @see org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator
 * @see JUnitDomainObjectComparer
 */

public class JUnitJPQLValidationTestSuite extends JUnitTestCase
{
    static JUnitDomainObjectComparer comparer;        //the global comparer object used in all tests

    public JUnitJPQLValidationTestSuite()
    {
        super();
    }

    public JUnitJPQLValidationTestSuite(String name)
    {
        super(name);
    }

    //This method is run at the end of EVERY test case method
    public void tearDown()
    {
        clearCache();
    }
    //This suite contains all tests contained in this class
    public static Test suite()
    {
        TestSuite suite = new TestSuite();
        suite.setName("JUnitJPQLValidationTestSuite");
        suite.addTest(new JUnitJPQLValidationTestSuite("testSetup"));
        suite.addTest(new JUnitJPQLValidationTestSuite("generalExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("recognitionExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("missingSelectExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest1"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest2"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest3"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest4"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest5"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest6"));
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest7"));
        //gf1166
        suite.addTest(new JUnitJPQLValidationTestSuite("malformedJPQLExceptionTest8"));
        suite.addTest(new JUnitJPQLValidationTestSuite("noAliasWithWHEREAndParameterExceptionTest"));
        //suite.addTest(new JUnitJPQLValidationTestSuite("unknownAbstractSchemaTypeTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("multipleDeclarationOfIdentificationVariable"));
        suite.addTest(new JUnitJPQLValidationTestSuite("aliasResolutionException"));
        suite.addTest(new JUnitJPQLValidationTestSuite("illegalArgumentExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("createNamedQueryThrowsIllegalArgumentExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("flushTxExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testExecuteUpdateTxException"));
        suite.addTest(new JUnitJPQLValidationTestSuite("noResultExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testGetSingleResultOnUpdate"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testGetSingleResultOnDelete"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testExecuteUpdateOnSelect"));
        suite.addTest(new JUnitJPQLValidationTestSuite("flushOptimisticLockExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("commitOptimisticLockExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("JTAOptimisticLockExceptionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testParameterNameValidation"));
        // JPQL should not validate floats, database may allow (could be 1.0 and valid, or db could truncate)
        //suite.addTest(new JUnitJPQLValidationTestSuite("testModArgumentValidation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testInExpressionValidation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testOrderableTypeInOrderByItem"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testNonExistentOrderByAlias"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidNavigation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidCollectionNavigation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testUnknownAttribute"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testUnknownEnumConstant"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testCommitRollbackException"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testParameterPositionValidation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testParameterPositionValidation2"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testParameterTypeValidation"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testEjbqlCaseSensitivity"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testEjbqlUnsupportJoinArgument"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidSetClause"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testUnsupportedCountDistinctOnOuterJoinedCompositePK"));
        suite.addTest(new JUnitJPQLValidationTestSuite("testInvalidHint"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidCharTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidOnClauseTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidSQLExpressionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidColumnExpressionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidFunctionExpressionTest"));
        suite.addTest(new JUnitJPQLValidationTestSuite("invalidOperatorExpressionTest"));

        return suite;
    }

    /**
     * The setup is done as a test, both to record its failure, and to allow execution in the server.
     */
    public void testSetup() {
        clearCache();
        //get session to start setup
        DatabaseSession session = JUnitTestCase.getServerSession();

        //create a new EmployeePopulator
        EmployeePopulator employeePopulator = new EmployeePopulator();

        new AdvancedTableCreator().replaceTables(session);

        //initialize the global comparer object
        comparer = new JUnitDomainObjectComparer();

        //set the session for the comparer to use
        comparer.setSession((AbstractSession)session.getActiveSession());

        //Populate the tables
        employeePopulator.buildExamples();

        //Persist the examples in the database
        employeePopulator.persistExample(session);
    }

    public void ensureInvalid(String jpql)
    {
        try {
            createEntityManager().createQuery(jpql).getResultList();
            fail("Illegal Argument Exception must be thrown");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void illegalArgumentExceptionTest() {
        ensureInvalid("SELECT FROM EMPLOYEE emp");
    }


    public void generalExceptionTest() {
        ensureInvalid("SELECT FROM EMPLOYEE emp");
    }

    public void recognitionExceptionTest() {
        ensureInvalid("SELECT OBJECT(emp) FROW Employee emp");
    }

    public void invalidCharTest() {
        ensureInvalid("Select !e from Employee e where ! e = e");
    }

    public void invalidOnClauseTest() {
        if (!isHermesParser()) {
            warning("invalidOnClauseTest only works with Hermes");
            return;
        }
        ensureInvalid("Select e from Employee e on e.id = 5");
        ensureInvalid("Select e from Employee e on");
        ensureInvalid("Select e from Employee e like");
        ensureInvalid("Select e from Employee e upper");
        ensureInvalid("Select e from Employee e case");
        ensureInvalid("Select e from Employee e select");
    }

    public void invalidSQLExpressionTest() {
        if (!isHermesParser()) {
            warning("invalidSQLExpressionTest only works with Hermes");
            return;
        }
        ensureInvalid("Select e from Employee e where sql");
        ensureInvalid("Select e from Employee e where sql(");
        ensureInvalid("Select e from Employee e where sql()");
        ensureInvalid("Select e from Employee e where sql(')");
    }

    public void invalidColumnExpressionTest() {
        if (!isHermesParser()) {
            warning("invalidColumnExpressionTest only works with Hermes");
            return;
        }
        ensureInvalid("Select e from Employee e where column");
        ensureInvalid("Select e from Employee e where column(");
        ensureInvalid("Select e from Employee e where column()");
        ensureInvalid("Select e from Employee e where column(')");
        ensureInvalid("Select e from Employee e where column('foo')");
        ensureInvalid("Select e from Employee e where column('foo', e.id, e.id)");
        ensureInvalid("Select e from Employee e where column('foo', 5)");
    }

    public void invalidOperatorExpressionTest() {
        if (!isHermesParser()) {
            warning("invalidColumnExpressionTest only works with Hermes");
            return;
        }
        ensureInvalid("Select e from Employee e where operator");
        ensureInvalid("Select e from Employee e where operator(");
        ensureInvalid("Select e from Employee e where operator()");
        ensureInvalid("Select e from Employee e where operator(')");
    }

    public void invalidFunctionExpressionTest() {
        if (!isHermesParser()) {
            warning("invalidFunctionExpressionTest only works with Hermes");
            return;
        }
        ensureInvalid("Select e from Employee e where function");
        ensureInvalid("Select e from Employee e where function(");
        ensureInvalid("Select e from Employee e where function()");
        ensureInvalid("Select e from Employee e where function(')");
    }

   public void missingSelectExceptionTest() {
       ensureInvalid("OBJECT(emp) FROM Employee emp");
   }


   public void malformedJPQLExceptionTest1()
   {

        String ejbqlString =  "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName == \"F";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Recognition Exception must be thrown");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
   }

   public void malformedJPQLExceptionTest2()
   {

        String ejbqlString =  "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = \"Fred\" AND 1";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Recognition Exception must be thrown");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
   }

    public void malformedJPQLExceptionTest3()
    {

        String ejbqlString =  "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = \"Fred\" OR \"Freda\"";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Recognition Exception must be thrown");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void malformedJPQLExceptionTest4()
    {

        String ejbqlString =  "SLEECT OBJECT(emp) FROM Employee emp WHERE emp.firstName = \"Fred\" OR \"Freda\"";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Recognition Exception must be thrown");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

    }

    public void malformedJPQLExceptionTest5()
    {

        String ejbqlString =  "SELECT c FORM Customer c";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword FORM");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT COUNT(c FROM Customer c";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword FROM");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c* FROM Customer c";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword *");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void malformedJPQLExceptionTest6()
    {

        /*String ejbqlString =  "SELECT c FROM Customer c WHERE c.name LIKE 1";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword 1");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertEquals(JPQLException.unexpectedToken, ((JPQLException) ex.getCause()).getErrorCode());
            assertTrue("Failed to throw expected IllegalArgumentException for a query having an unexpected keyword 1.", ex.getCause().getMessage().contains("unexpected token [1]"));
        }*/

        String ejbqlString =  "SELECT c FROM Customer c WHERE c.name is not nall";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword nall");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c FROM Customer c WHERE c.name is net null";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword net");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c FROM Customer c WHERE c.name is EMPYT";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword EMPYT");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c FROM Customer c WHERE c.name in 3.5";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword 3.5");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c FROM Customer c WHERE c.name MEMBER 6";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword 6");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        ejbqlString =  "SELECT c FROM Customer c WHERE c.name NOT BETEEN 6 and 7";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query using invalid keyword BETEEN");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

    }

    public void malformedJPQLExceptionTest7()
    {

        String ejbqlString =  "SELECT e FROM";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Expected unexpected end of query exception.");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

    }

    //gf1166  Wrap ANTLRException inside JPQLException
    public void malformedJPQLExceptionTest8()
    {

        String ejbqlString =  "SELECT e FROM";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Expected unexpected end of query exception.");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

    }

    public void noAliasWithWHEREAndParameterExceptionTest()
    {

        String ejbqlString =  "FROM Employee WHERE firstName = ?1";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Recognition Exception must be thrown");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void aliasResolutionException()
    {
        String ejbqlString = null;

        try {
            // invalid identification variable in WHERE clause
            ejbqlString = "SELECT employee FROM Employee employee WHERE emp.firstName = 'Fred'";
            createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "using an invalid identification variable in the WHERE clause.");
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        try {
            // invalid identification variable in SELECT clause
            ejbqlString = "SELECT OBJECT(nullRoot) FROM Employee emp";
            createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "selecting an invalid identification variable.");
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        try {
            // invalid identification variable in JOIN clause
            ejbqlString = "SELECT emp FROM Employee emp JOIN e.projects p WHERE p.name = 'Enterprise'";
            createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "using an invalid identification variable in a JOIN clause.");
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void unknownAbstractSchemaTypeTest()
    {
        String ejbqlString =  " SELECT OBJECT(i) FROM Integer i WHERE i.city = \"Ottawa\"";

        try
        {
            List result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Missing exception for query using unknown abstract schema type");
        }

        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void multipleDeclarationOfIdentificationVariable()
    {
        String ejbqlString;
        List result;

        try
        {
            ejbqlString = "SELECT o FROM Order o, Customer o";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail("Multiple declaration of identification variable must be thrown");
        }
        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        if (isHermesParser()) {
            // This should be valid now.
            ejbqlString = "SELECT c FROM Customer c Join c.orders o WHERE NOT EXISTS (SELECT o FROM c.orders o)";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
        }
    }

    public void testParameterNameValidation(){
        EntityManager em = this.createEntityManager();
        Query query = em.createQuery("Select e from Employee e where e.lastName like :name ");
        try{
            query.setParameter("l", "%ay");
            query.getResultList();
        }catch (IllegalArgumentException ex){
            assertTrue("Failed to throw expected IllegalArgumentException, when incorrect parameter name is used", ex.getMessage().contains("using a name"));
            return;
        }
        fail("Failed to throw expected IllegalArgumentException, when incorrect parameter name is used");
    }

    public void testParameterPositionValidation(){
        EntityManager em = this.createEntityManager();
        Query query = em.createQuery("Select e from Employee e where e.firstName like ?1 ");
        try{
            query.setParameter(2, "%ay");
            query.getResultList();
        }catch (IllegalArgumentException ex){
            assertTrue("Failed to throw expected IllegalArgumentException, when incorrect parameter name is used", ex.getMessage().contains("parameter at position"));
            return;
        }
        fail("Failed to throw expected IllegalArgumentException, when incorrect parameter position is used");
    }

    public void testParameterPositionValidation2() {

        EntityManager em = this.createEntityManager();
        Query query = em.createQuery("Select e from Employee e where e.firstName = ?1 AND e.lastName = ?3 ");
        try {
            query.setParameter(1, "foo");
            query.setParameter(2, "");
            query.setParameter(3, "bar");
            query.getResultList();
        } catch (IllegalArgumentException ex) {
            assertTrue("Failed to throw expected IllegalArgumentException, when incorrect parameter name is used", ex.getMessage().contains("parameter at position"));
            return;
        }
        fail("Failed to throw expected IllegalArgumentException, when incorrect parameter position is used");
    }

    public void testParameterTypeValidation() {
        EntityManager em = this.createEntityManager();
        Query query = em.createQuery("Select e from Employee e where e.firstName = :fname AND e.lastName = :lname ");
        try {
            query.setParameter("fname", "foo");
            query.setParameter("lname", new Integer(1));
            query.getResultList();
        } catch (IllegalArgumentException ex) {
            assertTrue("Failed to throw expected IllegalArgumentException, when parameter with incorrect type is used", ex.getMessage().contains("attempted to set a value of type"));
            return;
        }
        fail("Failed to throw expected IllegalArgumentException, when parameter with incorrect type is used");
    }

    public void testModArgumentValidation()
    {
        //Assert.assertFalse("Warning SQL/Sybase doesnot support MOD function",  JUnitTestCase.getServerSession().getPlatform().isSQLServer() || JUnitTestCase.getServerSession().getPlatform().isSybase() || JUnitTestCase.getServerSession().getPlatform().isSybase());

        String ejbqlString;
        List result;

        try
        {
            ejbqlString = "SELECT p FROM LargeProject p WHERE MOD(p.budget, 10) = 5";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            //fail("wrong data type for MOD function must be thrown");
        }
        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        try
        {
            ejbqlString = "SELECT p FROM LargeProject p WHERE MOD(10, p.budget) = 5";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            //fail("wrong data type for MOD function must be thrown");
        }
        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testInExpressionValidation()
    {
        String ejbqlString;
        List result;

        try {
            ejbqlString = "SELECT e FROM Employee e WHERE e.firstName IN (1, 2)";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            //fail("wrong type for IN expression exception must be thrown");
        }
        catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testOrderableTypeInOrderByItem() {
        EntityManager em = this.createEntityManager();

        if (isHermesParser()) {
            // This is now supported, orders by the address foreign key.
            Query query = em.createQuery("SELECT e FROM Employee e ORDER BY e.address");
            query.getResultList();
        }
    }

    public void testNonExistentOrderByAlias() {
        EntityManager em = this.createEntityManager();
        try {
            Query query = em.createQuery("SELECT e FROM Employee e ORDER BY firstName");
            query.getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query having an ORDER BY item with a non-existent alias");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testInvalidNavigation() {
        EntityManager em = this.createEntityManager();
        try {
            em.createQuery("SELECT e.firstName.invalid FROM Employee e").getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "navigating a state field of type String in the SELECT clause.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
        try {
            em.createQuery("SELECT e FROM Employee e WHERE e.firstName.invalid = 1").getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "navigating a state field of type String in the WHERE clause.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testInvalidCollectionNavigation() {
        EntityManager em = this.createEntityManager();
        try {
            String jpql = "SELECT e.phoneNumbers.type FROM Employee e";
            em.createQuery(jpql).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "navigating a collection valued association field in the SELECT clause.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
        try {
            String jpql =
                "SELECT e FROM Employee e WHERE e.phoneNumbers.type = 'Work'";
            em.createQuery(jpql).getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "navigating a collection valued association field in the WHERE clause.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testInvalidHint() {
        EntityManager em = createEntityManager();
        try {
            String jpql = "SELECT e.phoneNumbers.type FROM Employee e";
            Query query = em.createQuery(jpql);
            query.setHint(QueryHints.BATCH, "e.phoneNumbers");
            query.getResultList();
            fail("Failed to throw expected IllegalArgumentException for invalid query hint.");
        } catch (IllegalArgumentException ex) {
            // Expected.
        }
        try {
            String jpql = "SELECT e FROM Employee e";
            Query query = em.createQuery(jpql);
            query.setHint(QueryHints.BATCH, "e.phoneNumbers.areaCode");
            query.getResultList();
            fail("Failed to throw expected IllegalArgumentException for invalid query hint.");
        } catch (QueryException ex) {
            // Expected.
        }
        try {
            String jpql = "SELECT e FROM Employee e";
            Query query = em.createQuery(jpql);
            query.setHint(QueryHints.CACHE_USAGE, "foobar");
            query.getResultList();
            fail("Failed to throw expected IllegalArgumentException for invalid query hint.");
        } catch (IllegalArgumentException ex) {
            // Expected.
        }
        closeEntityManager(em);
    }

    public void testUnknownAttribute() {
        EntityManager em = this.createEntityManager();
        try {
            em.createQuery("SELECT e.unknown FROM Employee e").getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "selecting an unknown state or association field.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
        try {
            em.createQuery("SELECT e FROM Employee e WHERE e.unknown = 1").getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query " +
                 "using an unknown state or association field in the WHERE clause.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testUnknownEnumConstant() {
        EntityManager em = this.createEntityManager();
        try {
            //em.createQuery("SELECT e FROM Employee e WHERE e.status = EmployeeStatus.FULL_TIME");
            Query query = em.createQuery("SELECT e FROM Employee e WHERE e.status = EmployeeStatus.FULL_TIME");
            query.getResultList();
            fail("Failed to throw expected IllegalArgumentException for a query"+
                "unknown enumerated class constant.");
        } catch (IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

  /**
   * For this test you need to add a persistence unit named default1 in the persistence.xml file
   * in essentials_testmodels.jar.
   */
    public void flushOptimisticLockExceptionTest()
    {
        if (isOnServer()) {
            // Multi-persistece-unit not work on server.
            return;
        }
        EntityManager firstEm = createEntityManager();
        EntityManager secondEm = createAlternateEntityManager();

        String ejbqlString = "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName='Bob' ";

        secondEm.getTransaction().begin();
        try{
            firstEm.getTransaction().begin();
            try{
                Employee firstEmployee = (Employee) firstEm.createQuery(ejbqlString).getSingleResult();
                firstEmployee.setLastName("test");

                Employee secondEmployee = (Employee) secondEm.createQuery(ejbqlString).getSingleResult();
                secondEmployee.setLastName("test");

                firstEm.flush();
                firstEm.getTransaction().commit();
            }catch (RuntimeException ex){
                if (firstEm.getTransaction().isActive()){
                    firstEm.getTransaction().rollback();
                }
                firstEm.close();
                throw ex;
            }
            secondEm.flush();
            fail("javax.persistence.OptimisticLockException must be thrown during flush");
        } catch (PersistenceException e) {
            if (secondEm.getTransaction().isActive()){
                secondEm.getTransaction().rollback();
            }
            secondEm.close();
            undoEmployeeChanges();
            if (isKnownMySQLIssue(e.getCause())) {
                warning("EOFException found on MySQL db.  This is a known problem with the MySQL Database");
            } else {
                Assert.assertTrue(e instanceof javax.persistence.OptimisticLockException);
            }
        }
    }


     /* For this test you need to add a persistence unit named default1 in the persistence.xml file
       in essentials_testmodels.jar */
    public void commitOptimisticLockExceptionTest()
    {
          if (isOnServer()) {
            // Multi-persistece-unit not work on server.
            return;
        }
        EntityManager firstEm = createEntityManager();
        EntityManager secondEm = createAlternateEntityManager();

        String ejbqlString = "SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName='Bob' ";

       secondEm.getTransaction().begin();
       try{
            firstEm.getTransaction().begin();
            try{

                Employee firstEmployee = (Employee) firstEm.createQuery(ejbqlString).getSingleResult();
                firstEmployee.setLastName("test");

                Employee secondEmployee = (Employee) secondEm.createQuery(ejbqlString).getSingleResult();
                secondEmployee.setLastName("test");

                firstEm.getTransaction().commit();
            }catch (RuntimeException ex){
                if (firstEm.getTransaction().isActive()){
                    firstEm.getTransaction().rollback();
                }
                firstEm.close();
                throw ex;
            }
            secondEm.getTransaction().commit();
        } catch (Exception e){
            if (secondEm.getTransaction().isActive()){
                secondEm.getTransaction().rollback();
            }
            secondEm.close();
            undoEmployeeChanges();
            if (isKnownMySQLIssue(e.getCause())) {
                warning("EOFException found on MySQL db.  This is a known problem with the MySQL Database");
            } else {
                Assert.assertTrue("Exception not instance of opt Lock exception: " + e.getCause(), e.getCause() instanceof javax.persistence.OptimisticLockException);
            }
            return;
        }
        fail("javax.persistence.OptimisticLockException must be thrown during commit");
    }

    public void JTAOptimisticLockExceptionTest()
    {
        // This test is skipped for Symfoware as Symfoware does not support SQL generated by this test.
        // Bug 381302 In Symfoware, a base table name to be updated cannot be identical to
        // table name in from clause in query or subquery specification or to table name
        // referenced by view table identified by table name.
        if ( JUnitTestCase.getServerSession().getPlatform().isSymfoware() ) {
            return;
        }

        EntityManager em = createEntityManager();
        try {
            beginTransaction(em);
            try {

                Employee emp = (Employee) em.createQuery("SELECT OBJECT(emp) FROM Employee emp WHERE emp.firstName='Bob' ").getSingleResult();
                em.createQuery("Update Employee set lastName = 'test-bad' WHERE firstName='Bob' ").executeUpdate();
                emp.setLastName("test");
                commitTransaction(em);
            } catch (RuntimeException ex) {
                closeEntityManagerAndTransaction(em);
                Throwable lockException = ex;
                while ((lockException != null) && !(lockException instanceof OptimisticLockException)) {
                    lockException = lockException.getCause();
                }
                if (lockException instanceof OptimisticLockException) {
                    return;
                }
                throw ex;
            }
            fail("Lock exception should have been thrown");
        } finally {
            clearCache();
        }
    }

    /**
     * This method is a temporary solution to avoid failures in our nightly testing.
     * This allows a warning to be printed when MySQL fails with a specific error.
     * This is a known error in the MySQL db, and this method will be removed
     * when this error is resolved.
     *
     * @param exception
     * @return true if this exception is a specific known MySQL failure
     */
    public boolean isKnownMySQLIssue(Throwable exception) {
        Throwable e1 = exception;
        if (exception == null) {
            return false;
        }

        if (!(exception instanceof javax.persistence.OptimisticLockException) &&
                JUnitTestCase.getServerSession().getPlatform().isMySQL()) {
            while(e1 != null) {
                if (e1 instanceof EOFException) {
                    //found it - return true
                    return true;
                }
                e1 = e1.getCause();
            }
        }
        return false;
    }

    public void flushTxExceptionTest()
    {
        try
        {
            createEntityManager().flush();
        }
        catch (TransactionRequiredException e) {
            // ok.
        }
    }

    public void testExecuteUpdateTxException()
    {
        boolean testPass=false;
        String ejbqlString = "DELETE FROM Employee e WHERE e.lastName=\"doesNotExist\"";

        EntityManager em = createEntityManager();
        try
        {
            Object result = em.createQuery(ejbqlString).executeUpdate();

            //rollback for clean-up if above call does not fail, otherwise this may affect other tests
            if(!isTransactionActive(em)){
                beginTransaction(em);
            }
            rollbackTransaction(em);
        }
        catch (TransactionRequiredException e)
        {
            testPass = true;
        }
        finally
        {
            closeEntityManager(em);
        }
        Assert.assertTrue("TransactionRequiredException is expected", testPass);
    }

    public void createNamedQueryThrowsIllegalArgumentExceptionTest()
    {
        try
        {
            List result = createEntityManager().createNamedQuery("test").getResultList();
        }
        catch (IllegalArgumentException e) {
            // ok.
        }
    }

    public void noResultExceptionTest()
    {
        String ejbqlString = "SELECT OBJECT (emp) FROM Employee emp WHERE emp.lastName=\"doestNotExist\" ";

        try
        {
            Object result = createEntityManager().createQuery(ejbqlString).getSingleResult();
        }
        catch (Exception e)
        {
            Assert.assertTrue(e instanceof NoResultException);
        }
    }

    public void testGetSingleResultOnUpdate()
    {
        boolean testPass=false;
        String ejbqlString = "UPDATE Employee e SET e.salary = (e.salary + 1000) WHERE e.lastName='Chanley' ";

        try
        {
            Object result = createEntityManager().createQuery(ejbqlString).getSingleResult();
        }
        catch (IllegalStateException e)
        {
            testPass = true;
        }
        Assert.assertTrue(testPass);
    }


    public void testGetSingleResultOnDelete()
    {
        boolean testPass=false;
        String ejbqlString = "DELETE FROM Employee e WHERE e.lastName='Chanley' ";

        try
        {
            Object result = createEntityManager().createQuery(ejbqlString).getSingleResult();
        }
        catch (IllegalStateException e)
        {
            testPass = true;
        }
        Assert.assertTrue(testPass);
    }

    public void testExecuteUpdateOnSelect()
    {
        boolean testPass=false;
        String ejbqlString = "SELECT emp FROM Employee emp  WHERE emp.lastName='Smith' ";

        EntityManager em = createEntityManager();
        try  {
            beginTransaction(em);
            em.createQuery(ejbqlString).executeUpdate();
            commitTransaction(em);
        } catch (IllegalStateException e) {
            testPass = true;
        } finally {
            closeEntityManagerAndTransaction(em);
        }
        Assert.assertTrue(testPass);
    }


    public void testCommitRollbackException()
    {
        if (isOnServer()) {
            // Cannot create parallel entity managers in the server.
            return;
        }
        EntityManager em = createEntityManager();
        String ejbqlString = "SELECT OBJECT (emp) FROM Employee emp WHERE emp.firstName='Bob'";
        DirectToFieldMapping idMapping = null;
        String defaultFieldName = "";
        beginTransaction(em);
        try{
            Employee emp = (Employee) em.createQuery(ejbqlString).getSingleResult();
            idMapping = (DirectToFieldMapping) (((EntityManagerImpl)em.getDelegate()).getServerSession()).getClassDescriptor(Employee.class).getMappingForAttributeName("id");
            defaultFieldName = idMapping.getFieldName();
            idMapping.setFieldName("fake_id");
            emp.setId(323);
            commitTransaction(em);
        } catch (Exception e) {
            if(isTransactionActive(em)){
                rollbackTransaction(em);
                closeEntityManager(em);
            }
            Assert.assertTrue(e instanceof RollbackException);
        } finally {
            em = createEntityManager();
            beginTransaction(em);
            try{
                idMapping.setFieldName(defaultFieldName);
                commitTransaction(em);
            } catch (Exception e) {
                if(isTransactionActive(em)){
                    rollbackTransaction(em);
                    closeEntityManager(em);
                }
            }
        }
    }

    //fix for bugID 4670705
    public void testEjbqlCaseSensitivity()
    {
        boolean testPass = true;
        EntityManager em = createEntityManager();
        String ejbqlString = "SELECT OBJECT (E) FROM Employee e";

        try {
            List result = em.createQuery(ejbqlString).getResultList();
        } catch (Exception e) {
            testPass = false;
        }
        Assert.assertTrue(testPass);
    }

    //this test resets the last name of the employee Bob that is changed in some tests
    public void undoEmployeeChanges()
    {
        EntityManager em = createEntityManager();

        beginTransaction(em);
        try{
            String ejbqlString = "SELECT OBJECT (emp) FROM Employee emp WHERE emp.firstName ='Bob' ";

            Employee emp = (Employee) em.createQuery(ejbqlString).getSingleResult();
            emp.setLastName("Smith");

            em.flush();
            commitTransaction(em);
        } catch (RuntimeException e) {
            if(isTransactionActive(em)){
                rollbackTransaction(em);
                closeEntityManager(em);
            }
        }
    }

    public void testEjbqlUnsupportJoinArgument() {
        String ejbqlString;
        List result;

        try
        {
            ejbqlString = "SELECT e.firstName FROM Employee e JOIN e.period ep";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail ("JOINing of embedded entities is not allowed must be thrown");
        } catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }

        try
        {
            ejbqlString = "SELECT e.firstName FROM Employee e JOIN FETCH e.period";
            result = createEntityManager().createQuery(ejbqlString).getResultList();
            fail ("JOINing of embedded entities is not allowed must be thrown");
        } catch(IllegalArgumentException ex)
        {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        }
    }

    public void testInvalidSetClause() {
        String ejbqlString;
        EntityManager em = createEntityManager();
        beginTransaction(em);
        try {
            ejbqlString = "UPDATE Employee e SET e.projects = NULL";
            em.createQuery(ejbqlString).executeUpdate();
            fail ("Failed to throw expected IllegalArgumentException for query " +
                    " updating a collection valued relationship.");
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        } finally {
            closeEntityManagerAndTransaction(em);
        }

        //above exception would have marked the transaction for rollback,
        //so the test needs to start a new one
        em = createEntityManager();
        beginTransaction(em);

        try
        {
            ejbqlString = "UPDATE Employee e SET e.department.name = 'CHANGED'";
            em.createQuery(ejbqlString).executeUpdate();
            fail ("Failed to throw expected IllegalArgumentException for query " +
                    " updating a sate field of a related instance.");
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        } finally {
            closeEntityManagerAndTransaction(em);
        }
    }

    public void testUnsupportedCountDistinctOnOuterJoinedCompositePK() {
        try  {
            String jpql = "SELECT COUNT(DISTINCT p) FROM Employee e LEFT JOIN e.phoneNumbers p GROUP BY e.lastName";
            createEntityManager().createQuery(jpql).getResultList();
            if (!((DatabasePlatform)getPlatform()).supportsCountDistinctWithMultipleFields()) {
                fail ("Failed to throw expected IllegalArgumentException for query " +
                      " having a COUNT DISTINCT on a joined variable with a composite primary key.");
            }
        } catch(IllegalArgumentException ex) {
            Assert.assertTrue(ex.getCause() instanceof JPQLException);
        } catch(QueryException ex) {
            // OK
        }
    }

    public static EntityManager createAlternateEntityManager() {
        return Persistence.createEntityManagerFactory("default1", JUnitTestCaseHelper.getDatabaseProperties()).createEntityManager();
    }
}
