From: Sébastien Lesaint Date: Wed, 26 Jun 2019 13:56:09 +0000 (+0200) Subject: SONAR-12127 optimize migrations on LIVE_MEASURES and PROJECT_MEASURES X-Git-Tag: 8.0~425 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=515876435fd5a935147ea0e52b26ac798c269541;p=sonarqube.git SONAR-12127 optimize migrations on LIVE_MEASURES and PROJECT_MEASURES --- diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java index cc6a884b9b7..cd2a7d3a429 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/DatabaseUtils.java @@ -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); } diff --git a/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java b/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java index 3ea4852ccf5..23ae8e7dd32 100644 --- a/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java +++ b/server/sonar-db-core/src/test/java/org/sonar/db/AbstractDbTester.java @@ -405,9 +405,17 @@ public class AbstractDbTester 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); } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DropIndexBuilder.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DropIndexBuilder.java index 1828e4465d4..548cbfbf87c 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DropIndexBuilder.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DropIndexBuilder.java @@ -19,7 +19,11 @@ */ 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); + } + } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java index e580d473816..a6beab641fc 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java @@ -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 index 00000000000..d08933471a4 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndex.java @@ -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 index 00000000000..0f555576f04 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndex.java @@ -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()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java index ad4a5ed97d4..a86d9d78e8f 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasures.java @@ -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 index 00000000000..faf4bb4fe12 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresP.java @@ -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()); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java index 1f7757a4519..7f080c3243d 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70.java @@ -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 index 00000000000..58861ab22e6 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasures.java @@ -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 index 2a942e21771..00000000000 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasures.java +++ /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 index 00000000000..1f78dc960c1 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasures.java @@ -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 index 00000000000..554a084cc5c --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndex.java @@ -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 index 00000000000..acec9071aae --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresP.java @@ -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()); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java index 9115bba8ce0..2ad1464d4a1 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasures.java @@ -19,45 +19,165 @@ */ 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 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 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 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 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 notCommittedProjectUuids; + private String deletePreviousProjectUuid = null; + private String currentProjectUuid = null; + + private LiveMeasurePopulationMultiHandler(boolean firstAttempt, List 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. + *

+ * This requires statement selecting measures to be sorted by project_uuid and then component_uuid. + */ + 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 + '\'' + + '}'; + } + } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationConfigurationModuleTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationConfigurationModuleTest.java index ec155560d80..5d7a4fa62dd 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationConfigurationModuleTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/MigrationConfigurationModuleTest.java @@ -39,7 +39,7 @@ public class MigrationConfigurationModuleTest { // DbVersion classes + 20 // Others - + 3); + + 4); } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java index 27aa42089a5..d47d2ba8c4f 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/history/NoTableMigrationHistoryImplTest.java @@ -19,19 +19,22 @@ */ 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 index 00000000000..e4f697addad --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest.java @@ -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 index 00000000000..719ee1dee10 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest.java @@ -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(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java index 0272f61de1b..17865dfdd11 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTableLiveMeasuresTest.java @@ -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 index 00000000000..631888ee0a7 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/CreateTempTableLiveMeasuresPTest.java @@ -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(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java index 33776819931..c237a6c0b16 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DbVersion70Test.java @@ -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 index 00000000000..8befae37dc5 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest.java @@ -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 index a6188242b4c..00000000000 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonAndFileMeasuresTest.java +++ /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 index 00000000000..e0d21418e16 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest.java @@ -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 index 00000000000..536f34872ef --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest.java @@ -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 index 00000000000..5a160e591bd --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest.java @@ -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(); + } +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java index 10bd9221d05..f125ad3f0fb 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest.java @@ -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, 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, Object> field(String name) { + return m -> m.get(name); } private void generateProjectMeasures() { - Map 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 analysis1 = new HashMap<>(); - analysis1.put("UUID", "A1"); - analysis1.put("ISLAST", "FALSE"); - analysis1.put("COMPONENT_UUID", "PRJ1"); - db.executeInsert("SNAPSHOTS", analysis1); - - Map analysis2 = new HashMap<>(); - analysis2.put("UUID", "A2"); - analysis2.put("ISLAST", "TRUE"); - analysis2.put("COMPONENT_UUID", "PRJ1"); - db.executeInsert("SNAPSHOTS", analysis2); - - Map measure1 = new HashMap<>(); - measure1.put("COMPONENT_UUID", "PRJ1"); - measure1.put("ANALYSIS_UUID", "A1"); - measure1.put("METRIC_ID", "123"); - db.executeInsert("PROJECT_MEASURES", measure1); - - Map 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 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> 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 index 00000000000..291df9899c4 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddLiveMeasuresMetricIndexTest/live_measures.sql @@ -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 index 00000000000..7d8f2963a86 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/AddSnapshotIsLastIndexTest/snapshots.sql @@ -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 index 00000000000..e69de29bb2d 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 index 00000000000..be55d6f14e8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeleteFileMeasuresTest/initial.sql @@ -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 index 00000000000..be55d6f14e8 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DeletePersonMeasuresTest/initial.sql @@ -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 index 00000000000..53bee7b6bce --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropSnapshotIsLastIndexTest/snapshots.sql @@ -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 index 00000000000..0632f993b4e --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/DropTempTableLiveMeasuresPTest/live_measures_p.sql @@ -0,0 +1,4 @@ +CREATE TABLE "LIVE_MEASURES_P" ( + "PROJECT_UUID" VARCHAR(50) NOT NULL, + CONSTRAINT "PK_LIVE_MEASURES_P" PRIMARY KEY ("PROJECT_UUID") +); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql index b3dc4557dbb..cf0c97555c1 100644 --- a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v70/PopulateLiveMeasuresTest/initial.sql @@ -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");