提交 55e2d0c4 authored 作者: noelgrandin@gmail.com's avatar noelgrandin@gmail.com

Improved MS SQL Server and MySQL compatibility: support DELETE with TOP or LIMIT.

上级 fe9b5e70
......@@ -66,9 +66,10 @@ UPDATE PERSON P SET NAME=(SELECT A.NAME FROM ADDRESS A WHERE A.ID=P.ID);
"
"Commands (DML)","DELETE","
DELETE FROM tableName [ WHERE expression ]
DELETE [ TOP term ] FROM tableName [ WHERE expression ] [ LIMIT term ]
","
Deletes rows form a table.
If TOP or LIMIT is specified, at most the specified number of rows are deleted.
","
DELETE FROM TEST WHERE ID=2
"
......
......@@ -19,7 +19,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>Support for COSH, SINH, and TANH functions.
<li>Improved MS SQL Server and MySQL compatibility: support DELETE with TOP or LIMIT.
</li><li>Support for COSH, SINH, and TANH functions.
</li><li>Support for the % operator (modulo) thanks to Noel Grandin.
</li><li>Issue 288: Some right outer join queries failed to produce the correct result or
threw exceptions such as "column x must be in the group by list".
......
......@@ -302,7 +302,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Sequences: CURRVAL should be session specific. Compatibility with PostgreSQL.
</li><li>Auto-server: add option to define the IP address range or list.
</li><li>Index creation using deterministic functions.
</li><li>Support DELETE with TOP or LIMIT. See also: http://dev.mysql.com/doc/refman/5.1/de/delete.html
</li><li>ANALYZE: for unique indexes that allow null, count the number of null.
</li><li>AUTO_SERVER: support changing IP addresses (disable a network while the database is open).
</li><li>Avoid using java.util.Calendar internally because it's slow, complicated, and buggy.
......
......@@ -710,6 +710,10 @@ public class Parser {
private Delete parseDelete() {
Delete command = new Delete(session);
Expression limit = null;
if (readIf("TOP")) {
limit = readTerm().optimize(session);
}
currentPrepared = command;
int start = lastParseIndex;
readIf("FROM");
......@@ -719,6 +723,10 @@ public class Parser {
Expression condition = readExpression();
command.setCondition(condition);
}
if (readIf("LIMIT") && limit == null) {
limit = readTerm().optimize(session);
}
command.setLimit(limit);
setSQL(command, "DELETE", start);
return command;
}
......
......@@ -30,6 +30,11 @@ public class Delete extends Prepared {
private Expression condition;
private TableFilter tableFilter;
/**
* The limit expression as specified in the LIMIT or TOP clause.
*/
private Expression limitExpr;
public Delete(Session session) {
super(session);
}
......@@ -50,6 +55,10 @@ public class Delete extends Prepared {
table.fire(session, Trigger.DELETE, true);
table.lock(session, true, false);
RowList rows = new RowList(session);
int limitRows = -1;
if (limitExpr != null) {
limitRows = limitExpr.getValue(session).getInt();
}
try {
setCurrentRowNumber(0);
while (tableFilter.next()) {
......@@ -66,22 +75,36 @@ public class Delete extends Prepared {
}
}
int rowScanCount = 0;
int rowsDeleted = 0;
for (rows.reset(); rows.hasNext();) {
if ((++rowScanCount & 127) == 0) {
checkCanceled();
}
Row row = rows.next();
table.removeRow(session, row);
rowsDeleted++;
session.log(table, UndoLogRecord.DELETE, row);
if (limitRows != -1) {
if (rowsDeleted == limitRows) {
break;
}
}
}
if (table.fireRow()) {
int count = 0;
for (rows.reset(); rows.hasNext();) {
Row row = rows.next();
table.fireAfterRow(session, row, null, false);
count++;
if (limitRows != -1) {
if (count == limitRows) {
break;
}
}
}
}
table.fire(session, Trigger.DELETE, false);
return rows.size();
return rowsDeleted;
} finally {
rows.close();
}
......@@ -89,10 +112,14 @@ public class Delete extends Prepared {
public String getPlanSQL() {
StringBuilder buff = new StringBuilder();
buff.append("DELETE FROM ").append(tableFilter.getPlanSQL(false));
buff.append("DELETE ");
buff.append("FROM ").append(tableFilter.getPlanSQL(false));
if (condition != null) {
buff.append("\nWHERE ").append(StringUtils.unEnclose(condition.getSQL()));
}
if (limitExpr != null) {
buff.append("\nLIMIT (").append(StringUtils.unEnclose(limitExpr.getSQL())).append(")");
}
return buff.toString();
}
......@@ -119,4 +146,8 @@ public class Delete extends Prepared {
return CommandInterface.DELETE;
}
public void setLimit(Expression limit) {
this.limitExpr = limit;
}
}
......@@ -36,6 +36,7 @@ public class TestCases extends TestBase {
}
public void test() throws Exception {
testDeleteTop();
testUnicode();
testOuterJoin();
testCommentOnColumnWithSchemaEqualDatabase();
......@@ -1175,4 +1176,46 @@ public class TestCases extends TestBase {
}
}
private void testDeleteTop() throws SQLException {
deleteDb("cases");
Connection conn = getConnection("cases");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE TEST(id int) AS SELECT x FROM system_range(1, 100)");
stat.execute("DELETE TOP 10 FROM TEST");
ResultSet rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
assertTrue(rs.next());
assertEquals(90, rs.getInt(1));
stat.execute("DELETE FROM TEST LIMIT ((SELECT COUNT(*) FROM TEST) / 10)");
rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
assertTrue(rs.next());
assertEquals(81, rs.getInt(1));
rs = stat.executeQuery("EXPLAIN DELETE FROM TEST LIMIT ((SELECT COUNT(*) FROM TEST) / 10)");
rs.next();
assertEquals("DELETE FROM PUBLIC.TEST\n" +
" /* PUBLIC.TEST.tableScan */\n" +
"LIMIT ((SELECT\n" +
" COUNT(*)\n" +
"FROM PUBLIC.TEST\n" +
" /* PUBLIC.TEST.tableScan */\n" +
"/* direct lookup */) / 10)",
rs.getString(1));
PreparedStatement prep;
prep = conn.prepareStatement("SELECT * FROM TEST LIMIT ?");
prep.setInt(1, 10);
prep.execute();
prep = conn.prepareStatement("DELETE FROM TEST LIMIT ?");
prep.setInt(1, 10);
prep.execute();
rs = stat.executeQuery("SELECT COUNT(*) FROM TEST");
assertTrue(rs.next());
assertEquals(71, rs.getInt(1));
conn.close();
}
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论