]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-12127 optimize migrations on LIVE_MEASURES and PROJECT_MEASURES
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 26 Jun 2019 13:56:09 +0000 (15:56 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 28 Jun 2019 18:21:12 +0000 (20:21 +0200)
36 files changed:
server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java
server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DropIndexBuilder.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/v70/AddLiveMeasuresMetricIndex.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndex.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresP.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasures.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java [deleted file]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasures.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndex.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresP.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationConfigurationModuleTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java [deleted file]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest/live_measures.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest/snapshots.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest/empty.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest/initial.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest/initial.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest/snapshots.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest/live_measures_p.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql

index cc6a884b9b7ac7c411f88712253bdcf4f74ad1db..cd2a7d3a429d2f09c04d5d09fc587bbcfb7c016b 100644 (file)
@@ -294,17 +294,7 @@ public class DatabaseUtils {
   }
 
   private static boolean doTableExists(String table, Connection connection) {
-    String schema = null;
-
-    try {
-      // Using H2 with a JDBC TCP connection is throwing an exception
-      // See org.h2.engine.SessionRemote#getCurrentSchemaName()
-      if (!"H2 JDBC Driver".equals(connection.getMetaData().getDriverName())) {
-        schema = connection.getSchema();
-      }
-    } catch (SQLException e) {
-      Loggers.get(DatabaseUtils.class).warn("Fail to determine schema. Keeping it null for searching tables", e);
-    }
+    String schema = getSchema(connection);
 
     // table type is used to speed-up Oracle by removing introspection of system tables and aliases.
     try (ResultSet rs = connection.getMetaData().getTables(connection.getCatalog(), schema, table, TABLE_TYPE)) {
@@ -320,6 +310,44 @@ public class DatabaseUtils {
     }
   }
 
+  public static boolean indexExists(String table, String index, Connection connection) {
+    return doIndexExists(table, index, connection) ||
+      doIndexExists(table.toLowerCase(Locale.ENGLISH), index, connection) ||
+      doIndexExists(table.toUpperCase(Locale.ENGLISH), index, connection);
+  }
+
+  private static boolean doIndexExists(String table, String index, Connection connection) {
+    String schema = getSchema(connection);
+
+    try (ResultSet rs = connection.getMetaData().getIndexInfo(connection.getCatalog(), schema, table, false, true)) {
+      while (rs.next()) {
+        String indexName = rs.getString("INDEX_NAME");
+        if (index.equalsIgnoreCase(indexName)) {
+          return true;
+        }
+      }
+      return false;
+    } catch (SQLException e) {
+      throw wrapSqlException(e, "Can not check that table %s exists", table);
+    }
+  }
+
+  @CheckForNull
+  private static String getSchema(Connection connection) {
+    String schema = null;
+
+    try {
+      // Using H2 with a JDBC TCP connection is throwing an exception
+      // See org.h2.engine.SessionRemote#getCurrentSchemaName()
+      if (!"H2 JDBC Driver".equals(connection.getMetaData().getDriverName())) {
+        schema = connection.getSchema();
+      }
+    } catch (SQLException e) {
+      Loggers.get(DatabaseUtils.class).warn("Fail to determine schema. Keeping it null for searching tables", e);
+    }
+    return schema;
+  }
+
   public static IllegalStateException wrapSqlException(SQLException e, String message, Object... messageArgs) {
     return new IllegalStateException(format(message, messageArgs), e);
   }
index 3ea4852ccf572c7c36a513375a1f5b836061e4e0..23ae8e7dd329f4b16e50a0ac6b321a2f0d3cdd89 100644 (file)
@@ -405,9 +405,17 @@ public class AbstractDbTester<T extends CoreTestDb> extends ExternalResource {
   }
 
   public void assertTableDoesNotExist(String table) {
+    assertTableExists(table, false);
+  }
+
+  public void assertTableExists(String table) {
+    assertTableExists(table, true);
+  }
+
+  private void assertTableExists(String table, boolean expected) {
     try (Connection connection = getConnection()) {
       boolean tableExists = DatabaseUtils.tableExists(table, connection);
-      assertThat(tableExists).isFalse();
+      assertThat(tableExists).isEqualTo(expected);
     } catch (Exception e) {
       throw new IllegalStateException("Fail to check if table exists", e);
     }
index 1828e4465d473ce797208063361f4dd969ccd039..548cbfbf87cc064fe1959fe20a675dade48c9dd0 100644 (file)
  */
 package org.sonar.server.platform.db.migration.sql;
 
+import java.sql.Connection;
+import java.sql.SQLException;
 import java.util.List;
+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;
@@ -72,4 +76,11 @@ public class DropIndexBuilder {
     }
   }
 
+  public boolean indexExists(Database database) throws SQLException {
+    validateTableName(tableName);
+    validateIndexName(indexName);
+    try (Connection connection = database.getDataSource().getConnection()) {
+      return DatabaseUtils.indexExists(tableName, indexName, connection);
+    }
+  }
 }
index e580d4738168f0f1c8a91239cdbe93b55f39bf6f..a6beab641fc40f1bed45ee360a565e6dc18a0d20 100644 (file)
@@ -48,7 +48,7 @@ public abstract class DataChange implements MigrationStep {
 
   protected abstract void execute(Context context) throws SQLException;
 
-  private Connection createReadUncommittedConnection() throws SQLException {
+  protected Connection createReadUncommittedConnection() throws SQLException {
     Connection connection = db.getDataSource().getConnection();
     connection.setAutoCommit(false);
     if (connection.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)) {
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndex.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndex.java
new file mode 100644 (file)
index 0000000..d089334
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_VARCHAR_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+/**
+ * Add this index only once table is populated to improve efficiency of populating the table.
+ */
+public class AddLiveMeasuresMetricIndex extends DdlChange {
+
+  private static final String TABLE_NAME = "live_measures";
+
+  public AddLiveMeasuresMetricIndex(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new CreateIndexBuilder(getDialect())
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("component_uuid")
+        .setIsNullable(false)
+        .setLimit(UUID_VARCHAR_SIZE)
+        .build())
+      .addColumn(newIntegerColumnDefBuilder()
+        .setColumnName("metric_id")
+        .setIsNullable(false)
+        .build())
+      .setUnique(true)
+      .setTable(TABLE_NAME)
+      .setName("live_measures_component")
+      .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndex.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndex.java
new file mode 100644 (file)
index 0000000..0f55557
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BooleanColumnDef.newBooleanColumnDefBuilder;
+
+public class AddSnapshotIsLastIndex extends DdlChange {
+  static final String TABLE_NAME = "snapshots";
+  static final String INDEX_NAME = "ix_snapshot_is_last";
+
+  public AddSnapshotIsLastIndex(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(
+      new CreateIndexBuilder(getDialect())
+        .setTable(TABLE_NAME)
+        .setName(INDEX_NAME)
+        .setUnique(false)
+        .addColumn(newBooleanColumnDefBuilder().setColumnName("islast").setIsNullable(false).setDefaultValue(false).build())
+        .build());
+  }
+}
index ad4a5ed97d48b48745628b36bbd620797c503d24..a86d9d78e8f968b69583138743feb51cef4fef63 100644 (file)
@@ -31,6 +31,7 @@ import static org.sonar.server.platform.db.migration.def.BlobColumnDef.newBlobCo
 import static org.sonar.server.platform.db.migration.def.DecimalColumnDef.newDecimalColumnDefBuilder;
 import static org.sonar.server.platform.db.migration.def.IntegerColumnDef.newIntegerColumnDefBuilder;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_VARCHAR_SIZE;
 import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
 
 public class CreateTableLiveMeasures extends DdlChange {
@@ -43,21 +44,23 @@ public class CreateTableLiveMeasures extends DdlChange {
 
   @Override
   public void execute(Context context) throws SQLException {
+    VarcharColumnDef projectUuidCol = newVarcharColumnDefBuilder()
+      .setColumnName("project_uuid")
+      .setIsNullable(false)
+      .setLimit(UUID_VARCHAR_SIZE)
+      .build();
+
     context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME)
       .addPkColumn(newVarcharColumnDefBuilder()
         .setColumnName("uuid")
         .setIsNullable(false)
         .setLimit(VarcharColumnDef.UUID_SIZE)
         .build())
-      .addColumn(newVarcharColumnDefBuilder()
-        .setColumnName("project_uuid")
-        .setIsNullable(false)
-        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
-        .build())
+      .addColumn(projectUuidCol)
       .addColumn(newVarcharColumnDefBuilder()
         .setColumnName("component_uuid")
         .setIsNullable(false)
-        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
+        .setLimit(UUID_VARCHAR_SIZE)
         .build())
       .addColumn(newIntegerColumnDefBuilder()
         .setColumnName("metric_id")
@@ -97,29 +100,10 @@ public class CreateTableLiveMeasures extends DdlChange {
       .build());
 
     context.execute(new CreateIndexBuilder(getDialect())
-      .addColumn(newVarcharColumnDefBuilder()
-        .setColumnName("project_uuid")
-        .setIsNullable(false)
-        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
-        .build())
+      .addColumn(projectUuidCol)
       .setUnique(false)
       .setTable(TABLE_NAME)
       .setName("live_measures_project")
       .build());
-
-    context.execute(new CreateIndexBuilder(getDialect())
-      .addColumn(newVarcharColumnDefBuilder()
-        .setColumnName("component_uuid")
-        .setIsNullable(false)
-        .setLimit(VarcharColumnDef.UUID_VARCHAR_SIZE)
-        .build())
-      .addColumn(newIntegerColumnDefBuilder()
-        .setColumnName("metric_id")
-        .setIsNullable(false)
-        .build())
-      .setUnique(true)
-      .setTable(TABLE_NAME)
-      .setName("live_measures_component")
-      .build());
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresP.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresP.java
new file mode 100644 (file)
index 0000000..faf4bb4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_VARCHAR_SIZE;
+
+public class CreateTempTableLiveMeasuresP extends DdlChange {
+
+  static final String TABLE_NAME = "live_measures_p";
+
+  public CreateTempTableLiveMeasuresP(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    CreateTableBuilder builder = new CreateTableBuilder(getDialect(), TABLE_NAME);
+    if (!builder.tableExists(getDatabase())) {
+      context.execute(builder
+        .addPkColumn(new VarcharColumnDef.Builder()
+          .setColumnName("project_uuid")
+          .setLimit(UUID_VARCHAR_SIZE)
+          .setIsNullable(false)
+          .setIgnoreOracleUnit(true)
+          .build())
+        .build());
+    }
+  }
+}
index 1f7757a4519f2066f2181f62a6256228315b7413..7f080c3243d96da25bfe0aa91924093566cba763 100644 (file)
@@ -27,30 +27,39 @@ public class DbVersion70 implements DbVersion {
   @Override
   public void addSteps(MigrationStepRegistry registry) {
     registry
-      .add(1900, "Add QUALITY_GATES.IS_BUILT_IN", AddIsBuiltInToQualityGates.class)
-      .add(1901, "Populate QUALITY_GATES.IS_BUILT_IN", PopulateQualityGatesIsBuiltIn.class)
-      .add(1902, "Make QUALITY_GATES.IS_BUILT_IN not null", MakeQualityGatesIsBuiltInNotNullable.class)
-      .add(1903, "Remove quality gates loaded templates", RemoveQualityGateLoadedTemplates.class)
-      .add(1904, "Rename quality gate \"SonarQube way\" to \"Sonar way\"", RenameOldSonarQubeWayQualityGate.class)
-      .add(1905, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class)
-      .add(1906, "Create table live_measures", CreateTableLiveMeasures.class)
-      .add(1907, "Populate table live_measures", PopulateLiveMeasures.class)
-      .add(1908, "Delete person and file measures", DeletePersonAndFileMeasures.class)
-      .add(1909, "Drop index on project_measures.person_id", DropIndexOnPersonMeasures.class)
-      .add(1910, "Create ORG_QUALITY_GATES table", CreateOrgQualityGatesTable.class)
-      .add(1911, "Add ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID", AddDefaultQualityGateUuidToOrganizations.class)
-      .add(1912, "Create QUALITY_GATES.UUID", AddUuidToQualityGates.class)
-      .add(1913, "Populate QUALITY_GATES.UUID", PopulateUuidOnQualityGates.class)
-      .add(1914, "Make QUALITY_GATES.UUID not nullable", MakeUuidNotNullableOnQualityGates.class)
-      .add(1915, "Drop unique index on QUALITY_GATES.NAME", DropUniqueIndexOnQualityGatesName.class)
-      .add(1916, "Create builtin quality gate if required", CreateBuiltInQualityGate.class)
-      .add(1917, "Populate ORG_QUALITY_GATES table", PopulateOrgQualityGates.class)
-      .add(1918, "Populate default quality gate on organization", PopulateDefaultQualityGate.class)
-      .add(1919, "Associate existing quality gates to default organization", AssociateQualityGatesToDefaultOrganization.class)
-      .add(1920, "Read 'sonar.qualitygate' setting and set the value to default organization", ReadGlobalSonarQualityGateSettingToDefaultOrg.class)
-      .add(1921, "Delete 'sonar.qualitygate' setting at global level", DeleteGlobalSonarQualityGateSetting.class)
-      .add(1922, "Make ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID not nullable", SetDefaultQualityGateUuidAsNotNullableInOrganizations.class)
-      .add(1923, "Add users.homepage_type and users.homepage_parameter", AddHomepageToUsers.class)
+      .add(1930, "Add QUALITY_GATES.IS_BUILT_IN", AddIsBuiltInToQualityGates.class)
+      .add(1931, "Populate QUALITY_GATES.IS_BUILT_IN", PopulateQualityGatesIsBuiltIn.class)
+      .add(1932, "Make QUALITY_GATES.IS_BUILT_IN not null", MakeQualityGatesIsBuiltInNotNullable.class)
+      .add(1933, "Remove quality gates loaded templates", RemoveQualityGateLoadedTemplates.class)
+      .add(1934, "Rename quality gate \"SonarQube way\" to \"Sonar way\"", RenameOldSonarQubeWayQualityGate.class)
+      .add(1935, "Drop LOADED_TEMPLATES table", DropLoadedTemplatesTable.class)
+
+      // optimizations of migrations from 7.0 on project_measures and live_measures
+      .add(1936, "Delete person measures", DeletePersonMeasures.class)
+      .add(1937, "Drop index on project_measures.person_id", DropIndexOnPersonMeasures.class)
+      .add(1938, "Create table live_measures", CreateTableLiveMeasures.class)
+      .add(1939, "Add temporary index on SNAPSHOTS.ISLAST", AddSnapshotIsLastIndex.class)
+      .add(1940," Create temporary table LIVE_MEASURES_P", CreateTempTableLiveMeasuresP.class)
+      .add(1941, "Populate table live_measures", PopulateLiveMeasures.class)
+      .add(1942, "Add live_measures.metric_id index", AddLiveMeasuresMetricIndex.class)
+      .add(1943, "Drop temporary table LIVE_MEASURES_P", DropTempTableLiveMeasuresP.class)
+      .add(1944, "Drop temporary index on SNAPSHOTS.ISLAST", DropSnapshotIsLastIndex.class)
+      .add(1945, "Delete file measures", DeleteFileMeasures.class)
+
+      .add(1946, "Create ORG_QUALITY_GATES table", CreateOrgQualityGatesTable.class)
+      .add(1947, "Add ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID", AddDefaultQualityGateUuidToOrganizations.class)
+      .add(1948, "Create QUALITY_GATES.UUID", AddUuidToQualityGates.class)
+      .add(1949, "Populate QUALITY_GATES.UUID", PopulateUuidOnQualityGates.class)
+      .add(1950, "Make QUALITY_GATES.UUID not nullable", MakeUuidNotNullableOnQualityGates.class)
+      .add(1951, "Drop unique index on QUALITY_GATES.NAME", DropUniqueIndexOnQualityGatesName.class)
+      .add(1952, "Create builtin quality gate if required", CreateBuiltInQualityGate.class)
+      .add(1953, "Populate ORG_QUALITY_GATES table", PopulateOrgQualityGates.class)
+      .add(1954, "Populate default quality gate on organization", PopulateDefaultQualityGate.class)
+      .add(1955, "Associate existing quality gates to default organization", AssociateQualityGatesToDefaultOrganization.class)
+      .add(1956, "Read 'sonar.qualitygate' setting and set the value to default organization", ReadGlobalSonarQualityGateSettingToDefaultOrg.class)
+      .add(1957, "Delete 'sonar.qualitygate' setting at global level", DeleteGlobalSonarQualityGateSetting.class)
+      .add(1958, "Make ORGANIZATIONS.DEFAULT_QUALITY_GATE_UUID not nullable", SetDefaultQualityGateUuidAsNotNullableInOrganizations.class)
+      .add(1959, "Add users.homepage_type and users.homepage_parameter", AddHomepageToUsers.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasures.java
new file mode 100644 (file)
index 0000000..58861ab
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class DeleteFileMeasures extends DataChange {
+
+  public DeleteFileMeasures(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select c.uuid from projects c where c.qualifier = 'UTS' or  c.qualifier = 'FIL'");
+    massUpdate.rowPluralName("files");
+    massUpdate.update("delete from project_measures pm where pm.component_uuid=?")
+      .setBatchSize(10);
+
+    massUpdate.execute((row, update) -> {
+      String componentUuid = row.getString(1);
+      update.setString(1, componentUuid);
+      return true;
+    });
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java
deleted file mode 100644 (file)
index 2a942e2..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.version.v70;
-
-import java.sql.SQLException;
-import org.sonar.api.config.Configuration;
-import org.sonar.db.Database;
-import org.sonar.db.dialect.H2;
-import org.sonar.db.dialect.MsSql;
-import org.sonar.db.dialect.MySql;
-import org.sonar.db.dialect.Oracle;
-import org.sonar.db.dialect.PostgreSql;
-import org.sonar.server.platform.db.migration.step.DataChange;
-import org.sonar.server.platform.db.migration.step.MassUpdate;
-
-public class DeletePersonAndFileMeasures extends DataChange {
-
-  private final Configuration configuration;
-
-  public DeletePersonAndFileMeasures(Database db, Configuration configuration) {
-    super(db);
-    this.configuration = configuration;
-  }
-
-  @Override
-  protected void execute(Context context) throws SQLException {
-    if (configuration.getBoolean("sonar.sonarcloud.enabled").orElse(false)) {
-      // clean-up will be done in background so that interruption of service
-      // is reduced during upgrade
-      return;
-    }
-    MassUpdate massUpdate = context.prepareMassUpdate();
-    massUpdate.select("select uuid from snapshots");
-    massUpdate.rowPluralName("snapshots");
-    massUpdate.update(getDeleteSql()).setBatchSize(1);
-
-    massUpdate.execute((row, update) -> {
-      String analysisUuid = row.getString(1);
-      update.setString(1, analysisUuid);
-      if (getDialect().getId().equals(Oracle.ID)) {
-        update.setString(2, analysisUuid);
-      }
-      return true;
-    });
-  }
-
-  private String getDeleteSql() {
-    switch (getDialect().getId()) {
-      case MySql.ID:
-      case MsSql.ID:
-        return "delete pm from project_measures pm " +
-          "inner join projects c on c.uuid = pm.component_uuid " +
-          "where pm.analysis_uuid = ? " +
-          "and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null)";
-      case H2.ID:
-        return "delete from project_measures " +
-          "where id in ( " +
-          "  select pm.id from project_measures pm " +
-          "  inner join projects c on c.uuid = pm.component_uuid " +
-          "  where pm.analysis_uuid = ? " +
-          "  and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " +
-          ")";
-      case PostgreSql.ID:
-        return "delete from project_measures pm " +
-          "using projects c " +
-          "where pm.analysis_uuid = ? " +
-          "and c.uuid = pm.component_uuid " +
-          "and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) ";
-      case Oracle.ID:
-        return "delete from project_measures pm where exists (" +
-          "  select 1 from project_measures pm2 " +
-          "  inner join projects c on c.uuid = pm2.component_uuid " +
-          "  where pm2.analysis_uuid = ? " +
-          "  and (c.qualifier in ('UTS', 'FIL') or pm.person_id is not null) " +
-          "  and pm.id = pm2.id" +
-          ") and pm.analysis_uuid = ?";
-      default:
-        throw new IllegalStateException("Unsupported DB dialect: " + getDialect());
-    }
-  }
-}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasures.java
new file mode 100644 (file)
index 0000000..1f78dc9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.MySql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class DeletePersonMeasures extends DataChange {
+
+  public DeletePersonMeasures(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("select uuid from snapshots");
+    massUpdate.rowPluralName("snapshots");
+    massUpdate.update(getDeleteSql()).setBatchSize(1);
+
+    massUpdate.execute((row, update) -> {
+      String analysisUuid = row.getString(1);
+      update.setString(1, analysisUuid);
+      if (getDialect().getId().equals(Oracle.ID)) {
+        update.setString(2, analysisUuid);
+      }
+      return true;
+    });
+  }
+
+  private String getDeleteSql() {
+    switch (getDialect().getId()) {
+      case MySql.ID:
+      case MsSql.ID:
+        return "delete pm from project_measures pm " +
+          "inner join projects c on c.uuid = pm.component_uuid " +
+          "where pm.analysis_uuid = ? " +
+          "and pm.person_id is not null";
+      case H2.ID:
+        return "delete from project_measures " +
+          "where id in ( " +
+          "  select pm.id from project_measures pm " +
+          "  inner join projects c on c.uuid = pm.component_uuid " +
+          "  where pm.analysis_uuid = ? " +
+          "  and pm.person_id is not null" +
+          ")";
+      case PostgreSql.ID:
+        return "delete from project_measures pm " +
+          "using projects c " +
+          "where pm.analysis_uuid = ? " +
+          "and c.uuid = pm.component_uuid " +
+          "and pm.person_id is not null";
+      case Oracle.ID:
+        return "delete from project_measures pm where exists (" +
+          "  select 1 from project_measures pm2 " +
+          "  inner join projects c on c.uuid = pm2.component_uuid " +
+          "  where pm2.analysis_uuid = ? " +
+          "  and pm.person_id is not null " +
+          "  and pm.id = pm2.id" +
+          ") and pm.analysis_uuid = ?";
+      default:
+        throw new IllegalStateException("Unsupported DB dialect: " + getDialect());
+    }
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndex.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndex.java
new file mode 100644 (file)
index 0000000..554a084
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropSnapshotIsLastIndex extends DdlChange {
+  public DropSnapshotIsLastIndex(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(
+      new DropIndexBuilder(getDialect())
+        .setTable(AddSnapshotIsLastIndex.TABLE_NAME)
+        .setName(AddSnapshotIsLastIndex.INDEX_NAME)
+        .build());
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresP.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresP.java
new file mode 100644 (file)
index 0000000..acec907
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.sql.DropTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropTempTableLiveMeasuresP extends DdlChange {
+  public DropTempTableLiveMeasuresP(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    if (new CreateTableBuilder(getDialect(), CreateTempTableLiveMeasuresP.TABLE_NAME).tableExists(getDatabase())) {
+      DropTableBuilder builder = new DropTableBuilder(getDialect(), CreateTempTableLiveMeasuresP.TABLE_NAME);
+      context.execute(builder.build());
+    }
+  }
+}
index 9115bba8ce0dfbc423f56a759c2f447b91ad3c0f..2ad1464d4a14fe536a7ff6cdb581d3541bdb7480 100644 (file)
  */
 package org.sonar.server.platform.db.migration.version.v70;
 
+import java.sql.Connection;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.concurrent.Immutable;
 import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
 import org.sonar.core.util.Uuids;
 import org.sonar.db.Database;
 import org.sonar.server.platform.db.migration.step.DataChange;
 import org.sonar.server.platform.db.migration.step.MassUpdate;
+import org.sonar.server.platform.db.migration.step.Select;
+import org.sonar.server.platform.db.migration.step.SelectImpl;
+import org.sonar.server.platform.db.migration.step.SqlStatement;
+import org.sonar.server.platform.db.migration.step.Upsert;
+
+import static java.util.stream.Collectors.joining;
 
 public class PopulateLiveMeasures extends DataChange {
+  private static final Logger LOG = Loggers.get(PopulateLiveMeasures.class);
 
   private final System2 system2;
+  private final Database db;
 
   public PopulateLiveMeasures(Database db, System2 system2) {
     super(db);
+    this.db = db;
     this.system2 = system2;
   }
 
   @Override
   protected void execute(Context context) throws SQLException {
+    boolean firstAttempt = context.prepareSelect("select count(1) from live_measures_p")
+      .get(t -> t.getLong(1)) == 0;
+    if (!firstAttempt) {
+      LOG.info("Retry detected (non empty table live_measures_p). Handle it");
+    }
+
     long now = system2.now();
-    // reentrancy of migration
-    context.prepareUpsert("TRUNCATE TABLE live_measures").execute();
+    int projectBatchSize = 10;
+    String statement = "" +
+      " select" +
+      "   s.uuid, s.component_uuid" +
+      " from snapshots s" +
+      " where" +
+      "   s.islast = ?" +
+      (firstAttempt ? "" : "   and not exists (select 1 from live_measures_p lmp where lmp.project_uuid=s.component_uuid)");
+    try (Connection connection = createReadUncommittedConnection();
+      Select select = SelectImpl.create(db, connection, statement).setBoolean(1, true)) {
+      List<Row> rows = new ArrayList<>(projectBatchSize);
+      select.scroll(t -> {
+        rows.add(new Row(t.getString(1), t.getString(2)));
+        if (rows.size() == projectBatchSize) {
+          processProjectBatch(context, rows, firstAttempt, now);
 
-    MassUpdate massUpdate = context.prepareMassUpdate();
-    massUpdate.select("SELECT p.uuid, p.project_uuid, pm.metric_id, pm.value, pm.text_value, pm.variation_value_1, pm.measure_data " +
-      "FROM project_measures pm " +
-      "INNER JOIN projects p on p.uuid = pm.component_uuid " +
-      "INNER JOIN snapshots s on s.uuid = pm.analysis_uuid " +
-      "WHERE s.islast = ? and pm.person_id is null")
-      .setBoolean(1, true);
+          rows.clear();
+        }
+      });
 
-    massUpdate.update("INSERT INTO live_measures "
-      + "(uuid, component_uuid, project_uuid, metric_id, value, text_value, variation, measure_data, created_at, updated_at) "
-      + "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+      if (!rows.isEmpty()) {
+        processProjectBatch(context, rows, firstAttempt, now);
+      }
+    }
+  }
 
+  private static void processProjectBatch(Context context, List<Row> rows, boolean firstAttempt, long now) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate();
     massUpdate.rowPluralName("live measures");
-    massUpdate.execute((row, update) -> {
+    setSelect(rows, massUpdate);
+    massUpdate.update("delete from live_measures where project_uuid=?");
+    massUpdate.update(" insert into live_measures" +
+      "   (uuid, component_uuid, project_uuid, metric_id, value, text_value, variation, measure_data, created_at, updated_at)" +
+      " values" +
+      "   (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+    massUpdate.update("insert into live_measures_p (project_uuid) values (?)");
+    LiveMeasurePopulationMultiHandler handler = new LiveMeasurePopulationMultiHandler(firstAttempt, rows, now);
+    massUpdate.execute(handler);
+
+    Set<String> notCommittedProjectUuids = handler.notCommittedProjectUuids;
+    if (!notCommittedProjectUuids.isEmpty()) {
+      Upsert upsert = context.prepareUpsert("insert into live_measures_p (project_uuid) values (?)");
+      for (String projectUuid : notCommittedProjectUuids) {
+        upsert.setString(1, projectUuid);
+        upsert.execute();
+      }
+      upsert.commit();
+    }
+  }
+
+  private static void setSelect(List<Row> rows, MassUpdate massUpdate) throws SQLException {
+    String questionMarks = rows.stream().map(t -> "?").collect(joining(",", "(", ")"));
+    SqlStatement select = massUpdate.select("" +
+      " select" +
+      "   p.project_uuid, pm.component_uuid, pm.metric_id, pm.value, pm.text_value, pm.variation_value_1, pm.measure_data" +
+      " from project_measures pm" +
+      " inner join projects p on p.uuid = pm.component_uuid and p.project_uuid in " + questionMarks +
+      " where" +
+      "   pm.analysis_uuid in " + questionMarks +
+      " order by" +
+      "   p.project_uuid");
+    int i = 1;
+    for (Row row : rows) {
+      select.setString(i, row.getProjectUuid());
+      i++;
+    }
+    for (Row row : rows) {
+      select.setString(i, row.getAnalysisUuid());
+      i++;
+    }
+  }
+
+  private static class LiveMeasurePopulationMultiHandler implements MassUpdate.MultiHandler {
+    private final boolean firstAttempt;
+    private final long now;
+    private final Set<String> notCommittedProjectUuids;
+    private String deletePreviousProjectUuid = null;
+    private String currentProjectUuid = null;
+
+    private LiveMeasurePopulationMultiHandler(boolean firstAttempt, List<Row> rows, long now) {
+      this.firstAttempt = firstAttempt;
+      this.now = now;
+      this.notCommittedProjectUuids = rows.stream().map(Row::getProjectUuid).collect(Collectors.toSet());
+    }
+
+    @Override
+    public boolean handle(Select.Row row, SqlStatement update, int updateIndex) throws SQLException {
+      String projectUuid = row.getString(1);
+      switch (updateIndex) {
+        case 0:
+          return doRentranceDelete(update, projectUuid);
+        case 1:
+          return doLiveMeasureInsert(row, update, projectUuid);
+        case 2:
+          return doComponentDoneInsert(update, projectUuid);
+        default:
+          throw new IllegalStateException("Unsupported update index" + updateIndex);
+      }
+    }
+
+    private boolean doRentranceDelete(SqlStatement update, String projectUuid) throws SQLException {
+      if (firstAttempt) {
+        return false;
+      }
+
+      if (deletePreviousProjectUuid == null || !deletePreviousProjectUuid.equals(projectUuid)) {
+        update.setString(1, projectUuid);
+        deletePreviousProjectUuid = projectUuid;
+        return true;
+      }
+      return false;
+    }
+
+    private boolean doLiveMeasureInsert(Select.Row row, SqlStatement update, String projectUuid) throws SQLException {
       update.setString(1, Uuids.create());
-      update.setString(2, row.getString(1));
-      update.setString(3, row.getString(2));
+      update.setString(2, row.getString(2));
+      update.setString(3, projectUuid);
       update.setInt(4, row.getInt(3));
       update.setDouble(5, row.getNullableDouble(4));
       update.setString(6, row.getNullableString(5));
@@ -66,7 +186,51 @@ public class PopulateLiveMeasures extends DataChange {
       update.setLong(9, now);
       update.setLong(10, now);
       return true;
-    });
+    }
+
+    /**
+     * When currentProjectUuid changes, we know we are done will all the measures for this component and therefor
+     * can insert it into live_measures_p.
+     * <p>
+     * <strong>This requires statement selecting measures to be sorted by project_uuid and then component_uuid.</strong>
+     */
+    private boolean doComponentDoneInsert(SqlStatement update, String projectUuid) throws SQLException {
+      if (currentProjectUuid == null || currentProjectUuid.equals(projectUuid)) {
+        this.currentProjectUuid = projectUuid;
+        return false;
+      }
+      update.setString(1, currentProjectUuid);
+      this.notCommittedProjectUuids.remove(currentProjectUuid);
+      this.currentProjectUuid = projectUuid;
+      return true;
+    }
+
   }
 
+  @Immutable
+  private static final class Row {
+    private final String analysisUuid;
+    private final String projectUuid;
+
+    private Row(String analysisUuid, String projectUuid) {
+      this.analysisUuid = analysisUuid;
+      this.projectUuid = projectUuid;
+    }
+
+    public String getAnalysisUuid() {
+      return analysisUuid;
+    }
+
+    public String getProjectUuid() {
+      return projectUuid;
+    }
+
+    @Override
+    public String toString() {
+      return "Row{" +
+        "analysisUuid='" + analysisUuid + '\'' +
+        ", projectUuid='" + projectUuid + '\'' +
+        '}';
+    }
+  }
 }
index ec155560d802b3759c11a362fe953e0205f9fc77..5d7a4fa62ddead084222409d39d5395bbf31ff31 100644 (file)
@@ -39,7 +39,7 @@ public class MigrationConfigurationModuleTest {
         // DbVersion classes
         + 20
         // Others
-        + 3);
+        + 4);
   }
 
 }
index 27aa42089a53a0e957cc7b9aada01cba85368744..d47d2ba8c4fd6f9de5b19d59e5b7a91c4faddb8f 100644 (file)
  */
 package org.sonar.server.platform.db.migration.history;
 
-import java.sql.SQLException;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonar.db.CoreDbTester;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
 public class NoTableMigrationHistoryImplTest {
   @Rule
   public CoreDbTester dbTester = CoreDbTester.createEmpty();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
-  private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.database());
+  private MigrationHistoryMeddler migrationHistoryMeddler = mock(MigrationHistoryMeddler.class);
+  private MigrationHistoryImpl underTest = new MigrationHistoryImpl(dbTester.database(), migrationHistoryMeddler);
 
   @Test
   public void start_fails_with_ISE_if_table_history_does_not_exist() {
@@ -39,6 +42,8 @@ public class NoTableMigrationHistoryImplTest {
     expectedException.expectMessage("Migration history table is missing");
 
     underTest.start();
+
+    verifyZeroInteractions(migrationHistoryMeddler);
   }
 
 
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest.java
new file mode 100644 (file)
index 0000000..e4f697a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class AddLiveMeasuresMetricIndexTest {
+  private static final String TABLE = "live_measures";
+
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(AddLiveMeasuresMetricIndexTest.class, "live_measures.sql");
+
+  private AddLiveMeasuresMetricIndex underTest = new AddLiveMeasuresMetricIndex(db.database());
+
+  @Test
+  public void creates_table_on_empty_db() throws SQLException {
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable(TABLE)).isEqualTo(0);
+
+    db.assertUniqueIndex(TABLE, "live_measures_component", "component_uuid", "metric_id");
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest.java
new file mode 100644 (file)
index 0000000..719ee1d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+public class AddSnapshotIsLastIndexTest {
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddSnapshotIsLastIndexTest.class, "snapshots.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private AddSnapshotIsLastIndex underTest = new AddSnapshotIsLastIndex(dbTester.database());
+
+  @Test
+  public void add_index_on_snapshots_islast_column() throws SQLException {
+    underTest.execute();
+
+    dbTester.assertIndex("snapshots", "ix_snapshot_is_last", "islast");
+  }
+
+  @Test
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+}
index 0272f61de1be199a028d7639a19bc16f5e22c0a6..17865dfdd118b3951719214853cebeca56fc7800 100644 (file)
@@ -54,6 +54,5 @@ public class CreateTableLiveMeasuresTest {
     db.assertColumnDefinition(TABLE, "updated_at", Types.BIGINT, null, false);
 
     db.assertIndex(TABLE, "live_measures_project", "project_uuid");
-    db.assertUniqueIndex(TABLE, "live_measures_component", "component_uuid", "metric_id");
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest.java
new file mode 100644 (file)
index 0000000..631888e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+public class CreateTempTableLiveMeasuresPTest {
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(CreateTempTableLiveMeasuresPTest.class, "empty.sql");
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private CreateTempTableLiveMeasuresP underTest = new CreateTempTableLiveMeasuresP(dbTester.database());
+
+  @Test
+  public void create_table_live_measures_p() throws SQLException {
+    underTest.execute();
+
+    dbTester.assertColumnDefinition("live_measures_p", "project_uuid", Types.VARCHAR, 50, false);
+    dbTester.assertPrimaryKey("live_measures_p", "pk_live_measures_p", "project_uuid");
+  }
+
+  @Test
+  public void migration_is_reentrant() throws SQLException {
+    underTest.execute();
+
+    underTest.execute();
+  }
+}
index 3377681993155660d42ffa4d8efb13a4fae9bed8..c237a6c0b16c820628bdee1d6f22fc9c19873e71 100644 (file)
@@ -30,12 +30,12 @@ public class DbVersion70Test {
 
   @Test
   public void migrationNumber_starts_at_1900() {
-    verifyMinimumMigrationNumber(underTest, 1900);
+    verifyMinimumMigrationNumber(underTest, 1930);
   }
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 24);
+    verifyMigrationCount(underTest, 30);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest.java
new file mode 100644 (file)
index 0000000..8befae3
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.commons.lang.math.RandomUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DeleteFileMeasuresTest {
+  private static final AtomicInteger GENERATOR = new AtomicInteger();
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DeleteFileMeasuresTest.class, "initial.sql");
+
+  private DataChange underTest = new DeleteFileMeasures(db.database());
+
+  @Test
+  public void delete_file_and_person_measures() throws SQLException {
+    String projectUuid = "P1";
+    insertComponent(projectUuid, projectUuid, "PRJ", "TRK");
+    insertComponent("D1", projectUuid, "DIR", "DIR");
+    insertComponent("F1", projectUuid, "FIL", "FIL");
+    insertComponent("F2", projectUuid, "FIL", "UTS");
+    insertSnapshot("S1", projectUuid, false);
+    insertSnapshot("S2", projectUuid, true);
+    // past measures
+    long m1 = insertMeasure(projectUuid, "S1");
+    long m2 = insertMeasure("D1", "S1");
+    long m3 = insertMeasure("F1", "S1");
+    long m4 = insertMeasure("F2", "S1");
+    long m5 = insertPersonMeasure(projectUuid, "S1");
+    long m6 = insertPersonMeasure("F1", "S1");
+    // last measures
+    long m7 = insertMeasure(projectUuid, "S2");
+    long m8 = insertMeasure("D1", "S2");
+    long m9 = insertMeasure("F1", "S2");
+    long m10 = insertMeasure("F2", "S2");
+    long m11 = insertPersonMeasure(projectUuid, "S2");
+    long m12 = insertPersonMeasure("F1", "S2");
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
+    assertThatMeasuresAreExactly(m1, m2, m5, m7, m8, m11);
+
+    // migration is re-entrant
+    underTest.execute();
+    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
+    assertThatMeasuresAreExactly(m1, m2, m7, m5, m8, m11);
+  }
+
+  private void assertThatMeasuresAreExactly(long... expectedMeasureIds) {
+    long[] ids = db.select("select id as \"id\" from project_measures")
+      .stream()
+      .mapToLong(m -> (Long) m.get("id"))
+      .toArray();
+    assertThat(ids).containsOnly(expectedMeasureIds);
+  }
+
+  private void insertComponent(String uuid, String projectUuid, String scope, String qualifier) {
+    db.executeInsert("PROJECTS",
+      "ORGANIZATION_UUID", "O1",
+      "KEE", "" + GENERATOR.incrementAndGet(),
+      "UUID", uuid,
+      "PROJECT_UUID", projectUuid,
+      "MAIN_BRANCH_PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
+      "UUID_PATH", ".",
+      "ROOT_UUID", "" + GENERATOR.incrementAndGet(),
+      "PRIVATE", "true",
+      "QUALIFIER", qualifier,
+      "SCOPE", scope);
+  }
+
+  private void insertSnapshot(String uuid, String projectUuid, boolean last) {
+    db.executeInsert("SNAPSHOTS",
+      "UUID", uuid,
+      "COMPONENT_UUID", projectUuid,
+      "STATUS", "P",
+      "ISLAST", last);
+  }
+
+  private long insertMeasure(String componentUuid, String analysisUuid) {
+    long id = GENERATOR.incrementAndGet();
+    db.executeInsert("PROJECT_MEASURES",
+      "ID", id,
+      "METRIC_ID", "42",
+      "COMPONENT_UUID", componentUuid,
+      "ANALYSIS_UUID", analysisUuid);
+    return id;
+  }
+
+  private long insertPersonMeasure(String componentUuid, String analysisUuid) {
+    long id = GENERATOR.incrementAndGet();
+    db.executeInsert("PROJECT_MEASURES",
+      "ID", id,
+      "METRIC_ID", "42",
+      "COMPONENT_UUID", componentUuid,
+      "ANALYSIS_UUID", analysisUuid,
+      "PERSON_ID", RandomUtils.nextInt(100));
+    return id;
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java
deleted file mode 100644 (file)
index a618824..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 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.version.v70;
-
-import java.sql.SQLException;
-import java.util.concurrent.atomic.AtomicInteger;
-import org.apache.commons.lang.math.RandomUtils;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.config.internal.MapSettings;
-import org.sonar.db.CoreDbTester;
-import org.sonar.server.platform.db.migration.step.DataChange;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class DeletePersonAndFileMeasuresTest {
-  private static final AtomicInteger GENERATOR = new AtomicInteger();
-  @Rule
-  public CoreDbTester db = CoreDbTester.createForSchema(DeletePersonAndFileMeasuresTest.class, "initial.sql");
-
-  @Test
-  public void delete_file_and_person_measures() throws SQLException {
-    insertComponent("P1", "PRJ", "TRK");
-    insertComponent("D1", "DIR", "DIR");
-    insertComponent("F1", "FIL", "FIL");
-    insertComponent("F2", "FIL", "UTS");
-    insertSnapshot("S1", "P1", false);
-    insertSnapshot("S2", "P1", true);
-    // past measures
-    long m1 = insertMeasure("P1", "S1");
-    long m2 = insertMeasure("D1", "S1");
-    long m3 = insertMeasure("F1", "S1");
-    long m4 = insertMeasure("F2", "S1");
-    long m5 = insertPersonMeasure("P1", "S1");
-    long m6 = insertPersonMeasure("F1", "S1");
-    // last measures
-    long m7 = insertMeasure("P1", "S2");
-    long m8 = insertMeasure("D1", "S2");
-    long m9 = insertMeasure("F1", "S2");
-    long m10 = insertMeasure("F2", "S2");
-    long m11 = insertPersonMeasure("P1", "S2");
-    long m12 = insertPersonMeasure("F1", "S2");
-
-    run(false);
-
-    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
-    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
-    assertThatMeasuresAreExactly(m1, m2, m7, m8);
-
-    // migration is re-entrant
-    run(false);
-    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
-    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
-    assertThatMeasuresAreExactly(m1, m2, m7, m8);
-  }
-
-  @Test
-  public void migration_is_disabled_on_sonarcloud() throws SQLException {
-    insertComponent("F1", "FIL", "FIL");
-    insertSnapshot("S1", "P1", false);
-    insertMeasure("F1", "S1");
-    insertPersonMeasure("F1", "S1");
-
-    run(true);
-
-    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(1);
-    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(1);
-    assertThat(db.countRowsOfTable("PROJECT_MEASURES")).isEqualTo(2);
-  }
-
-  private void run(boolean sonarCloud) throws SQLException {
-    MapSettings settings = new MapSettings();
-    if (sonarCloud) {
-      settings.setProperty("sonar.sonarcloud.enabled", true);
-    }
-    DataChange underTest = new DeletePersonAndFileMeasures(db.database(), settings.asConfig());
-    underTest.execute();
-  }
-
-  private void assertThatMeasuresAreExactly(long... expectedMeasureIds) {
-    long[] ids = db.select("select id as \"id\" from project_measures")
-      .stream()
-      .mapToLong(m -> (Long) m.get("id"))
-      .toArray();
-    assertThat(ids).containsOnly(expectedMeasureIds);
-  }
-
-  private void insertComponent(String uuid, String scope, String qualifier) {
-    db.executeInsert("PROJECTS",
-      "ORGANIZATION_UUID", "O1",
-      "KEE", "" + GENERATOR.incrementAndGet(),
-      "UUID", uuid,
-      "PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
-      "MAIN_BRANCH_PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
-      "UUID_PATH", ".",
-      "ROOT_UUID", "" + GENERATOR.incrementAndGet(),
-      "PRIVATE", "true",
-      "QUALIFIER", qualifier,
-      "SCOPE", scope);
-  }
-
-  private void insertSnapshot(String uuid, String projectUuid, boolean last) {
-    db.executeInsert("SNAPSHOTS",
-      "UUID", uuid,
-      "COMPONENT_UUID", projectUuid,
-      "STATUS", "P",
-      "ISLAST", last);
-  }
-
-  private long insertMeasure(String componentUuid, String analysisUuid) {
-    long id = GENERATOR.incrementAndGet();
-    db.executeInsert("PROJECT_MEASURES",
-      "ID", id,
-      "METRIC_ID", "42",
-      "COMPONENT_UUID", componentUuid,
-      "ANALYSIS_UUID", analysisUuid);
-    return id;
-  }
-
-  private long insertPersonMeasure(String componentUuid, String analysisUuid) {
-    long id = GENERATOR.incrementAndGet();
-    db.executeInsert("PROJECT_MEASURES",
-      "ID", id,
-      "METRIC_ID", "42",
-      "COMPONENT_UUID", componentUuid,
-      "ANALYSIS_UUID", analysisUuid,
-      "PERSON_ID", RandomUtils.nextInt(100));
-    return id;
-  }
-}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest.java
new file mode 100644 (file)
index 0000000..e0d2141
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.commons.lang.math.RandomUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DeletePersonMeasuresTest {
+  private static final AtomicInteger GENERATOR = new AtomicInteger();
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DeletePersonMeasuresTest.class, "initial.sql");
+
+  private DataChange underTest = new DeletePersonMeasures(db.database());
+
+  @Test
+  public void delete_person_measures() throws SQLException {
+    insertComponent("P1", "PRJ", "TRK");
+    insertComponent("D1", "DIR", "DIR");
+    insertComponent("F1", "FIL", "FIL");
+    insertComponent("F2", "FIL", "UTS");
+    insertSnapshot("S1", "P1", false);
+    insertSnapshot("S2", "P1", true);
+    // past measures
+    long m1 = insertMeasure("P1", "S1");
+    long m2 = insertMeasure("D1", "S1");
+    long m3 = insertMeasure("F1", "S1");
+    long m4 = insertMeasure("F2", "S1");
+    long m5 = insertPersonMeasure("P1", "S1");
+    long m6 = insertPersonMeasure("F1", "S1");
+    // last measures
+    long m7 = insertMeasure("P1", "S2");
+    long m8 = insertMeasure("D1", "S2");
+    long m9 = insertMeasure("F1", "S2");
+    long m10 = insertMeasure("F2", "S2");
+    long m11 = insertPersonMeasure("P1", "S2");
+    long m12 = insertPersonMeasure("F1", "S2");
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
+    assertThatMeasuresAreExactly(m1, m2, m3, m4, m7, m8, m9, m10);
+
+    // migration is re-entrant
+    underTest.execute();
+    assertThat(db.countRowsOfTable("PROJECTS")).isEqualTo(4);
+    assertThat(db.countRowsOfTable("SNAPSHOTS")).isEqualTo(2);
+    assertThatMeasuresAreExactly(m1, m2, m3, m4, m7, m8, m9, m10);
+  }
+
+  private void assertThatMeasuresAreExactly(long... expectedMeasureIds) {
+    long[] ids = db.select("select id as \"id\" from project_measures")
+      .stream()
+      .mapToLong(m -> (Long) m.get("id"))
+      .toArray();
+    assertThat(ids).containsOnly(expectedMeasureIds);
+  }
+
+  private void insertComponent(String uuid, String scope, String qualifier) {
+    db.executeInsert("PROJECTS",
+      "ORGANIZATION_UUID", "O1",
+      "KEE", "" + GENERATOR.incrementAndGet(),
+      "UUID", uuid,
+      "PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
+      "MAIN_BRANCH_PROJECT_UUID", "" + GENERATOR.incrementAndGet(),
+      "UUID_PATH", ".",
+      "ROOT_UUID", "" + GENERATOR.incrementAndGet(),
+      "PRIVATE", "true",
+      "QUALIFIER", qualifier,
+      "SCOPE", scope);
+  }
+
+  private void insertSnapshot(String uuid, String projectUuid, boolean last) {
+    db.executeInsert("SNAPSHOTS",
+      "UUID", uuid,
+      "COMPONENT_UUID", projectUuid,
+      "STATUS", "P",
+      "ISLAST", last);
+  }
+
+  private long insertMeasure(String componentUuid, String analysisUuid) {
+    long id = GENERATOR.incrementAndGet();
+    db.executeInsert("PROJECT_MEASURES",
+      "ID", id,
+      "METRIC_ID", "42",
+      "COMPONENT_UUID", componentUuid,
+      "ANALYSIS_UUID", analysisUuid);
+    return id;
+  }
+
+  private long insertPersonMeasure(String componentUuid, String analysisUuid) {
+    long id = GENERATOR.incrementAndGet();
+    db.executeInsert("PROJECT_MEASURES",
+      "ID", id,
+      "METRIC_ID", "42",
+      "COMPONENT_UUID", componentUuid,
+      "ANALYSIS_UUID", analysisUuid,
+      "PERSON_ID", RandomUtils.nextInt(100));
+    return id;
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest.java
new file mode 100644 (file)
index 0000000..536f348
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class DropSnapshotIsLastIndexTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DropSnapshotIsLastIndexTest.class, "snapshots.sql");
+
+  private DropSnapshotIsLastIndex underTest = new DropSnapshotIsLastIndex(db.database());
+
+  @Test
+  public void drop_index_on_snapshots_islast() throws SQLException {
+    underTest.execute();
+
+    db.assertIndexDoesNotExist("snapshots", "ix_snapshot_is_last");
+  }
+
+  @Test
+  public void migration_is_reentrant() throws SQLException {
+    underTest.execute();
+
+    underTest.execute();
+  }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest.java
new file mode 100644 (file)
index 0000000..5a160e5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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.version.v70;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class DropTempTableLiveMeasuresPTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(DropTempTableLiveMeasuresPTest.class, "live_measures_p.sql");
+
+  private DropTempTableLiveMeasuresP underTest = new DropTempTableLiveMeasuresP(db.database());
+
+  @Test
+  public void drop_table_live_measures_p() throws SQLException {
+    String tableName = "live_measures_p";
+
+    db.assertTableExists(tableName);
+
+    underTest.execute();
+
+    db.assertTableDoesNotExist(tableName);
+  }
+
+  @Test
+  public void migration_is_reentrant() throws SQLException {
+    underTest.execute();
+
+    underTest.execute();
+  }
+}
index 10bd9221d05a6eabe8665b0557f1129f009dadaa..f125ad3f0fbb5a5b176f577d2455489053ef383f 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.platform.db.migration.version.v70;
 
 import java.sql.SQLException;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
@@ -63,12 +62,11 @@ public class PopulateLiveMeasuresTest {
       field("VALUE"),
       field("TEXT_VALUE"),
       field("VARIATION"),
-      field("MEASURE_DATA")
-    ).containsExactlyInAnyOrder(generateLiveMeasures());
-  }
+      field("MEASURE_DATA")).containsExactlyInAnyOrder(generateLiveMeasures());
 
-  private Function<Map<String, Object>, Object> field(String name) {
-    return m -> m.get(name);
+    assertThat(db.select("select project_uuid as \"PROJECT_UUID\" from live_measures_p"))
+      .extracting(t -> t.get("PROJECT_UUID"))
+      .containsOnly("PRJ1", "PRJ2");
   }
 
   @Test
@@ -85,60 +83,184 @@ public class PopulateLiveMeasuresTest {
       field("VALUE"),
       field("TEXT_VALUE"),
       field("VARIATION"),
-      field("MEASURE_DATA")
-    ).containsExactlyInAnyOrder(generateLiveMeasures());
+      field("MEASURE_DATA")).containsExactlyInAnyOrder(generateLiveMeasures());
+  }
+
+  @Test
+  public void do_not_fail_if_live_measure_of_component_already_partially_inserted() throws SQLException {
+    generateProjectMeasures();
+
+    db.executeInsert(
+      "LIVE_MEASURES",
+      "UUID", "foo",
+      "COMPONENT_UUID", "PRJ1",
+      "PROJECT_UUID", "PRJ1",
+      "METRIC_ID", 1010,
+      "CREATED_AT", 1L,
+      "UPDATED_AT", 1L
+    );
+
+    underTest.execute();
+
+  }
+
+  private Function<Map<String, Object>, Object> field(String name) {
+    return m -> m.get(name);
   }
 
   private void generateProjectMeasures() {
-    Map<String, Object> project = new HashMap<>();
-    project.put("UUID", "PRJ1");
-    project.put("ORGANIZATION_UUID", "ORG1");
-    project.put("UUID_PATH", "X");
-    project.put("ROOT_UUID", "X");
-    project.put("PROJECT_UUID", "PRJ1");
-    project.put("PRIVATE", "FALSE");
-    db.executeInsert("PROJECTS", project);
-
-    Map<String, Object> analysis1 = new HashMap<>();
-    analysis1.put("UUID", "A1");
-    analysis1.put("ISLAST", "FALSE");
-    analysis1.put("COMPONENT_UUID", "PRJ1");
-    db.executeInsert("SNAPSHOTS", analysis1);
-
-    Map<String, Object> analysis2 = new HashMap<>();
-    analysis2.put("UUID", "A2");
-    analysis2.put("ISLAST", "TRUE");
-    analysis2.put("COMPONENT_UUID", "PRJ1");
-    db.executeInsert("SNAPSHOTS", analysis2);
-
-    Map<String, Object> measure1 = new HashMap<>();
-    measure1.put("COMPONENT_UUID", "PRJ1");
-    measure1.put("ANALYSIS_UUID", "A1");
-    measure1.put("METRIC_ID", "123");
-    db.executeInsert("PROJECT_MEASURES", measure1);
-
-    Map<String, Object> measure2 = new HashMap<>();
-    measure2.put("COMPONENT_UUID", "PRJ1");
-    measure2.put("ANALYSIS_UUID", "A2");
-    measure2.put("METRIC_ID", "123");
-    measure2.put("VALUE", "234");
-    measure2.put("TEXT_VALUE", "TEXT_VALUEx");
-    measure2.put("VARIATION_VALUE_1", "345");
-    measure2.put("MEASURE_DATA", "FFFF");
-    db.executeInsert("PROJECT_MEASURES", measure2);
-
-    // measures with person_id not null are purged later
-    // by another migration
-    Map<String, Object> personMeasure = new HashMap<>();
-    personMeasure.put("COMPONENT_UUID", "PRJ1");
-    personMeasure.put("ANALYSIS_UUID", "A2");
-    personMeasure.put("METRIC_ID", "200");
-    personMeasure.put("VALUE", "234");
-    personMeasure.put("TEXT_VALUE", "TEXT_VALUEx");
-    personMeasure.put("VARIATION_VALUE_1", "345");
-    personMeasure.put("MEASURE_DATA", "FFFF");
-    personMeasure.put("PERSON_ID", "99");
-    db.executeInsert("PROJECT_MEASURES", personMeasure);
+    db.executeInsert("PROJECTS",
+      "UUID", "PRJ1",
+      "PROJECT_UUID", "PRJ1",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "DIR1",
+      "PROJECT_UUID", "PRJ1",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "FIL1",
+      "PROJECT_UUID", "PRJ1",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "PRJ2",
+      "PROJECT_UUID", "PRJ2",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "DIR2",
+      "PROJECT_UUID", "PRJ2",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "FIL2",
+      "PROJECT_UUID", "PRJ2",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "PRJ3",
+      "PROJECT_UUID", "PRJ3",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+    db.executeInsert("PROJECTS",
+      "UUID", "PRJ4",
+      "PROJECT_UUID", "PRJ4",
+      "ORGANIZATION_UUID", "ORG1",
+      "UUID_PATH", "X",
+      "ROOT_UUID", "X",
+      "PRIVATE", "FALSE");
+
+    // non last snapshot, none of its measures should be copied to live_measures
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "1A1",
+      "ISLAST", "FALSE",
+      "COMPONENT_UUID", "PRJ1");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ1",
+      "ANALYSIS_UUID", "1A1",
+      "METRIC_ID", "100");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "DIR1",
+      "ANALYSIS_UUID", "1A1",
+      "METRIC_ID", "110",
+      "VALUE", "11");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "FIL1",
+      "ANALYSIS_UUID", "1A1",
+      "METRIC_ID", "120",
+      "VALUE", "12");
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "1A2",
+      "ISLAST", "FALSE",
+      "COMPONENT_UUID", "PRJ2");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ2",
+      "ANALYSIS_UUID", "1A2",
+      "METRIC_ID", "200");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "DIR2",
+      "ANALYSIS_UUID", "1A2",
+      "METRIC_ID", "210",
+      "VALUE", "21");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "FIL2",
+      "ANALYSIS_UUID", "1A2",
+      "METRIC_ID", "220",
+      "VALUE", "22");
+    // PRJ3 has only non-last snapshot??!!?? => won't go into live_measures_p
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "1A3",
+      "ISLAST", "FALSE",
+      "COMPONENT_UUID", "PRJ3");
+
+    // last snapshot, all measure should be copied to live_measures
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "2A1",
+      "ISLAST", "TRUE",
+      "COMPONENT_UUID", "PRJ1");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ1",
+      "ANALYSIS_UUID", "2A1",
+      "METRIC_ID", "1010",
+      "VALUE", "101",
+      "TEXT_VALUE", "TEXT_VALUEx",
+      "VARIATION_VALUE_1", "345",
+      "MEASURE_DATA", "FFFF");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ1",
+      "ANALYSIS_UUID", "2A1",
+      "METRIC_ID", "1020",
+      "VALUE", "102");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "DIR1",
+      "ANALYSIS_UUID", "2A1",
+      "METRIC_ID", "1030",
+      "VALUE", "103");
+    // FIL1 has no measure for this snapshot => will trigger infinite loop if not taken into account
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "2A2",
+      "ISLAST", "TRUE",
+      "COMPONENT_UUID", "PRJ2");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ2",
+      "ANALYSIS_UUID", "2A2",
+      "METRIC_ID", "2010",
+      "VALUE", "201",
+      "TEXT_VALUE", "TEXT_VALUEx",
+      "VARIATION_VALUE_1", "345",
+      "MEASURE_DATA", "FFFF");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "PRJ2",
+      "ANALYSIS_UUID", "2A2",
+      "METRIC_ID", "2020",
+      "VALUE", "202");
+    db.executeInsert("PROJECT_MEASURES",
+      "COMPONENT_UUID", "DIR2",
+      "ANALYSIS_UUID", "2A2",
+      "METRIC_ID", "2030",
+      "VALUE", "203");
+    // FIL2 has no measure for this snapshot => will trigger infinite loop if not taken into account
+    // PRJ5 has last snapshot without measure => won't go into live_measures_p
+    db.executeInsert("SNAPSHOTS",
+      "UUID", "2A4",
+      "ISLAST", "FALSE",
+      "COMPONENT_UUID", "PRJ4");
   }
 
   private List<Map<String, Object>> getLiveMeasures() {
@@ -147,7 +269,12 @@ public class PopulateLiveMeasuresTest {
 
   private Tuple[] generateLiveMeasures() {
     return new Tuple[] {
-      tuple("PRJ1", "PRJ1", 123L, 234.0, "TEXT_VALUEx", 345.0, new byte[] {-1, -1})
+      tuple("PRJ1", "PRJ1", 1010L, 101.0, "TEXT_VALUEx", 345.0, new byte[] {-1, -1}),
+      tuple("PRJ1", "PRJ1", 1020L, 102.0, null, null, null),
+      tuple("DIR1", "PRJ1", 1030L, 103.0, null, null, null),
+      tuple("PRJ2", "PRJ2", 2010L, 201.0, "TEXT_VALUEx", 345.0, new byte[] {-1, -1}),
+      tuple("PRJ2", "PRJ2", 2020L, 202.0, null, null, null),
+      tuple("DIR2", "PRJ2", 2030L, 203.0, null, null, null)
     };
   }
 }
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest/live_measures.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest/live_measures.sql
new file mode 100644 (file)
index 0000000..291df98
--- /dev/null
@@ -0,0 +1,14 @@
+CREATE TABLE "LIVE_MEASURES" (
+  "UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "METRIC_ID" INTEGER NOT NULL,
+  "VALUE" DOUBLE,
+  "TEXT_VALUE" VARCHAR(4000),
+  "VARIATION" DOUBLE,
+  "MEASURE_DATA" BINARY,
+  "UPDATE_MARKER" VARCHAR(40),
+  "CREATED_AT" BIGINT NOT NULL,
+  "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest/snapshots.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest/snapshots.sql
new file mode 100644 (file)
index 0000000..7d8f296
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE TABLE "SNAPSHOTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(50) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "BUILD_DATE" BIGINT,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+  "PURGE_STATUS" INTEGER,
+  "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+  "VERSION" VARCHAR(500),
+  "PERIOD1_MODE" VARCHAR(100),
+  "PERIOD1_PARAM" VARCHAR(100),
+  "PERIOD1_DATE" BIGINT,
+  "PERIOD2_MODE" VARCHAR(100),
+  "PERIOD2_PARAM" VARCHAR(100),
+  "PERIOD2_DATE" BIGINT,
+  "PERIOD3_MODE" VARCHAR(100),
+  "PERIOD3_PARAM" VARCHAR(100),
+  "PERIOD3_DATE" BIGINT,
+  "PERIOD4_MODE" VARCHAR(100),
+  "PERIOD4_PARAM" VARCHAR(100),
+  "PERIOD4_DATE" BIGINT,
+  "PERIOD5_MODE" VARCHAR(100),
+  "PERIOD5_PARAM" VARCHAR(100),
+  "PERIOD5_DATE" BIGINT
+);
+CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest/empty.sql
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest/initial.sql
new file mode 100644 (file)
index 0000000..be55d6f
--- /dev/null
@@ -0,0 +1,98 @@
+CREATE TABLE "PROJECTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+  "KEE" VARCHAR(400),
+  "UUID" VARCHAR(50) NOT NULL,
+  "UUID_PATH" VARCHAR(1500) NOT NULL,
+  "ROOT_UUID" VARCHAR(50) NOT NULL,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "MODULE_UUID" VARCHAR(50),
+  "MODULE_UUID_PATH" VARCHAR(1500),
+  "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+  "NAME" VARCHAR(2000),
+  "DESCRIPTION" VARCHAR(2000),
+  "PRIVATE" BOOLEAN NOT NULL,
+  "TAGS" VARCHAR(500),
+  "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+  "SCOPE" VARCHAR(3),
+  "QUALIFIER" VARCHAR(10),
+  "DEPRECATED_KEE" VARCHAR(400),
+  "PATH" VARCHAR(2000),
+  "LANGUAGE" VARCHAR(20),
+  "COPY_COMPONENT_UUID" VARCHAR(50),
+  "LONG_NAME" VARCHAR(2000),
+  "DEVELOPER_UUID" VARCHAR(50),
+  "CREATED_AT" TIMESTAMP,
+  "AUTHORIZATION_UPDATED_AT" BIGINT,
+  "B_CHANGED" BOOLEAN,
+  "B_COPY_COMPONENT_UUID" VARCHAR(50),
+  "B_DESCRIPTION" VARCHAR(2000),
+  "B_ENABLED" BOOLEAN,
+  "B_UUID_PATH" VARCHAR(1500),
+  "B_LANGUAGE" VARCHAR(20),
+  "B_LONG_NAME" VARCHAR(500),
+  "B_MODULE_UUID" VARCHAR(50),
+  "B_MODULE_UUID_PATH" VARCHAR(1500),
+  "B_NAME" VARCHAR(500),
+  "B_PATH" VARCHAR(2000),
+  "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+
+CREATE TABLE "SNAPSHOTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(50) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "BUILD_DATE" BIGINT,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+  "PURGE_STATUS" INTEGER,
+  "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+  "VERSION" VARCHAR(500),
+  "PERIOD1_MODE" VARCHAR(100),
+  "PERIOD1_PARAM" VARCHAR(100),
+  "PERIOD1_DATE" BIGINT,
+  "PERIOD2_MODE" VARCHAR(100),
+  "PERIOD2_PARAM" VARCHAR(100),
+  "PERIOD2_DATE" BIGINT,
+  "PERIOD3_MODE" VARCHAR(100),
+  "PERIOD3_PARAM" VARCHAR(100),
+  "PERIOD3_DATE" BIGINT,
+  "PERIOD4_MODE" VARCHAR(100),
+  "PERIOD4_PARAM" VARCHAR(100),
+  "PERIOD4_DATE" BIGINT,
+  "PERIOD5_MODE" VARCHAR(100),
+  "PERIOD5_PARAM" VARCHAR(100),
+  "PERIOD5_DATE" BIGINT
+);
+CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
+
+
+CREATE TABLE "PROJECT_MEASURES" (
+  "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "VALUE" DOUBLE,
+  "METRIC_ID" INTEGER NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "TEXT_VALUE" VARCHAR(4000),
+  "ALERT_STATUS" VARCHAR(5),
+  "ALERT_TEXT" VARCHAR(4000),
+  "DESCRIPTION" VARCHAR(4000),
+  "PERSON_ID" INTEGER,
+  "VARIATION_VALUE_1" DOUBLE,
+  "VARIATION_VALUE_2" DOUBLE,
+  "VARIATION_VALUE_3" DOUBLE,
+  "VARIATION_VALUE_4" DOUBLE,
+  "VARIATION_VALUE_5" DOUBLE,
+  "MEASURE_DATA" BINARY
+);
+CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
+CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest/initial.sql
new file mode 100644 (file)
index 0000000..be55d6f
--- /dev/null
@@ -0,0 +1,98 @@
+CREATE TABLE "PROJECTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+  "KEE" VARCHAR(400),
+  "UUID" VARCHAR(50) NOT NULL,
+  "UUID_PATH" VARCHAR(1500) NOT NULL,
+  "ROOT_UUID" VARCHAR(50) NOT NULL,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "MODULE_UUID" VARCHAR(50),
+  "MODULE_UUID_PATH" VARCHAR(1500),
+  "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+  "NAME" VARCHAR(2000),
+  "DESCRIPTION" VARCHAR(2000),
+  "PRIVATE" BOOLEAN NOT NULL,
+  "TAGS" VARCHAR(500),
+  "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+  "SCOPE" VARCHAR(3),
+  "QUALIFIER" VARCHAR(10),
+  "DEPRECATED_KEE" VARCHAR(400),
+  "PATH" VARCHAR(2000),
+  "LANGUAGE" VARCHAR(20),
+  "COPY_COMPONENT_UUID" VARCHAR(50),
+  "LONG_NAME" VARCHAR(2000),
+  "DEVELOPER_UUID" VARCHAR(50),
+  "CREATED_AT" TIMESTAMP,
+  "AUTHORIZATION_UPDATED_AT" BIGINT,
+  "B_CHANGED" BOOLEAN,
+  "B_COPY_COMPONENT_UUID" VARCHAR(50),
+  "B_DESCRIPTION" VARCHAR(2000),
+  "B_ENABLED" BOOLEAN,
+  "B_UUID_PATH" VARCHAR(1500),
+  "B_LANGUAGE" VARCHAR(20),
+  "B_LONG_NAME" VARCHAR(500),
+  "B_MODULE_UUID" VARCHAR(50),
+  "B_MODULE_UUID_PATH" VARCHAR(1500),
+  "B_NAME" VARCHAR(500),
+  "B_PATH" VARCHAR(2000),
+  "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+
+CREATE TABLE "SNAPSHOTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(50) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "BUILD_DATE" BIGINT,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+  "PURGE_STATUS" INTEGER,
+  "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+  "VERSION" VARCHAR(500),
+  "PERIOD1_MODE" VARCHAR(100),
+  "PERIOD1_PARAM" VARCHAR(100),
+  "PERIOD1_DATE" BIGINT,
+  "PERIOD2_MODE" VARCHAR(100),
+  "PERIOD2_PARAM" VARCHAR(100),
+  "PERIOD2_DATE" BIGINT,
+  "PERIOD3_MODE" VARCHAR(100),
+  "PERIOD3_PARAM" VARCHAR(100),
+  "PERIOD3_DATE" BIGINT,
+  "PERIOD4_MODE" VARCHAR(100),
+  "PERIOD4_PARAM" VARCHAR(100),
+  "PERIOD4_DATE" BIGINT,
+  "PERIOD5_MODE" VARCHAR(100),
+  "PERIOD5_PARAM" VARCHAR(100),
+  "PERIOD5_DATE" BIGINT
+);
+CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
+
+
+CREATE TABLE "PROJECT_MEASURES" (
+  "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "VALUE" DOUBLE,
+  "METRIC_ID" INTEGER NOT NULL,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "ANALYSIS_UUID" VARCHAR(50) NOT NULL,
+  "TEXT_VALUE" VARCHAR(4000),
+  "ALERT_STATUS" VARCHAR(5),
+  "ALERT_TEXT" VARCHAR(4000),
+  "DESCRIPTION" VARCHAR(4000),
+  "PERSON_ID" INTEGER,
+  "VARIATION_VALUE_1" DOUBLE,
+  "VARIATION_VALUE_2" DOUBLE,
+  "VARIATION_VALUE_3" DOUBLE,
+  "VARIATION_VALUE_4" DOUBLE,
+  "VARIATION_VALUE_5" DOUBLE,
+  "MEASURE_DATA" BINARY
+);
+CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
+CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest/snapshots.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest/snapshots.sql
new file mode 100644 (file)
index 0000000..53bee7b
--- /dev/null
@@ -0,0 +1,29 @@
+CREATE TABLE "SNAPSHOTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "UUID" VARCHAR(50) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "BUILD_DATE" BIGINT,
+  "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+  "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+  "PURGE_STATUS" INTEGER,
+  "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+  "VERSION" VARCHAR(500),
+  "PERIOD1_MODE" VARCHAR(100),
+  "PERIOD1_PARAM" VARCHAR(100),
+  "PERIOD1_DATE" BIGINT,
+  "PERIOD2_MODE" VARCHAR(100),
+  "PERIOD2_PARAM" VARCHAR(100),
+  "PERIOD2_DATE" BIGINT,
+  "PERIOD3_MODE" VARCHAR(100),
+  "PERIOD3_PARAM" VARCHAR(100),
+  "PERIOD3_DATE" BIGINT,
+  "PERIOD4_MODE" VARCHAR(100),
+  "PERIOD4_PARAM" VARCHAR(100),
+  "PERIOD4_DATE" BIGINT,
+  "PERIOD5_MODE" VARCHAR(100),
+  "PERIOD5_PARAM" VARCHAR(100),
+  "PERIOD5_DATE" BIGINT
+);
+CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+CREATE INDEX "IX_SNAPSHOT_IS_LAST" ON "SNAPSHOTS" ("ISLAST");
+CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest/live_measures_p.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest/live_measures_p.sql
new file mode 100644 (file)
index 0000000..0632f99
--- /dev/null
@@ -0,0 +1,4 @@
+CREATE TABLE "LIVE_MEASURES_P" (
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  CONSTRAINT "PK_LIVE_MEASURES_P" PRIMARY KEY ("PROJECT_UUID")
+);
index b3dc4557dbbbf890778f7f17ba1522a5d4a29855..cf0c97555c17bc85328c367a4036106086f4242d 100644 (file)
@@ -72,6 +72,8 @@ CREATE TABLE "SNAPSHOTS" (
   "PERIOD5_DATE" BIGINT
 );
 CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
+-- temporary index added by previous DB migration
+CREATE INDEX "IX_SNAPSHOT_IS_LAST" ON "SNAPSHOTS" ("ISLAST");
 CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
 
 
@@ -89,7 +91,12 @@ CREATE TABLE "LIVE_MEASURES" (
   "UPDATED_AT" BIGINT NOT NULL
 );
 CREATE INDEX "LIVE_MEASURES_PROJECT" ON "LIVE_MEASURES" ("PROJECT_UUID");
-CREATE UNIQUE INDEX "LIVE_MEASURES_COMPONENT" ON "LIVE_MEASURES" ("COMPONENT_UUID", "METRIC_ID");
+
+-- temporary table created by previous DB migration
+CREATE TABLE "LIVE_MEASURES_P" (
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  CONSTRAINT "PK_LIVE_MEASURES_P" PRIMARY KEY ("PROJECT_UUID")
+);
 
 
 CREATE TABLE "PROJECT_MEASURES" (
@@ -111,6 +118,4 @@ CREATE TABLE "PROJECT_MEASURES" (
   "MEASURE_DATA" BINARY
 );
 CREATE INDEX "MEASURES_COMPONENT_UUID" ON "PROJECT_MEASURES" ("COMPONENT_UUID");
-CREATE INDEX "MEASURES_ANALYSIS_METRIC" ON "PROJECT_MEASURES" ("ANALYSIS_UUID", "METRIC_ID");
-CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID");