public int[] executeBatch() throws SQLException
{
- if(batch==null || batch.isEmpty())
- throw new PSQLException("postgresql.stat.batch.empty");
-
+ if(batch==null)
+ batch=new Vector();
int size=batch.size();
int[] result=new int[size];
int i=0;
- this.execute("begin"); // PTM: check this when autoCommit is false
try {
for(i=0;i<size;i++)
result[i]=this.executeUpdate((String)batch.elementAt(i));
- this.execute("commit"); // PTM: check this
} catch(SQLException e) {
- this.execute("abort"); // PTM: check this
- throw new PSQLException("postgresql.stat.batch.error",new Integer(i),batch.elementAt(i));
+ int[] resultSucceeded = new int[i];
+ System.arraycopy(result,0,resultSucceeded,0,i);
+
+ PBatchUpdateException updex =
+ new PBatchUpdateException("postgresql.stat.batch.error",
+ new Integer(i), batch.elementAt(i), resultSucceeded);
+ updex.setNextException(e);
+
+ throw updex;
+ } finally {
+ batch.removeAllElements();
}
return result;
}
--- /dev/null
+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
+ */
--- /dev/null
+package org.postgresql.util;
+
+import java.util.*;
+import java.text.*;
+
+/**
+ * A singleton class to translate JDBC driver messages in SQLException's.
+ */
+public class MessageTranslator {
+
+ // The singleton instance.
+ private static MessageTranslator instance = null;
+
+ private ResourceBundle bundle;
+
+ private MessageTranslator() {
+ try {
+ bundle = ResourceBundle.getBundle("org.postgresql.errors");
+ } catch(MissingResourceException e) {
+ // translation files have not been installed.
+ bundle = null;
+ }
+ }
+
+ // Synchronized, otherwise multiple threads may perform the test and
+ // assign to the singleton instance simultaneously.
+ private synchronized final static MessageTranslator getInstance() {
+ if (instance == null) {
+ instance = new MessageTranslator();
+ }
+ return instance;
+ }
+
+ public final static String translate(String id, Object[] args) {
+
+ MessageTranslator translator = MessageTranslator.getInstance();
+
+ return translator._translate(id, args);
+ }
+
+ private final String _translate(String id, Object[] args) {
+ String message;
+
+ if (bundle != null && id != null) {
+ // Now look up a localized message. If one is not found, then use
+ // the supplied message instead.
+ try {
+ message = bundle.getString(id);
+ } catch(MissingResourceException e) {
+ message = id;
+ }
+ } else {
+ message = id;
+ }
+
+ // Expand any arguments
+ if (args != null && message != null) {
+ message = MessageFormat.format(message,args);
+ }
+
+ return message;
+ }
+}
import java.io.*;
import java.sql.*;
-import java.text.*;
-import java.util.*;
/**
* This class extends SQLException, and provides our internationalisation handling
{
private String message;
- // Cache for future errors
- static ResourceBundle bundle;
-
/**
* This provides the same functionality to SQLException
* @param error Error string
translate(error,argv);
}
- /**
- * This does the actual translation
- */
- private void translate(String id,Object[] args)
- {
- if(bundle == null) {
- try {
- bundle = ResourceBundle.getBundle("org.postgresql.errors");
- } catch(MissingResourceException e) {
- // translation files have not been installed.
- message = id;
- }
+ private void translate(String error, Object[] args) {
+ message = MessageTranslator.translate(error,args);
}
- if (bundle != null) {
- // Now look up a localized message. If one is not found, then use
- // the supplied message instead.
- message = null;
- try {
- message = bundle.getString(id);
- } catch(MissingResourceException e) {
- message = id;
- }
- }
-
- // Expand any arguments
- if(args!=null && message != null)
- message = MessageFormat.format(message,args);
-
- }
-
/**
* Overides Throwable
*/
{
return message;
}
-
}