提交 729d931d authored 作者: Thomas Mueller's avatar Thomas Mueller

MERGE: if a unique key was violated (but not the primary key or the key columns…

MERGE: if a unique key was violated (but not the primary key or the key columns of the merge itself), the wrong exception was thrown.
上级 61fdac05
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>IBM DB2 and Apache Derby compatibility: support the pseudo-table SYSIBM.SYSDUMMY1. <ul><li>MERGE: if a unique key was violated (but not the primary key or the key columns of the merge itself),
the wrong exception was thrown.
</li><li>IBM DB2 and Apache Derby compatibility: support the pseudo-table SYSIBM.SYSDUMMY1.
</li><li>ROWNUM() did not work in combination with IN(..). The following query did not work as expected: </li><li>ROWNUM() did not work in combination with IN(..). The following query did not work as expected:
select * from (select rownum r from test) where r in (1, 2). select * from (select rownum r from test) where r in (1, 2).
</li><li>H2 Console autocomplete: the autocomplete feature didn't support quoted names. </li><li>H2 Console autocomplete: the autocomplete feature didn't support quoted names.
......
...@@ -162,8 +162,24 @@ public class Merge extends Prepared { ...@@ -162,8 +162,24 @@ public class Merge extends Prepared {
} }
} catch (DbException e) { } catch (DbException e) {
if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) { if (e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
// concurrent merge or insert // possibly a concurrent merge or insert
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName()); Index index = (Index) e.getSource();
if (index != null) {
// verify the index columns match the key
Column[] indexColumns = index.getColumns();
boolean indexMatchesKeys = false;
if (indexColumns.length <= keys.length) {
for (int i = 0; i < indexColumns.length; i++) {
if (indexColumns[i] != keys[i]) {
indexMatchesKeys = false;
break;
}
}
}
if (indexMatchesKeys) {
throw DbException.get(ErrorCode.CONCURRENT_UPDATE_1, table.getName());
}
}
} }
throw e; throw e;
} }
......
...@@ -78,7 +78,9 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -78,7 +78,9 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
*/ */
DbException getDuplicateKeyException() { DbException getDuplicateKeyException() {
String sql = getName() + " ON " + table.getSQL() + "(" + getColumnListSQL() + ")"; String sql = getName() + " ON " + table.getSQL() + "(" + getColumnListSQL() + ")";
return DbException.get(ErrorCode.DUPLICATE_KEY_1, sql); DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
return e;
} }
public String getPlanSQL() { public String getPlanSQL() {
......
...@@ -155,7 +155,9 @@ public class PageDataIndex extends PageIndex { ...@@ -155,7 +155,9 @@ public class PageDataIndex extends PageIndex {
if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) { if (mainIndexColumn >= 0 && mainIndexColumn < indexColumns.length) {
sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")"; sql += "(" + indexColumns[mainIndexColumn].getSQL() + ")";
} }
return DbException.get(ErrorCode.DUPLICATE_KEY_1, sql); DbException e = DbException.get(ErrorCode.DUPLICATE_KEY_1, sql);
e.setSource(this);
return e;
} }
private void addTry(Session session, Row row) { private void addTry(Session session, Row row) {
......
...@@ -31,6 +31,8 @@ public class DbException extends RuntimeException { ...@@ -31,6 +31,8 @@ public class DbException extends RuntimeException {
private static final Properties MESSAGES = new Properties(); private static final Properties MESSAGES = new Properties();
private Object source;
static { static {
try { try {
byte[] messages = Utils.getResource("/org/h2/res/_messages_en.prop"); byte[] messages = Utils.getResource("/org/h2/res/_messages_en.prop");
...@@ -348,4 +350,12 @@ public class DbException extends RuntimeException { ...@@ -348,4 +350,12 @@ public class DbException extends RuntimeException {
return io; return io;
} }
public Object getSource() {
return source;
}
public void setSource(Object source) {
this.source = source;
}
} }
...@@ -8,8 +8,11 @@ package org.h2.test.mvcc; ...@@ -8,8 +8,11 @@ package org.h2.test.mvcc;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import org.h2.constant.ErrorCode;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.Task; import org.h2.util.Task;
...@@ -28,12 +31,30 @@ public class TestMvccMultiThreaded extends TestBase { ...@@ -28,12 +31,30 @@ public class TestMvccMultiThreaded extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testMergeWithUniqueKeyViolation();
testConcurrentMerge(); testConcurrentMerge();
testConcurrentUpdate(""); testConcurrentUpdate("");
// not supported currently // not supported currently
// testConcurrentUpdate(";MULTI_THREADED=TRUE"); // testConcurrentUpdate(";MULTI_THREADED=TRUE");
} }
private void testMergeWithUniqueKeyViolation() throws Exception {
deleteDb("mvccMultiThreaded");
Connection conn = getConnection("mvccMultiThreaded");
Statement stat = conn.createStatement();
stat.execute("create table test(x int primary key, y int unique)");
stat.execute("insert into test values(1, 1)");
try {
stat.execute("merge into test values(2, 1)");
fail();
} catch (SQLException e) {
assertEquals(ErrorCode.DUPLICATE_KEY_1, e.getErrorCode());
}
stat.execute("merge into test values(1, 2)");
conn.close();
}
private void testConcurrentMerge() throws Exception { private void testConcurrentMerge() throws Exception {
deleteDb("mvccMultiThreaded"); deleteDb("mvccMultiThreaded");
int len = 3; int len = 3;
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论