DATAJDBC-357 - Polishing.

Implemented workaround for MySql not supporting offset without Limit.
Using `SELECT 1` as dummy order by since it is documented to be optimized away.
Renamed tests to match the project standard.

See also:

- https://stackoverflow.com/a/44106422
- https://stackoverflow.com/a/271650

Original pull request: #125.
This commit is contained in:
Jens Schauder
2019-04-09 14:36:58 +02:00
parent 488ef53157
commit f499017269
7 changed files with 29 additions and 18 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.data.relational.core.dialect;
* A clause representing Dialect-specific {@code LIMIT}.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 1.1
*/
public interface LimitClause {
@@ -26,7 +27,7 @@ public interface LimitClause {
/**
* Returns the {@code LIMIT} clause to limit results.
*
* @param limit the actual limit to use.
* @param limit the maximum number of lines returned when the resulting SQL snippet is used.
* @return rendered limit clause.
* @see #getLimitOffset(long, long)
*/
@@ -35,19 +36,21 @@ public interface LimitClause {
/**
* Returns the {@code OFFSET} clause to consume rows at a given offset.
*
* @param limit the actual limit to use.
* @return rendered limit clause.
* @param offset the numbers of rows that get skipped when the resulting SQL snippet is used.
* @return rendered offset clause.
* @see #getLimitOffset(long, long)
*/
String getOffset(long limit);
String getOffset(long offset);
/**
* Returns a combined {@code LIMIT/OFFSET} clause that limits results and starts consumption at the given
* {@code offset}.
*
* @param limit the actual limit to use.
* @param offset the offset to start from.
* @param limit the maximum number of lines returned when the resulting SQL snippet is used.
* @param offset the numbers of rows that get skipped when the resulting SQL snippet is used.
* @return rendered limit clause.
* @see #getLimit(long)
* @see #getOffset(long)
*/
String getLimitOffset(long limit, long offset);

View File

@@ -19,6 +19,7 @@ package org.springframework.data.relational.core.dialect;
* An SQL dialect for MySQL.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 1.1
*/
public class MySqlDialect extends AbstractDialect {
@@ -45,7 +46,9 @@ public class MySqlDialect extends AbstractDialect {
*/
@Override
public String getOffset(long offset) {
throw new UnsupportedOperationException("MySQL does not support OFFSET without LIMIT");
// Ugly but the official workaround for offset without limit
// see: https://stackoverflow.com/a/271650
return String.format("LIMIT %d, 18446744073709551615", offset);
}
/*
@@ -56,7 +59,7 @@ public class MySqlDialect extends AbstractDialect {
public String getLimitOffset(long limit, long offset) {
// LIMIT {[offset,] row_count}
return String.format("LIMIT %d, %d", offset, limit);
return String.format("LIMIT %s, %s", offset, limit);
}
/*

View File

@@ -33,7 +33,7 @@ public class SqlServerSelectRenderContext implements SelectRenderContext {
private static final String SYNTHETIC_ORDER_BY_FIELD = "__relational_row_number__";
private static final String SYNTHETIC_SELECT_LIST = ", ROW_NUMBER() over (ORDER BY CURRENT_TIMESTAMP) AS "
private static final String SYNTHETIC_SELECT_LIST = ", ROW_NUMBER() over (ORDER BY (SELECT 1)) AS "
+ SYNTHETIC_ORDER_BY_FIELD;
private final Function<Select, CharSequence> afterOrderBy;

View File

@@ -30,8 +30,9 @@ import org.springframework.data.relational.core.sql.render.SqlRenderer;
* Tests for {@link MySqlDialect}-specific rendering.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class MySqlDialectRenderingTests {
public class MySqlDialectRenderingUnitTests {
private final RenderContextFactory factory = new RenderContextFactory(MySqlDialect.INSTANCE);
@@ -57,8 +58,9 @@ public class MySqlDialectRenderingTests {
Table table = Table.create("foo");
Select select = StatementBuilder.select(table.asterisk()).from(table).offset(10).build();
assertThatThrownBy(() -> SqlRenderer.create(factory.createRenderContext()).render(select))
.isInstanceOf(UnsupportedOperationException.class);
String sql = SqlRenderer.create(factory.createRenderContext()).render(select);
assertThat(sql).isEqualTo("SELECT foo.* FROM foo LIMIT 10, 18446744073709551615");
}
@Test // DATAJDBC-278

View File

@@ -23,6 +23,7 @@ import org.junit.Test;
* Unit tests for {@link MySqlDialect}.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class MySqlDialectUnitTests {
@@ -48,7 +49,7 @@ public class MySqlDialectUnitTests {
LimitClause limit = MySqlDialect.INSTANCE.limit();
assertThatThrownBy(() -> limit.getOffset(10)).isInstanceOf(UnsupportedOperationException.class);
assertThat(limit.getOffset(10)).isEqualTo("LIMIT 10, 18446744073709551615");
}
@Test // DATAJDBC-278

View File

@@ -30,8 +30,9 @@ import org.springframework.data.relational.core.sql.render.SqlRenderer;
* Tests for {@link PostgresDialect}-specific rendering.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class PostgresDialectRenderingTests {
public class PostgresDialectRenderingUnitTests {
private final RenderContextFactory factory = new RenderContextFactory(PostgresDialect.INSTANCE);

View File

@@ -30,8 +30,9 @@ import org.springframework.data.relational.core.sql.render.SqlRenderer;
* Tests for {@link SqlServerDialect}-specific rendering.
*
* @author Mark Paluch
* @author Jens Schauder
*/
public class SqlServerDialectRenderingTests {
public class SqlServerDialectRenderingUnitTests {
private final RenderContextFactory factory = new RenderContextFactory(SqlServerDialect.INSTANCE);
@@ -73,7 +74,7 @@ public class SqlServerDialectRenderingTests {
String sql = SqlRenderer.create(factory.createRenderContext()).render(select);
assertThat(sql).isEqualTo(
"SELECT foo.*, ROW_NUMBER() over (ORDER BY CURRENT_TIMESTAMP) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY");
"SELECT foo.*, ROW_NUMBER() over (ORDER BY (SELECT 1)) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY");
}
@Test // DATAJDBC-278
@@ -85,7 +86,7 @@ public class SqlServerDialectRenderingTests {
String sql = SqlRenderer.create(factory.createRenderContext()).render(select);
assertThat(sql).isEqualTo(
"SELECT foo.*, ROW_NUMBER() over (ORDER BY CURRENT_TIMESTAMP) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 10 ROWS");
"SELECT foo.*, ROW_NUMBER() over (ORDER BY (SELECT 1)) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 10 ROWS");
}
@Test // DATAJDBC-278
@@ -97,7 +98,7 @@ public class SqlServerDialectRenderingTests {
String sql = SqlRenderer.create(factory.createRenderContext()).render(select);
assertThat(sql).isEqualTo(
"SELECT foo.*, ROW_NUMBER() over (ORDER BY CURRENT_TIMESTAMP) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY");
"SELECT foo.*, ROW_NUMBER() over (ORDER BY (SELECT 1)) AS __relational_row_number__ FROM foo ORDER BY __relational_row_number__ OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY");
}
@Test // DATAJDBC-278