Render LOCK clause through SelectRenderContext.

Closes #1078
This commit is contained in:
Jens Schauder
2021-11-02 14:18:38 +01:00
committed by Mark Paluch
parent 7e7e9d59c4
commit e9e07bd4a3
2 changed files with 89 additions and 4 deletions

View File

@@ -15,8 +15,10 @@
*/
package org.springframework.data.relational.core.sql.render;
import java.util.OptionalLong;
import java.util.function.Function;
import org.springframework.data.relational.core.sql.LockMode;
import org.springframework.data.relational.core.sql.Select;
/**
@@ -26,6 +28,7 @@ import org.springframework.data.relational.core.sql.Select;
*
* @author Mark Paluch
* @author Myeonghyeon Lee
* @author Jens Schauder
* @since 1.1
*/
public interface SelectRenderContext {
@@ -51,12 +54,35 @@ public interface SelectRenderContext {
/**
* Customization hook: Rendition of a part after {@code ORDER BY}. The rendering function is called always, regardless
* whether {@code ORDER BY} exists or not. Renders an empty string by default.
*
* whether {@code ORDER BY} exists or not.
* <p>
* Renders lock, limit and offset clause as appropriate.
* </p>
*
* @param hasOrderBy the actual value whether the {@link Select} statement has a {@code ORDER BY} clause.
* @return render {@link Function} invoked after rendering {@code ORDER BY}.
*/
default Function<Select, ? extends CharSequence> afterOrderBy(boolean hasOrderBy) {
return select -> "";
return select -> {
OptionalLong limit = select.getLimit();
OptionalLong offset = select.getOffset();
LockMode lockMode = select.getLockMode();
String lockPrefix = (lockMode == null) ? "" : " FOR UPDATE";
if (limit.isPresent() && offset.isPresent()) {
return lockPrefix
+ String.format(" OFFSET %d ROWS FETCH FIRST %d ROWS ONLY", offset.getAsLong(), limit.getAsLong());
}
if (limit.isPresent()) {
return lockPrefix + String.format(" FETCH FIRST %d ROWS ONLY", limit.getAsLong());
}
if (offset.isPresent()) {
return lockPrefix + String.format(" OFFSET %d ROWS", offset.getAsLong());
}
return lockPrefix;
};
}
}

View File

@@ -37,7 +37,7 @@ class SelectRendererUnitTests {
Table bar = SQL.table("bar");
Column foo = bar.column("foo");
Select select = Select.builder().select(foo).from(bar).limitOffset(1, 2).build();
Select select = Select.builder().select(foo).from(bar).build();
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT bar.foo FROM bar");
}
@@ -480,6 +480,65 @@ class SelectRendererUnitTests {
assertThat(rendered).isEqualTo("SELECT CAST(User.name AS VARCHAR2) FROM User");
}
@Test // GH-1076
void rendersLimitAndOffset() {
Table table_user = SQL.table("User");
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).limitOffset(10, 5).build();
final String rendered = SqlRenderer.toString(select);
assertThat(rendered).isEqualTo("SELECT User.name FROM User OFFSET 5 ROWS FETCH FIRST 10 ROWS ONLY");
}
@Test // GH-1076
void rendersLimit() {
Table table_user = SQL.table("User");
Select select = StatementBuilder.select(table_user.column("name")).from(table_user) //
.limit(3) //
.build();
final String rendered = SqlRenderer.toString(select);
assertThat(rendered).isEqualTo("SELECT User.name FROM User FETCH FIRST 3 ROWS ONLY");
}
@Test // GH-1076
void rendersLock() {
Table table_user = SQL.table("User");
Select select = StatementBuilder.select(table_user.column("name")).from(table_user) //
.lock(LockMode.PESSIMISTIC_READ) //
.build();
final String rendered = SqlRenderer.toString(select);
assertThat(rendered).isEqualTo("SELECT User.name FROM User FOR UPDATE");
}
@Test // GH-1076
void rendersLockAndOffset() {
Table table_user = SQL.table("User");
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).offset(3) //
.lock(LockMode.PESSIMISTIC_WRITE) //
.build();
final String rendered = SqlRenderer.toString(select);
assertThat(rendered).isEqualTo("SELECT User.name FROM User FOR UPDATE OFFSET 3 ROWS");
}
@Test // GH-1076
void rendersLockAndOffsetUsingDialect() {
Table table_user = SQL.table("User");
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).limitOffset(3, 6) //
.lock(LockMode.PESSIMISTIC_WRITE) //
.build();
String rendered = SqlRenderer.create(new RenderContextFactory(PostgresDialect.INSTANCE).createRenderContext())
.render(select);
assertThat(rendered).isEqualTo("SELECT User.name FROM User LIMIT 3 OFFSET 6 FOR UPDATE OF User");
}
@Test // GH-1007
void shouldRenderConditionAsExpression() {