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);
+ }
+ }
}