diff options
24 files changed, 735 insertions, 47 deletions
diff --git a/gradle.properties b/gradle.properties index 6580826a45e..3346946ca26 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,4 @@ elasticSearchServerVersion=8.16.3 projectType=application artifactoryUrl=https://repox.jfrog.io/repox jre_release_name=jdk-17.0.13+11 -webappVersion=2025.3.0.16221 +webappVersion=2025.3.0.16376 diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java index a7bf75927ec..b781c2ad344 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java @@ -2002,6 +2002,14 @@ oldCreationDate)); assertThat(db.countRowsOfTable(dbSession, "sca_issues_releases")).isEqualTo(2); + var issueReleaseChangeBase = Map.of("created_at", 0L, "updated_at", 0L); + db.executeInsert("sca_issue_rels_changes", merge(issueReleaseChangeBase, Map.of("uuid", "issue-release-change-uuid1", + "sca_issues_releases_uuid", "issue-release-uuid1"))); + db.executeInsert("sca_issue_rels_changes", merge(issueReleaseChangeBase, Map.of("uuid", "issue-release-change-uuid2", + "sca_issues_releases_uuid", "issue-release-uuid2"))); + + assertThat(db.countRowsOfTable(dbSession, "sca_issue_rels_changes")).isEqualTo(2); + var analysisBase = Map.of( "created_at", 0L, "updated_at", 0L, @@ -2031,6 +2039,7 @@ oldCreationDate)); assertThat(db.countRowsOfTable(dbSession, "sca_releases")).isEqualTo(1); assertThat(db.countRowsOfTable(dbSession, "sca_dependencies")).isEqualTo(1); assertThat(db.countRowsOfTable(dbSession, "sca_issues_releases")).isEqualTo(1); + assertThat(db.countRowsOfTable(dbSession, "sca_issue_rels_changes")).isEqualTo(1); assertThat(db.countRowsOfTable(dbSession, "sca_analyses")).isEqualTo(1); } @@ -2064,9 +2073,9 @@ oldCreationDate)); BranchDto branch1 = db.components().insertProjectBranch(project); BranchDto branch2 = db.components().insertProjectBranch(project); - db.executeInsert("architecture_graphs", Map.of("uuid", "12345", "branch_uuid", branch1.getUuid(), "source", "xoo", "type", "file_graph", "graph_data", "{}")); - db.executeInsert("architecture_graphs", Map.of("uuid", "123456", "branch_uuid", branch1.getUuid(), "source", "xoo", "type", "class_graph", "graph_data", "{}")); - db.executeInsert("architecture_graphs", Map.of("uuid", "1234567", "branch_uuid", branch2.getUuid(), "source", "xoo", "type", "file_graph", "graph_data", "{}")); + db.executeInsert("architecture_graphs", Map.of("uuid", "12345", "branch_uuid", branch1.getUuid(), "ecosystem", "xoo", "type", "file_graph", "graph_data", "{}")); + db.executeInsert("architecture_graphs", Map.of("uuid", "123456", "branch_uuid", branch1.getUuid(), "ecosystem", "xoo", "type", "class_graph", "graph_data", "{}")); + db.executeInsert("architecture_graphs", Map.of("uuid", "1234567", "branch_uuid", branch2.getUuid(), "ecosystem", "xoo", "type", "file_graph", "graph_data", "{}")); assertThat(db.countRowsOfTable(dbSession, "architecture_graphs")).isEqualTo(3); underTest.deleteBranch(dbSession, branch1.getUuid()); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java index d818e2361cf..fca991f9f28 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java @@ -543,6 +543,13 @@ class PurgeCommands { session.commit(); profiler.stop(); + // this must be done before deleting sca_issues_releases or we won't + // be able to find the rows + profiler.start("deleteScaIssuesReleasesChanges (sca_issue_rels_changes)"); + purgeMapper.deleteScaIssuesReleasesChangesByComponentUuid(componentUuid); + session.commit(); + profiler.stop(); + profiler.start("deleteScaIssuesReleases (sca_issues_releases)"); purgeMapper.deleteScaIssuesReleasesByComponentUuid(componentUuid); session.commit(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index 2502ac8e5d8..ab4b369aef6 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -201,9 +201,11 @@ public interface PurgeMapper { void deleteScaIssuesReleasesByComponentUuid(@Param("componentUuid") String componentUuid); - void deleteScaReleasesByComponentUuid(@Param("componentUuid") String componentUuid); + void deleteScaIssuesReleasesChangesByComponentUuid(@Param("componentUuid") String componentUuid); - void deleteArchitectureGraphsByBranchUuid(@Param("branchUuid") String branchUuid); + void deleteScaReleasesByComponentUuid(@Param("componentUuid") String componentUuid); void deleteScaLicenseProfileProjectsByProjectUuid(@Param("projectUuid") String projectUuid); + + void deleteArchitectureGraphsByBranchUuid(@Param("branchUuid") String branchUuid); } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index 9b0b865fc36..bc5c066d6b4 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -683,6 +683,11 @@ <delete id="deleteScaIssuesReleasesByComponentUuid"> delete from sca_issues_releases where sca_release_uuid in (select uuid from sca_releases where component_uuid = #{componentUuid,jdbcType=VARCHAR}) </delete> + <delete id="deleteScaIssuesReleasesChangesByComponentUuid"> + delete from sca_issue_rels_changes where sca_issues_releases_uuid in + (select sca_issues_releases.uuid from sca_issues_releases join sca_releases on sca_releases.uuid = sca_issues_releases.sca_release_uuid + where sca_releases.component_uuid = #{componentUuid,jdbcType=VARCHAR}) + </delete> <delete id="deleteScaReleasesByComponentUuid"> delete from sca_releases where component_uuid = #{componentUuid,jdbcType=VARCHAR} </delete> diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 1f942ae9125..44464c73e4d 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -121,12 +121,14 @@ CREATE INDEX "IDX_APP_PROJ_PROJECT_UUID" ON "APP_PROJECTS"("PROJECT_UUID" NULLS CREATE TABLE "ARCHITECTURE_GRAPHS"( "UUID" CHARACTER VARYING(40) NOT NULL, "BRANCH_UUID" CHARACTER VARYING(40) NOT NULL, - "SOURCE" CHARACTER VARYING(255) NOT NULL, + "ECOSYSTEM" CHARACTER VARYING(255) NOT NULL, "TYPE" CHARACTER VARYING(255) NOT NULL, - "GRAPH_DATA" CHARACTER LARGE OBJECT NOT NULL + "GRAPH_DATA" CHARACTER LARGE OBJECT NOT NULL, + "ANALYSIS_UUID" CHARACTER VARYING(40), + "PERSPECTIVE_KEY" CHARACTER VARYING(255) ); ALTER TABLE "ARCHITECTURE_GRAPHS" ADD CONSTRAINT "PK_ARCHITECTURE_GRAPHS" PRIMARY KEY("UUID"); -CREATE UNIQUE NULLS NOT DISTINCT INDEX "UQ_IDX_AG_BRANCH_TYPE_SOURCE" ON "ARCHITECTURE_GRAPHS"("BRANCH_UUID" NULLS FIRST, "TYPE" NULLS FIRST, "SOURCE" NULLS FIRST); +CREATE UNIQUE NULLS NOT DISTINCT INDEX "UQ_IDX_AG_BRCH_TP_SRC_PSPCTV" ON "ARCHITECTURE_GRAPHS"("BRANCH_UUID" NULLS FIRST, "TYPE" NULLS FIRST, "ECOSYSTEM" NULLS FIRST, "PERSPECTIVE_KEY" NULLS FIRST); CREATE TABLE "AUDITS"( "UUID" CHARACTER VARYING(40) NOT NULL, @@ -1113,7 +1115,8 @@ CREATE TABLE "SCA_ISSUES_RELEASES"( "CREATED_AT" BIGINT NOT NULL, "UPDATED_AT" BIGINT NOT NULL, "STATUS" CHARACTER VARYING(40) NOT NULL, - "ASSIGNEE_UUID" CHARACTER VARYING(40) + "ASSIGNEE_UUID" CHARACTER VARYING(40), + "PREVIOUS_MANUAL_STATUS" CHARACTER VARYING(40) ); ALTER TABLE "SCA_ISSUES_RELEASES" ADD CONSTRAINT "PK_SCA_ISSUES_RELEASES" PRIMARY KEY("UUID"); CREATE INDEX "SCA_ISSUES_RELEASES_SCA_ISSUE" ON "SCA_ISSUES_RELEASES"("SCA_ISSUE_UUID" NULLS FIRST); diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphsIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphsIT.java new file mode 100644 index 00000000000..167b797de15 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphsIT.java @@ -0,0 +1,55 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static java.sql.Types.VARCHAR; +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + + +class AddAnalysisUuidOnArchitectureGraphsIT { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String COLUMN_NAME = "analysis_uuid"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(AddAnalysisUuidOnArchitectureGraphs.class); + private final DdlChange underTest = new AddAnalysisUuidOnArchitectureGraphs(db.database()); + + @Test + void execute_shouldAddColumn() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, VarcharColumnDef.UUID_SIZE, true); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, VarcharColumnDef.UUID_SIZE, true); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphsIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphsIT.java new file mode 100644 index 00000000000..22f757cd152 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphsIT.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static java.sql.Types.VARCHAR; +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class AddPerspectiveKeyOnArchitectureGraphsIT { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String COLUMN_NAME = "perspective_key"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(AddPerspectiveKeyOnArchitectureGraphs.class); + private final DdlChange underTest = new AddPerspectiveKeyOnArchitectureGraphs(db.database()); + + @Test + void execute_shouldAddColumn() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 255, true); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 255, true); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleasesIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleasesIT.java new file mode 100644 index 00000000000..cf293d4c9bd --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleasesIT.java @@ -0,0 +1,53 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static java.sql.Types.VARCHAR; +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class AddPreviousManualStatusToScaIssuesReleasesIT { + private static final String TABLE_NAME = "sca_issues_releases"; + private static final String COLUMN_NAME = "previous_manual_status"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(AddPreviousManualStatusToScaIssuesReleases.class); + private final DdlChange underTest = new AddPreviousManualStatusToScaIssuesReleases(db.database()); + + @Test + void execute_shouldAddColumn() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 40, true); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + underTest.execute(); + underTest.execute(); + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, VARCHAR, 40, true); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphsIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphsIT.java new file mode 100644 index 00000000000..8cd566df9ce --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphsIT.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class CreateUniqueIndexOnArchitectureGraphsIT { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String INDEX_NAME = "uq_idx_ag_brch_tp_src_pspctv"; + private static final String COLUMN_NAME_BRANCH_UUID = "branch_uuid"; + private static final String COLUMN_NAME_TYPE = "type"; + private static final String COLUMN_NAME_ECOSYSTEM = "ecosystem"; + private static final String COLUMN_NAME_PERSPECTIVE_KEY = "perspective_key"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(CreateUniqueIndexOnArchitectureGraphs.class); + private final DdlChange underTest = new CreateUniqueIndexOnArchitectureGraphs(db.database()); + + @Test + void execute_shouldCreateIndex() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + underTest.execute(); + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME_BRANCH_UUID, COLUMN_NAME_TYPE, COLUMN_NAME_ECOSYSTEM, COLUMN_NAME_PERSPECTIVE_KEY); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + underTest.execute(); + underTest.execute(); + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME_BRANCH_UUID, COLUMN_NAME_TYPE, COLUMN_NAME_ECOSYSTEM, COLUMN_NAME_PERSPECTIVE_KEY); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphsIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphsIT.java new file mode 100644 index 00000000000..646be1f2f7c --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphsIT.java @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; + +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class DropIndexOnArchitectureGraphsIT { + + private static final String TABLE_NAME = "architecture_graphs"; + private static final String COLUMN_NAME_BRANCH_UUID = "branch_uuid"; + private static final String COLUMN_NAME_TYPE = "type"; + private static final String COLUMN_NAME_SOURCE = "source"; + private static final String INDEX_NAME = "uq_idx_ag_branch_type_source"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(DropIndexOnArchitectureGraphs.class); + private final DropIndexOnArchitectureGraphs underTest = new DropIndexOnArchitectureGraphs(db.database()); + + @Test + void index_is_dropped() throws SQLException { + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME_BRANCH_UUID, COLUMN_NAME_TYPE, COLUMN_NAME_SOURCE); + + underTest.execute(); + + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + } + + @Test + void migration_is_reentrant() throws SQLException { + db.assertUniqueIndex(TABLE_NAME, INDEX_NAME, COLUMN_NAME_BRANCH_UUID, COLUMN_NAME_TYPE, COLUMN_NAME_SOURCE); + + underTest.execute(); + underTest.execute(); + + db.assertIndexDoesNotExist(TABLE_NAME, INDEX_NAME); + } +} diff --git a/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRenameIT.java b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRenameIT.java new file mode 100644 index 00000000000..81c377724e9 --- /dev/null +++ b/server/sonar-db-migration/src/it/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRenameIT.java @@ -0,0 +1,63 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.SQLException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.sonar.db.MigrationDbTester; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static java.sql.Types.VARCHAR; +import static org.sonar.db.MigrationDbTester.createForMigrationStep; + +class UpdateArchitectureGraphsSourceColumnRenameIT { + + private static final String TABLE_NAME = "architecture_graphs"; + private static final String OLD_COLUMN = "source"; + private static final String NEW_COLUMN = "ecosystem"; + + @RegisterExtension + public final MigrationDbTester db = createForMigrationStep(UpdateArchitectureGraphsSourceColumnRename.class); + private final DdlChange underTest = new UpdateArchitectureGraphsSourceColumnRename(db.database()); + + @Test + void execute_shouldUpdateColumn() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, OLD_COLUMN, VARCHAR, null, null); + db.assertColumnDoesNotExist(TABLE_NAME, NEW_COLUMN); + + underTest.execute(); + + db.assertColumnDoesNotExist(TABLE_NAME, OLD_COLUMN); + db.assertColumnDefinition(TABLE_NAME, NEW_COLUMN, VARCHAR, null, null); + } + + @Test + void execute_shouldBeReentrant() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, OLD_COLUMN, VARCHAR, null, null); + db.assertColumnDoesNotExist(TABLE_NAME, NEW_COLUMN); + + underTest.execute(); + underTest.execute(); + + db.assertColumnDoesNotExist(TABLE_NAME, OLD_COLUMN); + db.assertColumnDefinition(TABLE_NAME, NEW_COLUMN, VARCHAR, null, null); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202502/CreateUniqueIndexOnArchitectureGraphs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202502/CreateUniqueIndexOnArchitectureGraphs.java index 802396d18b1..2664ff7c0e9 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202502/CreateUniqueIndexOnArchitectureGraphs.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202502/CreateUniqueIndexOnArchitectureGraphs.java @@ -46,15 +46,26 @@ public class CreateUniqueIndexOnArchitectureGraphs extends DdlChange { } private void createIndex(Context context, Connection connection) { - if (!DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) { - context.execute(new CreateIndexBuilder(getDialect()) - .setTable(TABLE_NAME) - .setName(INDEX_NAME) - .setUnique(true) - .addColumn(COLUMN_NAME_BRANCH_UUID, false) - .addColumn(COLUMN_NAME_TYPE, false) - .addColumn(COLUMN_NAME_SOURCE, false) - .build()); + if(!DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN_NAME_BRANCH_UUID)) { + return; } + if(!DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN_NAME_TYPE)) { + return; + } + if(!DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN_NAME_SOURCE)) { + return; + } + if (DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) { + return; + } + + context.execute(new CreateIndexBuilder(getDialect()) + .setTable(TABLE_NAME) + .setName(INDEX_NAME) + .setUnique(true) + .addColumn(COLUMN_NAME_BRANCH_UUID, false) + .addColumn(COLUMN_NAME_TYPE, false) + .addColumn(COLUMN_NAME_SOURCE, false) + .build()); } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphs.java new file mode 100644 index 00000000000..1110d46c570 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddAnalysisUuidOnArchitectureGraphs.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +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.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.db.DatabaseUtils.tableColumnExists; + +public class AddAnalysisUuidOnArchitectureGraphs extends DdlChange { + static final String TABLE_NAME = "architecture_graphs"; + static final String COLUMN_NAME = "analysis_uuid"; + + public AddAnalysisUuidOnArchitectureGraphs(Database db) { + super(db); + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (!tableColumnExists(connection, TABLE_NAME, COLUMN_NAME)) { + var columnDef = VarcharColumnDef.newVarcharColumnDefBuilder() + .setColumnName(COLUMN_NAME) + .setLimit(VarcharColumnDef.UUID_SIZE) + .setIsNullable(true) + .build(); + + context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) + .addColumn(columnDef) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphs.java new file mode 100644 index 00000000000..994e05650a0 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPerspectiveKeyOnArchitectureGraphs.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +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.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.db.DatabaseUtils.tableColumnExists; + +public class AddPerspectiveKeyOnArchitectureGraphs extends DdlChange { + static final String TABLE_NAME = "architecture_graphs"; + static final String COLUMN_NAME = "perspective_key"; + + public AddPerspectiveKeyOnArchitectureGraphs(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (!tableColumnExists(connection, TABLE_NAME, COLUMN_NAME)) { + var columnDef = VarcharColumnDef.newVarcharColumnDefBuilder() + .setColumnName(COLUMN_NAME) + .setLimit(255) + .setIsNullable(true) + .build(); + + context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) + .addColumn(columnDef) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleases.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleases.java new file mode 100644 index 00000000000..fb5514c42e9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/AddPreviousManualStatusToScaIssuesReleases.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +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.AddColumnsBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +import static org.sonar.db.DatabaseUtils.tableColumnExists; + +public class AddPreviousManualStatusToScaIssuesReleases extends DdlChange { + static final String TABLE_NAME = "sca_issues_releases"; + static final String COLUMN_NAME = "previous_manual_status"; + + public AddPreviousManualStatusToScaIssuesReleases(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (!tableColumnExists(connection, TABLE_NAME, COLUMN_NAME)) { + var columnDef = VarcharColumnDef.newVarcharColumnDefBuilder() + .setColumnName(COLUMN_NAME) + .setLimit(40) + .setIsNullable(true) + .build(); + + context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) + .addColumn(columnDef) + .build()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphs.java new file mode 100644 index 00000000000..1be744195c9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/CreateUniqueIndexOnArchitectureGraphs.java @@ -0,0 +1,61 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.step.DdlChange; + +public class CreateUniqueIndexOnArchitectureGraphs extends DdlChange { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String INDEX_NAME = "uq_idx_ag_brch_tp_src_pspctv"; + private static final String COLUMN_NAME_BRANCH_UUID = "branch_uuid"; + private static final String COLUMN_NAME_TYPE = "type"; + private static final String COLUMN_NAME_ECOSYSTEM = "ecosystem"; + private static final String COLUMN_NAME_PERSPECTIVE_KEY = "perspective_key"; + + public CreateUniqueIndexOnArchitectureGraphs(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + createIndex(context, connection); + } + } + + private void createIndex(Context context, Connection connection) { + if (!DatabaseUtils.indexExistsIgnoreCase(TABLE_NAME, INDEX_NAME, connection)) { + context.execute(new CreateIndexBuilder(getDialect()) + .setTable(TABLE_NAME) + .setName(INDEX_NAME) + .setUnique(true) + .addColumn(COLUMN_NAME_BRANCH_UUID, false) + .addColumn(COLUMN_NAME_TYPE, false) + .addColumn(COLUMN_NAME_ECOSYSTEM, false) + .addColumn(COLUMN_NAME_PERSPECTIVE_KEY, true) // nullable + .build()); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DbVersion202503.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DbVersion202503.java index d5be336bce6..ef284c6614f 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DbVersion202503.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DbVersion202503.java @@ -68,6 +68,12 @@ public class DbVersion202503 implements DbVersion { .add(2025_03_029, "Create unique index on SCA releases table", CreateUniqueIndexOnScaReleases.class) .add(2025_03_030, "Create SCA analyses table", CreateScaAnalysesTable.class) .add(2025_03_031, "Create unique index on SCA analyses table", CreateUniqueIndexOnScaAnalyses.class) + .add(2025_03_032, "Add 'analysis_uuid' column to 'architecture_graphs' table", AddAnalysisUuidOnArchitectureGraphs.class) + .add(2025_03_033, "Add 'perspective_key' column to 'architecture_graphs' table", AddPerspectiveKeyOnArchitectureGraphs.class) + .add(2025_03_034, "Drop unique index on 'architecture_graphs' table", DropIndexOnArchitectureGraphs.class) + .add(2025_03_035, "Rename column 'source' to 'ecosystem' on 'architecture_graphs' table", UpdateArchitectureGraphsSourceColumnRename.class) + .add(2025_03_036, "Create unique index on 'architecture_graphs' table", CreateUniqueIndexOnArchitectureGraphs.class) + .add(2025_03_037, "Add previous_manual_status to SCA issues releases", AddPreviousManualStatusToScaIssuesReleases.class) ; } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphs.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphs.java new file mode 100644 index 00000000000..d48236d6d76 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/DropIndexOnArchitectureGraphs.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.DropIndexChange; + +public class DropIndexOnArchitectureGraphs extends DropIndexChange { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String INDEX_NAME = "uq_idx_ag_branch_type_source"; + + public DropIndexOnArchitectureGraphs(Database db) { + super(db, INDEX_NAME, TABLE_NAME); + } + +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRename.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRename.java new file mode 100644 index 00000000000..0a19b0046f9 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v202503/UpdateArchitectureGraphsSourceColumnRename.java @@ -0,0 +1,33 @@ +/* + * SonarQube + * Copyright (C) 2009-2025 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.v202503; + +import org.sonar.db.Database; +import org.sonar.server.platform.db.migration.step.RenameVarcharColumnChange; + +public class UpdateArchitectureGraphsSourceColumnRename extends RenameVarcharColumnChange { + private static final String TABLE_NAME = "architecture_graphs"; + private static final String OLD_COLUMN_NAME = "source"; + private static final String NEW_COLUMN_NAME = "ecosystem"; + + public UpdateArchitectureGraphsSourceColumnRename(Database db) { + super(db, TABLE_NAME, OLD_COLUMN_NAME, NEW_COLUMN_NAME); + } +} diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 72b3bd272f0..9df2bb74034 100644 --- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -543,9 +543,8 @@ public class IssueIndex { addImpactFilters(query, filters); addComponentRelatedFilters(query, filters); addDatesFilter(filters, query); - addCreatedAfterByProjectsFilter(filters, query); + addNewCodeByProjectsFilter(filters, query); addNewCodeReferenceFilter(filters, query); - addNewCodeReferenceFilterByProjectsFilter(filters, query); return filters; } @@ -873,29 +872,19 @@ public class IssueIndex { } } - private static void addNewCodeReferenceFilterByProjectsFilter(AllFilters allFilters, IssueQuery query) { - Collection<String> newCodeOnReferenceByProjectUuids = query.newCodeOnReferenceByProjectUuids(); - BoolQueryBuilder boolQueryBuilder = boolQuery(); - - if (!newCodeOnReferenceByProjectUuids.isEmpty()) { - - newCodeOnReferenceByProjectUuids.forEach(projectOrProjectBranchUuid -> boolQueryBuilder.should(boolQuery() - .filter(termQuery(FIELD_ISSUE_BRANCH_UUID, projectOrProjectBranchUuid)) - .filter(termQuery(FIELD_ISSUE_NEW_CODE_REFERENCE, true)))); - - allFilters.addFilter("__is_new_code_reference_by_project_uuids", - new SimpleFieldFilterScope("newCodeReferenceByProjectUuids"), boolQueryBuilder); - } - } - - private static void addCreatedAfterByProjectsFilter(AllFilters allFilters, IssueQuery query) { + private static void addNewCodeByProjectsFilter(AllFilters allFilters, IssueQuery query) { Map<String, PeriodStart> createdAfterByProjectUuids = query.createdAfterByProjectUuids(); BoolQueryBuilder boolQueryBuilder = boolQuery(); createdAfterByProjectUuids.forEach((projectOrProjectBranchUuid, createdAfterDate) -> boolQueryBuilder.should(boolQuery() .filter(termQuery(FIELD_ISSUE_BRANCH_UUID, projectOrProjectBranchUuid)) .filter(rangeQuery(FIELD_ISSUE_FUNC_CREATED_AT).from(createdAfterDate.date().getTime(), createdAfterDate.inclusive())))); - allFilters.addFilter("__created_after_by_project_uuids", new SimpleFieldFilterScope("createdAfterByProjectUuids"), boolQueryBuilder); + Collection<String> newCodeOnReferenceByProjectUuids = query.newCodeOnReferenceByProjectUuids(); + newCodeOnReferenceByProjectUuids.forEach(projectOrProjectBranchUuid -> boolQueryBuilder.should(boolQuery() + .filter(termQuery(FIELD_ISSUE_BRANCH_UUID, projectOrProjectBranchUuid)) + .filter(termQuery(FIELD_ISSUE_NEW_CODE_REFERENCE, true)))); + + allFilters.addFilter("__new_code_by_project_uuids", new SimpleFieldFilterScope("newCodeByProjectUuids"), boolQueryBuilder); } private void validateCreationDateBounds(@Nullable Date createdBefore, @Nullable Date createdAfter) { diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java index 23daad4fed0..e0fa9ca61cc 100644 --- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java +++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFiltersTest.java @@ -456,7 +456,7 @@ class IssueIndexFiltersTest extends IssueIndexTestCommon { } @Test - void filter_by_new_code_reference_branches() { + void filter_by_new_reference_branches() { ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent(); IssueDoc project1Issue1 = newDocForProject(project1).setIsNewCodeReference(true); IssueDoc project1Issue2 = newDocForProject(project1).setIsNewCodeReference(false); @@ -474,14 +474,20 @@ class IssueIndexFiltersTest extends IssueIndexTestCommon { IssueDoc project2Branch1Issue1 = newDoc(project2Branch1, project2.uuid()).setIsNewCodeReference(false); IssueDoc project2Branch1Issue2 = newDoc(project2Branch1, project2.uuid()).setIsNewCodeReference(true); + ComponentDto project3 = db.components().insertPrivateProject().getMainBranchComponent(); + ComponentDto project3Branch1 = db.components().insertProjectBranch(project2); + IssueDoc project3Issue1 = newDoc(project3Branch1, project3.uuid()).setFuncCreationDate(new Date(1000L)); + IssueDoc project3Issue2 = newDoc(project3Branch1, project3.uuid()).setFuncCreationDate(new Date(2000L)); + indexIssues(project1Issue1, project1Issue2, project2Issue1, project2Issue2, - project1Branch1Issue1, project1Branch1Issue2, project2Branch1Issue1, project2Branch1Issue2); + project1Branch1Issue1, project1Branch1Issue2, project2Branch1Issue1, project2Branch1Issue2, project3Issue1, project3Issue2); // Search for issues of project 1 branch 1 and project 2 branch 1 that are new code on a branch using reference for new code assertThatSearchReturnsOnly(IssueQuery.builder() .mainBranch(false) - .newCodeOnReferenceByProjectUuids(Set.of(project1Branch1.uuid(), project2Branch1.uuid())), - project1Branch1Issue2.key(), project2Branch1Issue2.key()); + .newCodeOnReferenceByProjectUuids(Set.of(project1Branch1.uuid(), project2Branch1.uuid())) + .createdAfterByProjectUuids(Map.of(project3Branch1.uuid(), new IssueQuery.PeriodStart(new Date(1500), false))), + project1Branch1Issue2.key(), project2Branch1Issue2.key(), project3Issue2.key()); } @Test diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java index 6fe099a7209..530798c4395 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java @@ -37,12 +37,14 @@ import org.apache.commons.csv.CSVPrinter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; import org.sonar.core.util.ProcessWrapperFactory; import org.sonar.scanner.config.DefaultConfiguration; import org.sonar.scanner.repository.TelemetryCache; +import org.sonar.scanner.scan.filesystem.ProjectExclusionFilters; import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scm.git.JGitUtils; @@ -62,13 +64,16 @@ public class CliService { private final System2 system2; private final Server server; private final ScmConfiguration scmConfiguration; + private final ProjectExclusionFilters projectExclusionFilters; - public CliService(ProcessWrapperFactory processWrapperFactory, TelemetryCache telemetryCache, System2 system2, Server server, ScmConfiguration scmConfiguration) { + public CliService(ProcessWrapperFactory processWrapperFactory, TelemetryCache telemetryCache, System2 system2, Server server, ScmConfiguration scmConfiguration, + ProjectExclusionFilters projectExclusionFilters) { this.processWrapperFactory = processWrapperFactory; this.telemetryCache = telemetryCache; this.system2 = system2; this.server = server; this.scmConfiguration = scmConfiguration; + this.projectExclusionFilters = projectExclusionFilters; } public File generateManifestsZip(DefaultInputModule module, File cliExecutable, DefaultConfiguration configuration) throws IOException, IllegalStateException { @@ -123,7 +128,7 @@ public class CliService { } private @Nullable String getExcludeFlag(DefaultInputModule module, DefaultConfiguration configuration) throws IOException { - List<String> configExcludedPaths = getConfigExcludedPaths(configuration); + List<String> configExcludedPaths = getConfigExcludedPaths(configuration, projectExclusionFilters); List<String> scmIgnoredPaths = getScmIgnoredPaths(module); ArrayList<String> mergedExclusionPaths = new ArrayList<>(); @@ -143,11 +148,12 @@ public class CliService { return toCsvString(mergedExclusionPaths); } - private static List<String> getConfigExcludedPaths(DefaultConfiguration configuration) { + private static List<String> getConfigExcludedPaths(DefaultConfiguration configuration, ProjectExclusionFilters projectExclusionFilters) { + String[] sonarExclusions = projectExclusionFilters.getExclusionsConfig(InputFile.Type.MAIN); String[] scaExclusions = configuration.getStringArray(SCA_EXCLUSIONS_KEY); String[] scaExclusionsLegacy = configuration.getStringArray(LEGACY_SCA_EXCLUSIONS_KEY); - return Stream.of(scaExclusions, scaExclusionsLegacy) + return Stream.of(sonarExclusions, scaExclusions, scaExclusionsLegacy) .flatMap(Arrays::stream) .distinct() .toList(); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java index a6b10a95efa..7926860b731 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java @@ -36,6 +36,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; import org.mockito.MockedStatic; import org.sonar.api.batch.bootstrap.ProjectDefinition; +import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.platform.Server; @@ -44,6 +45,7 @@ import org.sonar.api.utils.System2; import org.sonar.core.util.ProcessWrapperFactory; import org.sonar.scanner.config.DefaultConfiguration; import org.sonar.scanner.repository.TelemetryCache; +import org.sonar.scanner.scan.filesystem.ProjectExclusionFilters; import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scm.git.GitScmProvider; import org.sonar.scm.git.JGitUtils; @@ -69,6 +71,7 @@ class CliServiceTest { ProcessWrapperFactory processWrapperFactory = mock(ProcessWrapperFactory.class, CALLS_REAL_METHODS); private MockedStatic<JGitUtils> jGitUtilsMock; DefaultConfiguration configuration = mock(DefaultConfiguration.class); + ProjectExclusionFilters projectExclusionFilters = mock(ProjectExclusionFilters.class); private CliService underTest; @@ -86,11 +89,12 @@ class CliServiceTest { jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))).thenReturn(List.of("ignored.txt")); when(server.getVersion()).thenReturn("1.0.0"); logTester.setLevel(INFO); + when(projectExclusionFilters.getExclusionsConfig(InputFile.Type.MAIN)).thenReturn(new String[0]); when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[0]); when(configuration.getStringArray(CliService.LEGACY_SCA_EXCLUSIONS_KEY)).thenReturn(new String[0]); when(configuration.getBoolean("sonar.sca.debug")).thenReturn(Optional.of(true)); - underTest = new CliService(processWrapperFactory, telemetryCache, System2.INSTANCE, server, scmConfiguration); + underTest = new CliService(processWrapperFactory, telemetryCache, System2.INSTANCE, server, scmConfiguration, projectExclusionFilters); } @AfterEach @@ -297,6 +301,18 @@ class CliServiceTest { } @Test + void generateZip_withExcludedManifestsAndSonarExcludesContainingDupes_mergesAndDedupes() throws Exception { + when(projectExclusionFilters.getExclusionsConfig(InputFile.Type.MAIN)).thenReturn(new String[] {"**/test1/**", "**/test4/**"}); + when(configuration.getStringArray(CliService.SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test2/**", "**/test1/**"}); + when(configuration.getStringArray(CliService.LEGACY_SCA_EXCLUSIONS_KEY)).thenReturn(new String[] {"**/test1/**", "**/test3/**"}); + + underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration); + + String capturedArgs = logTester.logs().stream().filter(log -> log.contains("Arguments Passed In:")).findFirst().get(); + assertThat(capturedArgs).contains("--exclude **/test1/**,**/test4/**,**/test2/**,**/test3/**,ignored.txt,.scannerwork/**"); + } + + @Test void generateZip_withScmIgnoresContainingBadCharacters_handlesTheBadCharacters() throws Exception { jGitUtilsMock.when(() -> JGitUtils.getAllIgnoredPaths(any(Path.class))) .thenReturn(List.of("**/test/**", "**/path with spaces/**", "**/path,with,commas/**", "**/path'with'quotes/**", "**/path\"with\"double\"quotes/**")); |