/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openjpa.persistence.identity;

import java.sql.Connection;
import java.util.List;

import jakarta.persistence.EntityManager;

import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.test.DatabasePlatform;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;

/**
 * Test that compound identity can consists of null component column value.
 *
 * This test uses pre-defined database tables created by DDL explicitly.
 * The tables have <em>logical</em> compound primary keys in the sense non-null
 * constraint is <em>not</em> set on the primary columns. The tables are populated
 * with SQL to contain null values in these columns.
 * The test verifies that results are returned as par expectation.
 * For more details, refer
 * <A href="https://issues.apache.org/jira/browse/OPENJPA-1397">JIRA-1397</A>
 *
 * @author Pinaki Poddar
 * @author Michael Vorburger
 */

@DatabasePlatform("org.apache.derby.jdbc.EmbeddedDriver")
public class TestCompundIdWithNull extends SingleEMFTestCase {
    private static boolean tablesCreated = false;

    @Override
    public void setUp() throws Exception {
        // Only run on Derby because we use DDL specific to Derby
        setSupportedDatabases(
            org.apache.openjpa.jdbc.sql.DerbyDictionary.class);
        if (isTestsDisabled()) {
            return;
        }

        // do not use CLEAR_TABLES or DROP_TABLES
        super.setUp(SimpleCompoundIdTestEntity.class, ComplexCompoundIdTestEntity.class, TypeEntity.class);
        if (!tablesCreated) {
            createTables(emf.createEntityManager());
            tablesCreated = true;
        }
    }

    public void testSimpleCompoundIdTestEntity() throws Exception  {
            EntityManager em = emf.createEntityManager();
            String jpql = "SELECT o FROM SimpleCompoundIdTestEntity o ORDER BY o.secondId";
            List<SimpleCompoundIdTestEntity> list = em.createQuery(jpql,SimpleCompoundIdTestEntity.class)
                .getResultList();
            assertEquals(2, list.size());
            assertEquals(Long.valueOf(123), list.get(0).getSecondId());

            SimpleCompoundIdTestEntity secondResult = list.get(1);
            assertNotNull("BUG (JIRA-1397)! Result list contains null in second element", secondResult);
            assertNull(secondResult.getSecondId());
            em.close();
    }


    public void testComplexCompoundIdTestEntity() throws Exception  {
        EntityManager em = emf.createEntityManager();
        String jpql = "SELECT o FROM ComplexCompoundIdTestEntity o ORDER BY o.type";
        List<ComplexCompoundIdTestEntity> list = em.createQuery(jpql,ComplexCompoundIdTestEntity.class)
            .getResultList();
        assertEquals(2, list.size());
        ComplexCompoundIdTestEntity secondResult = list.get(1);
        assertNotNull("Result list contains null in second element", secondResult);
        assertNull("Result list's second record secondId field was not null", secondResult.getType());
        em.close();
    }

    /**
     * Create tables with logical compound keys without non-null constraint.
     * Populate them with null values in some of the columns.
     */
    private void createTables(EntityManager em) throws Exception {
        em.getTransaction().begin();
        OpenJPAEntityManager kem = OpenJPAPersistence.cast(em);

        Connection conn = (Connection) kem.getConnection();
        // NOTE that 'logically' test_simple has a ", CONSTRAINT test_simple_pk PRIMARY KEY (firstId, secondId)",
        // but at least Derby doesn't permit NULL then.. in our real-world underlying schema that leads
        // to this there are *NO* PRIMARY KEY on any tables, but there is a logical model expressed
        // elsewhere stating that those two columns uniquely identify a row.
        try {
            conn.createStatement().execute("DROP TABLE test_type");
            conn.createStatement().execute("DROP TABLE test_simple");
            conn.createStatement().execute("DROP TABLE test_complex");
        } catch (Exception e) {
        }

        conn.createStatement().execute("CREATE TABLE test_simple(firstId NUMERIC, secondId NUMERIC)");
        conn.createStatement().execute("INSERT INTO test_simple(firstId, secondId) VALUES (1, 123)");
        conn.createStatement().execute("INSERT INTO test_simple(firstId, secondId) VALUES (1, NULL)");

        conn.createStatement().execute("CREATE TABLE test_type(id NUMERIC CONSTRAINT test_type_pk PRIMARY KEY, " +
                "code VARCHAR(16))");
        conn.createStatement().execute("INSERT INTO test_type(id, code) VALUES (987, 'ABC')");

        conn.createStatement().execute("CREATE TABLE test_complex(id NUMERIC, type_id NUMERIC)");
        conn.createStatement().execute("INSERT INTO test_complex(id, type_id) VALUES (1, 987)");
        conn.createStatement().execute("INSERT INTO test_complex(id, type_id) VALUES (1, NULL)");

        conn.close();

        em.flush();
        em.getTransaction().commit();
        em.close();
    }

}
