package org.postgresql.test.jdbc2;

import org.postgresql.test.JDBC2Tests;
import junit.framework.TestCase;
import java.sql.*;

/**
 * Test case for Statement.batchExecute()
 */
public class BatchExecuteTest extends TestCase {

	private Connection con;
	private Statement stmt;

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

	// Set up the fixture for this testcase: a connection to a database with
	// a table for this test.
	protected void setUp() throws Exception {
		con = JDBC2Tests.openDB();
		stmt = con.createStatement();

		// Drop the test table if it already exists for some reason. It is
		// not an error if it doesn't exist.
		try {
			stmt.executeUpdate("DROP TABLE testbatch");
		} catch (SQLException e) {
			 // Intentionally ignore. We cannot distinguish "table does not
			 // exist" from other errors, since PostgreSQL doesn't support
			 // error codes yet.
		}

		stmt.executeUpdate("CREATE TABLE testbatch(pk INTEGER, col1 INTEGER)");
		stmt.executeUpdate("INSERT INTO testbatch VALUES(1, 0)");

		// Generally recommended with batch updates. By default we run all
		// tests in this test case with autoCommit disabled.
		con.setAutoCommit(false);
	}

	// Tear down the fixture for this test case.
	protected void tearDown() throws Exception {
		con.setAutoCommit(true);
		if (stmt != null) {
			stmt.executeUpdate("DROP TABLE testbatch");
			stmt.close();
		}
		if (con != null) {
			  JDBC2Tests.closeDB(con);
		}
	}

	public void testSupportsBatchUpdates() throws Exception {
		DatabaseMetaData dbmd = con.getMetaData();
		assertTrue(dbmd.supportsBatchUpdates());
	}

	private void assertCol1HasValue(int expected) throws Exception {
		Statement getCol1 = con.createStatement();

		ResultSet rs =
			getCol1.executeQuery("SELECT col1 FROM testbatch WHERE pk = 1");
		assertTrue(rs.next());

		int actual = rs.getInt("col1");

		assertEquals(expected, actual);

		assertEquals(false, rs.next());

		rs.close();
		getCol1.close();
	}

	public void testExecuteEmptyBatch() throws Exception {
		int[] updateCount = stmt.executeBatch();
		assertEquals(0,updateCount.length);

		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1");
		stmt.clearBatch();
		updateCount = stmt.executeBatch();
		assertEquals(0,updateCount.length);
	}

	public void testClearBatch() throws Exception {
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1");
		assertCol1HasValue(0);
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1");
		assertCol1HasValue(0);
		stmt.clearBatch();
		assertCol1HasValue(0);
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 4 WHERE pk = 1");
		assertCol1HasValue(0);
		stmt.executeBatch();
		assertCol1HasValue(4);
		con.commit();
		assertCol1HasValue(4);
	}

	public void testSelectThrowsException() throws Exception {
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1");
		stmt.addBatch("SELECT col1 FROM testbatch WHERE pk = 1");
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1");

		try {
			stmt.executeBatch();
			fail("Should raise a BatchUpdateException because of the SELECT");
		} catch (BatchUpdateException e) {
			int [] updateCounts = e.getUpdateCounts();
			assertEquals(1,updateCounts.length);
			assertEquals(1,updateCounts[0]);
		} catch (SQLException e) {
			fail( "Should throw a BatchUpdateException instead of " +
			      "a generic SQLException: " + e);
		}
	}

	public void testPreparedStatement() throws Exception {
		PreparedStatement pstmt = con.prepareStatement(
			"UPDATE testbatch SET col1 = col1 + ? WHERE PK = ?" );

		// Note that the first parameter changes for every statement in the
		// batch, whereas the second parameter remains constant.
		pstmt.setInt(1,1);
		pstmt.setInt(2,1);
		pstmt.addBatch();
		assertCol1HasValue(0);

		pstmt.setInt(1,2);
		pstmt.addBatch();
		assertCol1HasValue(0);

		pstmt.setInt(1,4);
		pstmt.addBatch();
		assertCol1HasValue(0);

		pstmt.executeBatch();
		assertCol1HasValue(7);

		con.commit();
		assertCol1HasValue(7);

		con.rollback();
		assertCol1HasValue(7);

		pstmt.close();
	}

	/**
	*/
	public void testTransactionalBehaviour() throws Exception {
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 1 WHERE pk = 1");
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 2 WHERE pk = 1");
		stmt.executeBatch();
		con.rollback();
		assertCol1HasValue(0);

		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 4 WHERE pk = 1");
		stmt.addBatch("UPDATE testbatch SET col1 = col1 + 8 WHERE pk = 1");

		// The statement has been added to the batch, but it should not yet
		// have been executed.
		assertCol1HasValue(0);

		int[] updateCounts = stmt.executeBatch();
		assertEquals(2,updateCounts.length);
		assertEquals(1,updateCounts[0]);
		assertEquals(1,updateCounts[1]);

		assertCol1HasValue(12);
		con.commit();
		assertCol1HasValue(12);
		con.rollback();
		assertCol1HasValue(12);
	}
}

/* TODO tests that can be added to this test case
   - SQLExceptions chained to a BatchUpdateException
   - test PreparedStatement as thoroughly as Statement
 */
