提交 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;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueNull;
/**
* This class represents the statement
......@@ -57,11 +59,14 @@ public class Delete extends Prepared {
RowList rows = new RowList(session);
int limitRows = -1;
if (limitExpr != null) {
limitRows = limitExpr.getValue(session).getInt();
Value v = limitExpr.getValue(session);
if (v != ValueNull.INSTANCE) {
limitRows = v.getInt();
}
}
try {
setCurrentRowNumber(0);
while (tableFilter.next()) {
while (limitRows != 0 && tableFilter.next()) {
setCurrentRowNumber(rows.size() + 1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row row = tableFilter.get();
......@@ -71,24 +76,20 @@ public class Delete extends Prepared {
}
if (!done) {
rows.add(row);
if (limitRows >= 0 && rows.size() >= limitRows) {
break;
}
}
}
}
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;
......@@ -96,15 +97,10 @@ public class Delete extends Prepared {
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 rowsDeleted;
return rows.size();
} finally {
rows.close();
}
......
......@@ -456,10 +456,14 @@ public class Select extends Query {
}
private void queryDistinct(ResultTarget result, long limitRows) {
if (limitRows != 0 && offsetExpr != null) {
// limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE
limitRows += offsetExpr.getValue(session).getInt();
// limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE
// limitRows is never 0 here
if (limitRows > 0 && offsetExpr != null) {
int offset = offsetExpr.getValue(session).getInt();
if (offset > 0) {
limitRows += offset;
}
}
int rowNumber = 0;
setCurrentRowNumber(0);
......@@ -481,7 +485,7 @@ public class Select extends Query {
Value[] row = { value };
result.addRow(row);
rowNumber++;
if ((sort == null || sortUsingIndex) && limitRows != 0 && rowNumber >= limitRows) {
if ((sort == null || sortUsingIndex) && limitRows > 0 && rowNumber >= limitRows) {
break;
}
if (sampleSize > 0 && rowNumber >= sampleSize) {
......@@ -491,10 +495,14 @@ public class Select extends Query {
}
private void queryFlat(int columnCount, ResultTarget result, long limitRows) {
if (limitRows != 0 && offsetExpr != null) {
// limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE
limitRows += offsetExpr.getValue(session).getInt();
// limitRows must be long, otherwise we get an int overflow
// if limitRows is at or near Integer.MAX_VALUE
// limitRows is never 0 here
if (limitRows > 0 && offsetExpr != null) {
int offset = offsetExpr.getValue(session).getInt();
if (offset > 0) {
limitRows += offset;
}
}
int rowNumber = 0;
setCurrentRowNumber(0);
......@@ -515,7 +523,7 @@ public class Select extends Query {
}
result.addRow(row);
rowNumber++;
if ((sort == null || sortUsingIndex) && limitRows != 0 && result.getRowCount() >= limitRows) {
if ((sort == null || sortUsingIndex) && limitRows > 0 && result.getRowCount() >= limitRows) {
break;
}
if (sampleSize > 0 && rowNumber >= sampleSize) {
......@@ -544,12 +552,13 @@ public class Select extends Query {
}
protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) {
int limitRows = maxRows;
int limitRows = maxRows == 0 ? -1 : maxRows;
if (limitExpr != null) {
int l = limitExpr.getValue(session).getInt();
if (limitRows == 0) {
Value v = limitExpr.getValue(session);
int l = v == ValueNull.INSTANCE ? -1 : v.getInt();
if (limitRows < 0) {
limitRows = l;
} else {
} else if (l >= 0) {
limitRows = Math.min(l, limitRows);
}
}
......@@ -569,7 +578,7 @@ public class Select extends Query {
if (isGroupQuery && !isGroupSortedQuery) {
result = createLocalResult(result);
}
if (limitRows != 0 || offsetExpr != null) {
if (limitRows >= 0 || offsetExpr != null) {
result = createLocalResult(result);
}
topTableFilter.startQuery(session);
......@@ -590,23 +599,25 @@ public class Select extends Query {
}
}
ResultTarget to = result != null ? result : target;
if (isQuickAggregateQuery) {
queryQuick(columnCount, to);
} else if (isGroupQuery) {
if (isGroupSortedQuery) {
queryGroupSorted(columnCount, to);
if (limitRows != 0) {
if (isQuickAggregateQuery) {
queryQuick(columnCount, to);
} else if (isGroupQuery) {
if (isGroupSortedQuery) {
queryGroupSorted(columnCount, to);
} else {
queryGroup(columnCount, result);
}
} else if (isDistinctQuery) {
queryDistinct(to, limitRows);
} else {
queryGroup(columnCount, result);
queryFlat(columnCount, to, limitRows);
}
} else if (isDistinctQuery) {
queryDistinct(to, limitRows);
} else {
queryFlat(columnCount, to, limitRows);
}
if (offsetExpr != null) {
result.setOffset(offsetExpr.getValue(session).getInt());
}
if (limitRows != 0) {
if (limitRows >= 0) {
result.setLimit(limitRows);
}
if (result != null) {
......
......@@ -30,6 +30,7 @@ import org.h2.util.New;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
/**
* Represents a union SELECT statement.
......@@ -119,16 +120,27 @@ public class SelectUnion extends Query {
return new LocalResult(session, expressionArray, columnCount);
}
protected LocalResult queryWithoutCache(int maxrows, ResultTarget target) {
if (maxrows != 0) {
if (limitExpr != null) {
maxrows = Math.min(limitExpr.getValue(session).getInt(), maxrows);
protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) {
if (maxRows != 0) {
// maxRows is set (maxRows 0 means no limit)
int l;
if (limitExpr == null) {
l = -1;
} else {
Value v = limitExpr.getValue(session);
l = v == ValueNull.INSTANCE ? -1 : v.getInt();
}
limitExpr = ValueExpression.get(ValueInt.get(maxrows));
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(l));
}
if (session.getDatabase().getSettings().optimizeInsertFromSelect) {
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);
right.query(0, target);
return null;
......@@ -206,7 +218,10 @@ public class SelectUnion extends Query {
result.setOffset(offsetExpr.getValue(session).getInt());
}
if (limitExpr != null) {
result.setLimit(limitExpr.getValue(session).getInt());
Value v = limitExpr.getValue(session);
if (v != ValueNull.INSTANCE) {
result.setLimit(v.getInt());
}
}
result.done();
if (target != null) {
......
......@@ -106,6 +106,7 @@ import org.h2.test.synth.TestJoin;
import org.h2.test.synth.TestKill;
import org.h2.test.synth.TestKillRestart;
import org.h2.test.synth.TestKillRestartMulti;
import org.h2.test.synth.TestLimit;
import org.h2.test.synth.TestMultiThreaded;
import org.h2.test.synth.TestNestedJoins;
import org.h2.test.synth.TestOuterJoins;
......@@ -632,6 +633,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestBtreeIndex().runTest(this);
new TestCrashAPI().runTest(this);
new TestFuzzOptimizations().runTest(this);
new TestLimit().runTest(this);
new TestRandomSQL().runTest(this);
new TestRandomCompare().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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论