aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-db-migration
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-db-migration')
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexBuilder.java71
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexChange.java48
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexBuilderTest.java97
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexChangeTest.java139
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest.java2
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest/schema.sql2
6 files changed, 183 insertions, 176 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexBuilder.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexBuilder.java
deleted file mode 100644
index d1a013cf428..00000000000
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexBuilder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migration.step;
-
-import java.util.List;
-import org.sonar.db.dialect.Dialect;
-import org.sonar.db.dialect.H2;
-import org.sonar.db.dialect.MsSql;
-import org.sonar.db.dialect.Oracle;
-import org.sonar.db.dialect.PostgreSql;
-
-import static java.util.Collections.singletonList;
-import static org.sonar.server.platform.db.migration.def.Validations.validateIndexNameIgnoreCase;
-import static org.sonar.server.platform.db.migration.def.Validations.validateTableName;
-
-/**
- * Should not be used directly.
- * Use {@link org.sonar.server.platform.db.migration.step.DropIndexChange} instead.
- */
-class DropIndexBuilder {
-
- private final Dialect dialect;
- private String tableName;
- private String indexName;
-
- DropIndexBuilder(Dialect dialect) {
- this.dialect = dialect;
- }
-
- public DropIndexBuilder setTable(String s) {
- this.tableName = s;
- return this;
- }
-
- public DropIndexBuilder setName(String s) {
- this.indexName = s;
- return this;
- }
-
- public List<String> build() {
- validateTableName(tableName);
- validateIndexNameIgnoreCase(indexName);
- return singletonList(createSqlStatement());
- }
-
- private String createSqlStatement() {
- return switch (dialect.getId()) {
- case MsSql.ID -> "DROP INDEX " + indexName + " ON " + tableName;
- case Oracle.ID -> "DROP INDEX " + indexName;
- case H2.ID, PostgreSql.ID -> "DROP INDEX IF EXISTS " + indexName;
- default -> throw new IllegalStateException("Unsupported dialect for drop of index: " + dialect);
- };
- }
-}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexChange.java
index aa25b092117..2e248c4f98c 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexChange.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropIndexChange.java
@@ -22,31 +22,67 @@ package org.sonar.server.platform.db.migration.step;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Optional;
+import javax.annotation.Nullable;
import org.sonar.db.Database;
import org.sonar.db.DatabaseUtils;
+import org.sonar.db.dialect.Dialect;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.server.platform.db.migration.def.Validations;
public abstract class DropIndexChange extends DdlChange {
private final String indexName;
private final String tableName;
+ private final DbUtilsWrapper dbUtilWrapper;
- public DropIndexChange(Database db, String indexName, String tableName) {
+ protected DropIndexChange(Database db, String indexName, String tableName) {
+ this(db, indexName, tableName, new DbUtilsWrapper(), new ValidationsWrapper());
+ }
+ protected DropIndexChange(Database db, String indexName, String tableName, DbUtilsWrapper dbUtilsWrapper, ValidationsWrapper validationsWrapper) {
super(db);
+ validationsWrapper.validateIndexName(indexName);
+ validationsWrapper.validateTableName(tableName);
this.indexName = indexName;
this.tableName = tableName;
+ this.dbUtilWrapper = dbUtilsWrapper;
}
@Override
public void execute(Context context) throws SQLException {
- Optional<String> indexName = findExistingIndexName();
- indexName.ifPresent(index -> context.execute(new DropIndexBuilder(getDialect())
- .setTable(tableName)
- .setName(index)
- .build()));
+ findExistingIndexName()
+ .map(index -> createDropIndexSqlStatement(getDialect(), index))
+ .ifPresent(context::execute);
}
private Optional<String> findExistingIndexName() throws SQLException {
try (Connection connection = getDatabase().getDataSource().getConnection()) {
+ return dbUtilWrapper.findExistingIndex(connection, tableName, indexName);
+ }
+ }
+
+ private String createDropIndexSqlStatement(Dialect dialect, String actualIndexName) {
+ return switch (dialect.getId()) {
+ case MsSql.ID -> "DROP INDEX " + actualIndexName + " ON " + tableName;
+ case Oracle.ID -> "DROP INDEX " + actualIndexName;
+ case H2.ID, PostgreSql.ID -> "DROP INDEX IF EXISTS " + actualIndexName;
+ default -> throw new IllegalStateException("Unsupported dialect for drop of index: " + dialect);
+ };
+ }
+
+ protected static class DbUtilsWrapper {
+ public Optional<String> findExistingIndex(Connection connection, String tableName, String indexName) {
return DatabaseUtils.findExistingIndex(connection, tableName, indexName);
}
}
+
+ protected static class ValidationsWrapper {
+ public String validateIndexName(@Nullable String indexName) {
+ return Validations.validateIndexName(indexName);
+ }
+ public String validateTableName(@Nullable String tableName) {
+ return Validations.validateTableName(tableName);
+ }
+ }
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexBuilderTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexBuilderTest.java
deleted file mode 100644
index cb3d7e36fc2..00000000000
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexBuilderTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.server.platform.db.migration.step;
-
-import java.util.List;
-import org.junit.Test;
-import org.sonar.db.dialect.Dialect;
-import org.sonar.db.dialect.H2;
-import org.sonar.db.dialect.MsSql;
-import org.sonar.db.dialect.Oracle;
-import org.sonar.db.dialect.PostgreSql;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-
-public class DropIndexBuilderTest {
-
-
- @Test
- public void drop_index_in_table() {
- verifySql(new H2(), "DROP INDEX IF EXISTS issues_key");
- verifySql(new MsSql(), "DROP INDEX issues_key ON issues");
- verifySql(new Oracle(), "DROP INDEX issues_key");
- verifySql(new PostgreSql(), "DROP INDEX IF EXISTS issues_key");
- }
-
- private static void verifySql(Dialect dialect, String expectedSql) {
- List<String> actual = new DropIndexBuilder(dialect)
- .setTable("issues")
- .setName("issues_key")
- .build();
- assertThat(actual).containsExactly(expectedSql);
- }
-
- @Test
- public void throw_NPE_if_table_name_is_missing() {
- assertThatThrownBy(() -> {
- new DropIndexBuilder(new H2())
- .setName("issues_key")
- .build();
- })
- .isInstanceOf(NullPointerException.class)
- .hasMessage("Table name can't be null");
- }
-
- @Test
- public void throw_IAE_if_table_name_is_not_valid() {
- assertThatThrownBy(() -> {
- new DropIndexBuilder(new H2())
- .setTable("(not valid)")
- .setName("issues_key")
- .build();
- })
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Table name must be lower case and contain only alphanumeric chars or '_', got '(not valid)'");
- }
-
- @Test
- public void throw_NPE_if_index_name_is_missing() {
- assertThatThrownBy(() -> {
- new DropIndexBuilder(new H2())
- .setTable("issues")
- .build();
- })
- .isInstanceOf(NullPointerException.class)
- .hasMessage("Index name can't be null");
- }
-
- @Test
- public void throw_IAE_if_index_name_is_not_valid() {
- assertThatThrownBy(() -> {
- new DropIndexBuilder(new H2())
- .setTable("issues")
- .setName("(not valid)")
- .build();
- })
- .isInstanceOf(IllegalArgumentException.class)
- .hasMessage("Index name must contain only alphanumeric chars or '_', got '(not valid)'");
- }
-}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexChangeTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexChangeTest.java
new file mode 100644
index 00000000000..b6ef2cb8c53
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/step/DropIndexChangeTest.java
@@ -0,0 +1,139 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.step;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Optional;
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.Dialect;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.RETURNS_MOCKS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class DropIndexChangeTest {
+ private static final String TABLE_NAME = "components";
+ private static final String INDEX_NAME = "projects_module_uuid";
+
+ @Test
+ public void execute_whenCalledWithH2_shouldGenerateProperSql() throws SQLException {
+ Assertions.assertThat(runExecute(H2.ID, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .contains(INDEX_NAME);
+ }
+
+ @Test
+ public void execute_whenCalledWithOracle_shouldGenerateProperSql() throws SQLException {
+ Assertions.assertThat(runExecute(Oracle.ID, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .contains(INDEX_NAME);
+ }
+
+ @Test
+ public void execute_whenCalledWithPg_shouldGenerateProperSql() throws SQLException {
+ Assertions.assertThat(runExecute(PostgreSql.ID, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .contains(INDEX_NAME);
+ }
+
+ @Test
+ public void execute_whenCalledWithMsSql_shouldGenerateProperSql() throws SQLException {
+ Assertions.assertThat(runExecute(MsSql.ID, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .contains(INDEX_NAME);
+ }
+
+ @Test
+ public void execute_whenCalledWithWrongDbId_shouldFail() throws SQLException {
+ final String invalidDialectId = "invalid_dialect_id";
+ Assertions.assertThatThrownBy(() -> runExecute(invalidDialectId, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageStartingWith("Unsupported dialect for drop of index:");
+ }
+
+ @Test
+ public void execute_whenNoIndexFound_shouldSkipExecution() throws SQLException {
+ Assertions.assertThat(runExecute(H2.ID, TABLE_NAME, INDEX_NAME, INDEX_NAME))
+ .contains(INDEX_NAME);
+ }
+
+ @Test
+ public void execute_whenActualIndexIsLongerThanMax_shouldGenerateProperSql() throws SQLException {
+ final String actualIndexName = "idx_123456789123456789123456789_" + INDEX_NAME;
+ Assertions.assertThat(runExecute(H2.ID, TABLE_NAME, INDEX_NAME, actualIndexName))
+ .contains(actualIndexName);
+ }
+
+ @Test
+ public void execute_whenDifferentIndexName_shouldFindFromDb() throws SQLException {
+ final String actualIndexName = "idx_123_" + INDEX_NAME;
+ Assertions.assertThat(runExecute(H2.ID, TABLE_NAME, INDEX_NAME, actualIndexName))
+ .contains(actualIndexName);
+ }
+
+ @Test
+ public void execute_whenNoIndexFound_shouldSkip() throws SQLException {
+ Assertions.assertThat(runExecute(H2.ID, TABLE_NAME, INDEX_NAME, null, false))
+ .isEmpty();
+ }
+
+ private String runExecute(String dialectId, String tableName, String knownIndexName, String actualIndexName) throws SQLException {
+ return runExecute(dialectId, tableName, knownIndexName, actualIndexName, true).get();
+ }
+
+ private Optional<String> runExecute(String dialectId, String tableName, String knownIndexName, String actualIndexName, boolean expectResult) throws SQLException {
+ Dialect dialect = mock(Dialect.class);
+ when(dialect.getId()).thenReturn(dialectId);
+
+ Database db = Mockito.mock(Database.class, Mockito.withSettings().defaultAnswer(RETURNS_MOCKS));
+ when(db.getDialect()).thenReturn(dialect);
+
+ DdlChange.Context con = mock(DdlChange.Context.class);
+
+ DropIndexChange.DbUtilsWrapper dbUtils = mock(DropIndexChange.DbUtilsWrapper.class);
+ when(dbUtils.findExistingIndex(any(Connection.class), eq(tableName), eq(knownIndexName))).thenReturn(Optional.ofNullable(actualIndexName));
+
+ DropIndexChange.ValidationsWrapper validationsUtils = mock(DropIndexChange.ValidationsWrapper.class);
+
+ DropIndexChange underTest = new DropIndexChange(db, knownIndexName, tableName, dbUtils, validationsUtils) {};
+ underTest.execute(con);
+
+ // validate that the validations are called
+ verify(validationsUtils).validateTableName(tableName);
+ verify(validationsUtils).validateIndexName(knownIndexName);
+
+ if (expectResult) {
+ ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
+ verify(con).execute(sqlCaptor.capture());
+ return Optional.of(sqlCaptor.getValue());
+ } else {
+ verify(con, Mockito.never()).execute(any(String.class));
+ return Optional.empty();
+ }
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest.java
index 2db08eefe1a..8b03d61e1d7 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest.java
@@ -29,7 +29,7 @@ import static org.sonar.db.CoreDbTester.createForSchema;
public class DropIndexForComponentsProjectUuidWithSpecialNameTest {
private static final String TABLE = "components";
- private static final String INDEX = "idx_1234_projects_project_uuid";
+ private static final String INDEX = "idx_123456789123456789_projects_project_uuid";
@Rule
public final CoreDbTester db = createForSchema(DropIndexForComponentsProjectUuidWithSpecialNameTest.class, "schema.sql");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest/schema.sql
index aece92157fe..5cb5c6a7884 100644
--- a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest/schema.sql
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v97/DropIndexForComponentsProjectUuidWithSpecialNameTest/schema.sql
@@ -34,7 +34,7 @@ CREATE TABLE "COMPONENTS"(
);
CREATE UNIQUE INDEX "PROJECTS_KEE" ON "COMPONENTS"("KEE" NULLS FIRST);
CREATE INDEX "PROJECTS_MODULE_UUID" ON "COMPONENTS"("MODULE_UUID" NULLS FIRST);
-CREATE INDEX "IDX_1234_PROJECTS_PROJECT_UUID" ON "COMPONENTS"("PROJECT_UUID" NULLS FIRST);
+CREATE INDEX "IDX_123456789123456789_PROJECTS_PROJECT_UUID" ON "COMPONENTS"("PROJECT_UUID" NULLS FIRST);
CREATE INDEX "PROJECTS_QUALIFIER" ON "COMPONENTS"("QUALIFIER" NULLS FIRST);
CREATE INDEX "PROJECTS_ROOT_UUID" ON "COMPONENTS"("ROOT_UUID" NULLS FIRST);
CREATE INDEX "PROJECTS_UUID" ON "COMPONENTS"("UUID" NULLS FIRST);