]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-15234 Make 9.x migrations re-entrant
authorJacek <jacek.poreda@sonarsource.com>
Mon, 2 Aug 2021 09:09:39 +0000 (11:09 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 3 Aug 2021 20:07:45 +0000 (20:07 +0000)
14 files changed:
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DbPrimaryKeyConstraintFinder.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTable.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsData.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropManualMeasuresTable.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTableTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTableTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest.java
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql [new file with mode: 0644]

index 01754aebd0c3ab6285f2c294bbf5e119031d392b..66758cb72d0a5d6c0a54e6817886824c94ec1928 100644 (file)
@@ -113,10 +113,6 @@ public class DbPrimaryKeyConstraintFinder {
       + "WHERE table_name = '%s' and constraint_type = 'PRIMARY KEY'", tableName.toUpperCase(Locale.ENGLISH));
   }
 
-  static IllegalStateException constraintNotFoundException(String tableName) {
-    return new IllegalStateException(format("Cannot find constraint for table '%s'", tableName));
-  }
-
   // FIXME:: this method should be moved somewhere else
   String getPostgresSqlSequence(String tableName, String columnName) throws SQLException {
     try (Connection connection = db.getDataSource().getConnection();
index 6fe760c69d5e8560d7afab2180a7e909be20d10e..177f2fc2e1a461fbbd31d22da461d4d9f515c51a 100644 (file)
@@ -62,6 +62,10 @@ public abstract class DataChange implements MigrationStep {
     return res;
   }
 
+  protected Database getDatabase() {
+    return db;
+  }
+
   public static class Context {
     private final Database db;
     private final Connection readConnection;
index eef87adac494a377442d0a0461161a58d05e8608..bf9670f8a6e68241c00335f028e54d93fdd8cc90 100644 (file)
@@ -19,7 +19,9 @@
  */
 package org.sonar.server.platform.db.migration.version.v91;
 
+import java.sql.SQLException;
 import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef;
 import org.sonar.server.platform.db.migration.def.ColumnDef;
 import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
@@ -33,7 +35,6 @@ import static java.util.stream.Stream.of;
 import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
-import java.sql.SQLException;
 
 public class CreateAuditTable extends DdlChange {
 
@@ -43,6 +44,10 @@ public class CreateAuditTable extends DdlChange {
 
   @Override
   public void execute(Context context) throws SQLException {
+    if (auditTableExists()) {
+      return;
+    }
+
     BigIntegerColumnDef createdAtColumn = newBigIntegerColumnDefBuilder().setColumnName("created_at").setIsNullable(false).build();
     var tableName = "audits";
     context.execute(new CreateTableBuilder(getDialect(), tableName)
@@ -70,4 +75,10 @@ public class CreateAuditTable extends DdlChange {
   private static VarcharColumnDef.Builder newVarcharColumnBuilder(String column) {
     return newVarcharColumnDefBuilder().setColumnName(column);
   }
+
+  private boolean auditTableExists() throws SQLException {
+    try (var connection = getDatabase().getDataSource().getConnection()) {
+      return DatabaseUtils.tableExists("audits", connection);
+    }
+  }
 }
index c1036f8667d34cd730cd2ef45cc91ba5373a7466..6667a0d88720196653360afd22b4f6834ba3ea0c 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91;
 
 import java.sql.SQLException;
 import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.server.platform.db.migration.step.DataChange;
 
 abstract class DropCustomMetricsData extends DataChange {
@@ -31,6 +32,10 @@ abstract class DropCustomMetricsData extends DataChange {
 
   @Override
   protected void execute(Context context) throws SQLException {
+    if (!checkIfUserManagedColumnExists()) {
+      return;
+    }
+
     var massUpdate = context.prepareMassUpdate();
     massUpdate.select(selectQuery()).setBoolean(1, true);
     massUpdate.update(updateQuery());
@@ -41,6 +46,15 @@ abstract class DropCustomMetricsData extends DataChange {
     });
   }
 
+  private boolean checkIfUserManagedColumnExists() throws SQLException {
+    try (var connection = getDatabase().getDataSource().getConnection()) {
+      if (DatabaseUtils.tableColumnExists(connection, "metrics", "user_managed")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   abstract String selectQuery();
 
   abstract String updateQuery();
index 3856a5adce824497306e5e17a92cc56ef0e3bbea..209126cc82f8348b4944d95431a698725aa150ee 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91;
 
 import java.sql.SQLException;
 import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.server.platform.db.migration.sql.DropTableBuilder;
 import org.sonar.server.platform.db.migration.step.DdlChange;
 
@@ -33,6 +34,15 @@ public class DropManualMeasuresTable extends DdlChange {
 
   @Override
   public void execute(Context context) throws SQLException {
+    if (!manualMeasuresExists()) {
+      return;
+    }
     context.execute(new DropTableBuilder(getDialect(), TABLE_NAME).build());
   }
+
+  private boolean manualMeasuresExists() throws SQLException {
+    try (var connection = getDatabase().getDataSource().getConnection()) {
+      return DatabaseUtils.tableExists(TABLE_NAME, connection);
+    }
+  }
 }
index db4c4b809568eba3f834fd210122b2427445c91b..56e8bb18f10f58c1ec3d9a983da221bcf5af4ef4 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91;
 
 import java.sql.SQLException;
 import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.dialect.MsSql;
 import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder;
 import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder;
@@ -31,18 +32,28 @@ public class DropUserManagedColumnFromMetricsTable extends DdlChange {
   private static final String TABLE_NAME = "metrics";
   private static final String COLUMN = "user_managed";
 
-  private final Database db;
-
   public DropUserManagedColumnFromMetricsTable(Database db) {
     super(db);
-    this.db = db;
   }
 
   @Override
   public void execute(Context context) throws SQLException {
-    if (MsSql.ID.equals(db.getDialect().getId())) {
-      context.execute(new DropMsSQLDefaultConstraintsBuilder(db).setTable(TABLE_NAME).setColumns(COLUMN).build());
+    if (!checkIfUseManagedColumnExists()) {
+      return;
+    }
+
+    if (MsSql.ID.equals(getDatabase().getDialect().getId())) {
+      context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(TABLE_NAME).setColumns(COLUMN).build());
+    }
+    context.execute(new DropColumnsBuilder(getDatabase().getDialect(), TABLE_NAME, COLUMN).build());
+  }
+
+  private boolean checkIfUseManagedColumnExists() throws SQLException {
+    try (var connection = getDatabase().getDataSource().getConnection()) {
+      if (DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN)) {
+        return true;
+      }
     }
-    context.execute(new DropColumnsBuilder(db.getDialect(), TABLE_NAME, COLUMN).build());
+    return false;
   }
 }
index c1be0fdbd831c943f28eea9dda54929b1f2b7893..30a67f44027e69e90ffc05d150b31c005664980b 100644 (file)
@@ -42,4 +42,16 @@ public class CreateAuditTableTest {
     db.assertTableExists(TABLE_NAME);
     db.assertIndex(TABLE_NAME, "audits_created_at", "created_at");
   }
+
+  @Test
+  public void migration_should_be_reentrant() throws SQLException {
+    db.assertTableDoesNotExist(TABLE_NAME);
+
+    underTest.execute();
+    //re-entrant
+    underTest.execute();
+
+    db.assertTableExists(TABLE_NAME);
+    db.assertIndex(TABLE_NAME, "audits_created_at", "created_at");
+  }
 }
index 1eb208caf589bd52e651e94622378ee8e8b2a92c..2a63bc6f27a47153309894bc2de76f52a6e8379d 100644 (file)
@@ -37,6 +37,9 @@ public class DropCustomMetricsLiveMeasuresDataTest {
   @Rule
   public final CoreDbTester db = CoreDbTester.createForSchema(DropCustomMetricsLiveMeasuresDataTest.class, "schema.sql");
 
+  @Rule
+  public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropCustomMetricsLiveMeasuresDataTest.class, "no_user_managed_column.sql");
+
   private final DataChange underTest = new DropCustomMetricsLiveMeasuresData(db.database());
 
   @Test
@@ -124,6 +127,23 @@ public class DropCustomMetricsLiveMeasuresDataTest {
       .containsExactlyInAnyOrder("lm-1", "lm-2", "lm-3", "lm-4");
   }
 
+  @Test
+  public void does_not_fail_when_no_user_managed_column() throws SQLException {
+    insertMetric("metric-1", false);
+    insertMetric("metric-2", false);
+
+    insertLiveMeasure("lm-1", "metric-1");
+    insertLiveMeasure("lm-2", "metric-1");
+    insertLiveMeasure("lm-3", "metric-2");
+    insertLiveMeasure("lm-4", "metric-2");
+
+    DataChange underTest = new DropCustomMetricsLiveMeasuresData(dbWithoutColumn.database());
+    underTest.execute();
+
+    assertThat(db.select("select uuid from live_measures").stream().map(row -> row.get("UUID")).collect(Collectors.toList()))
+        .containsExactlyInAnyOrder("lm-1", "lm-2", "lm-3", "lm-4");
+  }
+
   private void insertLiveMeasure(String uuid, String metricUuid) {
     db.executeInsert("live_measures",
       "UUID", uuid,
index 9bf44f01f6ad3a665adb74c3787498d7372c2c99..74988e691ffb8edcd6c86b0fd993beb6dfa66e76 100644 (file)
@@ -37,6 +37,9 @@ public class DropCustomMetricsProjectMeasuresDataTest {
   @Rule
   public final CoreDbTester db = CoreDbTester.createForSchema(DropCustomMetricsProjectMeasuresDataTest.class, "schema.sql");
 
+  @Rule
+  public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropCustomMetricsProjectMeasuresDataTest.class, "no_user_managed_column.sql");
+
   private final DataChange underTest = new DropCustomMetricsProjectMeasuresData(db.database());
 
   @Test
@@ -124,6 +127,23 @@ public class DropCustomMetricsProjectMeasuresDataTest {
       .containsExactlyInAnyOrder("pm-1", "pm-2", "pm-3", "pm-4");
   }
 
+  @Test
+  public void does_not_fail_when_no_user_managed_column() throws SQLException {
+    insertMetric("metric-1", false);
+    insertMetric("metric-2", false);
+
+    insertProjectMeasure("pm-1", "metric-1");
+    insertProjectMeasure("pm-2", "metric-1");
+    insertProjectMeasure("pm-3", "metric-2");
+    insertProjectMeasure("pm-4", "metric-2");
+
+    DataChange underTest = new DropCustomMetricsProjectMeasuresData(dbWithoutColumn.database());
+    underTest.execute();
+
+    assertThat(db.select("select uuid from project_measures").stream().map(row -> row.get("UUID")).collect(Collectors.toList()))
+        .containsExactlyInAnyOrder("pm-1", "pm-2", "pm-3", "pm-4");
+  }
+
   private void insertProjectMeasure(String uuid, String metricUuid) {
     db.executeInsert("project_measures",
       "UUID", uuid,
index 5c734fe22267796cda3ad737b46e5a208a0b8620..da712d80d663141d308b18f00274f10f026bcf73 100644 (file)
@@ -41,4 +41,13 @@ public class DropUserManagedColumnFromMetricsTableTest {
     underTest.execute();
     db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
   }
+
+  @Test
+  public void migration_should_be_reentrant() throws SQLException {
+    db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true);
+    underTest.execute();
+    // re-entrant
+    underTest.execute();
+    db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+  }
 }
index 98ffe2ab21ed0d2c6ae056edb404adb8701a9c91..7476e750a47117bfc497d2a66f549dbbbc87025a 100644 (file)
@@ -34,6 +34,9 @@ public class DropUserManagedMetricsDataTest {
   @Rule
   public final CoreDbTester db = CoreDbTester.createForSchema(DropUserManagedMetricsDataTest.class, "schema.sql");
 
+  @Rule
+  public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropUserManagedMetricsDataTest.class, "no_user_managed_column.sql");
+
   private final DataChange underTest = new DropUserManagedMetricsData(db.database());
 
   @Test
@@ -99,6 +102,18 @@ public class DropUserManagedMetricsDataTest {
     assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(3);
   }
 
+  @Test
+  public void does_not_fail_when_no_user_managed_column() throws SQLException {
+    insertMetric("1", false);
+    insertMetric("2", false);
+    insertMetric("3", false);
+
+    DataChange underTest = new DropCustomMetricsProjectMeasuresData(dbWithoutColumn.database());
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(3);
+  }
+
   private void insertMetric(String uuid, boolean userManaged) {
     db.executeInsert(TABLE_NAME,
       "UUID", uuid,
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql
new file mode 100644 (file)
index 0000000..4003c83
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE TABLE "METRICS"(
+    "UUID" VARCHAR(40) NOT NULL,
+    "NAME" VARCHAR(64) NOT NULL,
+    "DESCRIPTION" VARCHAR(255),
+    "DIRECTION" INTEGER DEFAULT 0 NOT NULL,
+    "DOMAIN" VARCHAR(64),
+    "SHORT_NAME" VARCHAR(64),
+    "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL,
+    "VAL_TYPE" VARCHAR(8),
+    "ENABLED" BOOLEAN DEFAULT TRUE,
+    "WORST_VALUE" DOUBLE,
+    "BEST_VALUE" DOUBLE,
+    "OPTIMIZED_BEST_VALUE" BOOLEAN,
+    "HIDDEN" BOOLEAN,
+    "DELETE_HISTORICAL_DATA" BOOLEAN,
+    "DECIMAL_SCALE" INTEGER
+);
+ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql
new file mode 100644 (file)
index 0000000..4003c83
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE TABLE "METRICS"(
+    "UUID" VARCHAR(40) NOT NULL,
+    "NAME" VARCHAR(64) NOT NULL,
+    "DESCRIPTION" VARCHAR(255),
+    "DIRECTION" INTEGER DEFAULT 0 NOT NULL,
+    "DOMAIN" VARCHAR(64),
+    "SHORT_NAME" VARCHAR(64),
+    "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL,
+    "VAL_TYPE" VARCHAR(8),
+    "ENABLED" BOOLEAN DEFAULT TRUE,
+    "WORST_VALUE" DOUBLE,
+    "BEST_VALUE" DOUBLE,
+    "OPTIMIZED_BEST_VALUE" BOOLEAN,
+    "HIDDEN" BOOLEAN,
+    "DELETE_HISTORICAL_DATA" BOOLEAN,
+    "DECIMAL_SCALE" INTEGER
+);
+ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql
new file mode 100644 (file)
index 0000000..4003c83
--- /dev/null
@@ -0,0 +1,19 @@
+CREATE TABLE "METRICS"(
+    "UUID" VARCHAR(40) NOT NULL,
+    "NAME" VARCHAR(64) NOT NULL,
+    "DESCRIPTION" VARCHAR(255),
+    "DIRECTION" INTEGER DEFAULT 0 NOT NULL,
+    "DOMAIN" VARCHAR(64),
+    "SHORT_NAME" VARCHAR(64),
+    "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL,
+    "VAL_TYPE" VARCHAR(8),
+    "ENABLED" BOOLEAN DEFAULT TRUE,
+    "WORST_VALUE" DOUBLE,
+    "BEST_VALUE" DOUBLE,
+    "OPTIMIZED_BEST_VALUE" BOOLEAN,
+    "HIDDEN" BOOLEAN,
+    "DELETE_HISTORICAL_DATA" BOOLEAN,
+    "DECIMAL_SCALE" INTEGER
+);
+ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME");