/*
 * Firebird Open Source J2ee connector - jdbc driver
 *
 * Distributable under LGPL license.
 * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * LGPL License for more details.
 *
 * This file was created by members of the firebird development team.
 * All individual contributions remain the Copyright (C) of those
 * individuals.  Contributors to this file are either listed here or
 * can be obtained from a CVS history command.
 *
 * All rights reserved.
 */
package org.firebirdsql.jdbc;

import java.sql.*;

import org.firebirdsql.common.FBTestBase;
import org.firebirdsql.gds.ISCConstants;
import org.firebirdsql.gds.TransactionParameterBuffer;

/**
 * Test cases for FirebirdConnection interface.
 * 
 * @author <a href="mailto:rrokytskyy@users.sourceforge.net">Roman Rokytskyy</a>
 */
public class TestFBConnection extends FBTestBase {
    
    public static final String CREATE_TABLE = ""
        + "CREATE TABLE test ("
        + "  col1 INTEGER"
        + ")"
        ;
    
    public static final String DROP_TABLE = ""
        + "DROP TABLE test"
        ;
    
    public static final String INSERT_DATA = ""
        + "INSERT INTO test(col1) VALUES(?)"
        ;

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

	protected void setUp() throws Exception {
		super.setUp();
        
        Connection connection = getConnectionViaDriverManager();
        try {
            Statement ddlStmt = connection.createStatement();
            try {
                try {
                    ddlStmt.execute(DROP_TABLE);
                } catch(SQLException ex) {
                    // ignore
                }
                
                ddlStmt.execute(CREATE_TABLE);
                
            } finally {
                ddlStmt.close();
            }
            
        } finally {
        	connection.close();
        }
        
	}

	protected void tearDown() throws Exception {
        
        Connection connection = getConnectionViaDriverManager();
        try {
            Statement ddlStmt = connection.createStatement();
            try {
                ddlStmt.execute(DROP_TABLE);
            } finally {
                ddlStmt.close();
            }
        } finally {
            connection.close();
        }
        
        super.tearDown();
    }
    
    /**
     * Test if {@link FirebirdConnection#setTransactionParameters(int, int[])}
     * method works correctly.
     * 
     * @throws Exception if something went wrong.
     */
    public void testTpbMapping() throws Exception {
        
        Connection conA = getConnectionViaDriverManager();
        try {
            conA.setAutoCommit(false);
            
            PreparedStatement ps = conA.prepareStatement(INSERT_DATA);
            ps.setInt(1, 1);
            ps.execute();
            ps.close();
            
            conA.commit();
            
            Connection conB = getConnectionViaDriverManager();
            try {
                conB.setAutoCommit(false);

                /*
                
                // This is correct way to set transaction parameters
                // However, we use deprecated methods to check the
                // backward compatibility
                
                TransactionParameterBuffer tpb = ((FirebirdConnection)conB).createTransactionParameterBuffer();
                tpb.addArgument(TransactionParameterBuffer.READ_COMMITTED);
                tpb.addArgument(TransactionParameterBuffer.REC_VERSION);
                tpb.addArgument(TransactionParameterBuffer.WRITE);
                tpb.addArgument(TransactionParameterBuffer.NOWAIT);
                
                ((FirebirdConnection)conB).setTransactionParameters(tpb);
                */
                
                ((FirebirdConnection)conB).setTransactionParameters(
                        Connection.TRANSACTION_READ_COMMITTED,
                        new int[] {
                        	FirebirdConnection.TPB_READ_COMMITTED,
                            FirebirdConnection.TPB_REC_VERSION,
                            FirebirdConnection.TPB_WRITE,
                            FirebirdConnection.TPB_NOWAIT
                        });
                conB.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                
                Statement stmtA = conA.createStatement();
                Statement stmtB = conB.createStatement();
                
                stmtA.execute("UPDATE test SET col1 = 2");
                try {
                	stmtB.execute("UPDATE test SET col1 = 3");
                    fail("Should notify about a deadlock.");
                } catch(SQLException ex) {
                	// everything is fine
                }
                
                stmtA.close();
                stmtB.close();
                
            } finally {
                conB.close();
            }
            
        } finally {
            conA.close();
        }
    }
    

    public void testStatementCompletion() throws Exception {
        Connection connection = getConnectionViaDriverManager();
        try {
            connection.setAutoCommit(false);
            Statement stmt = connection.createStatement();
            try {
                stmt.executeQuery("SELECT * FROM rdb$database");
                connection.rollback();
                connection.setAutoCommit(true);
            } finally {
                stmt.close();
            }
            
            stmt = connection.createStatement();
            try {
                stmt.executeQuery("SELECT * FROM rdb$database");
                stmt.executeQuery("SELECT * FROM rdb$database");
            } finally {
                stmt.close();
            }
        } finally {
            connection.close();
        }
    }
    
    public void testExecuteStatementTwice() throws Exception {
        Connection connection = getConnectionViaDriverManager();
        
        Statement ddlStmt = connection.createStatement();
        try {
            try {
                ddlStmt.execute("DROP TABLE test_exec_twice");
            } catch(SQLException ex) {
                // ignore
            }
            
            ddlStmt.execute("CREATE TABLE test_exec_twice(col1 VARCHAR(100))");
        } finally {
            ddlStmt.close();
        }
        
        try {
            connection.setAutoCommit(false);
            connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            
            String select1 = "SELECT * FROM test_exec_twice";
            String select2 = select1 + " WHERE col1 > ? ORDER BY col1";

            PreparedStatement pstmt = connection.prepareStatement(select2);

            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(select1);  // throws Exception on the 2nd call
            rs.close();

            pstmt.setString(1, "ABC");
            rs = pstmt.executeQuery();
            for (int i = 0; i < 10 && rs.next(); i++)
                ;   // do something
            rs.close();

            // on the following 2nd call the exception gets thrown
            rs = stmt.executeQuery(select1);  // throws Exception on the 2nd call
            rs.close();

            pstmt.setString(1, "ABC");
            rs = pstmt.executeQuery();
            for (int i = 0; i < 10 && rs.next(); i++)
                ;   // do something
            rs.close();

            
        } finally {
            connection.commit();
            connection.close();
        }
    }
    
    public void testLockTable() throws Exception {
        FirebirdConnection connection = 
            (FirebirdConnection)getConnectionViaDriverManager();
        
        try {
            Statement stmt = connection.createStatement();
            try {
                stmt.execute("CREATE TABLE test_lock(col1 INTEGER)");
            } catch(SQLException ex) {
                // ignore
            }
        } finally {
            connection.close();
        }

        connection = (FirebirdConnection)getConnectionViaDriverManager();
        try {
            
            Statement stmt = connection.createStatement();
            try {
            
                TransactionParameterBuffer tpb = 
                    connection.getTransactionParameters(Connection.TRANSACTION_READ_COMMITTED);
                
                tpb.removeArgument(TransactionParameterBuffer.WAIT);
                tpb.addArgument(TransactionParameterBuffer.NOWAIT);
                
                connection.setTransactionParameters(Connection.TRANSACTION_READ_COMMITTED, tpb);
                connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
                
                connection.setAutoCommit(false);
                
                FirebirdConnection anotherConnection = 
                    (FirebirdConnection)getConnectionViaDriverManager();
                anotherConnection.setAutoCommit(false);
                
                try {
                    TransactionParameterBuffer anotherTpb = 
                        anotherConnection.createTransactionParameterBuffer();
                    
                    anotherTpb.addArgument(TransactionParameterBuffer.CONSISTENCY);
                    anotherTpb.addArgument(TransactionParameterBuffer.WRITE);
                    anotherTpb.addArgument(TransactionParameterBuffer.NOWAIT);
                    
                    anotherTpb.addArgument(TransactionParameterBuffer.PROTECTED);
                    anotherTpb.addArgument(TransactionParameterBuffer.LOCK_WRITE, "TEST_LOCK");
                    
                    anotherConnection.setTransactionParameters(anotherTpb);
                    
                    Statement anotherStmt = anotherConnection.createStatement();
                    try {
                        anotherStmt.execute("INSERT INTO test_lock VALUES(1)");
                    } finally {
                        anotherStmt.close();
                    }
                    
                    try {
                        stmt.execute("INSERT INTO test_lock VALUES(2)");
                        fail("Should throw an error because of lock conflict.");
                    } catch(SQLException ex) {
                        assertEquals(ISCConstants.isc_lock_conflict, ex.getErrorCode());
                    }
                    
                } finally {
                    anotherConnection.close();
                }
            
            } finally {
                stmt.close();
            }
        } finally {
            connection.close();
        }
    }
    
    public void testMetaDataTransaction() throws Exception {
        Connection connection = getConnectionViaDriverManager();
        try {
            connection.setAutoCommit(true);
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet rs = metaData.getTables(null, null, "RDB$DATABASE", null);
            rs.close();
        } finally {
            connection.close();
        }
    }
    
    public void testTransactionCoordinatorAutoCommitChange() throws Exception {
        
        Connection connection = getConnectionViaDriverManager();
        try {
            
            PreparedStatement ps = connection.prepareStatement(
                "SELECT * FROM rdb$database");
            
            try {
                connection.setAutoCommit(false);
            } finally {
                ps.close();
            }
            
        } finally {
            connection.close();
        }
        
    }
}
