001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2011 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.plugins.dbcleaner.api;
021
022 import java.util.List;
023
024 import javax.persistence.Query;
025
026 import org.apache.commons.configuration.Configuration;
027 import org.sonar.api.batch.Event;
028 import org.sonar.api.database.DatabaseSession;
029 import org.sonar.api.database.model.MeasureData;
030 import org.sonar.api.database.model.MeasureModel;
031 import org.sonar.api.database.model.RuleFailureModel;
032 import org.sonar.api.database.model.Snapshot;
033 import org.sonar.api.database.model.SnapshotSource;
034 import org.sonar.api.design.DependencyDto;
035 import org.sonar.api.utils.TimeProfiler;
036 import org.sonar.jpa.entity.DuplicationBlock;
037
038 /**
039 * @since 2.5
040 */
041 public final class PurgeUtils {
042
043 public static final int DEFAULT_MINIMUM_PERIOD_IN_HOURS = 12;
044 public static final String PROP_KEY_MINIMUM_PERIOD_IN_HOURS = "sonar.purge.minimumPeriodInHours";
045
046 /**
047 * Maximum elements in the SQL statement "IN" due to an Oracle limitation (see error ORA-01795)
048 */
049 public static final int MAX_IN_ELEMENTS = 950;
050
051 private PurgeUtils() {
052 // only static methods
053 }
054
055 public static int getMinimumPeriodInHours(Configuration conf) {
056 int hours = DEFAULT_MINIMUM_PERIOD_IN_HOURS;
057 if (conf != null) {
058 hours = conf.getInt(PROP_KEY_MINIMUM_PERIOD_IN_HOURS, DEFAULT_MINIMUM_PERIOD_IN_HOURS);
059 }
060 return hours;
061 }
062
063 public static void deleteSnapshotsData(DatabaseSession session, List<Integer> snapshotIds) {
064 deleteMeasuresBySnapshotId(session, snapshotIds);
065 deleteSources(session, snapshotIds);
066 deleteViolations(session, snapshotIds);
067 deleteDependencies(session, snapshotIds);
068 deleteDuplicationBlocks(session, snapshotIds);
069 deleteEvents(session, snapshotIds);
070 deleteSnapshots(session, snapshotIds);
071 }
072
073 public static void deleteDependencies(DatabaseSession session, List<Integer> snapshotIds) {
074 executeQuery(session, "delete dependencies", snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.fromSnapshotId in (:ids)");
075 executeQuery(session, "delete dependencies", snapshotIds, "delete from " + DependencyDto.class.getSimpleName() + " d where d.toSnapshotId in (:ids)");
076 }
077
078 /**
079 * Delete all measures, including MEASURE_DATA
080 */
081 public static void deleteMeasuresBySnapshotId(DatabaseSession session, List<Integer> snapshotIds) {
082 executeQuery(session, "delete measures by snapshot id", snapshotIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.snapshotId in (:ids)");
083 executeQuery(session, "delete measures by snapshot id", snapshotIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.snapshotId in (:ids)");
084 }
085
086 /**
087 * Delete all measures, including MEASURE_DATA
088 */
089 public static void deleteMeasuresById(DatabaseSession session, List<Integer> measureIds) {
090 executeQuery(session, "delete measures by id", measureIds, "delete from " + MeasureData.class.getSimpleName() + " m where m.measure.id in (:ids)");
091 executeQuery(session, "delete measures by id", measureIds, "delete from " + MeasureModel.class.getSimpleName() + " m where m.id in (:ids)");
092 }
093
094 /**
095 * Delete SNAPSHOT_SOURCES table
096 */
097 public static void deleteSources(DatabaseSession session, List<Integer> snapshotIds) {
098 executeQuery(session, "delete sources", snapshotIds, "delete from " + SnapshotSource.class.getSimpleName() + " e where e.snapshotId in (:ids)");
099 }
100
101 /**
102 * Delete violations (RULE_FAILURES table)
103 */
104 public static void deleteViolations(DatabaseSession session, List<Integer> snapshotIds) {
105 executeQuery(session, "delete violations", snapshotIds, "delete from " + RuleFailureModel.class.getSimpleName() + " e where e.snapshotId in (:ids)");
106 }
107
108 /**
109 * @since 2.11
110 */
111 private static void deleteDuplicationBlocks(DatabaseSession session, List<Integer> snapshotIds) {
112 executeQuery(session, "delete duplication blocks", snapshotIds, "delete from " + DuplicationBlock.class.getSimpleName() + " e where e.snapshotId in (:ids)");
113 }
114
115 /**
116 * Delete EVENTS table
117 */
118 public static void deleteEvents(DatabaseSession session, List<Integer> snapshotIds) {
119 executeQuery(session, "delete events", snapshotIds, "delete from " + Event.class.getSimpleName() + " e where e.snapshot.id in (:ids)");
120 }
121
122 /**
123 * Delete SNAPSHOTS table
124 */
125 public static void deleteSnapshots(DatabaseSession session, List<Integer> snapshotIds) {
126 executeQuery(session, "delete snapshots", snapshotIds, "delete from " + Snapshot.class.getSimpleName() + " s where s.id in (:ids)");
127 }
128
129 /**
130 * Paginate execution of SQL requests to avoid exceeding size of rollback segment
131 */
132 public static void executeQuery(DatabaseSession session, String description, List<Integer> ids, String hql) {
133 if (ids == null || ids.isEmpty()) {
134 return;
135 }
136
137 TimeProfiler profiler = new TimeProfiler().setLevelToDebug().start("Execute " + description);
138
139 int index = 0;
140 while (index < ids.size()) {
141 Query query = session.createQuery(hql);
142 List<Integer> paginedSids = ids.subList(index, Math.min(ids.size(), index + MAX_IN_ELEMENTS));
143 query.setParameter("ids", paginedSids);
144 query.executeUpdate();
145 index += MAX_IN_ELEMENTS;
146 session.commit();
147 }
148
149 profiler.stop();
150 }
151
152 }