[#7430] ResultQuery.fetchOne() should not fetch second record on a LIMIT 1 query

This commit is contained in:
lukaseder 2018-04-27 11:35:25 +02:00
parent 0b917a5c7e
commit c2c125be98
3 changed files with 66 additions and 5 deletions

View File

@ -532,7 +532,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
@Override
public final R fetchOne() {
return Tools.fetchOne(fetchLazy());
return Tools.fetchOne(fetchLazy(), hasLimit1());
}
@Override
@ -627,7 +627,7 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
@Override
public final R fetchSingle() {
return Tools.fetchSingle(fetchLazy());
return Tools.fetchSingle(fetchLazy(), hasLimit1());
}
@Override
@ -1475,4 +1475,14 @@ abstract class AbstractResultQuery<R extends Record> extends AbstractQuery imple
return fetch();
}
}
@SuppressWarnings("rawtypes")
private final boolean hasLimit1() {
if (this instanceof SelectQueryImpl) {
Limit l = ((SelectQueryImpl) this).getLimit();
return !l.withTies() && l.limitOne();
}
return false;
}
}

View File

@ -360,12 +360,23 @@ final class Limit extends AbstractQueryPart {
}
/**
* Whether this limit has an offset of zero
* Whether this limit has a limit of zero
*/
final boolean limitZero() {
return numberOfRows == null;
}
/**
* Whether this limit has a limit of one
*/
final boolean limitOne() {
return !limitZero()
&& !withTies()
&& numberOfRows instanceof Param
&& Integer.valueOf(1).equals(((Param<?>) numberOfRows).getValue());
}
/**
* Whether this limit has an offset of zero
*/

View File

@ -1705,11 +1705,31 @@ final class Tools {
* element
*/
static final <R extends Record> R fetchOne(Cursor<R> cursor) throws TooManyRowsException {
return fetchOne(cursor, false);
}
/**
* Get the only element from a cursor or <code>null</code>, or throw an
* exception.
* <p>
* [#2373] This method will always close the argument cursor, as it is
* supposed to be completely consumed by this method.
*
* @param cursor The cursor
* @param hasLimit1 Whether a LIMIT clause is present that guarantees at
* most one row
* @return The only element from the cursor or <code>null</code>
* @throws TooManyRowsException Thrown if the cursor returns more than one
* element
*/
static final <R extends Record> R fetchOne(Cursor<R> cursor, boolean hasLimit1) throws TooManyRowsException {
try {
// [#7001] Fetching at most two rows rather than at most one row
// (and then checking of additional rows) improves debug logs
Result<R> result = cursor.fetchNext(2);
// [#7430] Avoid fetching the second row (additional overhead) if
// there is a guarantee of at most one row
Result<R> result = cursor.fetchNext(hasLimit1 ? 1 : 2);
int size = result.size();
if (size == 0)
@ -1737,11 +1757,31 @@ final class Tools {
* element
*/
static final <R extends Record> R fetchSingle(Cursor<R> cursor) throws NoDataFoundException, TooManyRowsException {
return fetchSingle(cursor, false);
}
/**
* Get the only element from a cursor, or throw an exception.
* <p>
* [#2373] This method will always close the argument cursor, as it is
* supposed to be completely consumed by this method.
*
* @param cursor The cursor
* @param hasLimit1 Whether a LIMIT clause is present that guarantees at
* most one row
* @return The only element from the cursor
* @throws NoDataFoundException Thrown if the cursor did not return any rows
* @throws TooManyRowsException Thrown if the cursor returns more than one
* element
*/
static final <R extends Record> R fetchSingle(Cursor<R> cursor, boolean hasLimit1) throws NoDataFoundException, TooManyRowsException {
try {
// [#7001] Fetching at most two rows rather than at most one row
// (and then checking of additional rows) improves debug logs
Result<R> result = cursor.fetchNext(2);
// [#7430] Avoid fetching the second row (additional overhead) if
// there is a guarantee of at most one row
Result<R> result = cursor.fetchNext(hasLimit1 ? 1 : 2);
int size = result.size();
if (size == 0)