Skip to content

Commit

Permalink
fix issue #41 with MySQL long lock names (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
blagerweij authored Nov 14, 2023
1 parent c77689a commit 891eb37
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import java.util.Date;
import java.util.Locale;

import static com.github.blagerweij.sessionlock.util.StringUtils.toUpperCase;

/**
* Employs MSSQL application resource locks.
*
Expand Down Expand Up @@ -59,8 +61,7 @@ public boolean supports(Database database) {
}

private String getChangeLogLockName() {
return (database.getDefaultSchemaName() + "." + database.getDatabaseChangeLogLockTableName())
.toUpperCase(Locale.ROOT);
return toUpperCase(database.getDefaultSchemaName() + "." + database.getDatabaseChangeLogLockTableName());
}

private static Integer getIntegerResult(PreparedStatement stmt) throws SQLException {
Expand Down Expand Up @@ -117,7 +118,7 @@ protected void releaseLock(final Connection con) throws SQLException, LockExcept
Integer unlocked = getIntegerResult(stmt);
if (!Integer.valueOf(0).equals(unlocked)) {
throw new LockException(
"RELEASE_LOCK() returned " + String.valueOf(unlocked).toUpperCase(Locale.ROOT));
"RELEASE_LOCK() returned " + toUpperCase(String.valueOf(unlocked)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
import java.sql.SQLException;
import java.util.Date;
import java.util.Locale;

import com.github.blagerweij.sessionlock.util.StringUtils;
import liquibase.database.Database;
import liquibase.database.core.MySQLDatabase;
import liquibase.exception.LockException;
import liquibase.lockservice.DatabaseChangeLogLock;

import static com.github.blagerweij.sessionlock.util.StringUtils.toUpperCase;
import static com.github.blagerweij.sessionlock.util.StringUtils.truncate;

/**
* Employs MySQL user-level (a.k.a. application-level or advisory) locks.
*
Expand Down Expand Up @@ -47,10 +52,9 @@ public boolean supports(Database database) {
return (database instanceof MySQLDatabase);
}

private String getChangeLogLockName() {
protected String getChangeLogLockName() {
// MySQL 5.7 and later enforces a maximum length on lock names of 64 characters.
return (database.getDefaultSchemaName() + "." + database.getDatabaseChangeLogLockTableName())
.toUpperCase(Locale.ROOT);
return toUpperCase(truncate((database.getDefaultSchemaName() + "." + database.getDatabaseChangeLogLockTableName()), 64));
}

private static Integer getIntegerResult(PreparedStatement stmt) throws SQLException {
Expand Down Expand Up @@ -96,7 +100,7 @@ protected void releaseLock(Connection con) throws SQLException, LockException {
Integer unlocked = getIntegerResult(stmt);
if (!Integer.valueOf(1).equals(unlocked)) {
throw new LockException(
"RELEASE_LOCK() returned " + String.valueOf(unlocked).toUpperCase(Locale.ROOT));
"RELEASE_LOCK() returned " + toUpperCase(String.valueOf(unlocked)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import liquibase.exception.LockException;
import liquibase.lockservice.DatabaseChangeLogLock;

import static com.github.blagerweij.sessionlock.util.StringUtils.toUpperCase;

/**
* Employs Oracle user-level (a.k.a. application-level or advisory) locks.
*
Expand All @@ -40,8 +42,7 @@ public boolean supports(Database database) {
}

private String getChangeLogLockName() {
return (database.getLiquibaseCatalogName() + "." + database.getDatabaseChangeLogLockTableName())
.toUpperCase(Locale.ROOT);
return toUpperCase(database.getLiquibaseCatalogName() + "." + database.getDatabaseChangeLogLockTableName());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.github.blagerweij.sessionlock.util;

import java.util.Locale;

public class StringUtils {

StringUtils() {
throw new IllegalArgumentException("Utility class");
}

public static String truncate(final String str, final int maxlength) {
if (str == null || str.length() <= maxlength) {
return str;
} else {
return str.substring(0, maxlength);
}
}

public static String toUpperCase(final String str) {
if (str == null) {
return null;
} else {
return str.toUpperCase(Locale.ROOT);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
Expand All @@ -22,18 +24,20 @@
import liquibase.lockservice.DatabaseChangeLogLock;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

public class MySQLLockServiceTest {

private MySQLLockService lockService;

private Connection dbCon;
private MySQLDatabase database;

@BeforeEach
@BeforeEach
public void setUp() {
dbCon = mock(Connection.class);

MySQLDatabase database = new MySQLDatabase();
database = new MySQLDatabase();
database.setDefaultCatalogName("test_schema");
database = spy(database);
when(database.getConnection()).thenReturn(new JdbcConnection(dbCon));
Expand Down Expand Up @@ -86,6 +90,22 @@ public void acquireFailure() throws Exception {
assertThatThrownBy(() -> lockService.acquireLock()).isInstanceOf(LockException.class);
}

@Test
@SuppressWarnings("resource")
public void lockNameTooLongShouldFail() throws Exception {
database.setDefaultCatalogName("this_name_is_too_long_and_should_be_truncated_since_mysql_does_not_like_long_locknames");
PreparedStatement stmt = mock(PreparedStatement.class);
ResultSet rs = mock(ResultSet.class);
when(stmt.executeQuery()).thenReturn(rs);
when(dbCon.prepareStatement(MySQLLockService.SQL_GET_LOCK)).thenReturn(stmt);
ArgumentCaptor<String> lockNameCaptor = ArgumentCaptor.forClass(String.class);
doNothing().when(stmt).setString(eq(1), lockNameCaptor.capture());
when(rs.next()).thenReturn(true, false);
when(rs.getObject(1)).thenReturn(1);
assertThat(lockService.acquireLock()).isTrue();
assertThat(lockNameCaptor.getValue()).isEqualTo("THIS_NAME_IS_TOO_LONG_AND_SHOULD_BE_TRUNCATED_SINCE_MYSQL_DOES_N");
}

@Test
@SuppressWarnings("resource")
public void releaseSuccess() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.blagerweij.sessionlock.util;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class StringUtilsTest {
@Test
void constructor() {
assertThatThrownBy(() -> new StringUtils()).isInstanceOf(IllegalArgumentException.class);
}

@Test
void truncate() {
assertThat(StringUtils.truncate("testing", 4)).isEqualTo("test");
assertThat(StringUtils.truncate("test", 4)).isEqualTo("test");
assertThat(StringUtils.truncate("t", 4)).isEqualTo("t");
assertThat(StringUtils.truncate(null, 4)).isEqualTo(null);
}

@Test
void toUpperCase() {
assertThat(StringUtils.toUpperCase("testing")).isEqualTo("TESTING");
assertThat(StringUtils.toUpperCase(null)).isEqualTo(null);
}
}

0 comments on commit 891eb37

Please sign in to comment.