Unverified 提交 064d7ba8 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1028 from katzyn/on_update

Fix more issues with ON DUPLICATE KEY UPDATE in MySQL mode
...@@ -21,6 +21,12 @@ Change Log ...@@ -21,6 +21,12 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1027: Add support for fully qualified names in MySQL compatibility mode
</li>
<li>Issue #178: INSERT ON DUPLICATE KEY UPDATE returns wrong generated key
</li>
<li>PR #1025: Remove BitField and replace its usages with BitSet
</li>
<li>Issue #1019: Console incorrectly sorts BigDecimal columns alphanumerically <li>Issue #1019: Console incorrectly sorts BigDecimal columns alphanumerically
</li> </li>
<li>PR #1021: Update JdbcDatabaseMetaData to JDBC 4.1 (Java 7) <li>PR #1021: Update JdbcDatabaseMetaData to JDBC 4.1 (Java 7)
......
...@@ -1232,7 +1232,24 @@ public class Parser { ...@@ -1232,7 +1232,24 @@ public class Parser {
read("KEY"); read("KEY");
read("UPDATE"); read("UPDATE");
do { do {
Column column = parseColumn(table); String columnName = readColumnIdentifier();
if (readIf(".")) {
String schemaOrTableName = columnName;
String tableOrColumnName = readColumnIdentifier();
if (readIf(".")) {
if (!table.getSchema().getName().equals(schemaOrTableName)) {
throw DbException.get(ErrorCode.SCHEMA_NAME_MUST_MATCH);
}
columnName = readColumnIdentifier();
} else {
columnName = tableOrColumnName;
tableOrColumnName = schemaOrTableName;
}
if (!table.getName().equals(tableOrColumnName)) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableOrColumnName);
}
}
Column column = table.getColumn(columnName);
read("="); read("=");
Expression expression; Expression expression;
if (readIf("DEFAULT")) { if (readIf("DEFAULT")) {
......
...@@ -181,11 +181,15 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -181,11 +181,15 @@ public class Insert extends Prepared implements ResultTarget {
try { try {
table.addRow(session, newRow); table.addRow(session, newRow);
} catch (DbException de) { } catch (DbException de) {
if (!handleOnDuplicate(de)) { if (handleOnDuplicate(de)) {
// MySQL returns 2 for updated row
// TODO: detect no-op change
rowNumber++;
} else {
// INSERT IGNORE case // INSERT IGNORE case
rowNumber--; rowNumber--;
continue;
} }
continue;
} }
generatedKeys.confirmRow(newRow); generatedKeys.confirmRow(newRow);
session.log(table, UndoLogRecord.INSERT, newRow); session.log(table, UndoLogRecord.INSERT, newRow);
...@@ -399,16 +403,17 @@ public class Insert extends Prepared implements ResultTarget { ...@@ -399,16 +403,17 @@ public class Insert extends Prepared implements ResultTarget {
} }
buff.append(prepareUpdateCondition(foundIndex).getSQL()); buff.append(prepareUpdateCondition(foundIndex).getSQL());
String sql = buff.toString(); String sql = buff.toString();
Prepared command = session.prepare(sql); Update command = (Update) session.prepare(sql);
command.setUpdateToCurrentValuesReturnsZero(true);
for (Parameter param : command.getParameters()) { for (Parameter param : command.getParameters()) {
Parameter insertParam = parameters.get(param.getIndex()); Parameter insertParam = parameters.get(param.getIndex());
param.setValue(insertParam.getValue(session)); param.setValue(insertParam.getValue(session));
} }
command.update(); boolean result = command.update() > 0;
for (String variableName : variableNames) { for (String variableName : variableNames) {
session.setVariable(variableName, ValueNull.INSTANCE); session.setVariable(variableName, ValueNull.INSTANCE);
} }
return true; return result;
} }
private Expression prepareUpdateCondition(Index foundIndex) { private Expression prepareUpdateCondition(Index foundIndex) {
......
...@@ -49,6 +49,8 @@ public class Update extends Prepared { ...@@ -49,6 +49,8 @@ public class Update extends Prepared {
/** The limit expression as specified in the LIMIT clause. */ /** The limit expression as specified in the LIMIT clause. */
private Expression limitExpr; private Expression limitExpr;
private boolean updateToCurrentValuesReturnsZero;
private final ArrayList<Column> columns = New.arrayList(); private final ArrayList<Column> columns = New.arrayList();
private final HashMap<Column, Expression> expressionMap = new HashMap<>(); private final HashMap<Column, Expression> expressionMap = new HashMap<>();
...@@ -134,7 +136,7 @@ public class Update extends Prepared { ...@@ -134,7 +136,7 @@ public class Update extends Prepared {
} }
newRow.setValue(i, newValue); newRow.setValue(i, newValue);
} }
if (setOnUpdate) { if (setOnUpdate || updateToCurrentValuesReturnsZero) {
setOnUpdate = false; setOnUpdate = false;
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
// Use equals here to detect changes from numeric 0 to 0.0 and similar // Use equals here to detect changes from numeric 0 to 0.0 and similar
...@@ -152,6 +154,8 @@ public class Update extends Prepared { ...@@ -152,6 +154,8 @@ public class Update extends Prepared {
} }
} }
} }
} else if (updateToCurrentValuesReturnsZero) {
count--;
} }
} }
table.validateConvertUpdateSequence(session, newRow); table.validateConvertUpdateSequence(session, newRow);
...@@ -268,4 +272,14 @@ public class Update extends Prepared { ...@@ -268,4 +272,14 @@ public class Update extends Prepared {
public void setSourceTableFilter(TableFilter sourceTableFilter) { public void setSourceTableFilter(TableFilter sourceTableFilter) {
this.sourceTableFilter = sourceTableFilter; this.sourceTableFilter = sourceTableFilter;
} }
/**
* Sets expected update count for update to current values case.
*
* @param updateToCurrentValuesReturnsZero if zero should be returned as update
* count if update set row to current values
*/
public void setUpdateToCurrentValuesReturnsZero(boolean updateToCurrentValuesReturnsZero) {
this.updateToCurrentValuesReturnsZero = updateToCurrentValuesReturnsZero;
}
} }
...@@ -63,8 +63,31 @@ public class TestCompatibility extends TestBase { ...@@ -63,8 +63,31 @@ public class TestCompatibility extends TestBase {
stat.execute("create schema s2"); stat.execute("create schema s2");
stat.execute("create table s2.test(id int primary key, name varchar(255))"); stat.execute("create table s2.test(id int primary key, name varchar(255))");
stat.execute("insert into s2.test(id, name) values(1, 'a')"); stat.execute("insert into s2.test(id, name) values(1, 'a')");
stat.execute("insert into s2.test(id, name) values(1, 'b') " + assertEquals(2, stat.executeUpdate("insert into s2.test(id, name) values(1, 'b') " +
"on duplicate key update name = values(name)"); "on duplicate key update name = values(name)"));
assertEquals(0, stat.executeUpdate("insert into s2.test(id, name) values(1, 'b') " +
"on duplicate key update name = values(name)"));
assertEquals(1, stat.executeUpdate("insert into s2.test(id, name) values(2, 'c') " +
"on duplicate key update name = values(name)"));
ResultSet rs = stat.executeQuery("select id, name from s2.test order by id");
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals("b", rs.getString(2));
assertTrue(rs.next());
assertEquals(2, rs.getInt(1));
assertEquals("c", rs.getString(2));
assertFalse(rs.next());
// Check qualified names in ON UPDATE case
assertEquals(2, stat.executeUpdate("insert into s2.test(id, name) values(2, 'd') " +
"on duplicate key update test.name = values(name)"));
assertThrows(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, stat)
.executeUpdate("insert into s2.test(id, name) values(2, 'd') " +
"on duplicate key update test2.name = values(name)");
assertEquals(2, stat.executeUpdate("insert into s2.test(id, name) values(2, 'e') " +
"on duplicate key update s2.test.name = values(name)"));
assertThrows(ErrorCode.SCHEMA_NAME_MUST_MATCH, stat)
.executeUpdate("insert into s2.test(id, name) values(2, 'd') " +
"on duplicate key update s3.test.name = values(name)");
stat.execute("drop schema s2 cascade"); stat.execute("drop schema s2 cascade");
c.close(); c.close();
} }
......
...@@ -771,4 +771,4 @@ openoffice organize libre systemtables gmane sea borders announced millennium al ...@@ -771,4 +771,4 @@ openoffice organize libre systemtables gmane sea borders announced millennium al
opti excessively opti excessively
iterators tech enums incompatibilities loses reimplement readme reorganize milli subdirectory linkplain inspections iterators tech enums incompatibilities loses reimplement readme reorganize milli subdirectory linkplain inspections
geometries sourceschema destschema generatedcolumn alphanumerically geometries sourceschema destschema generatedcolumn alphanumerically usages
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论