]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5804 delete file_sources when deleting a project or during the data cleaning...
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 2 Dec 2014 14:54:24 +0000 (15:54 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 2 Dec 2014 15:25:49 +0000 (16:25 +0100)
15 files changed:
server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java
sonar-core/src/main/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTask.java
sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java
sonar-core/src/main/java/org/sonar/core/purge/IdUuidPair.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/purge/IdUuidPairs.java [new file with mode: 0644]
sonar-core/src/main/java/org/sonar/core/purge/PurgeCommands.java
sonar-core/src/main/java/org/sonar/core/purge/PurgeDao.java
sonar-core/src/main/java/org/sonar/core/purge/PurgeMapper.java
sonar-core/src/main/resources/org/sonar/core/purge/PurgeMapper.xml
sonar-core/src/test/java/org/sonar/core/computation/dbcleaner/DefaultPurgeTaskTest.java
sonar-core/src/test/java/org/sonar/core/purge/IdUuidPairsTest.java [new file with mode: 0644]
sonar-core/src/test/java/org/sonar/core/purge/PurgeDaoTest.java
sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml [new file with mode: 0644]
sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/shouldDeleteProject.xml

index 91c2a08dbf7c96b079b63ce497bda39c90f54560..b7c655660348a4b5e02b477717d186cf0f2b7fd4 100644 (file)
@@ -25,6 +25,7 @@ import org.sonar.api.resources.Scopes;
 import org.sonar.core.component.ComponentDto;
 import org.sonar.core.persistence.DbSession;
 import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.purge.IdUuidPair;
 import org.sonar.core.purge.PurgeDao;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
@@ -51,7 +52,7 @@ public class ComponentCleanerService implements ServerComponent {
       if (!Scopes.PROJECT.equals(project.scope())) {
         throw new IllegalArgumentException("Only projects can be deleted");
       }
-      purgeDao.deleteResourceTree(project.getId());
+      purgeDao.deleteResourceTree(new IdUuidPair(project.getId(), project.uuid()));
       dbSession.commit();
 
       deleteFromIndices(project.uuid());
index 51128905fa07834ceb3e7a579f3018b3a10ea980..2e0f073571e3e466ccdc37e01712b2cf5c9d3cdc 100644 (file)
@@ -28,10 +28,12 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.utils.TimeUtils;
 import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner;
+import org.sonar.core.purge.IdUuidPair;
 import org.sonar.core.purge.PurgeConfiguration;
 import org.sonar.core.purge.PurgeDao;
 import org.sonar.core.purge.PurgeProfiler;
 import org.sonar.core.resource.ResourceDao;
+import org.sonar.core.resource.ResourceDto;
 import org.sonar.plugins.dbcleaner.api.PurgeTask;
 
 import static org.sonar.core.purge.PurgeConfiguration.newDefaultPurgeConfiguration;
@@ -57,7 +59,8 @@ public class DefaultPurgeTask implements PurgeTask {
 
   @Override
   public DefaultPurgeTask delete(long resourceId) {
-    purgeDao.deleteResourceTree(resourceId);
+    ResourceDto project = resourceDao.getResource(resourceId);
+    purgeDao.deleteResourceTree(new IdUuidPair(project.getId(), project.getUuid()));
     return this;
   }
 
index 410afd9297054ed5f1c39d8e67cffdc826a9299c..43d1f26cefc4da1e3892d90712966b6c5f62b02b 100644 (file)
@@ -24,11 +24,7 @@ import com.google.common.io.Closeables;
 import org.apache.ibatis.builder.xml.XMLMapperBuilder;
 import org.apache.ibatis.logging.LogFactory;
 import org.apache.ibatis.mapping.Environment;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.ExecutorType;
-import org.apache.ibatis.session.SqlSession;
-import org.apache.ibatis.session.SqlSessionFactory;
-import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.ibatis.session.*;
 import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
 import org.apache.ibatis.type.JdbcType;
 import org.slf4j.LoggerFactory;
@@ -45,14 +41,7 @@ import org.sonar.core.component.db.SnapshotMapper;
 import org.sonar.core.computation.db.AnalysisReportDto;
 import org.sonar.core.computation.db.AnalysisReportMapper;
 import org.sonar.core.config.Logback;
-import org.sonar.core.dashboard.ActiveDashboardDto;
-import org.sonar.core.dashboard.ActiveDashboardMapper;
-import org.sonar.core.dashboard.DashboardDto;
-import org.sonar.core.dashboard.DashboardMapper;
-import org.sonar.core.dashboard.WidgetDto;
-import org.sonar.core.dashboard.WidgetMapper;
-import org.sonar.core.dashboard.WidgetPropertyDto;
-import org.sonar.core.dashboard.WidgetPropertyMapper;
+import org.sonar.core.dashboard.*;
 import org.sonar.core.dependency.DependencyDto;
 import org.sonar.core.dependency.DependencyMapper;
 import org.sonar.core.dependency.ResourceSnapshotDto;
@@ -61,56 +50,23 @@ import org.sonar.core.duplication.DuplicationMapper;
 import org.sonar.core.duplication.DuplicationUnitDto;
 import org.sonar.core.graph.jdbc.GraphDto;
 import org.sonar.core.graph.jdbc.GraphDtoMapper;
-import org.sonar.core.issue.db.ActionPlanDto;
-import org.sonar.core.issue.db.ActionPlanMapper;
-import org.sonar.core.issue.db.ActionPlanStatsDto;
-import org.sonar.core.issue.db.ActionPlanStatsMapper;
-import org.sonar.core.issue.db.IssueChangeDto;
-import org.sonar.core.issue.db.IssueChangeMapper;
-import org.sonar.core.issue.db.IssueDto;
-import org.sonar.core.issue.db.IssueFilterDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteDto;
-import org.sonar.core.issue.db.IssueFilterFavouriteMapper;
-import org.sonar.core.issue.db.IssueFilterMapper;
-import org.sonar.core.issue.db.IssueMapper;
-import org.sonar.core.measure.db.MeasureDto;
-import org.sonar.core.measure.db.MeasureFilterDto;
-import org.sonar.core.measure.db.MeasureFilterMapper;
-import org.sonar.core.measure.db.MeasureMapper;
-import org.sonar.core.measure.db.MetricDto;
-import org.sonar.core.measure.db.MetricMapper;
+import org.sonar.core.issue.db.*;
+import org.sonar.core.measure.db.*;
 import org.sonar.core.notification.db.NotificationQueueDto;
 import org.sonar.core.notification.db.NotificationQueueMapper;
-import org.sonar.core.permission.GroupWithPermissionDto;
-import org.sonar.core.permission.PermissionTemplateDto;
-import org.sonar.core.permission.PermissionTemplateGroupDto;
-import org.sonar.core.permission.PermissionTemplateMapper;
-import org.sonar.core.permission.PermissionTemplateUserDto;
-import org.sonar.core.permission.UserWithPermissionDto;
+import org.sonar.core.permission.*;
 import org.sonar.core.persistence.dialect.Dialect;
 import org.sonar.core.persistence.migration.v44.Migration44Mapper;
 import org.sonar.core.persistence.migration.v45.Migration45Mapper;
 import org.sonar.core.persistence.migration.v50.Migration50Mapper;
 import org.sonar.core.properties.PropertiesMapper;
 import org.sonar.core.properties.PropertyDto;
+import org.sonar.core.purge.IdUuidPair;
 import org.sonar.core.purge.PurgeMapper;
 import org.sonar.core.purge.PurgeableSnapshotDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto;
-import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper;
-import org.sonar.core.qualitygate.db.QualityGateConditionDto;
-import org.sonar.core.qualitygate.db.QualityGateConditionMapper;
-import org.sonar.core.qualitygate.db.QualityGateDto;
-import org.sonar.core.qualitygate.db.QualityGateMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleDto;
-import org.sonar.core.qualityprofile.db.ActiveRuleMapper;
-import org.sonar.core.qualityprofile.db.ActiveRuleParamDto;
-import org.sonar.core.qualityprofile.db.QualityProfileDto;
-import org.sonar.core.qualityprofile.db.QualityProfileMapper;
-import org.sonar.core.resource.ResourceDto;
-import org.sonar.core.resource.ResourceIndexDto;
-import org.sonar.core.resource.ResourceIndexerMapper;
-import org.sonar.core.resource.ResourceKeyUpdaterMapper;
-import org.sonar.core.resource.ResourceMapper;
+import org.sonar.core.qualitygate.db.*;
+import org.sonar.core.qualityprofile.db.*;
+import org.sonar.core.resource.*;
 import org.sonar.core.rule.RuleDto;
 import org.sonar.core.rule.RuleMapper;
 import org.sonar.core.rule.RuleParamDto;
@@ -123,17 +79,7 @@ import org.sonar.core.technicaldebt.db.CharacteristicMapper;
 import org.sonar.core.technicaldebt.db.RequirementMigrationDto;
 import org.sonar.core.template.LoadedTemplateDto;
 import org.sonar.core.template.LoadedTemplateMapper;
-import org.sonar.core.user.AuthorDto;
-import org.sonar.core.user.AuthorMapper;
-import org.sonar.core.user.GroupDto;
-import org.sonar.core.user.GroupMapper;
-import org.sonar.core.user.GroupMembershipDto;
-import org.sonar.core.user.GroupMembershipMapper;
-import org.sonar.core.user.GroupRoleDto;
-import org.sonar.core.user.RoleMapper;
-import org.sonar.core.user.UserDto;
-import org.sonar.core.user.UserMapper;
-import org.sonar.core.user.UserRoleDto;
+import org.sonar.core.user.*;
 
 import java.io.InputStream;
 
@@ -152,6 +98,21 @@ public class MyBatis implements BatchComponent, ServerComponent {
     this.queue = queue;
   }
 
+  public static void closeQuietly(SqlSession session) {
+    if (session != null) {
+      try {
+        session.close();
+      } catch (Exception e) {
+        LoggerFactory.getLogger(MyBatis.class).warn("Fail to close session", e);
+        // do not re-throw the exception
+      }
+    }
+  }
+
+  private static JdbcTransactionFactory createTransactionFactory() {
+    return new JdbcTransactionFactory();
+  }
+
   public MyBatis start() {
     LogFactory.useSlf4jLogging();
 
@@ -218,6 +179,7 @@ public class MyBatis implements BatchComponent, ServerComponent {
     loadAlias(conf, "RequirementMigration", RequirementMigrationDto.class);
     loadAlias(conf, "Activity", ActivityDto.class);
     loadAlias(conf, "AnalysisReport", AnalysisReportDto.class);
+    loadAlias(conf, "IdUuidPair", IdUuidPair.class);
 
     // AuthorizationMapper has to be loaded before IssueMapper because this last one used it
     loadMapper(conf, "org.sonar.core.user.AuthorizationMapper");
@@ -279,17 +241,6 @@ public class MyBatis implements BatchComponent, ServerComponent {
     return new DbSession(queue, session);
   }
 
-  public static void closeQuietly(SqlSession session) {
-    if (session != null) {
-      try {
-        session.close();
-      } catch (Exception e) {
-        LoggerFactory.getLogger(MyBatis.class).warn("Fail to close session", e);
-        // do not re-throw the exception
-      }
-    }
-  }
-
   private void loadMappers(Configuration mybatisConf, Class<?>... mapperClasses) {
     for (Class mapperClass : mapperClasses) {
       loadMapper(mybatisConf, mapperClass);
@@ -325,8 +276,4 @@ public class MyBatis implements BatchComponent, ServerComponent {
   private void loadAlias(Configuration conf, String alias, Class dtoClass) {
     conf.getTypeAliasRegistry().registerAlias(alias, dtoClass);
   }
-
-  private static JdbcTransactionFactory createTransactionFactory() {
-    return new JdbcTransactionFactory();
-  }
 }
diff --git a/sonar-core/src/main/java/org/sonar/core/purge/IdUuidPair.java b/sonar-core/src/main/java/org/sonar/core/purge/IdUuidPair.java
new file mode 100644 (file)
index 0000000..f19ef88
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.purge;
+
+public class IdUuidPair {
+  private Long id;
+  private String uuid;
+
+  public IdUuidPair() {
+  }
+
+  public IdUuidPair(long id, String uuid) {
+    this.id = id;
+    this.uuid = uuid;
+  }
+
+  public Long getId() {
+    return id;
+  }
+
+  public void setId(long id) {
+    this.id = id;
+  }
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public void setUuid(String uuid) {
+    this.uuid = uuid;
+  }
+}
diff --git a/sonar-core/src/main/java/org/sonar/core/purge/IdUuidPairs.java b/sonar-core/src/main/java/org/sonar/core/purge/IdUuidPairs.java
new file mode 100644 (file)
index 0000000..1ad2939
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.purge;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+public class IdUuidPairs {
+  private IdUuidPairs() {
+  }
+
+  public static List<Long> ids(List<IdUuidPair> pairs) {
+    List<Long> ids = Lists.newArrayList();
+
+    for (IdUuidPair idUuidPair : pairs) {
+      ids.add(idUuidPair.getId());
+    }
+
+    return ids;
+  }
+
+  public static List<String> uuids(List<IdUuidPair> idUuidPairList) {
+    List<String> uuids = Lists.newArrayList();
+
+    for (IdUuidPair idUuidPair : idUuidPairList) {
+      uuids.add(idUuidPair.getUuid());
+    }
+
+    return uuids;
+  }
+}
index 3a4e0ac18e691f30aa4e0a0f58eb8d9435983e85..a68c3fa58ceb40fa501050ad9801aa405c078da6 100644 (file)
@@ -279,4 +279,10 @@ class PurgeCommands {
     profiler.stop();
   }
 
+  public void deleteFileSources(String rootUuid) {
+    profiler.start("deleteFileSources (file_sources)");
+    purgeMapper.deleteFileSourcesByProjectUuid(rootUuid);
+    session.commit();
+    profiler.stop();
+  }
 }
index a7bde27f95e0d1b030c186c5e142e4c4a5606b88..c43f3df6d93632a00a1ddf7912dbea372061662b 100644 (file)
@@ -41,11 +41,11 @@ import java.util.List;
  * @since 2.14
  */
 public class PurgeDao {
+  private static final Logger LOG = LoggerFactory.getLogger(PurgeDao.class);
   private final MyBatis mybatis;
   private final ResourceDao resourceDao;
-  private static final Logger LOG = LoggerFactory.getLogger(PurgeDao.class);
-  private PurgeProfiler profiler;
   private final System2 system2;
+  private PurgeProfiler profiler;
 
   public PurgeDao(MyBatis mybatis, ResourceDao resourceDao, PurgeProfiler profiler, System2 system2) {
     this.mybatis = mybatis;
@@ -134,9 +134,9 @@ public class PurgeDao {
     session.select("org.sonar.core.purge.PurgeMapper.selectResourceIdsToDisable", project.getId(), new ResultHandler() {
       @Override
       public void handleResult(ResultContext resultContext) {
-        Long resourceId = (Long) resultContext.getResultObject();
-        if (resourceId != null) {
-          disableResource(resourceId, purgeMapper);
+        IdUuidPair resourceIdUuid = (IdUuidPair) resultContext.getResultObject();
+        if (resourceIdUuid.getId() != null) {
+          disableResource(resourceIdUuid, purgeMapper);
         }
       }
     });
@@ -162,17 +162,22 @@ public class PurgeDao {
     return result;
   }
 
-  public PurgeDao deleteResourceTree(long rootProjectId) {
+  public PurgeDao deleteResourceTree(IdUuidPair rootIdUuid) {
     final DbSession session = mybatis.openSession(true);
     final PurgeMapper mapper = session.getMapper(PurgeMapper.class);
     try {
-      deleteProject(rootProjectId, mapper, new PurgeCommands(session, profiler));
+      deleteProject(rootIdUuid.getId(), mapper, new PurgeCommands(session, profiler));
+      deleteFileSources(rootIdUuid.getUuid(), new PurgeCommands(session, profiler));
       return this;
     } finally {
       MyBatis.closeQuietly(session);
     }
   }
 
+  private void deleteFileSources(String rootUuid, PurgeCommands commands) {
+    commands.deleteFileSources(rootUuid);
+  }
+
   private void deleteProject(long rootProjectId, PurgeMapper mapper, PurgeCommands commands) {
     List<Long> childrenIds = mapper.selectProjectIdsByRootId(rootProjectId);
     for (Long childId : childrenIds) {
@@ -183,9 +188,11 @@ public class PurgeDao {
     commands.deleteResources(resourceIds);
   }
 
-  private void disableResource(long resourceId, PurgeMapper mapper) {
+  private void disableResource(IdUuidPair resourceIdUuid, PurgeMapper mapper) {
+    long resourceId = resourceIdUuid.getId();
     mapper.deleteResourceIndex(Arrays.asList(resourceId));
     mapper.setSnapshotIsLastToFalse(resourceId);
+    mapper.deleteFileSourcesByUuid(resourceIdUuid.getUuid());
     mapper.disableResource(resourceId);
     mapper.resolveResourceIssuesNotAlreadyResolved(resourceId, new Date(system2.now()));
   }
index b3c26d95a07445061fc9671043ed53831865b91b..f498b1224961db63e6da246e9d584e30ab26451f 100644 (file)
@@ -103,4 +103,8 @@ public interface PurgeMapper {
   void deleteOldClosedIssueChanges(@Param("rootProjectId") long rootProjectId, @Nullable @Param("toDate") Date toDate);
 
   void deleteOldClosedIssues(@Param("rootProjectId") long rootProjectId, @Nullable @Param("toDate") Date toDate);
+
+  void deleteFileSourcesByProjectUuid(String rootProjectUuid);
+
+  void deleteFileSourcesByUuid(String fileUuid);
 }
index a7a82c75d0a0d3d174780770798a9fa1de404660..c017309048503b6015756ab85b2a08841e49fbb1 100644 (file)
@@ -47,7 +47,7 @@
       </if>
     </where>
   </select>
-  
+
   <select id="selectSnapshotIdsByResource" parameterType="map" resultType="long">
     select s.id from snapshots s
     <where>
@@ -72,8 +72,8 @@
     not exists(select e.id from events e where e.snapshot_id=s.id)
   </select>
 
-  <select id="selectResourceIdsToDisable" resultType="long" parameterType="long">
-    select p.id from projects p
+  <select id="selectResourceIdsToDisable" resultType="IdUuidPair" parameterType="long">
+    select p.id, p.uuid from projects p
     where (p.id=#{id} or p.root_id=#{id}) and p.enabled=${_true}
     and not exists(select s.project_id from snapshots s where s.islast=${_true} and s.project_id=p.id)
   </select>
   <delete id="deleteSnapshotMeasures" parameterType="map">
     delete from project_measures where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotSource" parameterType="map">
     delete from snapshot_sources where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotGraphs" parameterType="map">
     delete from graphs where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotDependenciesFromSnapshotId" parameterType="map">
     delete from dependencies where from_snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotDuplications" parameterType="map">
     delete from duplications_index where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotEvents" parameterType="map">
     delete from events where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshot" parameterType="map">
     delete from snapshots where id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteSnapshotWastedMeasures" parameterType="map">
     delete from project_measures
     <where>
-    snapshot_id in
-    <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
+      snapshot_id in
+      <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
         #{snapshotId}
-    </foreach>
-    and (rule_id is not null or person_id is not null
-    <if test="mids.size()>0">
-      or metric_id in
-      <foreach item="mid" index="index" collection="mids" open="(" separator="," close=")">#{mid}</foreach>
-    </if>)
+      </foreach>
+      and (rule_id is not null or person_id is not null
+      <if test="mids.size()>0">
+        or metric_id in
+        <foreach item="mid" index="index" collection="mids" open="(" separator="," close=")">#{mid}</foreach>
+      </if>
+      )
     </where>
   </delete>
 
   </update>
 
   <update id="resolveResourceIssuesNotAlreadyResolved" parameterType="map">
-    UPDATE issues SET status='CLOSED',resolution='REMOVED',updated_at=#{date},issue_close_date=#{date}, issue_update_date=#{date}
+    UPDATE issues SET status='CLOSED',resolution='REMOVED',updated_at=#{date},issue_close_date=#{date},
+    issue_update_date=#{date}
     WHERE component_id=#{resourceId} AND resolution IS NULL
   </update>
 
   <delete id="deleteSnapshotData" parameterType="map">
     delete from snapshot_data where snapshot_id in
     <foreach collection="snapshotIds" open="(" close=")" item="snapshotId" separator=",">
-        #{snapshotId}
+      #{snapshotId}
     </foreach>
   </delete>
 
   <delete id="deleteResourceIssueChanges" parameterType="map">
     delete from issue_changes ic
     where exists (select * from issues i where i.kee=ic.issue_key and i.component_id in
-      <foreach collection="resourceIds" open="(" close=")" item="resourceId" separator=",">
-        #{resourceId}
-      </foreach>
+    <foreach collection="resourceIds" open="(" close=")" item="resourceId" separator=",">
+      #{resourceId}
+    </foreach>
     )
   </delete>
 
     </foreach>
   </delete>
 
+  <delete id="deleteFileSourcesByProjectUuid">
+    delete from file_sources where project_uuid=#{rootProjectUuid}
+  </delete>
+
+  <delete id="deleteFileSourcesByUuid">
+    delete from file_sources where file_uuid=#{fileUuid}
+  </delete>
 
   <delete id="deleteOldClosedIssueChanges" parameterType="map">
     delete from issue_changes ic
index 5871fb717612c7b467d7c34878b97034306a3b7c..c03d4ada9886c49ad5ea1249b7b24b2273ae82bd 100644 (file)
@@ -30,6 +30,7 @@ import org.sonar.api.config.Settings;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.core.computation.dbcleaner.period.DefaultPeriodCleaner;
+import org.sonar.core.purge.IdUuidPair;
 import org.sonar.core.purge.PurgeConfiguration;
 import org.sonar.core.purge.PurgeDao;
 import org.sonar.core.purge.PurgeProfiler;
@@ -139,8 +140,10 @@ public class DefaultPurgeTaskTest {
 
   @Test
   public void call_dao_delete_when_deleting() throws Exception {
+    when(resourceDao.getResource(123L)).thenReturn(new ResourceDto().setId(123L).setUuid("A"));
+
     sut.delete(123L);
 
-    verify(purgeDao, times(1)).deleteResourceTree(123L);
+    verify(purgeDao, times(1)).deleteResourceTree(any(IdUuidPair.class));
   }
 }
diff --git a/sonar-core/src/test/java/org/sonar/core/purge/IdUuidPairsTest.java b/sonar-core/src/test/java/org/sonar/core/purge/IdUuidPairsTest.java
new file mode 100644 (file)
index 0000000..5fc4367
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.core.purge;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class IdUuidPairsTest {
+  @Test
+  public void extract_ids() throws Exception {
+    List<IdUuidPair> idUuidPairList = Lists.newArrayList(new IdUuidPair(1L, "ABCD"), new IdUuidPair(2L, "EFGH"));
+
+    List<Long> ids = IdUuidPairs.ids(idUuidPairList);
+
+    assertThat(ids).containsOnly(1L, 2L);
+  }
+
+  @Test
+  public void extract_uuids() {
+    List<IdUuidPair> idUuidPairList = Lists.newArrayList(new IdUuidPair(1L, "ABCD"), new IdUuidPair(2L, "EFGH"));
+
+    List<String> uuids = IdUuidPairs.uuids(idUuidPairList);
+
+    assertThat(uuids).containsOnly("ABCD", "EFGH");
+  }
+}
index fd598dfd8bd3a6568ba8c23d2408422ac50f7fb8..e0bc253992d17bdf0c8a1ab2e50aec22b32b3acd 100644 (file)
@@ -39,6 +39,15 @@ public class PurgeDaoTest extends AbstractDaoTestCase {
 
   PurgeDao dao;
 
+  private static PurgeableSnapshotDto getById(List<PurgeableSnapshotDto> snapshots, long id) {
+    for (PurgeableSnapshotDto snapshot : snapshots) {
+      if (snapshot.getSnapshotId() == id) {
+        return snapshot;
+      }
+    }
+    return null;
+  }
+
   @Before
   public void createDao() {
     system2 = mock(System2.class);
@@ -55,12 +64,19 @@ public class PurgeDaoTest extends AbstractDaoTestCase {
   }
 
   @Test
-  public void shouldPurgeProject() {
+  public void should_purge_project() {
     setupData("shouldPurgeProject");
     dao.purge(new PurgeConfiguration(1L, new String[0], 30));
     checkTables("shouldPurgeProject", "projects", "snapshots");
   }
 
+  @Test
+  public void delete_file_sources_of_disabled_resources() {
+    setupData("delete_file_sources_of_disabled_resources");
+    dao.purge(new PurgeConfiguration(1L, new String[0], 30, system2));
+    checkTables("delete_file_sources_of_disabled_resources", "file_sources");
+  }
+
   @Test
   public void shouldDeleteHistoricalDataOfDirectoriesAndFiles() {
     setupData("shouldDeleteHistoricalDataOfDirectoriesAndFiles");
@@ -96,20 +112,11 @@ public class PurgeDaoTest extends AbstractDaoTestCase {
     assertThat(getById(snapshots, 5L).hasEvents()).isTrue();
   }
 
-  private static PurgeableSnapshotDto getById(List<PurgeableSnapshotDto> snapshots, long id) {
-    for (PurgeableSnapshotDto snapshot : snapshots) {
-      if (snapshot.getSnapshotId() == id) {
-        return snapshot;
-      }
-    }
-    return null;
-  }
-
   @Test
-  public void shouldDeleteProject() {
+  public void should_delete_project_and_associated_data() {
     setupData("shouldDeleteProject");
-    dao.deleteResourceTree(1L);
-    assertEmptyTables("projects", "snapshots", "action_plans", "issues", "issue_changes");
+    dao.deleteResourceTree(new IdUuidPair(1L, "A"));
+    assertEmptyTables("projects", "snapshots", "action_plans", "issues", "issue_changes", "file_sources");
   }
 
   @Test
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources-result.xml
new file mode 100644 (file)
index 0000000..c6eb797
--- /dev/null
@@ -0,0 +1,5 @@
+<dataset>
+
+  <file_sources id="2" project_uuid="ABCD" file_uuid="KLMN" data="[null]" line_hashes="[null]" data_hash="321654988"
+                created_at="123456789" updated_at="123456789"/>
+</dataset>
diff --git a/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml b/sonar-core/src/test/resources/org/sonar/core/purge/PurgeDaoTest/delete_file_sources_of_disabled_resources.xml
new file mode 100644 (file)
index 0000000..c461e9d
--- /dev/null
@@ -0,0 +1,79 @@
+<dataset>
+
+  <!-- the project -->
+  <projects id="1" enabled="[true]" root_id="[null]" uuid="ABCD" project_uuid="ABCD" module_uuid="[null]"
+            module_uuid_path="[null]" created_at="[null]"
+            long_name="[null]" scope="PRJ" qualifier="TRK" kee="project" name="project"
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+            deprecated_kee="[null]" authorization_updated_at="[null]"/>
+
+  <!-- the directory -->
+  <projects id="2" enabled="[true]" root_id="1" uuid="EFGH" project_uuid="ABCD" module_uuid="ABCD"
+            module_uuid_path="[null]" created_at="[null]"
+            long_name="[null]" scope="DIR" qualifier="DIR" kee="project:my/dir" name="my/dir"
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+            deprecated_kee="[null]" authorization_updated_at="[null]"/>
+
+  <!-- the files -->
+  <projects id="3" enabled="[true]" root_id="1" uuid="GHIJ" project_uuid="ABCD" module_uuid="ABCD"
+            module_uuid_path="ABCD" created_at="[null]"
+            long_name="[null]" scope="FIL" qualifier="FIL" kee="project:my/dir/File.java" name="my/dir/File.java"
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+            deprecated_kee="[null]" authorization_updated_at="[null]"/>
+  <projects id="4" enabled="[true]" root_id="1" uuid="KLMN" project_uuid="ABCD" module_uuid="ABCD"
+            module_uuid_path="ABCD" created_at="[null]"
+            long_name="[null]" scope="FIL" qualifier="FIL" kee="project:my/dir/DeletedFile.java"
+            name="my/dir/DeletedFile.java"
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+            deprecated_kee="[null]" authorization_updated_at="[null]"/>
+
+  <snapshots id="1"
+             project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+             build_date="2008-12-02 13:58:00.00" version="[null]" path="[null]"/>
+
+  <snapshots id="2"
+             project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+             build_date="2008-12-02 13:58:00.00" version="[null]" path="[null]"/>
+
+  <snapshots id="3"
+             project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[false]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+             build_date="2008-12-02 13:58:00.00" version="[null]" path="[null]"/>
+
+  <!-- isLast is true, don't want to delete associated source lines -->
+  <snapshots id="4"
+             project_id="4" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+             status="P" islast="[true]" purge_status="[null]"
+             period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+             period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+             period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+             period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+             period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+             depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+             build_date="2008-12-02 13:58:00.00" version="[null]" path="[null]"/>
+
+  <file_sources id="1" project_uuid="ABCD" file_uuid="GHIJ" data="[null]" line_hashes="[null]" data_hash="321654987"
+                created_at="123456789" updated_at="123456789"/>
+  <file_sources id="2" project_uuid="ABCD" file_uuid="KLMN" data="[null]" line_hashes="[null]" data_hash="321654988"
+                created_at="123456789" updated_at="123456789"/>
+</dataset>
index 34b5391a7c29e16d00e9a8f0b19a0cb96ce03e71..91e1c3c1c58491688a4f876ba194b77c9fa311f1 100644 (file)
@@ -4,7 +4,8 @@
   <projects id="1" enabled="[true]" root_id="[null]"
             uuid="A" project_uuid="A" module_uuid="[null]" module_uuid_path="[null]"
             long_name="[null]" scope="PRJ" qualifier="TRK" kee="project" name="project"
-            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]"
+            authorization_updated_at="[null]"/>
 
   <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]"
              status="P" islast="[false]" purge_status="[null]"
@@ -18,7 +19,7 @@
              version="[null]" path="[null]"/>
 
   <action_plans id="1" kee="ABCD" project_id="1" name="SHORT_TERM" description="[null]" deadline="[null]"
-                user_login="igor" status="[null]" created_at="[null]" updated_at="[null]" />
+                user_login="igor" status="[null]" created_at="[null]" updated_at="[null]"/>
 
   <issues id="1" kee="ABCDE" component_id="1" status="CLOSED" resolution="[null]" line="200" severity="BLOCKER"
           reporter="perceval" assignee="arthur" rule_id="500"
           issue_close_date="2013-04-16"
       />
 
-  <issue_changes id="1" kee="[null]" issue_key="ABCDF" created_at="[null]" updated_at="[null]" user_login="admin" change_type="comment" change_data="abc"/>
+  <issue_changes id="1" kee="[null]" issue_key="ABCDF" created_at="[null]" updated_at="[null]" user_login="admin"
+                 change_type="comment" change_data="abc"/>
 
   <!-- modules -->
   <projects id="2" enabled="[true]" root_id="1"
             uuid="B" project_uuid="A" module_uuid="A" module_uuid_path="A"
             long_name="[null]" scope="PRJ" qualifier="BRC" kee="module1" name="module1"
-            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]"
+            authorization_updated_at="[null]"/>
 
   <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
              status="P" islast="[false]" purge_status="[null]"
@@ -67,7 +70,8 @@
   <projects id="3" enabled="[false]" root_id="1"
             uuid="C" project_uuid="A" module_uuid="A" module_uuid_path="A"
             long_name="[null]" scope="PRJ" qualifier="BRC" kee="module2" name="module2"
-            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]"
+            authorization_updated_at="[null]"/>
 
   <snapshots id="3" project_id="3" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
              status="P" islast="[true]" purge_status="[null]"
@@ -84,7 +88,8 @@
   <projects id="4" enabled="[false]" root_id="3"
             uuid="D" project_uuid="A" module_uuid="C" module_uuid_path="A.C"
             long_name="[null]" scope="FIL" qualifier="FIL" kee="module2:File.java" name="File"
-            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]" authorization_updated_at="[null]" />
+            description="[null]" language="java" copy_resource_id="[null]" person_id="[null]"
+            authorization_updated_at="[null]"/>
 
   <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
              status="P" islast="[true]" purge_status="[null]"
              depth="[null]" scope="FIL" qualifier="FIL" created_at="2008-12-02 13:58:00.00"
              build_date="2008-12-02 13:58:00.00"
              version="[null]" path="[null]"/>
+  <file_sources id="1" project_uuid="A" file_uuid="D" data="[null]" line_hashes="[null]" data_hash="321654987"
+                created_at="123456789" updated_at="123456789"/>
 </dataset>