]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7480 optimize SQL request in QP Search WS
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 25 Mar 2016 15:08:24 +0000 (16:08 +0100)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 31 Mar 2016 13:22:07 +0000 (15:22 +0200)
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileFactory.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchAction.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/SearchDataLoader.java
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileMapper.java
sonar-db/src/main/resources/org/sonar/db/qualityprofile/QualityProfileMapper.xml
sonar-db/src/test/java/org/sonar/db/qualityprofile/QualityProfileDaoTest.java

index f5e458a27771418a182dbfd3d2e806cbe141e634..29903ee8dccd9d74552bf37be842a17b5718f667 100644 (file)
@@ -21,8 +21,10 @@ package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.Lists;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.Set;
 import javax.annotation.CheckForNull;
 import org.apache.commons.lang.RandomStringUtils;
 import org.apache.commons.lang.StringUtils;
@@ -138,6 +140,11 @@ public class QProfileFactory {
     return db.qualityProfileDao().selectDefaultProfile(session, language);
   }
 
+  @CheckForNull
+  public List<QualityProfileDto> getDefaults(DbSession session, Collection<String> languageKeys) {
+    return db.qualityProfileDao().selectDefaultProfiles(session, languageKeys);
+  }
+
   public void setDefault(String profileKey) {
     DbSession dbSession = db.openSession(false);
     try {
@@ -179,6 +186,10 @@ public class QProfileFactory {
     return db.qualityProfileDao().selectByProjectAndLanguage(session, projectKey, language);
   }
 
+  public List<QualityProfileDto> getByProjectAndLanguages(DbSession session, String projectKey, Set<String> languageKeys) {
+    return db.qualityProfileDao().selectByProjectAndLanguages(session, projectKey, languageKeys);
+  }
+
   QualityProfileDto getByNameAndLanguage(String name, String language) {
     DbSession dbSession = db.openSession(false);
     try {
@@ -193,6 +204,10 @@ public class QProfileFactory {
     return db.qualityProfileDao().selectByNameAndLanguage(name, language, session);
   }
 
+  public List<QualityProfileDto> getByNameAndLanguages(DbSession session, String name, Collection<String> languages) {
+    return db.qualityProfileDao().selectByNameAndLanguages(name, languages, session);
+  }
+
   private void checkNotDefault(QualityProfileDto p) {
     if (p.isDefault()) {
       throw new BadRequestException("The profile marked as default can not be deleted: " + p.getKey());
index 1e23d75a0f99914bef0153faac0c39d123edb4bd..64230d8b40b2b2ffa761320cd8a8258efdd8e178 100644 (file)
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
@@ -100,8 +101,9 @@ public class QProfileLoader {
   }
 
   public Map<String, Long> countAllActiveRules() {
-    Map<String, Long> counts = new HashMap<>();
-    for (Map.Entry<String, Long> entry : activeRuleIndex.countAllByQualityProfileKey().entrySet()) {
+    Set<Map.Entry<String, Long>> entries = activeRuleIndex.countAllByQualityProfileKey().entrySet();
+    Map<String, Long> counts = new HashMap<>(entries.size());
+    for (Map.Entry<String, Long> entry : entries) {
       counts.put(entry.getKey(), entry.getValue());
     }
     return counts;
index 033280ff0e64e67a8479bfe9949bdb2f1cacf370..d05d1fa7a1b4f4d6cc5da9d03314d4aada78f8bc 100644 (file)
@@ -108,7 +108,7 @@ public class SearchAction implements QProfileWsAction {
 
   private SearchWsResponse buildResponse(SearchData data) {
     List<QProfile> profiles = data.getProfiles();
-    Map<String, QProfile> profilesByKey = uniqueIndex(profiles, new QProfileToKey());
+    Map<String, QProfile> profilesByKey = uniqueIndex(profiles, QProfileToKey.INSTANCE);
 
     QualityProfiles.SearchWsResponse.Builder response = QualityProfiles.SearchWsResponse.newBuilder();
     QualityProfile.Builder profileBuilder = QualityProfile.newBuilder();
@@ -169,7 +169,9 @@ public class SearchAction implements QProfileWsAction {
     return value != null;
   }
 
-  private static class QProfileToKey implements Function<QProfile, String> {
+  private enum QProfileToKey implements Function<QProfile, String> {
+    INSTANCE;
+
     @Override
     public String apply(@Nonnull QProfile input) {
       return input.key();
index 0ee947618341c11964aa605b1e3316ee3df4a995..71348447368b31cc8ded92c9a5b310dcdaf3b827 100644 (file)
  */
 package org.sonar.server.qualityprofile.ws;
 
+import com.google.common.base.Function;
 import com.google.common.base.Predicate;
-import java.util.ArrayList;
+import com.google.common.collect.Sets;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.builder.CompareToBuilder;
@@ -39,7 +45,6 @@ import org.sonar.server.qualityprofile.QProfileLoader;
 import org.sonar.server.qualityprofile.QProfileLookup;
 import org.sonarqube.ws.client.qualityprofile.SearchWsRequest;
 
-import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.collect.FluentIterable.from;
 import static java.lang.String.format;
 import static org.sonar.server.ws.WsUtils.checkRequest;
@@ -51,6 +56,7 @@ public class SearchDataLoader {
   private final QProfileFactory profileFactory;
   private final DbClient dbClient;
   private final ComponentFinder componentFinder;
+  private final IsLanguageKnown isLanguageKnown;
 
   public SearchDataLoader(Languages languages, QProfileLookup profileLookup, QProfileLoader profileLoader, QProfileFactory profileFactory, DbClient dbClient,
     ComponentFinder componentFinder) {
@@ -60,6 +66,7 @@ public class SearchDataLoader {
     this.profileFactory = profileFactory;
     this.dbClient = dbClient;
     this.componentFinder = componentFinder;
+    this.isLanguageKnown = new IsLanguageKnown();
   }
 
   SearchData load(SearchWsRequest request) {
@@ -72,7 +79,7 @@ public class SearchDataLoader {
   }
 
   private List<QProfile> findProfiles(SearchWsRequest request) {
-    List<QProfile> profiles;
+    Collection<QProfile> profiles;
     if (askDefaultProfiles(request)) {
       profiles = findDefaultProfiles(request);
     } else if (hasComponentKey(request)) {
@@ -81,105 +88,125 @@ public class SearchDataLoader {
       profiles = findAllProfiles(request);
     }
 
-    return orderProfiles(profiles);
+    return from(profiles).toSortedList(QProfileComparator.INSTANCE);
   }
 
-  private static List<QProfile> orderProfiles(List<QProfile> profiles) {
-    return from(profiles)
-      .toSortedList(QProfileComparator.INSTANCE);
-  }
+  private Collection<QProfile> findDefaultProfiles(SearchWsRequest request) {
+    String profileName = request.getProfileName();
 
-  private List<QProfile> findAllProfiles(SearchWsRequest request) {
-    String language = request.getLanguage();
+    Set<String> languageKeys = getLanguageKeys();
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      Map<String, QProfile> qualityProfiles = new HashMap<>(languageKeys.size());
 
-    List<QProfile> profiles = language != null ? profileLookup.profiles(language)
-      : profileLookup.allProfiles();
+      Set<String> missingLanguageKeys = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName);
+      Set<String> noDefaultProfileLanguageKeys = lookupDefaults(dbSession, qualityProfiles, missingLanguageKeys);
 
-    return from(profiles)
-      .filter(new IsLanguageKnown())
-      .toList();
+      if (!noDefaultProfileLanguageKeys.isEmpty()) {
+        throw new IllegalStateException(format("No quality profile can been found on language(s) '%s'", noDefaultProfileLanguageKeys));
+      }
+
+      return qualityProfiles.values();
+    }
   }
 
-  private List<QProfile> findProjectProfiles(SearchWsRequest request) {
-    String moduleKey = request.getProjectKey();
+  private Collection<QProfile> findProjectProfiles(SearchWsRequest request) {
+    String componentKey = request.getProjectKey();
     String profileName = request.getProfileName();
 
-    List<QProfile> profiles = new ArrayList<>();
+    Set<String> languageKeys = getLanguageKeys();
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      Map<String, QProfile> qualityProfiles = new HashMap<>(languageKeys.size());
+
+      // look up profiles by profileName (if any) for each language
+      Set<String> unresolvedLanguages = lookupByProfileName(dbSession, qualityProfiles, languageKeys, profileName);
+      // look up profile by componentKey for each language for which we don't have one yet
+      Set<String> stillUnresolvedLanguages = lookupByModuleKey(dbSession, qualityProfiles, unresolvedLanguages, componentKey);
+      // look up profile by default for each language for which we don't have one yet
+      Set<String> noDefaultProfileLanguages = lookupDefaults(dbSession, qualityProfiles, stillUnresolvedLanguages);
 
-    DbSession dbSession = dbClient.openSession(false);
-    try {
-      for (Language language : languages.all()) {
-        String languageKey = language.getKey();
-        ComponentDto project = getProject(moduleKey, dbSession);
-        profiles.add(getProfile(dbSession, languageKey, project.key(), profileName));
+      if (!noDefaultProfileLanguages.isEmpty()) {
+        throw new IllegalStateException(format("No quality profile can been found on language(s) '%s' for project '%s'", noDefaultProfileLanguages, componentKey));
       }
-    } finally {
-      dbClient.closeSession(dbSession);
+
+      return qualityProfiles.values();
     }
+  }
+
+  private List<QProfile> findAllProfiles(SearchWsRequest request) {
+    String language = request.getLanguage();
 
-    return profiles;
+    if (language == null) {
+      return from(profileLookup.allProfiles()).filter(isLanguageKnown).toList();
+    }
+    return profileLookup.profiles(language);
+  }
+
+  private Set<String> lookupByProfileName(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys, @Nullable String profileName) {
+    if (languageKeys.isEmpty() || profileName == null) {
+      return languageKeys;
+    }
+
+    addAllFromDto(qualityProfiles, profileFactory.getByNameAndLanguages(dbSession, profileName, languageKeys));
+    return difference(languageKeys, qualityProfiles.keySet());
+  }
+
+  private Set<String> lookupByModuleKey(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys, @Nullable String moduleKey) {
+    if (languageKeys.isEmpty() || moduleKey == null) {
+      return languageKeys;
+    }
+
+    ComponentDto project = getProject(moduleKey, dbSession);
+    addAllFromDto(qualityProfiles, profileFactory.getByProjectAndLanguages(dbSession, project.getKey(), languageKeys));
+    return difference(languageKeys, qualityProfiles.keySet());
   }
 
   private ComponentDto getProject(String moduleKey, DbSession session) {
     ComponentDto module = componentFinder.getByKey(session, moduleKey);
-    if (!module.isRootProject()) {
-      return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid());
-    } else {
+    if (module.isRootProject()) {
       return module;
     }
+    return dbClient.componentDao().selectOrFailByUuid(session, module.projectUuid());
   }
 
-  private List<QProfile> findDefaultProfiles(SearchWsRequest request) {
-    String profileName = request.getProfileName();
-    List<QProfile> profiles = new ArrayList<>();
+  private Set<String> lookupDefaults(DbSession dbSession, Map<String, QProfile> qualityProfiles, Set<String> languageKeys) {
+    if (languageKeys.isEmpty()) {
+      return languageKeys;
+    }
 
-    DbSession dbSession = dbClient.openSession(false);
-    try {
-      for (Language language : languages.all()) {
-        profiles.add(getDefaultProfile(dbSession, language.getKey(), profileName));
-      }
-    } finally {
-      dbClient.closeSession(dbSession);
+    addAll(qualityProfiles, findDefaultProfiles(dbSession, languageKeys));
+    return difference(languageKeys, qualityProfiles.keySet());
+  }
+
+  private static <T> Set<T> difference(Set<T> languageKeys, Set<T> set2) {
+    return Sets.newHashSet(Sets.difference(languageKeys, set2));
+  }
+
+  private void addAllFromDto(Map<String, QProfile> qualityProfiles, Collection<QualityProfileDto> list) {
+    for (QualityProfileDto qualityProfileDto : list) {
+      qualityProfiles.put(qualityProfileDto.getLanguage(), QualityProfileDtoToQProfile.INSTANCE.apply(qualityProfileDto));
     }
+  }
 
-    return profiles;
-  }
-
-  private static QProfile profileDtoToQProfile(QualityProfileDto dto) {
-    return new QProfile()
-      .setKey(dto.getKey())
-      .setName(dto.getName())
-      .setLanguage(dto.getLanguage())
-      .setDefault(dto.isDefault())
-      .setRulesUpdatedAt(dto.getRulesUpdatedAt());
-  }
-
-  /**
-   * First try to find a quality profile matching the given name (if provided) and current language
-   * If no profile found, try to find the quality profile set on the project (if provided)
-   * If still no profile found, try to find the default profile of the language
-   * <p/>
-   * Never return null because a default profile should always be set on each language
-   */
-  private QProfile getProfile(DbSession dbSession, String languageKey, @Nullable String projectKey, @Nullable String profileName) {
-    QualityProfileDto profileDto = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null;
-    if (profileDto == null && projectKey != null) {
-      profileDto = profileFactory.getByProjectAndLanguage(dbSession, projectKey, languageKey);
+  private void addAll(Map<String, QProfile> qualityProfiles, Collection<QProfile> list) {
+    for (QProfile qProfile : list) {
+      qualityProfiles.put(qProfile.language(), qProfile);
     }
-    profileDto = profileDto != null ? profileDto : profileFactory.getDefault(dbSession, languageKey);
-    checkState(profileDto != null, format("No quality profile can been found on language '%s' for project '%s'", languageKey, projectKey));
+  }
 
-    return profileDtoToQProfile(profileDto);
+  private Set<String> getLanguageKeys() {
+    return from(Arrays.asList(languages.all())).transform(LanguageToKey.INSTANCE).toSet();
   }
 
-  private QProfile getDefaultProfile(DbSession dbSession, String languageKey, @Nullable String profileName) {
-    QualityProfileDto profile = profileName != null ? profileFactory.getByNameAndLanguage(dbSession, profileName, languageKey) : null;
-    if (profile == null) {
-      profile = profileFactory.getDefault(dbSession, languageKey);
+  private List<QProfile> findDefaultProfiles(Set<String> languageKeys) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      return findDefaultProfiles(dbSession, languageKeys);
     }
-    checkState(profile != null, format("No quality profile can been found on language '%s'", languageKey));
+  }
 
-    return profileDtoToQProfile(profile);
+  private List<QProfile> findDefaultProfiles(final DbSession dbSession, Set<String> languageKeys) {
+    return from(profileFactory.getDefaults(dbSession, languageKeys))
+      .transform(QualityProfileDtoToQProfile.INSTANCE)
+      .toList();
   }
 
   private static void validateRequest(SearchWsRequest request) {
@@ -193,6 +220,10 @@ public class SearchDataLoader {
     checkRequest(!isDefault || !hasComponentKey, "The default parameter cannot be provided at the same time than the component key");
   }
 
+  private static boolean askDefaultProfiles(SearchWsRequest request) {
+    return request.getDefaults();
+  }
+
   private static boolean hasProfileName(SearchWsRequest request) {
     return request.getProfileName() != null;
   }
@@ -201,10 +232,6 @@ public class SearchDataLoader {
     return request.getProjectKey() != null;
   }
 
-  private static Boolean askDefaultProfiles(SearchWsRequest request) {
-    return request.getDefaults();
-  }
-
   private static boolean hasLanguage(SearchWsRequest request) {
     return request.getLanguage() != null;
   }
@@ -220,6 +247,32 @@ public class SearchDataLoader {
     }
   }
 
+  private enum LanguageToKey implements Function<Language, String> {
+    INSTANCE;
+
+    @Override
+    @Nonnull
+    public String apply(@Nonnull Language input) {
+      return input.getKey();
+    }
+  }
+
+  private enum QualityProfileDtoToQProfile implements Function<QualityProfileDto, QProfile> {
+    INSTANCE;
+
+    @Override
+    @Nonnull
+    public QProfile apply(@Nonnull QualityProfileDto input) {
+      return new QProfile()
+        .setKey(input.getKey())
+        .setName(input.getName())
+        .setLanguage(input.getLanguage())
+        .setDefault(input.isDefault())
+        .setRulesUpdatedAt(input.getRulesUpdatedAt());
+    }
+
+  }
+
   private class IsLanguageKnown implements Predicate<QProfile> {
     @Override
     public boolean apply(@Nonnull QProfile profile) {
index ca2ec555f1cfd90cca31bdabcc6e4bd21b3395df..fbe2d1913c294ccce310591014943fe19a7875e5 100644 (file)
  */
 package org.sonar.db.qualityprofile;
 
+import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 import org.sonar.db.MyBatis;
 import org.sonar.db.RowNotFoundException;
@@ -155,8 +159,7 @@ public class QualityProfileDao implements Dao {
   }
 
   /**
-   * @deprecated Replaced by
-   *    {@link #selectAll(DbSession)}
+   * @deprecated Replaced by {@link #selectAll(DbSession)}
    */
   @Deprecated
   public List<QualityProfileDto> selectAll() {
@@ -169,7 +172,18 @@ public class QualityProfileDao implements Dao {
   }
 
   @CheckForNull
-  public QualityProfileDto selectDefaultProfile(DbSession session, String language) {
+  public List<QualityProfileDto> selectDefaultProfiles(final DbSession session, Collection<String> languageKeys) {
+    return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+      @Override
+      @Nonnull
+      public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+        return mapper(session).selectDefaultProfiles(input);
+      }
+    });
+  }
+
+  @CheckForNull
+  public QualityProfileDto selectDefaultProfile(final DbSession session, String language) {
     return mapper(session).selectDefaultProfile(language);
   }
 
@@ -198,6 +212,16 @@ public class QualityProfileDao implements Dao {
     return mapper(session).selectByProjectAndLanguage(projectKey, language);
   }
 
+  public List<QualityProfileDto> selectByProjectAndLanguages(final DbSession session, final String projectKey, Collection<String> languageKeys) {
+    return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+      @Override
+      @Nonnull
+      public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+        return mapper(session).selectByProjectAndLanguages(projectKey, input);
+      }
+    });
+  }
+
   public List<QualityProfileDto> selectByLanguage(String language) {
     DbSession session = mybatis.openSession(false);
     try {
@@ -273,6 +297,16 @@ public class QualityProfileDao implements Dao {
     return mapper(session).selectByNameAndLanguage(name, language);
   }
 
+  public List<QualityProfileDto> selectByNameAndLanguages(final String name, Collection<String> languageKeys, final DbSession session) {
+    return DatabaseUtils.executeLargeInputs(languageKeys, new Function<List<String>, List<QualityProfileDto>>() {
+      @Override
+      @Nonnull
+      public List<QualityProfileDto> apply(@Nonnull List<String> input) {
+        return mapper(session).selectByNameAndLanguages(name, input);
+      }
+    });
+  }
+
   public List<ComponentDto> selectProjects(String profileName, String language) {
     DbSession session = mybatis.openSession(false);
     try {
@@ -346,5 +380,4 @@ public class QualityProfileDao implements Dao {
   private QualityProfileMapper mapper(DbSession session) {
     return session.getMapper(QualityProfileMapper.class);
   }
-
 }
index d2f9e73a9597588c82c4c1f8e368b38b4d5c20ae..b8a6ef79c0918429bfe0673960c4ea9cb7f78523 100644 (file)
@@ -37,9 +37,13 @@ public interface QualityProfileMapper {
   @CheckForNull
   QualityProfileDto selectDefaultProfile(@Param("language") String language);
 
+  List<QualityProfileDto> selectDefaultProfiles(@Param("languages") List<String> languages);
+
   @CheckForNull
   QualityProfileDto selectByNameAndLanguage(@Param("name") String name, @Param("language") String language);
 
+  List<QualityProfileDto> selectByNameAndLanguages(@Param("name") String name, @Param("languages") List<String> languages);
+
   @CheckForNull
   QualityProfileDto selectById(@Param("id") Integer id);
 
@@ -70,6 +74,8 @@ public interface QualityProfileMapper {
 
   QualityProfileDto selectByProjectAndLanguage(@Param("projectKey") String projectKey, @Param("language") String language);
 
+  List<QualityProfileDto> selectByProjectAndLanguages(@Param("projectKey") String projectKey, @Param("languages") List<String> input);
+
   void insertProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileKey") String profileKey);
 
   void updateProjectProfileAssociation(@Param("projectUuid") String projectUuid, @Param("profileKey") String profileKey);
index 873a983c4cbae2c9547a56e7c9b1fd235696f9fa..edc2563f8e201dac88b009c79d0965f67adc803e 100644 (file)
     WHERE p.name=#{name} AND p.language=#{language}
   </select>
 
+  <select id="selectByNameAndLanguages" parameterType="map" resultType="QualityProfile">
+    SELECT
+    <include refid="profilesColumns"/>
+    FROM rules_profiles p
+    <where>
+      p.name=#{name}
+      AND p.language in
+      <foreach collection="languages" open="(" close=")" item="language" separator=",">
+        #{language}
+      </foreach>
+    </where>
+  </select>
+
   <select id="selectByKey" parameterType="string" resultType="QualityProfile">
     SELECT
     <include refid="profilesColumns"/>
     SELECT
     <include refid="profilesColumns"/>
     FROM rules_profiles p
-    WHERE p.is_default=${_true}
-    AND p.language=#{language}
+    <where>
+      p.is_default=${_true}
+      AND p.language=#{language}
+    </where>
+  </select>
+
+  <select id="selectDefaultProfiles" parameterType="map" resultType="QualityProfile">
+    SELECT
+    <include refid="profilesColumns"/>
+    FROM rules_profiles p
+    <where>
+      p.is_default=${_true}
+      AND p.language in
+      <foreach collection="languages" open="(" close=")" item="language" separator=",">
+        #{language}
+      </foreach>
+    </where>
   </select>
 
   <select id="selectProjects" resultType="Component">
     <include refid="profilesColumns"/>
     FROM rules_profiles p
     JOIN project_qprofiles pp ON pp.profile_key=p.kee
-    JOIN projects project ON pp.project_uuid=project.uuid
-    AND project.kee=#{projectKey}
+    JOIN projects project ON pp.project_uuid=project.uuid AND project.kee=#{projectKey}
     WHERE p.language=#{language}
   </select>
 
+  <select id="selectByProjectAndLanguages" parameterType="map" resultType="QualityProfile">
+    SELECT
+    <include refid="profilesColumns"/>
+    FROM rules_profiles p
+    JOIN project_qprofiles pp ON pp.profile_key=p.kee
+    JOIN projects project ON pp.project_uuid=project.uuid AND project.kee=#{projectKey}
+    <where>
+      p.language in
+      <foreach collection="languages" open="(" close=")" item="language" separator=",">
+        #{language}
+      </foreach>
+    </where>
+  </select>
+
   <insert id="insertProjectProfileAssociation" keyColumn="id" useGeneratedKeys="true">
     INSERT INTO project_qprofiles (project_uuid, profile_key) VALUES (#{projectUuid}, #{profileKey})
   </insert>
index f53a1edd2b1cfd99e0a2805872225ef46aaa9593..d47a6e19467850db6a41ee43320d153a8547ce92 100644 (file)
@@ -28,6 +28,8 @@ import org.sonar.api.utils.System2;
 import org.sonar.core.util.UtcDateUtils;
 import org.sonar.db.DbTester;
 
+import static com.google.common.collect.ImmutableList.of;
+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;
@@ -129,6 +131,18 @@ public class QualityProfileDaoTest {
     assertThat(dao.selectDefaultProfile("js")).isNull();
   }
 
+  @Test
+  public void get_default_profiles() {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+
+    List<QualityProfileDto> java = dao.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");
+  }
+
   @Test
   public void get_by_name_and_language() {
     dbTester.prepareDbUnit(getClass(), "shared.xml");
@@ -143,6 +157,22 @@ public class QualityProfileDaoTest {
     assertThat(dao.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());
+    assertThat(dtos).hasSize(1);
+    QualityProfileDto dto = dtos.iterator().next();
+    assertThat(dto.getId()).isEqualTo(1);
+    assertThat(dto.getName()).isEqualTo("Sonar Way");
+    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);
+  }
+
   @Test
   public void find_by_language() {
     dbTester.prepareDbUnit(getClass(), "select_by_language.xml");
@@ -236,4 +266,16 @@ public class QualityProfileDaoTest {
     assertThat(dao.selectByProjectAndLanguage(dbTester.getSession(), "org.codehaus.sonar:sonar", "unkown")).isNull();
     assertThat(dao.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"));
+    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();
+  }
 }