提交 7a8584bc authored 作者: Thomas Mueller's avatar Thomas Mueller

A query with an explicit LIMIT 0 will now return no rows (so far it meant no…

A query with an explicit LIMIT 0 will now return no rows (so far it meant no limit), which is compatible with PostgreSQL and MySQL. A negative limit value (as well as LIMIT NULL) mean no limit (so far a negative limit meant a limit of one row).
上级 cf646510
...@@ -20,6 +20,8 @@ import org.h2.table.PlanItem; ...@@ -20,6 +20,8 @@ import org.h2.table.PlanItem;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/** /**
* This class represents the statement * This class represents the statement
...@@ -57,11 +59,14 @@ public class Delete extends Prepared { ...@@ -57,11 +59,14 @@ public class Delete extends Prepared {
RowList rows = new RowList(session); RowList rows = new RowList(session);
int limitRows = -1; int limitRows = -1;
if (limitExpr != null) { if (limitExpr != null) {
limitRows = limitExpr.getValue(session).getInt(); Value v = limitExpr.getValue(session);
if (v != ValueNull.INSTANCE) {
limitRows = v.getInt();
}
} }
try { try {
setCurrentRowNumber(0); setCurrentRowNumber(0);
while (tableFilter.next()) { while (limitRows != 0 && tableFilter.next()) {
setCurrentRowNumber(rows.size() + 1); setCurrentRowNumber(rows.size() + 1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row row = tableFilter.get(); Row row = tableFilter.get();
...@@ -71,24 +76,20 @@ public class Delete extends Prepared { ...@@ -71,24 +76,20 @@ public class Delete extends Prepared {
} }
if (!done) { if (!done) {
rows.add(row); rows.add(row);
if (limitRows >= 0 && rows.size() >= limitRows) {
break;
}
} }
} }
} }
int rowScanCount = 0; int rowScanCount = 0;
int rowsDeleted = 0;
for (rows.reset(); rows.hasNext();) { for (rows.reset(); rows.hasNext();) {
if ((++rowScanCount & 127) == 0) { if ((++rowScanCount & 127) == 0) {
checkCanceled(); checkCanceled();
} }
Row row = rows.next(); Row row = rows.next();
table.removeRow(session, row); table.removeRow(session, row);
rowsDeleted++;
session.log(table, UndoLogRecord.DELETE, row); session.log(table, UndoLogRecord.DELETE, row);
if (limitRows != -1) {
if (rowsDeleted == limitRows) {
break;
}
}
} }
if (table.fireRow()) { if (table.fireRow()) {
int count = 0; int count = 0;
...@@ -96,15 +97,10 @@ public class Delete extends Prepared { ...@@ -96,15 +97,10 @@ public class Delete extends Prepared {
Row row = rows.next(); Row row = rows.next();
table.fireAfterRow(session, row, null, false); table.fireAfterRow(session, row, null, false);
count++; count++;
if (limitRows != -1) {
if (count == limitRows) {
break;
}
}
} }
} }
table.fire(session, Trigger.DELETE, false); table.fire(session, Trigger.DELETE, false);
return rowsDeleted; return rows.size();
} finally { } finally {
rows.close(); rows.close();
} }
......
...@@ -456,10 +456,14 @@ public class Select extends Query { ...@@ -456,10 +456,14 @@ public class Select extends Query {
} }
private void queryDistinct(ResultTarget result, long limitRows) { private void queryDistinct(ResultTarget result, long limitRows) {
if (limitRows != 0 && offsetExpr != null) {
// limitRows must be long, otherwise we get an int overflow // limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE // if limitRows is at or near Integer.MAX_VALUE
limitRows += offsetExpr.getValue(session).getInt(); // limitRows is never 0 here
if (limitRows > 0 && offsetExpr != null) {
int offset = offsetExpr.getValue(session).getInt();
if (offset > 0) {
limitRows += offset;
}
} }
int rowNumber = 0; int rowNumber = 0;
setCurrentRowNumber(0); setCurrentRowNumber(0);
...@@ -481,7 +485,7 @@ public class Select extends Query { ...@@ -481,7 +485,7 @@ public class Select extends Query {
Value[] row = { value }; Value[] row = { value };
result.addRow(row); result.addRow(row);
rowNumber++; rowNumber++;
if ((sort == null || sortUsingIndex) && limitRows != 0 && rowNumber >= limitRows) { if ((sort == null || sortUsingIndex) && limitRows > 0 && rowNumber >= limitRows) {
break; break;
} }
if (sampleSize > 0 && rowNumber >= sampleSize) { if (sampleSize > 0 && rowNumber >= sampleSize) {
...@@ -491,10 +495,14 @@ public class Select extends Query { ...@@ -491,10 +495,14 @@ public class Select extends Query {
} }
private void queryFlat(int columnCount, ResultTarget result, long limitRows) { private void queryFlat(int columnCount, ResultTarget result, long limitRows) {
if (limitRows != 0 && offsetExpr != null) {
// limitRows must be long, otherwise we get an int overflow // limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE // if limitRows is at or near Integer.MAX_VALUE
limitRows += offsetExpr.getValue(session).getInt(); // limitRows is never 0 here
if (limitRows > 0 && offsetExpr != null) {
int offset = offsetExpr.getValue(session).getInt();
if (offset > 0) {
limitRows += offset;
}
} }
int rowNumber = 0; int rowNumber = 0;
setCurrentRowNumber(0); setCurrentRowNumber(0);
...@@ -515,7 +523,7 @@ public class Select extends Query { ...@@ -515,7 +523,7 @@ public class Select extends Query {
} }
result.addRow(row); result.addRow(row);
rowNumber++; rowNumber++;
if ((sort == null || sortUsingIndex) && limitRows != 0 && result.getRowCount() >= limitRows) { if ((sort == null || sortUsingIndex) && limitRows > 0 && result.getRowCount() >= limitRows) {
break; break;
} }
if (sampleSize > 0 && rowNumber >= sampleSize) { if (sampleSize > 0 && rowNumber >= sampleSize) {
...@@ -544,12 +552,13 @@ public class Select extends Query { ...@@ -544,12 +552,13 @@ public class Select extends Query {
} }
protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) { protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) {
int limitRows = maxRows; int limitRows = maxRows == 0 ? -1 : maxRows;
if (limitExpr != null) { if (limitExpr != null) {
int l = limitExpr.getValue(session).getInt(); Value v = limitExpr.getValue(session);
if (limitRows == 0) { int l = v == ValueNull.INSTANCE ? -1 : v.getInt();
if (limitRows < 0) {
limitRows = l; limitRows = l;
} else { } else if (l >= 0) {
limitRows = Math.min(l, limitRows); limitRows = Math.min(l, limitRows);
} }
} }
...@@ -569,7 +578,7 @@ public class Select extends Query { ...@@ -569,7 +578,7 @@ public class Select extends Query {
if (isGroupQuery && !isGroupSortedQuery) { if (isGroupQuery && !isGroupSortedQuery) {
result = createLocalResult(result); result = createLocalResult(result);
} }
if (limitRows != 0 || offsetExpr != null) { if (limitRows >= 0 || offsetExpr != null) {
result = createLocalResult(result); result = createLocalResult(result);
} }
topTableFilter.startQuery(session); topTableFilter.startQuery(session);
...@@ -590,6 +599,7 @@ public class Select extends Query { ...@@ -590,6 +599,7 @@ public class Select extends Query {
} }
} }
ResultTarget to = result != null ? result : target; ResultTarget to = result != null ? result : target;
if (limitRows != 0) {
if (isQuickAggregateQuery) { if (isQuickAggregateQuery) {
queryQuick(columnCount, to); queryQuick(columnCount, to);
} else if (isGroupQuery) { } else if (isGroupQuery) {
...@@ -603,10 +613,11 @@ public class Select extends Query { ...@@ -603,10 +613,11 @@ public class Select extends Query {
} else { } else {
queryFlat(columnCount, to, limitRows); queryFlat(columnCount, to, limitRows);
} }
}
if (offsetExpr != null) { if (offsetExpr != null) {
result.setOffset(offsetExpr.getValue(session).getInt()); result.setOffset(offsetExpr.getValue(session).getInt());
} }
if (limitRows != 0) { if (limitRows >= 0) {
result.setLimit(limitRows); result.setLimit(limitRows);
} }
if (result != null) { if (result != null) {
......
...@@ -30,6 +30,7 @@ import org.h2.util.New; ...@@ -30,6 +30,7 @@ import org.h2.util.New;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
/** /**
* Represents a union SELECT statement. * Represents a union SELECT statement.
...@@ -119,16 +120,27 @@ public class SelectUnion extends Query { ...@@ -119,16 +120,27 @@ public class SelectUnion extends Query {
return new LocalResult(session, expressionArray, columnCount); return new LocalResult(session, expressionArray, columnCount);
} }
protected LocalResult queryWithoutCache(int maxrows, ResultTarget target) { protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) {
if (maxrows != 0) { if (maxRows != 0) {
if (limitExpr != null) { // maxRows is set (maxRows 0 means no limit)
maxrows = Math.min(limitExpr.getValue(session).getInt(), maxrows); int l;
if (limitExpr == null) {
l = -1;
} else {
Value v = limitExpr.getValue(session);
l = v == ValueNull.INSTANCE ? -1 : v.getInt();
}
if (l < 0) {
// for limitExpr, 0 means no rows, and -1 means no limit
l = maxRows;
} else {
l = Math.min(l, maxRows);
} }
limitExpr = ValueExpression.get(ValueInt.get(maxrows)); limitExpr = ValueExpression.get(ValueInt.get(l));
} }
if (session.getDatabase().getSettings().optimizeInsertFromSelect) { if (session.getDatabase().getSettings().optimizeInsertFromSelect) {
if (unionType == UNION_ALL && target != null) { if (unionType == UNION_ALL && target != null) {
if (sort == null && !distinct && maxrows == 0 && offsetExpr == null && limitExpr == null) { if (sort == null && !distinct && maxRows == 0 && offsetExpr == null && limitExpr == null) {
left.query(0, target); left.query(0, target);
right.query(0, target); right.query(0, target);
return null; return null;
...@@ -206,7 +218,10 @@ public class SelectUnion extends Query { ...@@ -206,7 +218,10 @@ public class SelectUnion extends Query {
result.setOffset(offsetExpr.getValue(session).getInt()); result.setOffset(offsetExpr.getValue(session).getInt());
} }
if (limitExpr != null) { if (limitExpr != null) {
result.setLimit(limitExpr.getValue(session).getInt()); Value v = limitExpr.getValue(session);
if (v != ValueNull.INSTANCE) {
result.setLimit(v.getInt());
}
} }
result.done(); result.done();
if (target != null) { if (target != null) {
......
...@@ -106,6 +106,7 @@ import org.h2.test.synth.TestJoin; ...@@ -106,6 +106,7 @@ import org.h2.test.synth.TestJoin;
import org.h2.test.synth.TestKill; import org.h2.test.synth.TestKill;
import org.h2.test.synth.TestKillRestart; import org.h2.test.synth.TestKillRestart;
import org.h2.test.synth.TestKillRestartMulti; import org.h2.test.synth.TestKillRestartMulti;
import org.h2.test.synth.TestLimit;
import org.h2.test.synth.TestMultiThreaded; import org.h2.test.synth.TestMultiThreaded;
import org.h2.test.synth.TestNestedJoins; import org.h2.test.synth.TestNestedJoins;
import org.h2.test.synth.TestOuterJoins; import org.h2.test.synth.TestOuterJoins;
...@@ -632,6 +633,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -632,6 +633,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestBtreeIndex().runTest(this); new TestBtreeIndex().runTest(this);
new TestCrashAPI().runTest(this); new TestCrashAPI().runTest(this);
new TestFuzzOptimizations().runTest(this); new TestFuzzOptimizations().runTest(this);
new TestLimit().runTest(this);
new TestRandomSQL().runTest(this); new TestRandomSQL().runTest(this);
new TestRandomCompare().runTest(this); new TestRandomCompare().runTest(this);
new TestKillRestart().runTest(this); new TestKillRestart().runTest(this);
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.synth;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.test.TestBase;
/**
* The LIMIT, OFFSET, maxRows.
*/
public class TestLimit extends TestBase {
private Statement stat;
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase test = TestBase.createCaller().init();
// test.config.traceTest = true;
test.config.nestedJoins = true;
test.test();
}
public void test() throws Exception {
deleteDb("limit");
Connection conn = getConnection("limit");
stat = conn.createStatement();
stat.execute("create table test(id int) as select x from system_range(1, 10)");
for (int maxRows = 0; maxRows < 12; maxRows++) {
stat.setMaxRows(maxRows);
for (int limit = -2; limit < 12; limit++) {
for (int offset = -2; offset < 12; offset++) {
int l = limit < 0 ? 10 : Math.min(10, limit);
for (int d = 0; d < 2; d++) {
int m = maxRows <= 0 ? 10 : Math.min(10, maxRows);
int expected = Math.min(m, l);
if (offset > 0) {
expected = Math.max(0, Math.min(10 - offset, expected));
}
String s = "select " + (d == 1 ? "distinct " : "") +
" * from test limit " + (limit == -2 ? "null" : limit) +
" offset " + (offset == -2 ? "null" : offset);
assertRow(expected, s);
String union = "(" + s + ") union (" + s + ")";
assertRow(expected, union);
m = maxRows <= 0 ? 20 : Math.min(20, maxRows);
if (offset > 0) {
l = Math.max(0, Math.min(10 - offset, l));
}
expected = Math.min(m, l * 2);
union = "(" + s + ") union all (" + s + ")";
assertRow(expected, union);
for (int unionLimit = -2; unionLimit < 5; unionLimit++) {
int e = unionLimit < 0 ? 20 : Math.min(20, unionLimit);
e = Math.min(expected, e);
String u = union + " limit " + (unionLimit == -2 ? "null" : unionLimit);
assertRow(e, u);
}
}
}
}
}
assertEquals(0, stat.executeUpdate("delete from test limit 0"));
assertEquals(1, stat.executeUpdate("delete from test limit 1"));
assertEquals(2, stat.executeUpdate("delete from test limit 2"));
assertEquals(7, stat.executeUpdate("delete from test limit null"));
stat.execute("insert into test select x from system_range(1, 10)");
assertEquals(10, stat.executeUpdate("delete from test limit -1"));
conn.close();
deleteDb("limit");
}
private void assertRow(int expected, String sql) throws SQLException {
try {
assertResultRowCount(expected, stat.executeQuery(sql));
} catch (AssertionError e) {
stat.executeQuery(sql + " -- cache killer");
throw e;
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论