Author: reschke
Date: Mon Feb  1 20:18:57 2016
New Revision: 1727995

URL: http://svn.apache.org/viewvc?rev=1727995&view=rev
Log:
OAK-3937: Batch createOrUpdate() may fail with primary key violation

Add test case and a workaround for PostgreSQL

Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBCTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java?rev=1727995&r1=1727994&r2=1727995&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBC.java
 Mon Feb  1 20:18:57 2016
@@ -302,7 +302,11 @@ public class RDBDocumentStoreJDBC {
             results = stmt.executeBatch();
         } catch (BatchUpdateException ex) {
             LOG.debug("Some of the batch updates failed", ex);
-            results = ex.getUpdateCounts();
+            if (this.dbInfo == RDBDocumentStoreDB.POSTGRES) {
+                results = new int[0];
+            } else {
+                results = ex.getUpdateCounts();
+            }
         } finally {
             stmt.close();
         }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java?rev=1727995&r1=1727994&r2=1727995&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/MultiDocumentStoreTest.java
 Mon Feb  1 20:18:57 2016
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -215,8 +214,6 @@ public class MultiDocumentStoreTest exte
 
     @Test
     public void concurrentBatchUpdate() throws Exception {
-        // OAK-3924 and OAK-3937
-        assumeTrue(dsf == DocumentStoreFixture.MONGO);
         final CountDownLatch ready = new CountDownLatch(2);
         final CountDownLatch go = new CountDownLatch(1);
         final List<String> ids = Lists.newArrayList();
@@ -249,7 +246,7 @@ public class MultiDocumentStoreTest exte
                     exceptions.add(e);
                 }
             }
-        });
+        }, "t1");
         final Map<String, NodeDocument> result2 = Maps.newHashMap();
         Thread t2 = new Thread(new Runnable() {
             @Override
@@ -274,7 +271,7 @@ public class MultiDocumentStoreTest exte
                     exceptions.add(e);
                 }
             }
-        });
+        }, "t2");
         t1.start();
         t2.start();
         ready.await();
@@ -290,7 +287,7 @@ public class MultiDocumentStoreTest exte
             if (d1 != null) {
                 assertNull(d2);
             } else {
-                assertNotNull(d2);
+                assertNotNull("id " + id + " is in neither result set", d2);
             }
         }
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBCTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBCTest.java?rev=1727995&r1=1727994&r2=1727995&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBCTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/rdb/RDBDocumentStoreJDBCTest.java
 Mon Feb  1 20:18:57 2016
@@ -16,16 +16,22 @@
  */
 package org.apache.jackrabbit.oak.plugins.document.rdb;
 
+import static com.google.common.collect.ImmutableSet.of;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
 import java.io.UnsupportedEncodingException;
+import java.sql.BatchUpdateException;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.apache.jackrabbit.oak.plugins.document.AbstractDocumentStoreTest;
 import org.apache.jackrabbit.oak.plugins.document.Collection;
@@ -49,7 +55,7 @@ public class RDBDocumentStoreJDBCTest ex
         // https://issues.apache.org/jira/browse/OAK-3938
         assumeTrue(super.dsf != DocumentStoreFixture.RDB_ORACLE);
 
-        String table = 
((RDBDocumentStore)super.ds).getTable(Collection.NODES).getName();
+        String table = ((RDBDocumentStore) 
super.ds).getTable(Collection.NODES).getName();
 
         Connection con = super.rdbDataSource.getConnection();
         con.setReadOnly(false);
@@ -84,7 +90,8 @@ public class RDBDocumentStoreJDBCTest ex
             batchSt.close();
             con.commit();
 
-            // System.out.println(super.dsname + " " + 
Arrays.toString(batchResult));
+            // System.out.println(super.dsname + " " +
+            // Arrays.toString(batchResult));
 
             assertEquals(3, batchResult.length);
             assertFalse("Row was updated although not present, status: " + 
batchResult[0], isSuccess(batchResult[0]));
@@ -95,12 +102,88 @@ public class RDBDocumentStoreJDBCTest ex
         }
     }
 
+    @Test
+    public void batchFailingInsertResult() throws SQLException {
+
+        // https://issues.apache.org/jira/browse/OAK-3937
+        assumeTrue(super.dsf != DocumentStoreFixture.RDB_PG);
+
+        String table = ((RDBDocumentStore) 
super.ds).getTable(Collection.NODES).getName();
+
+        Connection con = super.rdbDataSource.getConnection();
+        con.setReadOnly(false);
+        try {
+            PreparedStatement st = con.prepareStatement("DELETE FROM " + table 
+ " WHERE ID in (?, ?, ?)");
+            setIdInStatement(st, 1, "key-1");
+            setIdInStatement(st, 2, "key-2");
+            setIdInStatement(st, 3, "key-3");
+            st.executeUpdate();
+            st.close();
+            con.commit();
+
+            removeMe.add("key-3");
+
+            st = con.prepareStatement("INSERT INTO " + table + " (id) VALUES 
(?)");
+            setIdInStatement(st, 1, "key-3");
+            st.executeUpdate();
+            st.close();
+            con.commit();
+
+            removeMe.add("key-1");
+            removeMe.add("key-2");
+
+            PreparedStatement batchSt = con.prepareStatement("INSERT INTO " + 
table + " (id) VALUES (?)");
+            setIdInStatement(batchSt, 1, "key-1");
+            batchSt.addBatch();
+
+            setIdInStatement(batchSt, 1, "key-2");
+            batchSt.addBatch();
+
+            setIdInStatement(batchSt, 1, "key-3");
+            batchSt.addBatch();
+
+            int[] batchResult = null;
+            try {
+                batchSt.executeBatch();
+                fail("Batch operation should fail");
+            } catch (BatchUpdateException e) {
+                batchResult = e.getUpdateCounts();
+            }
+            batchSt.close();
+            con.commit();
+
+            // System.out.println(super.dsname + " " + 
Arrays.toString(batchResult));
+
+            assertTrue(batchResult.length >= 2);
+            assertTrue("Row should be inserted correctly.", 
isSuccess(batchResult[0]));
+            assertTrue("Row should be inserted correctly.", 
isSuccess(batchResult[1]));
+            if (batchResult.length == 3) {
+                assertTrue("Row already exists, shouldn't be inserted.", 
!isSuccess(batchResult[2]));
+            }
+
+            PreparedStatement rst = con.prepareStatement("SELECT id FROM " + 
table + " WHERE id in (?, ?, ?)");
+            setIdInStatement(rst, 1, "key-1");
+            setIdInStatement(rst, 2, "key-2");
+            setIdInStatement(rst, 3, "key-3");
+            ResultSet results = rst.executeQuery();
+            Set<String> ids = new HashSet<String>();
+            while (results.next()) {
+                ids.add(getIdFromRS(results, 1));
+            }
+            results.close();
+            rst.close();
+            assertEquals("Some of the rows weren't inserted.", of("key-1", 
"key-2", "key-3"), ids);
+        } finally {
+            con.close();
+        }
+    }
+
     private static boolean isSuccess(int result) {
         return result == 1 || result == Statement.SUCCESS_NO_INFO;
     }
 
     private void setIdInStatement(PreparedStatement stmt, int idx, String id) 
throws SQLException {
-        boolean binaryId = 
((RDBDocumentStore)super.ds).getTable(Collection.NODES).isIdBinary();
+        boolean binaryId = ((RDBDocumentStore) 
super.ds).getTable(Collection.NODES).isIdBinary();
         if (binaryId) {
             try {
                 stmt.setBytes(idx, id.getBytes("UTF-8"));
@@ -111,4 +194,17 @@ public class RDBDocumentStoreJDBCTest ex
             stmt.setString(idx, id);
         }
     }
+
+    private String getIdFromRS(ResultSet rs, int idx) throws SQLException {
+        boolean binaryId = ((RDBDocumentStore) 
super.ds).getTable(Collection.NODES).isIdBinary();
+        if (binaryId) {
+            try {
+                return new String(rs.getBytes(idx), "UTF-8");
+            } catch (UnsupportedEncodingException ex) {
+                throw new DocumentStoreException(ex);
+            }
+        } else {
+            return rs.getString(idx);
+        }
+    }
 }


Reply via email to