import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.ProjectData;
import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.dialect.Dialect;
import org.sonar.db.event.EventComponentChangeDto;
import org.sonar.db.event.EventDto;
import org.sonar.db.event.EventTesting;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
-import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
import static org.mockito.Mockito.verify;
db.components().insertSnapshot(project.getMainBranchComponent(), t -> t.setStatus(STATUS_UNPROCESSED).setLast(false));
SnapshotDto lastAnalysis = db.components().insertSnapshot(project.getMainBranchComponent(), t -> t.setStatus(STATUS_PROCESSED).setLast(true));
- underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchComponent().uuid(), project.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler());
+ underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchComponent().uuid(), project.projectUuid()), PurgeListener.EMPTY,
+ new PurgeProfiler());
dbSession.commit();
assertThat(uuidsOfAnalysesOfRoot(project.getMainBranchComponent())).containsOnly(pastAnalysis.getUuid(), lastAnalysis.getUuid());
BranchDto pr3 = db.components().insertProjectBranch(project.getProjectDto(), b -> b.setBranchType(BranchType.PULL_REQUEST).setExcludeFromPurge(true));
addComponentsSnapshotsAndIssuesToBranch(pr3, rule, 100);
- updateBranchCreationDate(date31DaysAgo, branch1.getUuid(), branch2.getUuid(), branch3.getUuid(), branch4.getUuid(), branch5.getUuid(), pr1.getUuid(), pr2.getUuid(), pr3.getUuid());
+ updateBranchCreationDate(date31DaysAgo, branch1.getUuid(), branch2.getUuid(), branch3.getUuid(), branch4.getUuid(), branch5.getUuid(), pr1.getUuid(), pr2.getUuid(),
+ pr3.getUuid());
- underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchDto().getUuid(), project.getProjectDto().getUuid()), PurgeListener.EMPTY, new PurgeProfiler());
+ underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchDto().getUuid(), project.getProjectDto().getUuid()), PurgeListener.EMPTY,
+ new PurgeProfiler());
dbSession.commit();
assertThat(uuidsIn("components")).containsOnly(
ComponentDto file = db.components().insertComponent(newFileDto(pullRequestComponent, dir));
db.issues().insert(rule, pullRequestComponent, file);
- underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchComponent().branchUuid(), project.getProjectDto().getUuid()), PurgeListener.EMPTY, new PurgeProfiler());
+ underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchComponent().branchUuid(), project.getProjectDto().getUuid()), PurgeListener.EMPTY,
+ new PurgeProfiler());
dbSession.commit();
assertThat(uuidsIn("components")).containsOnly(project.getMainBranchDto().getUuid(), nonMainBranch.getUuid(), recentPullRequest.getUuid());
IssueDto openOnEnabledComponent = db.issues().insert(rule, mainBranch, enabledFile, issue -> issue.setStatus("OPEN"));
IssueDto confirmOnEnabledComponent = db.issues().insert(rule, mainBranch, enabledFile, issue -> issue.setStatus("CONFIRM"));
- assertThat(db.countSql("select count(*) from snapshots where purge_status = 1")).isZero();
+ assertThat(countPurgedSnapshots()).isZero();
assertThat(db.countSql("select count(*) from issues where status = 'CLOSED'")).isZero();
assertThat(db.countSql("select count(*) from issues where resolution = 'REMOVED'")).isZero();
purgeListener, new PurgeProfiler());
dbSession.commit();
- // set purge_status=1 for non-last snapshot
- assertThat(db.countSql("select count(*) from snapshots where purge_status = 1")).isOne();
+ // set purged=true for non-last snapshot
+ assertThat(countPurgedSnapshots()).isOne();
// close open issues of selected
assertThat(db.countSql("select count(*) from issues where status = 'CLOSED'")).isEqualTo(4);
underTest.deleteBranch(dbSession, appBranch.getUuid());
dbSession.commit();
- assertThat(uuidsIn("components")).containsOnly(project.getMainBranchDto().getUuid(), app.getMainBranchDto().getUuid(), otherApp.getMainBranchDto().getUuid(), otherAppBranch.getUuid());
+ assertThat(uuidsIn("components")).containsOnly(project.getMainBranchDto().getUuid(), app.getMainBranchDto().getUuid(), otherApp.getMainBranchDto().getUuid(),
+ otherAppBranch.getUuid());
assertThat(uuidsIn("projects")).containsOnly(project.getProjectDto().getUuid(), app.getProjectDto().getUuid(), otherApp.getProjectDto().getUuid());
assertThat(uuidsIn("snapshots")).containsOnly(otherAppAnalysis.getUuid(), appAnalysis.getUuid(), otherAppBranchAnalysis.getUuid());
- assertThat(uuidsIn("project_branches")).containsOnly(project.getMainBranchDto().getUuid(), app.getMainBranchDto().getUuid(), otherApp.getMainBranchDto().getUuid(), otherAppBranch.getUuid());
+ assertThat(uuidsIn("project_branches")).containsOnly(project.getMainBranchDto().getUuid(), app.getMainBranchDto().getUuid(), otherApp.getMainBranchDto().getUuid(),
+ otherAppBranch.getUuid());
assertThat(uuidsIn("project_measures")).containsOnly(appMeasure.getUuid(), otherAppMeasure.getUuid(), otherAppBranchMeasure.getUuid());
assertThat(uuidsIn("app_projects", "application_uuid")).containsOnly(app.getProjectDto().getUuid(), otherApp.getProjectDto().getUuid());
assertThat(uuidsIn("app_branch_project_branch", "application_branch_uuid")).containsOnly(otherAppBranch.getUuid());
ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent());
ProjectData anotherProject = db.components().insertPrivateProject();
-
CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.getProjectDto().getUuid());
insertCeTaskInput(projectTask.getUuid());
CeActivityDto branchTask = insertCeActivity(branch, project.getProjectDto().getUuid());
assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid());
assertThat(taskUuidsIn("ce_task_input")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task");
- underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey());
+ underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(),
+ project.getProjectDto().getKey());
dbSession.commit();
assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid());
assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid());
assertThat(taskUuidsIn("ce_scanner_context")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task");
- underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey());
+ underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(),
+ project.getProjectDto().getKey());
dbSession.commit();
assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid());
ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent());
ProjectData anotherProject = db.components().insertPrivateProject();
-
CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.projectUuid());
insertCeTaskCharacteristics(projectTask.getUuid(), 3);
CeActivityDto branchTask = insertCeActivity(branch, project.projectUuid());
assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid());
assertThat(taskUuidsIn("ce_task_characteristics")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task");
- underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey());
+ underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(),
+ project.getProjectDto().getKey());
dbSession.commit();
assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid());
assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid());
assertThat(taskUuidsIn("ce_task_message")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task");
- underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey());
+ underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(),
+ project.getProjectDto().getKey());
dbSession.commit();
assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid());
verifyNoEffect(componentDbTester.insertPrivateProject().getMainBranchComponent());
verifyNoEffect(componentDbTester.insertPublicProject().getMainBranchComponent());
verifyNoEffect(componentDbTester.insertPrivatePortfolio());
- verifyNoEffect(componentDbTester.insertPrivatePortfolio(), componentDbTester.insertPrivateProject().getMainBranchComponent(), componentDbTester.insertPublicProject().getMainBranchComponent());
+ verifyNoEffect(componentDbTester.insertPrivatePortfolio(), componentDbTester.insertPrivateProject().getMainBranchComponent(),
+ componentDbTester.insertPublicProject().getMainBranchComponent());
}
@Test
@Test
public void deleteNonRootComponents_deletes_only_non_root_components_of_a_project_from_table_components() {
- ComponentDto project = new Random().nextBoolean() ? db.components().insertPublicProject().getMainBranchComponent() : db.components().insertPrivateProject().getMainBranchComponent();
+ ComponentDto project = new Random().nextBoolean() ? db.components().insertPublicProject().getMainBranchComponent()
+ : db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto dir1 = db.components().insertComponent(newDirectory(project, "A/B"));
ComponentDto file1 = db.components().insertComponent(newFileDto(project, dir1));
@Test
public void deleteNonRootComponents_deletes_only_specified_non_root_components_of_a_project_from_table_components() {
- ComponentDto project = new Random().nextBoolean() ? db.components().insertPublicProject().getMainBranchComponent() : db.components().insertPrivateProject().getMainBranchComponent();
+ ComponentDto project = new Random().nextBoolean() ? db.components().insertPublicProject().getMainBranchComponent()
+ : db.components().insertPrivateProject().getMainBranchComponent();
ComponentDto dir1 = db.components().insertComponent(newDirectory(project, "A/B"));
ComponentDto dir2 = db.components().insertComponent(newDirectory(project, "A/C"));
ComponentDto file1 = db.components().insertComponent(newFileDto(project, dir1));
underTest.deleteNonRootComponentsInView(dbSession, asList(subview1, pc2));
assertThat(uuidsIn("components"))
- .containsOnly(view.uuid(), projects[0].getMainBranchComponent().uuid(), projects[1].getMainBranchComponent().uuid(), projects[2].getMainBranchComponent().uuid(), subview2.uuid(), pc1.uuid());
+ .containsOnly(view.uuid(), projects[0].getMainBranchComponent().uuid(), projects[1].getMainBranchComponent().uuid(), projects[2].getMainBranchComponent().uuid(),
+ subview2.uuid(), pc1.uuid());
assertThat(uuidsIn("projects")).containsOnly(projects[0].getProjectDto().getUuid(), projects[1].getProjectDto().getUuid(), projects[2].getProjectDto().getUuid());
}
private void insertPropertyFor(ComponentDto... components) {
Stream.of(components).forEach(componentDto -> db.properties().insertProperty(new PropertyDto()
- .setKey(randomAlphabetic(3))
- .setValue(randomAlphabetic(3))
- .setEntityUuid(componentDto.uuid()),
+ .setKey(randomAlphabetic(3))
+ .setValue(randomAlphabetic(3))
+ .setEntityUuid(componentDto.uuid()),
componentDto.getKey(), componentDto.name(), componentDto.qualifier(), null));
}
private void insertPropertyFor(Collection<BranchDto> branches) {
branches.stream().forEach(branchDto -> db.properties().insertProperty(new PropertyDto()
- .setKey(randomAlphabetic(3))
- .setValue(randomAlphabetic(3))
- .setEntityUuid(branchDto.getUuid()),
+ .setKey(randomAlphabetic(3))
+ .setValue(randomAlphabetic(3))
+ .setEntityUuid(branchDto.getUuid()),
null, branchDto.getKey(), null, null));
}
addComponentsSnapshotsAndIssuesToBranch(componentDto, rule, branchAge);
}
+ private int countPurgedSnapshots() {
+ Dialect dialect = db.getDbClient().getDatabase().getDialect();
+ String bool = dialect.getTrueSqlValue();
+ return db.countSql("select count(*) from snapshots where purged = " + bool);
+ }
+
}
period1_mode,
period1_param,
period1_date,
- revision
+ revision,
+ purged
)
values (
#{uuid, jdbcType=VARCHAR},
#{periodMode, jdbcType=VARCHAR},
#{periodParam, jdbcType=VARCHAR},
#{periodDate, jdbcType=BIGINT},
- #{revision, jdbcType=VARCHAR}
+ #{revision, jdbcType=VARCHAR},
+ ${_false}
)
</insert>
</mapper>
and s.islast=#{islast}
</if>
<if test="notPurged != null and notPurged">
- and (s.purge_status is null or s.purge_status=0)
+ and s.purged = ${_false}
</if>
<if test="status != null">
and s.status in
update
snapshots
set
- purge_status = 1
+ purged = ${_true}
where
uuid in
<foreach collection="analysisUuids" open="(" close=")" item="analysisUuid" separator=",">
"STATUS" CHARACTER VARYING(4) DEFAULT 'U' NOT NULL,
"ISLAST" BOOLEAN DEFAULT FALSE NOT NULL,
"VERSION" CHARACTER VARYING(500),
- "PURGE_STATUS" INTEGER,
"BUILD_STRING" CHARACTER VARYING(100),
"REVISION" CHARACTER VARYING(100),
"BUILD_DATE" BIGINT,
"PERIOD1_MODE" CHARACTER VARYING(100),
"PERIOD1_PARAM" CHARACTER VARYING(100),
"PERIOD1_DATE" BIGINT,
- "CREATED_AT" BIGINT
+ "CREATED_AT" BIGINT,
+ "PURGED" BOOLEAN NOT NULL
);
ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT_UUID" ON "SNAPSHOTS"("ROOT_COMPONENT_UUID" NULLS FIRST);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class CreateBooleanPurgedColumnInSnapshots extends DdlChange {
+ private static final String COLUMN_NAME = "purged";
+ private static final String TABLE_NAME = "snapshots";
+
+ public CreateBooleanPurgedColumnInSnapshots(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ if (checkIfColumnExists()) {
+ return;
+ }
+ BooleanColumnDef columnDef = BooleanColumnDef.newBooleanColumnDefBuilder(COLUMN_NAME).setIsNullable(true).build();
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME).addColumn(columnDef).build());
+ }
+
+ public boolean checkIfColumnExists() throws SQLException {
+ try (var connection = getDatabase().getDataSource().getConnection()) {
+ if (DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN_NAME)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
.add(10_2_018, "Drop index 'component_uuid' in 'snapshots' table", DropIndexComponentUuidInSnapshots.class)
.add(10_2_019, "Rename 'component_uuid' in 'snapshots' table to 'root_component_uuid'", RenameComponentUuidInSnapshots.class)
.add(10_2_020, "Create index 'snapshots_root_component_uuid' in 'snapshots' table", CreateIndexRootComponentUuidInSnapshots.class)
+
+ .add(10_2_021, "Create 'purged' column in 'snapshots' table", CreateBooleanPurgedColumnInSnapshots.class)
+ .add(10_2_022, "Populate 'purged' column in 'snapshots' table", PopulatePurgedColumnInSnapshots.class)
+ .add(10_2_023, "Make 'purged' column not nullable in 'snapshots' table", MakePurgedColumnNotNullableInSnapshots.class)
+ .add(10_2_024, "Drop 'purge_status' column in 'snapshots' table", DropPurgeStatusColumnInSnapshots.class)
;
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DropColumnChange;
+
+public class DropPurgeStatusColumnInSnapshots extends DropColumnChange {
+ private static final String COLUMN_NAME = "purge_status";
+ private static final String TABLE_NAME = "snapshots";
+
+ public DropPurgeStatusColumnInSnapshots(Database db) {
+ super(db, TABLE_NAME, COLUMN_NAME);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BooleanColumnDef;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class MakePurgedColumnNotNullableInSnapshots extends DdlChange {
+ private static final String COLUMN_NAME = "purged";
+ private static final String TABLE_NAME = "snapshots";
+ public MakePurgedColumnNotNullableInSnapshots(Database db) {
+ super(db);
+ }
+ @Override
+ public void execute(Context context) throws SQLException {
+ BooleanColumnDef columnDef = BooleanColumnDef.newBooleanColumnDefBuilder(COLUMN_NAME).setIsNullable(false).build();
+ context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME).updateColumn(columnDef).build());
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class PopulatePurgedColumnInSnapshots extends DataChange {
+ private static final String SELECT_QUERY = """
+ SELECT s.uuid, s.purge_status
+ FROM snapshots s
+ WHERE s.purged is null
+ """;
+
+ private static final String UPDATE_QUERY = """
+ UPDATE snapshots
+ SET purged=?
+ WHERE uuid=?
+ """;
+
+ public PopulatePurgedColumnInSnapshots(Database db) {
+ super(db);
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ if (!checkIfColumnExists()) {
+ return;
+ }
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(SELECT_QUERY);
+ massUpdate.update(UPDATE_QUERY);
+
+ massUpdate.execute((row, update, index) -> {
+ String snapshotUuid = row.getString(1);
+ Integer purgedStatus = row.getNullableInt(2);
+ update.setBoolean(1, purgedStatus != null && purgedStatus == 1)
+ .setString(2, snapshotUuid);
+ return true;
+ });
+ }
+
+ public boolean checkIfColumnExists() throws SQLException {
+ try (var connection = getDatabase().getDataSource().getConnection()) {
+ if (DatabaseUtils.tableColumnExists(connection, "snapshots", "purge_status")) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BOOLEAN;
+
+public class CreateBooleanPurgedColumnInSnapshotsTest {
+
+ private static final String TABLE_NAME = "snapshots";
+ private static final String COLUMN_NAME = "purged";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(CreateBooleanPurgedColumnInSnapshotsTest.class, "schema.sql");
+
+ private final CreateBooleanPurgedColumnInSnapshots underTest = new CreateBooleanPurgedColumnInSnapshots(db.database());
+
+ @Test
+ public void execute_whenColumnDoesNotExist_shouldCreatePurgedColumn() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, null);
+ }
+
+ @Test
+ public void execute_whenExecutedTwice_shouldNotFail() throws SQLException {
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ underTest.execute();
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, null);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+public class DropPurgeStatusColumnInSnapshotsTest {
+
+ private static final String TABLE_NAME = "snapshots";
+ private static final String COLUMN_NAME = "purge_status";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(DropPurgeStatusColumnInSnapshotsTest.class, "schema.sql");
+
+ private final DropPurgeStatusColumnInSnapshots underTest = new DropPurgeStatusColumnInSnapshots(db.database());
+
+ @Test
+ public void drops_column() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.INTEGER, null, null);
+ underTest.execute();
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.INTEGER, null, null);
+ underTest.execute();
+ underTest.execute();
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BOOLEAN;
+
+public class MakePurgedColumnNotNullableInSnapshotsTest {
+ private static final String TABLE_NAME = "snapshots";
+ private static final String COLUMN_NAME = "purged";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(MakePurgedColumnNotNullableInSnapshotsTest.class, "schema.sql");
+
+ private final MakePurgedColumnNotNullableInSnapshots underTest = new MakePurgedColumnNotNullableInSnapshots(db.database());
+
+ @Test
+ public void execute_whenColumnIsNullable_shouldMakeColumnNullable() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, true);
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, false);
+ }
+
+ @Test
+ public void execute_whenExecutedTwice_shouldMakeColumnNullable() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, true);
+ underTest.execute();
+ underTest.execute();
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BOOLEAN, null, false);
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v102;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.tuple;
+
+public class PopulatePurgedColumnInSnapshotsTest {
+ private static final String TABLE_NAME = "snapshots";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(PopulatePurgedColumnInSnapshotsTest.class, "schema.sql");
+
+ private final PopulatePurgedColumnInSnapshots underTest = new PopulatePurgedColumnInSnapshots(db.database());
+
+ @Test
+ public void execute_whenSnapshotsDoesNotExist_shouldNotFail() {
+ assertThatCode(underTest::execute)
+ .doesNotThrowAnyException();
+ }
+
+ @Test
+ public void execute_whenSnapshotsExist_shouldPopulatePurgedColumn() throws SQLException {
+ insertSnapshot("uuid-1", null);
+ insertSnapshot("uuid-2", 1);
+ insertSnapshot("uuid-3", 0);
+ insertSnapshot("uuid-4", null);
+
+ underTest.execute();
+
+ assertThat(db.select("select UUID, PURGED from snapshots"))
+ .extracting(stringObjectMap -> stringObjectMap.get("UUID"), stringObjectMap -> stringObjectMap.get("PURGED"))
+ .containsExactlyInAnyOrder(
+ tuple("uuid-1", false),
+ tuple("uuid-2", true),
+ tuple("uuid-3", false),
+ tuple("uuid-4", false));
+ }
+
+ private void insertSnapshot(String uuid, @Nullable Integer status) {
+ db.executeInsert(TABLE_NAME,
+ "UUID", uuid,
+ "ROOT_COMPONENT_UUID", "",
+ "STATUS", "",
+ "ISLAST", true,
+ "PURGE_STATUS", status,
+ "PURGED", null);
+ }
+}
--- /dev/null
+CREATE TABLE "SNAPSHOTS"(
+ "UUID" CHARACTER VARYING(50) NOT NULL,
+ "ROOT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+ "STATUS" CHARACTER VARYING(4) DEFAULT 'U' NOT NULL,
+ "ISLAST" BOOLEAN DEFAULT FALSE NOT NULL,
+ "VERSION" CHARACTER VARYING(500),
+ "PURGE_STATUS" INTEGER,
+ "BUILD_STRING" CHARACTER VARYING(100),
+ "REVISION" CHARACTER VARYING(100),
+ "BUILD_DATE" BIGINT,
+ "PERIOD1_MODE" CHARACTER VARYING(100),
+ "PERIOD1_PARAM" CHARACTER VARYING(100),
+ "PERIOD1_DATE" BIGINT,
+ "CREATED_AT" BIGINT
+);
+ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT_UUID" ON "SNAPSHOTS"("ROOT_COMPONENT_UUID" NULLS FIRST);
--- /dev/null
+CREATE TABLE "SNAPSHOTS"(
+ "UUID" CHARACTER VARYING(50) NOT NULL,
+ "ROOT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+ "STATUS" CHARACTER VARYING(4) DEFAULT 'U' NOT NULL,
+ "ISLAST" BOOLEAN DEFAULT FALSE NOT NULL,
+ "VERSION" CHARACTER VARYING(500),
+ "PURGE_STATUS" INTEGER,
+ "BUILD_STRING" CHARACTER VARYING(100),
+ "REVISION" CHARACTER VARYING(100),
+ "BUILD_DATE" BIGINT,
+ "PERIOD1_MODE" CHARACTER VARYING(100),
+ "PERIOD1_PARAM" CHARACTER VARYING(100),
+ "PERIOD1_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "PURGED" BOOLEAN NOT NULL
+);
+ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT_UUID" ON "SNAPSHOTS"("ROOT_COMPONENT_UUID" NULLS FIRST);
--- /dev/null
+CREATE TABLE "SNAPSHOTS"(
+ "UUID" CHARACTER VARYING(50) NOT NULL,
+ "ROOT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+ "STATUS" CHARACTER VARYING(4) DEFAULT 'U' NOT NULL,
+ "ISLAST" BOOLEAN DEFAULT FALSE NOT NULL,
+ "VERSION" CHARACTER VARYING(500),
+ "PURGE_STATUS" INTEGER,
+ "BUILD_STRING" CHARACTER VARYING(100),
+ "REVISION" CHARACTER VARYING(100),
+ "BUILD_DATE" BIGINT,
+ "PERIOD1_MODE" CHARACTER VARYING(100),
+ "PERIOD1_PARAM" CHARACTER VARYING(100),
+ "PERIOD1_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "PURGED" BOOLEAN
+);
+ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT_UUID" ON "SNAPSHOTS"("ROOT_COMPONENT_UUID" NULLS FIRST);
--- /dev/null
+CREATE TABLE "SNAPSHOTS"(
+ "UUID" CHARACTER VARYING(50) NOT NULL,
+ "ROOT_COMPONENT_UUID" CHARACTER VARYING(50) NOT NULL,
+ "STATUS" CHARACTER VARYING(4) DEFAULT 'U' NOT NULL,
+ "ISLAST" BOOLEAN DEFAULT FALSE NOT NULL,
+ "VERSION" CHARACTER VARYING(500),
+ "PURGE_STATUS" INTEGER,
+ "BUILD_STRING" CHARACTER VARYING(100),
+ "REVISION" CHARACTER VARYING(100),
+ "BUILD_DATE" BIGINT,
+ "PERIOD1_MODE" CHARACTER VARYING(100),
+ "PERIOD1_PARAM" CHARACTER VARYING(100),
+ "PERIOD1_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "PURGED" BOOLEAN
+);
+ALTER TABLE "SNAPSHOTS" ADD CONSTRAINT "PK_SNAPSHOTS" PRIMARY KEY("UUID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT_UUID" ON "SNAPSHOTS"("ROOT_COMPONENT_UUID" NULLS FIRST);