aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-06-22 11:38:41 +0200
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-06-24 11:00:24 +0200
commit655df791c25001f56578ef00643fd0b5ae935357 (patch)
tree51f946fc708a7a9f7ffe5bb28a915c8ff6b6e438 /sonar-db
parent57ad62d14359412694fd43b74a363085a79fd68a (diff)
downloadsonarqube-655df791c25001f56578ef00643fd0b5ae935357.tar.gz
sonarqube-655df791c25001f56578ef00643fd0b5ae935357.zip
SONAR-7789 Add and populate DB column RULES_PROFILES.LAST_USED
Diffstat (limited to 'sonar-db')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java4
-rw-r--r--sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDto.java11
-rw-r--r--sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java2
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java5
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java4
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfiles.java45
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfiles.java91
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml31
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql2
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl3
-rw-r--r--sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java94
-rw-r--r--sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java3
-rw-r--r--sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java40
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java2
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest.java77
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest.java136
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/delete-result.xml2
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/insert-result.xml6
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/shared.xml4
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/update-result.xml4
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest/rules_profiles.sql13
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest/rules_profiles.sql84
22 files changed, 604 insertions, 59 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
index b1d54d299e9..a88742ac859 100644
--- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
@@ -61,6 +61,10 @@ public class QualityProfileDao implements Dao {
return dto;
}
+ public List<QualityProfileDto> selectByKeys(DbSession session, List<String> keys) {
+ return executeLargeInputs(keys, mapper(session)::selectByKeys);
+ }
+
public List<QualityProfileDto> selectAll(DbSession session) {
return mapper(session).selectAll();
}
diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDto.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDto.java
index addb250f35e..944b3353c8a 100644
--- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDto.java
+++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDto.java
@@ -33,6 +33,7 @@ public class QualityProfileDto extends Dto<String> {
private String language;
private String parentKee;
private String rulesUpdatedAt;
+ private Long lastUsed;
private boolean isDefault;
/**
@@ -112,6 +113,16 @@ public class QualityProfileDto extends Dto<String> {
return this;
}
+ @CheckForNull
+ public Long getLastUsed() {
+ return lastUsed;
+ }
+
+ public QualityProfileDto setLastUsed(@Nullable Long lastUsed) {
+ this.lastUsed = lastUsed;
+ return this;
+ }
+
public boolean isDefault() {
return isDefault;
}
diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
index b8a6ef79c09..50a67bc3ddf 100644
--- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
+++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
@@ -52,6 +52,8 @@ public interface QualityProfileMapper {
List<QualityProfileDto> selectByLanguage(String language);
+ List<QualityProfileDto> selectByKeys(@Param("keys") List<String> keys);
+
// INHERITANCE
@CheckForNull
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
index c5c58f99d32..9e699e2d1b9 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
@@ -30,7 +30,7 @@ import org.sonar.db.MyBatis;
public class DatabaseVersion {
- public static final int LAST_VERSION = 1_245;
+ public static final int LAST_VERSION = 1_247;
/**
* The minimum supported version which can be upgraded. Lower
@@ -90,8 +90,7 @@ public class DatabaseVersion {
"user_roles",
"user_tokens",
"widgets",
- "widget_properties"
- );
+ "widget_properties");
private MyBatis mybatis;
public DatabaseVersion(MyBatis mybatis) {
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
index 4f4d7cfef7b..df052f0228d 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
@@ -88,6 +88,7 @@ import org.sonar.db.version.v60.AddAnalysisUuidColumnToDuplicationsIndex;
import org.sonar.db.version.v60.AddComponentUuidColumnToDuplicationsIndex;
import org.sonar.db.version.v60.AddComponentUuidColumnToMeasures;
import org.sonar.db.version.v60.AddComponentUuidColumnsToSnapshots;
+import org.sonar.db.version.v60.AddLastUsedColumnToRulesProfiles;
import org.sonar.db.version.v60.AddUuidColumnToSnapshots;
import org.sonar.db.version.v60.AddUuidColumnsToProjects;
import org.sonar.db.version.v60.AddUuidColumnsToResourceIndex;
@@ -117,6 +118,7 @@ import org.sonar.db.version.v60.PopulateAnalysisUuidOfDuplicationsIndex;
import org.sonar.db.version.v60.PopulateComponentUuidColumnsOfSnapshots;
import org.sonar.db.version.v60.PopulateComponentUuidOfDuplicationsIndex;
import org.sonar.db.version.v60.PopulateComponentUuidOfMeasures;
+import org.sonar.db.version.v60.PopulateLastUsedColumnOfRulesProfiles;
import org.sonar.db.version.v60.PopulateUuidColumnOnSnapshots;
import org.sonar.db.version.v60.PopulateUuidColumnsOfProjects;
import org.sonar.db.version.v60.PopulateUuidColumnsOfResourceIndex;
@@ -227,6 +229,8 @@ public class MigrationStepModule extends Module {
CleanOrphanRowsInProjects.class,
MakeUuidColumnsNotNullOnProjects.class,
DropIdColumnsFromProjects.class,
+ AddLastUsedColumnToRulesProfiles.class,
+ PopulateLastUsedColumnOfRulesProfiles.class,
// SNAPSHOTS.UUID
AddUuidColumnToSnapshots.class,
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfiles.java b/sonar-db/src/main/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfiles.java
new file mode 100644
index 00000000000..bcdcfe4cf2a
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfiles.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.BigDecimalColumnDef.newBigDecimalColumnDefBuilder;
+
+public class AddLastUsedColumnToRulesProfiles extends DdlChange {
+
+ private static final String TABLE_QUALITY_PROFILES = "rules_profiles";
+
+ public AddLastUsedColumnToRulesProfiles(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDatabase().getDialect(), TABLE_QUALITY_PROFILES)
+ .addColumn(newBigDecimalColumnDefBuilder().setColumnName("last_used").setIsNullable(true).build())
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfiles.java b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfiles.java
new file mode 100644
index 00000000000..66f98d233f7
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfiles.java
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class PopulateLastUsedColumnOfRulesProfiles extends BaseDataChange {
+
+ private static final Pattern PATTERN_QP_KEY = Pattern.compile("\"key\"\\s*:\\s*\"(.*?)\"");
+
+ public PopulateLastUsedColumnOfRulesProfiles(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ Map<String, Long> lastAnalysisDatesByQualityProfileKey = buildQualityProfilesMap(context);
+ if (lastAnalysisDatesByQualityProfileKey.isEmpty()) {
+ return;
+ }
+
+ populateLastUsedColumn(context, lastAnalysisDatesByQualityProfileKey);
+ }
+
+ private static Map<String, Long> buildQualityProfilesMap(Context context) throws SQLException {
+ Map<String, Long> lastAnalysisDatesByQPKeys = new HashMap<>();
+
+ context.prepareSelect("select s.created_at, pm.text_value " +
+ "from project_measures pm " +
+ " inner join snapshots s on pm.snapshot_id = s.id " +
+ " inner join metrics m on pm.metric_id=m.id " +
+ "where s.islast=? " +
+ " and m.name='quality_profiles' " +
+ "order by s.created_at ")
+ .setBoolean(1, true)
+ .scroll(row -> {
+ long analysisDate = row.getLong(1);
+ String json = row.getString(2);
+ Matcher matcher = PATTERN_QP_KEY.matcher(json);
+ while (matcher.find()) {
+ lastAnalysisDatesByQPKeys.put(matcher.group(1), analysisDate);
+ }
+ });
+ return lastAnalysisDatesByQPKeys;
+ }
+
+ private static void populateLastUsedColumn(Context context, Map<String, Long> lastAnalysisDatesByQualityProfileKey) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id, kee from rules_profiles where last_used is null");
+ massUpdate.update("update rules_profiles set last_used=? where id=?");
+ massUpdate.rowPluralName("rules_profiles");
+ massUpdate.execute((row, update) -> handle(lastAnalysisDatesByQualityProfileKey, row, update));
+ }
+
+ private static boolean handle(Map<String, Long> lastAnalysisDatesByQualityProfileKey, Select.Row row, SqlStatement update) throws SQLException {
+ int qualityProfileId = row.getInt(1);
+ String qualityProfileKey = row.getString(2);
+
+ update.setLong(1, lastAnalysisDatesByQualityProfileKey.get(qualityProfileKey));
+ update.setInt(2, qualityProfileId);
+
+ return true;
+ }
+}
diff --git a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
index edc2563f8e2..e33f92db1d2 100644
--- a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
+++ b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
@@ -12,22 +12,25 @@
p.is_default as isDefault,
p.created_at as createdAt,
p.updated_at as updatedAt,
- p.rules_updated_at as rulesUpdatedAt
+ p.rules_updated_at as rulesUpdatedAt,
+ p.last_used as lastUsed
</sql>
<insert id="insert" parameterType="QualityProfile" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
- INSERT INTO rules_profiles (kee, parent_kee, name, language, is_default, created_at, updated_at, rules_updated_at)
- VALUES (#{kee}, #{parentKee}, #{name}, #{language}, #{isDefault}, #{createdAt}, #{updatedAt}, #{rulesUpdatedAt,})
+ INSERT INTO rules_profiles (kee, parent_kee, name, language, is_default, created_at, updated_at, rules_updated_at, last_used)
+ VALUES (#{kee, jdbcType=VARCHAR}, #{parentKee,jdbcType=VARCHAR}, #{name, jdbcType=VARCHAR}, #{language, jdbcType=VARCHAR}, #{isDefault, jdbcType=BOOLEAN},
+ #{createdAt, jdbcType=TIMESTAMP}, #{updatedAt, jdbcType=TIMESTAMP}, #{rulesUpdatedAt, jdbcType=VARCHAR}, #{lastUsed, jdbcType=BIGINT})
</insert>
<update id="update" parameterType="QualityProfile">
UPDATE rules_profiles SET
- name=#{name},
- language=#{language},
- is_default=#{isDefault},
- parent_kee=#{parentKee},
- updated_at=#{updatedAt},
- rules_updated_at=#{rulesUpdatedAt}
+ name=#{name, jdbcType=VARCHAR},
+ language=#{language, jdbcType=VARCHAR},
+ is_default=#{isDefault, jdbcType=BOOLEAN},
+ parent_kee=#{parentKee, jdbcType=VARCHAR},
+ updated_at=#{updatedAt, jdbcType=TIMESTAMP},
+ rules_updated_at=#{rulesUpdatedAt, jdbcType=VARCHAR},
+ last_used=#{lastUsed, jdbcType=BIGINT}
WHERE id=#{id}
</update>
@@ -69,6 +72,16 @@
WHERE p.kee=#{id}
</select>
+ <select id="selectByKeys" parameterType="string" resultType="QualityProfile">
+ SELECT
+ <include refid="profilesColumns"/>
+ FROM rules_profiles p
+ WHERE p.kee in
+ <foreach collection="keys" open="(" close=")" item="key" separator=",">
+ #{key}
+ </foreach>
+ </select>
+
<select id="selectByLanguage" parameterType="String" resultType="QualityProfile">
SELECT
<include refid="profilesColumns"/>
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
index 6f8bca251cf..9c370a1c52d 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
+++ b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
@@ -452,6 +452,8 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1242');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1243');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1244');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1245');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1246');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1247');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482');
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
index aa11e3b2bab..6ae2e93429f 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -21,7 +21,8 @@ CREATE TABLE "RULES_PROFILES" (
"RULES_UPDATED_AT" VARCHAR(100),
"IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE,
"CREATED_AT" TIMESTAMP,
- "UPDATED_AT" TIMESTAMP
+ "UPDATED_AT" TIMESTAMP,
+ "LAST_USED" BIGINT
);
CREATE TABLE "PROJECT_QPROFILES" (
diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java
index d47a6e19467..d0f52c3643b 100644
--- a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java
@@ -26,14 +26,18 @@ import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UtcDateUtils;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import static com.google.common.collect.ImmutableList.of;
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-
+import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto;
public class QualityProfileDaoTest {
@@ -41,8 +45,11 @@ public class QualityProfileDaoTest {
@Rule
public DbTester dbTester = DbTester.create(system);
+ DbClient dbClient = dbTester.getDbClient();
+ DbSession dbSession = dbTester.getSession();
+ QualityProfileDbTester qualityProfileDb = new QualityProfileDbTester(dbTester);
- QualityProfileDao dao = dbTester.getDbClient().qualityProfileDao();
+ QualityProfileDao underTest = dbTester.getDbClient().qualityProfileDao();
@Before
public void createDao() {
@@ -57,9 +64,9 @@ public class QualityProfileDaoTest {
.setName("ABCDE")
.setLanguage("xoo");
- dao.insert(dto);
+ underTest.insert(dto);
- dbTester.assertDbUnit(getClass(), "insert-result.xml", new String[]{"created_at", "updated_at", "rules_updated_at"}, "rules_profiles");
+ dbTester.assertDbUnit(getClass(), "insert-result.xml", new String[] {"created_at", "updated_at", "rules_updated_at"}, "rules_profiles");
}
@Test
@@ -73,16 +80,16 @@ public class QualityProfileDaoTest {
.setParentKee("fghij")
.setDefault(false);
- dao.update(dto);
+ underTest.update(dto);
- dbTester.assertDbUnit(getClass(), "update-result.xml", new String[]{"created_at", "updated_at", "rules_updated_at"}, "rules_profiles");
+ dbTester.assertDbUnit(getClass(), "update-result.xml", new String[] {"created_at", "updated_at", "rules_updated_at"}, "rules_profiles");
}
@Test
public void delete() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- dao.delete(1);
+ underTest.delete(1);
dbTester.assertDbUnit(getClass(), "delete-result.xml", "rules_profiles");
}
@@ -91,7 +98,7 @@ public class QualityProfileDaoTest {
public void find_all() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- List<QualityProfileDto> dtos = dao.selectAll(dbTester.getSession());
+ List<QualityProfileDto> dtos = underTest.selectAll(dbTester.getSession());
assertThat(dtos).hasSize(2);
@@ -112,7 +119,7 @@ public class QualityProfileDaoTest {
public void find_all_is_sorted_by_profile_name() {
dbTester.prepareDbUnit(getClass(), "select_all_is_sorted_by_profile_name.xml");
- List<QualityProfileDto> dtos = dao.selectAll();
+ List<QualityProfileDto> dtos = underTest.selectAll();
assertThat(dtos).hasSize(3);
assertThat(dtos.get(0).getName()).isEqualTo("First");
@@ -124,44 +131,44 @@ public class QualityProfileDaoTest {
public void get_default_profile() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- QualityProfileDto java = dao.selectDefaultProfile("java");
+ QualityProfileDto java = underTest.selectDefaultProfile("java");
assertThat(java).isNotNull();
assertThat(java.getKey()).isEqualTo("java_sonar_way");
- assertThat(dao.selectDefaultProfile("js")).isNull();
+ assertThat(underTest.selectDefaultProfile("js")).isNull();
}
@Test
public void get_default_profiles() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- List<QualityProfileDto> java = dao.selectDefaultProfiles(dbTester.getSession(), singletonList("java"));
+ List<QualityProfileDto> java = underTest.selectDefaultProfiles(dbTester.getSession(), singletonList("java"));
assertThat(java).extracting("key").containsOnly("java_sonar_way");
- assertThat(dao.selectDefaultProfiles(dbTester.getSession(), singletonList("js"))).isEmpty();
- assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("java", "js"))).extracting("key").containsOnly("java_sonar_way");
- assertThat(dao.selectDefaultProfiles(dbTester.getSession(), of("js", "java"))).extracting("key").containsOnly("java_sonar_way");
+ assertThat(underTest.selectDefaultProfiles(dbTester.getSession(), singletonList("js"))).isEmpty();
+ assertThat(underTest.selectDefaultProfiles(dbTester.getSession(), of("java", "js"))).extracting("key").containsOnly("java_sonar_way");
+ assertThat(underTest.selectDefaultProfiles(dbTester.getSession(), of("js", "java"))).extracting("key").containsOnly("java_sonar_way");
}
@Test
public void get_by_name_and_language() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- QualityProfileDto dto = dao.selectByNameAndLanguage("Sonar Way", "java", dbTester.getSession());
+ QualityProfileDto dto = underTest.selectByNameAndLanguage("Sonar Way", "java", dbTester.getSession());
assertThat(dto.getId()).isEqualTo(1);
assertThat(dto.getName()).isEqualTo("Sonar Way");
assertThat(dto.getLanguage()).isEqualTo("java");
assertThat(dto.getParentKee()).isNull();
- assertThat(dao.selectByNameAndLanguage("Sonar Way", "java", dbTester.getSession())).isNotNull();
- assertThat(dao.selectByNameAndLanguage("Sonar Way", "unknown", dbTester.getSession())).isNull();
+ assertThat(underTest.selectByNameAndLanguage("Sonar Way", "java", dbTester.getSession())).isNotNull();
+ assertThat(underTest.selectByNameAndLanguage("Sonar Way", "unknown", dbTester.getSession())).isNull();
}
@Test
public void get_by_name_and_languages() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- List<QualityProfileDto> dtos = dao.selectByNameAndLanguages("Sonar Way", singletonList("java"), dbTester.getSession());
+ List<QualityProfileDto> dtos = underTest.selectByNameAndLanguages("Sonar Way", singletonList("java"), dbTester.getSession());
assertThat(dtos).hasSize(1);
QualityProfileDto dto = dtos.iterator().next();
assertThat(dto.getId()).isEqualTo(1);
@@ -169,15 +176,15 @@ public class QualityProfileDaoTest {
assertThat(dto.getLanguage()).isEqualTo("java");
assertThat(dto.getParentKee()).isNull();
- assertThat(dao.selectByNameAndLanguages("Sonar Way", singletonList("unknown"), dbTester.getSession())).isEmpty();
- assertThat(dao.selectByNameAndLanguages("Sonar Way", of("java", "unknown"), dbTester.getSession())).extracting("id").containsOnly(1);
+ assertThat(underTest.selectByNameAndLanguages("Sonar Way", singletonList("unknown"), dbTester.getSession())).isEmpty();
+ assertThat(underTest.selectByNameAndLanguages("Sonar Way", of("java", "unknown"), dbTester.getSession())).extracting("id").containsOnly(1);
}
@Test
public void find_by_language() {
dbTester.prepareDbUnit(getClass(), "select_by_language.xml");
- List<QualityProfileDto> result = dao.selectByLanguage("java");
+ List<QualityProfileDto> result = underTest.selectByLanguage("java");
assertThat(result).hasSize(2);
assertThat(result.get(0).getName()).isEqualTo("Sonar Way 1");
assertThat(result.get(1).getName()).isEqualTo("Sonar Way 2");
@@ -187,20 +194,20 @@ public class QualityProfileDaoTest {
public void get_by_id() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- QualityProfileDto dto = dao.selectById(1);
+ QualityProfileDto dto = underTest.selectById(1);
assertThat(dto.getId()).isEqualTo(1);
assertThat(dto.getName()).isEqualTo("Sonar Way");
assertThat(dto.getLanguage()).isEqualTo("java");
assertThat(dto.getParentKee()).isNull();
- assertThat(dao.selectById(555)).isNull();
+ assertThat(underTest.selectById(555)).isNull();
}
@Test
public void get_parent_by_id() {
dbTester.prepareDbUnit(getClass(), "inheritance.xml");
- QualityProfileDto dto = dao.selectParentById(1);
+ QualityProfileDto dto = underTest.selectParentById(1);
assertThat(dto.getId()).isEqualTo(3);
}
@@ -208,7 +215,7 @@ public class QualityProfileDaoTest {
public void find_children() {
dbTester.prepareDbUnit(getClass(), "inheritance.xml");
- List<QualityProfileDto> dtos = dao.selectChildren(dbTester.getSession(), "java_parent");
+ List<QualityProfileDto> dtos = underTest.selectChildren(dbTester.getSession(), "java_parent");
assertThat(dtos).hasSize(2);
@@ -229,21 +236,21 @@ public class QualityProfileDaoTest {
public void select_projects() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- assertThat(dao.selectProjects("Sonar Way", "java")).hasSize(2);
+ assertThat(underTest.selectProjects("Sonar Way", "java")).hasSize(2);
}
@Test
public void count_projects() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- assertThat(dao.countProjects("Sonar Way", "java")).isEqualTo(2);
+ assertThat(underTest.countProjects("Sonar Way", "java")).isEqualTo(2);
}
@Test
public void count_projects_by_profile() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- assertThat(dao.countProjectsByProfileKey()).containsOnly(
+ assertThat(underTest.countProjectsByProfileKey()).containsOnly(
MapEntry.entry("java_sonar_way", 2L),
MapEntry.entry("js_sonar_way", 2L));
}
@@ -252,7 +259,7 @@ public class QualityProfileDaoTest {
public void select_by_project_id_and_language() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- QualityProfileDto dto = dao.selectByProjectAndLanguage(1L, "java");
+ QualityProfileDto dto = underTest.selectByProjectAndLanguage(1L, "java");
assertThat(dto.getId()).isEqualTo(1);
}
@@ -260,22 +267,35 @@ public class QualityProfileDaoTest {
public void select_by_project_key_and_language() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- QualityProfileDto dto = dao.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "java");
+ QualityProfileDto dto = underTest.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "java");
assertThat(dto.getId()).isEqualTo(1);
- assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "unkown")).isNull();
- assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "unknown", "java")).isNull();
+ assertThat(underTest.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "unkown")).isNull();
+ assertThat(underTest.selectByProjectAndLanguage(dbTester.getSession(), "unknown", "java")).isNull();
}
@Test
public void select_by_project_key_and_languages() {
dbTester.prepareDbUnit(getClass(), "projects.xml");
- List<QualityProfileDto> dto = dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("java"));
+ List<QualityProfileDto> dto = underTest.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("java"));
assertThat(dto).extracting("id").containsOnly(1);
- assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("unkown"))).isEmpty();
- assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", of("java", "unkown"))).extracting("id").containsOnly(1);
- assertThat(dao.selectByProjectAndLanguages(dbTester.getSession(), "unknown", singletonList("java"))).isEmpty();
+ assertThat(underTest.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", singletonList("unkown"))).isEmpty();
+ assertThat(underTest.selectByProjectAndLanguages(dbTester.getSession(), "org.codehaus.sonar:sonar", of("java", "unkown"))).extracting("id").containsOnly(1);
+ assertThat(underTest.selectByProjectAndLanguages(dbTester.getSession(), "unknown", singletonList("java"))).isEmpty();
+ }
+
+ @Test
+ public void selectByKeys() {
+ qualityProfileDb.insertQualityProfiles(newQualityProfileDto().setKey("qp-key-1"), newQualityProfileDto().setKee("qp-key-2"), newQualityProfileDto().setKee("qp-key-3"));
+
+ assertThat(underTest.selectOrFailByKey(dbSession, "qp-key-1")).isNotNull();
+ assertThat(underTest.selectByKey(dbSession, "qp-key-1")).isNotNull();
+ assertThat(underTest.selectByKey(dbSession, "qp-key-42")).isNull();
+ assertThat(underTest.selectByKeys(dbSession, newArrayList("qp-key-1", "qp-key-3", "qp-key-42")))
+ .hasSize(2)
+ .extracting(QualityProfileDto::getKey).containsOnlyOnce("qp-key-1", "qp-key-3");
+ assertThat(underTest.selectByKeys(dbSession, emptyList())).isEmpty();
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java
index 0dd2ed189dd..582b8f546e5 100644
--- a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java
+++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java
@@ -35,6 +35,7 @@ public class QualityProfileDbTester {
public void insertQualityProfiles(QualityProfileDto qualityProfile, QualityProfileDto... qualityProfiles) {
dbClient.qualityProfileDao().insert(dbSession, qualityProfile, qualityProfiles);
+ dbSession.commit();
}
public void insertProjectWithQualityProfileAssociations(ComponentDto project, QualityProfileDto... qualityProfiles) {
@@ -42,6 +43,8 @@ public class QualityProfileDbTester {
for (QualityProfileDto qualityProfile : qualityProfiles) {
dbClient.qualityProfileDao().insertProjectProfileAssociation(project.uuid(), qualityProfile.getKey(), dbSession);
}
+
+ dbSession.commit();
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java
new file mode 100644
index 00000000000..836b5beca31
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileTesting.java
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.qualityprofile;
+
+import java.util.Date;
+import org.sonar.core.util.Uuids;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.apache.commons.lang.math.RandomUtils.nextLong;
+
+public class QualityProfileTesting {
+ public static QualityProfileDto newQualityProfileDto() {
+ String uuid = Uuids.createFast();
+ QualityProfileDto dto = QualityProfileDto.createFor(uuid)
+ .setName(uuid)
+ .setLanguage(randomAlphanumeric(20))
+ .setLastUsed(nextLong());
+ dto.setCreatedAt(new Date())
+ .setUpdatedAt(new Date());
+ return dto;
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
index da5b4c20ed2..aef48b2d99b 100644
--- a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
@@ -29,6 +29,6 @@ public class MigrationStepModuleTest {
public void verify_count_of_added_MigrationStep_types() {
ComponentContainer container = new ComponentContainer();
new MigrationStepModule().configure(container);
- assertThat(container.size()).isEqualTo(102);
+ assertThat(container.size()).isEqualTo(104);
}
}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest.java
new file mode 100644
index 00000000000..1dc10d74079
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+public class AddLastUsedColumnToRulesProfilesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddLastUsedColumnToRulesProfilesTest.class, "rules_profiles.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddLastUsedColumnToRulesProfiles underTest = new AddLastUsedColumnToRulesProfiles(db.database());
+
+ @Test
+ public void migration_adds_column_to_empty_table() throws SQLException {
+ underTest.execute();
+
+ verifyAddedColumns();
+ }
+
+ @Test
+ public void migration_adds_column_to_populated_table() throws SQLException {
+ for (int i = 0; i < 9; i++) {
+ db.executeInsert(
+ "rules_profiles",
+ "name", "NAME_" + i,
+ "language", "java",
+ "kee", "" + i,
+ "rules_updated_at", "2016-06-21");
+ }
+ db.commit();
+
+ underTest.execute();
+
+ verifyAddedColumns();
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Fail to execute ");
+ underTest.execute();
+ }
+
+ private void verifyAddedColumns() {
+ db.assertColumnDefinition("rules_profiles", "last_used", Types.BIGINT, null, true);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest.java
new file mode 100644
index 00000000000..6c178ca0c3b
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest.java
@@ -0,0 +1,136 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PopulateLastUsedColumnOfRulesProfilesTest {
+ private static final String QUALITY_PROFILES_TABLE = "rules_profiles";
+ private static final String METRICS_TABLE = "metrics";
+ private static final String MEASURES_TABLE = "project_measures";
+ private static final String SNAPSHOTS_TABLE = "snapshots";
+ private static final String METRIC_ID = "1";
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, PopulateLastUsedColumnOfRulesProfilesTest.class, "rules_profiles.sql");
+
+ PopulateLastUsedColumnOfRulesProfiles underTest = new PopulateLastUsedColumnOfRulesProfiles(db.database());
+
+ @Test
+ public void migration_has_no_effect_on_empty_tables() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable(QUALITY_PROFILES_TABLE)).isEqualTo(0);
+ assertThat(db.countRowsOfTable(METRICS_TABLE)).isEqualTo(0);
+ assertThat(db.countRowsOfTable(MEASURES_TABLE)).isEqualTo(0);
+ assertThat(db.countRowsOfTable(SNAPSHOTS_TABLE)).isEqualTo(0);
+ }
+
+ @Test
+ public void migration_update_quality_profiles_last_used() throws SQLException {
+ insertQualityProfilesMetric();
+ insertQualityProfile(1, "first-quality-profile");
+ insertQualityProfile(2, "second-quality-profile");
+ insertQualityProfile(3, "third-quality-profile");
+ insertQualityProfile(4, "fourth-quality-profile");
+ insertMeasure(1, "first-quality-profile", "second-quality-profile");
+ insertMeasure(2, "second-quality-profile", "third-quality-profile");
+
+ underTest.execute();
+
+ assertLastUsedForQP("first-quality-profile", 1);
+ assertLastUsedForQP("second-quality-profile", 2);
+ assertLastUsedForQP("third-quality-profile", 2);
+ assertNoLastUsedForQP("fourth-quality-profile");
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ insertQualityProfilesMetric();
+ insertQualityProfile(1, "first-quality-profile");
+ insertMeasure(1, "first-quality-profile");
+
+ underTest.execute();
+ assertLastUsedForQP("first-quality-profile", 1);
+
+ underTest.execute();
+ assertLastUsedForQP("first-quality-profile", 1);
+ }
+
+ private void assertLastUsedForQP(String qualityProfileKey, long expectedLastUsed) {
+ assertThat(selectLastUser(qualityProfileKey)).isEqualTo(expectedLastUsed);
+ }
+
+ private void assertNoLastUsedForQP(String qualityProfileKey) {
+ assertThat(selectLastUser(qualityProfileKey)).isNull();
+ }
+
+ @CheckForNull
+ private Long selectLastUser(String qualityProfileKey) {
+ return (Long) db.selectFirst(String.format("select last_used as \"lastUsed\" from rules_profiles where kee ='%s'", qualityProfileKey)).get("lastUsed");
+ }
+
+ private void insertQualityProfile(long id, String key) {
+ db.executeInsert(QUALITY_PROFILES_TABLE,
+ "id", valueOf(id),
+ "name", key,
+ "kee", key);
+ }
+
+ private void insertMeasure(long id, String... keys) {
+ db.executeInsert(
+ SNAPSHOTS_TABLE,
+ "id", valueOf(id),
+ "uuid", valueOf(id),
+ "component_uuid", valueOf(id),
+ "root_component_uuid", valueOf(id),
+ "islast", "TRUE",
+ "created_at", valueOf(id));
+
+ db.executeInsert(
+ MEASURES_TABLE,
+ "id", valueOf(id),
+ "snapshot_id", valueOf(id),
+ "metric_id", METRIC_ID,
+ "component_uuid", valueOf(id),
+ "text_value", toJson(keys));
+ }
+
+ private void insertQualityProfilesMetric() {
+ db.executeInsert(METRICS_TABLE,
+ "id", METRIC_ID,
+ "name", "quality_profiles");
+ }
+
+ private static String toJson(String... keys) {
+ return Arrays.stream(keys).map(key -> "\"key\" : \"" + key + "\"").collect(Collectors.joining(", ", "{", "}"));
+ }
+}
diff --git a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/delete-result.xml b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/delete-result.xml
index fac2543235e..a503c6b3950 100644
--- a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/delete-result.xml
+++ b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/delete-result.xml
@@ -1,6 +1,6 @@
<dataset>
<rules_profiles id="2" name="Sonar Way" language="js" parent_kee="[null]" kee="js_sonar_way" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="123456789"/>
</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/insert-result.xml b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/insert-result.xml
index 8a9e9ce1ada..9090f8728dc 100644
--- a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/insert-result.xml
+++ b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/insert-result.xml
@@ -1,13 +1,13 @@
<dataset>
<rules_profiles id="1" name="Sonar Way" language="java" parent_kee="[null]" kee="java_sonar_way" is_default="[true]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="[null]"/>
<rules_profiles id="2" name="Sonar Way" language="js" parent_kee="[null]" kee="js_sonar_way" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="123456789"/>
<rules_profiles id="3" name="ABCDE" language="xoo" parent_kee="[null]" kee="abcde" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="[null]"/>
</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/shared.xml b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/shared.xml
index bf8e0113b24..92509d96554 100644
--- a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/shared.xml
+++ b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/shared.xml
@@ -1,9 +1,9 @@
<dataset>
<rules_profiles id="1" name="Sonar Way" language="java" parent_kee="[null]" kee="java_sonar_way" is_default="[true]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="[null]"/>
<rules_profiles id="2" name="Sonar Way" language="js" parent_kee="[null]" kee="js_sonar_way" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="123456789"/>
</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/update-result.xml b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/update-result.xml
index 3d54167f58d..952372c6a12 100644
--- a/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/update-result.xml
+++ b/sonar-db/src/test/resources/org/sonar/db/qualityprofile/QualityProfileDaoTest/update-result.xml
@@ -1,10 +1,10 @@
<dataset>
<rules_profiles id="1" name="New Name" language="js" parent_kee="fghij" kee="java_sonar_way" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="[null]"/>
<rules_profiles id="2" name="Sonar Way" language="js" parent_kee="[null]" kee="js_sonar_way" is_default="[false]"
- rules_updated_at="[null]" created_at="[null]" updated_at="[null]"/>
+ rules_updated_at="[null]" created_at="[null]" updated_at="[null]" last_used="123456789"/>
</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest/rules_profiles.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest/rules_profiles.sql
new file mode 100644
index 00000000000..cddbb2038a8
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/AddLastUsedColumnToRulesProfilesTest/rules_profiles.sql
@@ -0,0 +1,13 @@
+CREATE TABLE "RULES_PROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "LANGUAGE" VARCHAR(20),
+ "KEE" VARCHAR(255) NOT NULL,
+ "PARENT_KEE" VARCHAR(255),
+ "RULES_UPDATED_AT" VARCHAR(100),
+ "IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE UNIQUE INDEX "UNIQ_QPROF_KEY" ON "RULES_PROFILES" ("KEE");
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest/rules_profiles.sql b/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest/rules_profiles.sql
new file mode 100644
index 00000000000..8f6183f65fe
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v60/PopulateLastUsedColumnOfRulesProfilesTest/rules_profiles.sql
@@ -0,0 +1,84 @@
+CREATE TABLE "RULES_PROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "LANGUAGE" VARCHAR(20),
+ "KEE" VARCHAR(255) NOT NULL,
+ "PARENT_KEE" VARCHAR(255),
+ "RULES_UPDATED_AT" VARCHAR(100),
+ "IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "LAST_USED" BIGINT
+);
+
+CREATE TABLE "PROJECT_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "VALUE" DOUBLE,
+ "METRIC_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+ "SNAPSHOT_ID" INTEGER,
+ "TEXT_VALUE" VARCHAR(4000),
+ "ALERT_STATUS" VARCHAR(5),
+ "ALERT_TEXT" VARCHAR(4000),
+ "DESCRIPTION" VARCHAR(4000),
+ "PERSON_ID" INTEGER,
+ "VARIATION_VALUE_1" DOUBLE,
+ "VARIATION_VALUE_2" DOUBLE,
+ "VARIATION_VALUE_3" DOUBLE,
+ "VARIATION_VALUE_4" DOUBLE,
+ "VARIATION_VALUE_5" DOUBLE,
+ "MEASURE_DATA" BINARY(167772150)
+);
+
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(50) NOT NULL,
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "COMPONENT_UUID" VARCHAR(50) NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_COMPONENT_UUID" VARCHAR(50) NOT NULL,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
+
+CREATE TABLE "METRICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(64) NOT NULL,
+ "DESCRIPTION" VARCHAR(255),
+ "DIRECTION" INTEGER NOT NULL DEFAULT 0,
+ "DOMAIN" VARCHAR(64),
+ "SHORT_NAME" VARCHAR(64),
+ "QUALITATIVE" BOOLEAN NOT NULL DEFAULT FALSE,
+ "VAL_TYPE" VARCHAR(8),
+ "USER_MANAGED" BOOLEAN DEFAULT FALSE,
+ "ENABLED" BOOLEAN DEFAULT TRUE,
+ "WORST_VALUE" DOUBLE,
+ "BEST_VALUE" DOUBLE,
+ "OPTIMIZED_BEST_VALUE" BOOLEAN,
+ "HIDDEN" BOOLEAN,
+ "DELETE_HISTORICAL_DATA" BOOLEAN,
+ "DECIMAL_SCALE" INTEGER
+);