Skip to content

Commit

Permalink
Truncate long transaction and savepoint names.
Browse files Browse the repository at this point in the history
Signed-off-by: Nayan Hajratwala <nayan@chikli.com>
[resolves #264][#265]
  • Loading branch information
nhajratw authored and mp911de committed Mar 28, 2023
1 parent c7c91d8 commit f40d131
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 8 deletions.
23 changes: 15 additions & 8 deletions src/main/java/io/r2dbc/mssql/MssqlConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,14 @@ public Mono<Void> beginTransaction(TransactionDefinition transactionDefinition)

builder.append("BEGIN TRANSACTION");
if (name != null) {
String nameToUse = sanitize(name);
String nameToUse = sanitize(name, 32);
Assert.isTrue(IDENTIFIER_PATTERN.matcher(nameToUse).matches(), "Transaction names must contain only characters and numbers and must not exceed 32 characters");
builder.append(" ").append(nameToUse);

if (mark != null) {
String markToUse = sanitize(mark);
String markToUse = sanitize(mark, 128);
Assert.isTrue(IDENTIFIER128_PATTERN.matcher(markToUse.substring(0, Math.min(128, markToUse.length()))).matches(), "Transaction names must contain only characters and numbers and" +
" must not " +
"exceed 128 characters");
" must not exceed 128 characters");
builder.append(' ').append("WITH MARK '").append(markToUse).append("'");
}
}
Expand Down Expand Up @@ -208,7 +207,7 @@ public Mono<Void> createSavepoint(String name) {

Assert.requireNonNull(name, "Savepoint name must not be null");

String nameToUse = sanitize(name);
String nameToUse = sanitize(name, 32);
Assert.isTrue(IDENTIFIER_PATTERN.matcher(nameToUse).matches(), "Save point names must contain only characters and numbers and must not exceed 32 characters");

return useTransactionStatus(tx -> {
Expand Down Expand Up @@ -279,7 +278,7 @@ public Mono<Void> rollbackTransaction() {
public Mono<Void> rollbackTransactionToSavepoint(String name) {

Assert.requireNonNull(name, "Savepoint name must not be null");
String nameToUse = sanitize(name);
String nameToUse = sanitize(name, 32);
Assert.isTrue(IDENTIFIER_PATTERN.matcher(nameToUse).matches(), "Save point names must contain only characters and numbers and must not exceed 32 characters");

return useTransactionStatus(tx -> {
Expand Down Expand Up @@ -413,8 +412,16 @@ private static String renderSetIsolationLevel(IsolationLevel isolationLevel) {
return "SET TRANSACTION ISOLATION LEVEL " + isolationLevel.asSql();
}

private static String sanitize(String identifier) {
return identifier.replace('-', '_').replace('.', '_');
static String sanitize(final String identifier, final int maxLength) {
String sanitized = identifier
.replace('-', '_')
.replace('.', '_')
.substring(Math.max(0, identifier.length() - maxLength));

if (!Character.isLetterOrDigit(sanitized.charAt(0))) {
sanitized = sanitized.substring(1);
}
return sanitized;
}

private Mono<Void> exchange(String sql) {
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/io/r2dbc/mssql/MssqlConnectionUnitTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.r2dbc.mssql.message.token.SqlBatch;
import io.r2dbc.spi.IsolationLevel;
import io.r2dbc.spi.ValidationDepth;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
Expand Down Expand Up @@ -357,6 +358,30 @@ void remoteValidationShouldFail() {

}

@Nested
class SanitizeTests {
@Test
void shorterThanMax() {
assertThat(MssqlConnection.sanitize("12345", 10)).isEqualTo("12345");
}

@Test
void exactlyMax() {
assertThat(MssqlConnection.sanitize("1234567", 7)).isEqualTo("1234567");
}

@Test
void greaterThanMax() {
assertThat(MssqlConnection.sanitize("1234567", 3)).isEqualTo("567");
}

@Test
void dropStartingPunctuation() {
assertThat(MssqlConnection.sanitize("1_23_4", 5)).isEqualTo("23_4");
}

}

private static Stream<IsolationLevel> isolationLevels() {
return Stream.of(MssqlIsolationLevel.SERIALIZABLE, MssqlIsolationLevel.READ_COMMITTED,
MssqlIsolationLevel.READ_UNCOMMITTED, MssqlIsolationLevel.REPEATABLE_READ,
Expand Down

0 comments on commit f40d131

Please sign in to comment.