Skip to content

Commit e9e07bd

Browse files
schaudermp911de
authored andcommitted
Render LOCK clause through SelectRenderContext.
Closes #1078
1 parent 7e7e9d5 commit e9e07bd

File tree

2 files changed

+89
-4
lines changed

2 files changed

+89
-4
lines changed

spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/render/SelectRenderContext.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616
package org.springframework.data.relational.core.sql.render;
1717

18+
import java.util.OptionalLong;
1819
import java.util.function.Function;
1920

21+
import org.springframework.data.relational.core.sql.LockMode;
2022
import org.springframework.data.relational.core.sql.Select;
2123

2224
/**
@@ -26,6 +28,7 @@
2628
*
2729
* @author Mark Paluch
2830
* @author Myeonghyeon Lee
31+
* @author Jens Schauder
2932
* @since 1.1
3033
*/
3134
public interface SelectRenderContext {
@@ -51,12 +54,35 @@ public interface SelectRenderContext {
5154

5255
/**
5356
* Customization hook: Rendition of a part after {@code ORDER BY}. The rendering function is called always, regardless
54-
* whether {@code ORDER BY} exists or not. Renders an empty string by default.
55-
*
57+
* whether {@code ORDER BY} exists or not.
58+
* <p>
59+
* Renders lock, limit and offset clause as appropriate.
60+
* </p>
61+
*
5662
* @param hasOrderBy the actual value whether the {@link Select} statement has a {@code ORDER BY} clause.
5763
* @return render {@link Function} invoked after rendering {@code ORDER BY}.
5864
*/
5965
default Function<Select, ? extends CharSequence> afterOrderBy(boolean hasOrderBy) {
60-
return select -> "";
66+
67+
return select -> {
68+
69+
OptionalLong limit = select.getLimit();
70+
OptionalLong offset = select.getOffset();
71+
LockMode lockMode = select.getLockMode();
72+
73+
String lockPrefix = (lockMode == null) ? "" : " FOR UPDATE";
74+
75+
if (limit.isPresent() && offset.isPresent()) {
76+
return lockPrefix
77+
+ String.format(" OFFSET %d ROWS FETCH FIRST %d ROWS ONLY", offset.getAsLong(), limit.getAsLong());
78+
}
79+
if (limit.isPresent()) {
80+
return lockPrefix + String.format(" FETCH FIRST %d ROWS ONLY", limit.getAsLong());
81+
}
82+
if (offset.isPresent()) {
83+
return lockPrefix + String.format(" OFFSET %d ROWS", offset.getAsLong());
84+
}
85+
return lockPrefix;
86+
};
6187
}
6288
}

spring-data-relational/src/test/java/org/springframework/data/relational/core/sql/render/SelectRendererUnitTests.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void shouldRenderSingleColumn() {
3737
Table bar = SQL.table("bar");
3838
Column foo = bar.column("foo");
3939

40-
Select select = Select.builder().select(foo).from(bar).limitOffset(1, 2).build();
40+
Select select = Select.builder().select(foo).from(bar).build();
4141

4242
assertThat(SqlRenderer.toString(select)).isEqualTo("SELECT bar.foo FROM bar");
4343
}
@@ -480,6 +480,65 @@ void shouldRenderCast() {
480480
assertThat(rendered).isEqualTo("SELECT CAST(User.name AS VARCHAR2) FROM User");
481481
}
482482

483+
@Test // GH-1076
484+
void rendersLimitAndOffset() {
485+
486+
Table table_user = SQL.table("User");
487+
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).limitOffset(10, 5).build();
488+
489+
final String rendered = SqlRenderer.toString(select);
490+
assertThat(rendered).isEqualTo("SELECT User.name FROM User OFFSET 5 ROWS FETCH FIRST 10 ROWS ONLY");
491+
}
492+
493+
@Test // GH-1076
494+
void rendersLimit() {
495+
496+
Table table_user = SQL.table("User");
497+
Select select = StatementBuilder.select(table_user.column("name")).from(table_user) //
498+
.limit(3) //
499+
.build();
500+
501+
final String rendered = SqlRenderer.toString(select);
502+
assertThat(rendered).isEqualTo("SELECT User.name FROM User FETCH FIRST 3 ROWS ONLY");
503+
}
504+
505+
@Test // GH-1076
506+
void rendersLock() {
507+
508+
Table table_user = SQL.table("User");
509+
Select select = StatementBuilder.select(table_user.column("name")).from(table_user) //
510+
.lock(LockMode.PESSIMISTIC_READ) //
511+
.build();
512+
513+
final String rendered = SqlRenderer.toString(select);
514+
assertThat(rendered).isEqualTo("SELECT User.name FROM User FOR UPDATE");
515+
}
516+
517+
@Test // GH-1076
518+
void rendersLockAndOffset() {
519+
520+
Table table_user = SQL.table("User");
521+
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).offset(3) //
522+
.lock(LockMode.PESSIMISTIC_WRITE) //
523+
.build();
524+
525+
final String rendered = SqlRenderer.toString(select);
526+
assertThat(rendered).isEqualTo("SELECT User.name FROM User FOR UPDATE OFFSET 3 ROWS");
527+
}
528+
529+
@Test // GH-1076
530+
void rendersLockAndOffsetUsingDialect() {
531+
532+
Table table_user = SQL.table("User");
533+
Select select = StatementBuilder.select(table_user.column("name")).from(table_user).limitOffset(3, 6) //
534+
.lock(LockMode.PESSIMISTIC_WRITE) //
535+
.build();
536+
537+
String rendered = SqlRenderer.create(new RenderContextFactory(PostgresDialect.INSTANCE).createRenderContext())
538+
.render(select);
539+
assertThat(rendered).isEqualTo("SELECT User.name FROM User LIMIT 3 OFFSET 6 FOR UPDATE OF User");
540+
}
541+
483542
@Test // GH-1007
484543
void shouldRenderConditionAsExpression() {
485544

0 commit comments

Comments
 (0)