]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8089 Merge ComponentDao#selectChildren and selectDescendants
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Nov 2016 10:28:44 +0000 (11:28 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 8 Nov 2016 10:12:51 +0000 (11:12 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStepTest.java
sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java
sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java
sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java
sonar-db/src/main/java/org/sonar/db/purge/PurgeDao.java
sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml
sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java

index 3ddb250ea0b2207542da339c1850f8dd89b4762f..93db78f2ab7e8e4032247896ea70b046515cbafb 100644 (file)
  */
 package org.sonar.server.component.ws;
 
-import static com.google.common.base.MoreObjects.firstNonNull;
-import static com.google.common.collect.FluentIterable.from;
-import static com.google.common.collect.Sets.newHashSet;
-import static java.lang.String.format;
-import static java.util.Collections.emptyMap;
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
-import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
-import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
-import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
-import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
-import static org.sonar.server.ws.WsUtils.checkRequest;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_ID;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_KEY;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
-
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Sets;
@@ -65,6 +45,26 @@ import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.WsComponents.TreeWsResponse;
 import org.sonarqube.ws.client.component.TreeWsRequest;
 
+import static com.google.common.base.MoreObjects.firstNonNull;
+import static com.google.common.collect.FluentIterable.from;
+import static com.google.common.collect.Sets.newHashSet;
+import static java.lang.String.format;
+import static java.util.Collections.emptyMap;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
+import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
+import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_KEY;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
+
 public class TreeAction implements ComponentsWsAction {
   private static final int MAX_SIZE = 500;
   private static final int QUERY_MINIMUM_LENGTH = 3;
@@ -158,13 +158,13 @@ public class TreeAction implements ComponentsWsAction {
       int total;
       switch (treeWsRequest.getStrategy()) {
         case CHILDREN_STRATEGY:
-          components = dbClient.componentDao().selectChildren(dbSession, query);
-          total = dbClient.componentDao().countChildren(dbSession, query);
+          components = dbClient.componentDao().selectDescendants(dbSession, query);
+          total = components.size();
           break;
         case LEAVES_STRATEGY:
         case ALL_STRATEGY:
           components = dbClient.componentDao().selectDescendants(dbSession, query);
-          total = dbClient.componentDao().countDescendants(dbSession, query);
+          total = components.size();
           break;
         default:
           throw new IllegalStateException("Unknown component tree strategy");
index 5ffd9591cd51b0e75dc9d5e1a035bbf2bbc7962b..38e1f2ab9142d7d312a847a2f3497c5ac07b3ca2 100644 (file)
@@ -41,8 +41,11 @@ import org.sonar.core.hash.SourceLinesHashesComputer;
 import org.sonar.core.util.CloseableIterator;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTreeQuery;
+import org.sonar.db.component.ComponentTreeQuery.Strategy;
 import org.sonar.db.source.FileSourceDto;
+import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
@@ -50,21 +53,18 @@ import org.sonar.server.computation.task.projectanalysis.component.DepthTraversa
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
 import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
 import org.sonar.server.computation.task.projectanalysis.filemove.FileSimilarity.File;
-import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepository;
 import org.sonar.server.computation.task.step.ComputationStep;
 
 import static com.google.common.base.Splitter.on;
 import static com.google.common.collect.FluentIterable.from;
 import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
 import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
 
 public class FileMoveDetectionStep implements ComputationStep {
   protected static final int MIN_REQUIRED_SCORE = 85;
   private static final Logger LOG = Loggers.get(FileMoveDetectionStep.class);
   private static final List<String> FILE_QUALIFIERS = asList(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
-  private static final List<String> SORT_FIELDS = singletonList("name");
   private static final Splitter LINES_HASHES_SPLITTER = on('\n');
 
   private final AnalysisMetadataHolder analysisMetadataHolder;
@@ -152,15 +152,14 @@ public class FileMoveDetectionStep implements ComputationStep {
     try (DbSession dbSession = dbClient.openSession(false)) {
       // FIXME no need to use such a complex query, joining on SNAPSHOTS and retrieving all column of table PROJECTS, replace with dedicated
       // mapper method
-      return from(dbClient.componentDao().selectDescendants(
+      List<ComponentDto> componentDtos = dbClient.componentDao().selectDescendants(
         dbSession,
         ComponentTreeQuery.builder()
           .setBaseUuid(rootHolder.getRoot().getUuid())
           .setQualifiers(FILE_QUALIFIERS)
-          .setSortFields(SORT_FIELDS)
-          .setPageSize(Integer.MAX_VALUE)
-          .setPage(1)
-          .build()))
+          .setStrategy(Strategy.LEAVES)
+          .build());
+      return from(componentDtos)
             .transform(componentDto -> new DbComponent(componentDto.getId(), componentDto.key(), componentDto.uuid(), componentDto.path()))
             .uniqueIndex(DbComponent::getKey);
     }
index 8a2d059c215e3c288c3de22775e53edad2f409a6..04f5c44c397ced3b611fb044bfef89bb74000a13 100644 (file)
@@ -172,13 +172,13 @@ public class ComponentTreeDataLoader {
     switch (strategy) {
       case CHILDREN_STRATEGY:
         return new ComponentDtosAndTotal(
-          dbClient.componentDao().selectChildren(dbSession, dbQuery),
-          dbClient.componentDao().countChildren(dbSession, dbQuery));
+          dbClient.componentDao().selectDescendants(dbSession, dbQuery),
+          0);
       case LEAVES_STRATEGY:
       case ALL_STRATEGY:
         return new ComponentDtosAndTotal(
           dbClient.componentDao().selectDescendants(dbSession, dbQuery),
-          dbClient.componentDao().countDescendants(dbSession, dbQuery));
+          0);
       default:
         throw new IllegalStateException("Unknown component tree strategy");
     }
index f19049bcabc2d294d378d18c0faf35f055cd88aa..15bb327294c282ad8dc9029a232ef59523d970ad 100644 (file)
@@ -44,11 +44,11 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTreeQuery;
 import org.sonar.db.source.FileSourceDao;
 import org.sonar.db.source.FileSourceDto;
+import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
-import org.sonar.server.computation.task.projectanalysis.analysis.Analysis;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.projectanalysis.source.SourceLinesRepositoryRule;
 
 import static com.google.common.base.Joiner.on;
@@ -287,9 +287,6 @@ public class FileMoveDetectionStepTest {
 
     ComponentTreeQuery query = captor.getValue();
     assertThat(query.getBaseUuid()).isEqualTo(PROJECT.getUuid());
-    assertThat(query.getPage()).isEqualTo(1);
-    assertThat(query.getPageSize()).isEqualTo(Integer.MAX_VALUE);
-    assertThat(query.getSqlSort()).isEqualTo("LOWER(p.name) ASC, p.name ASC");
     assertThat(query.getQualifiers()).containsOnly(FILE, UNIT_TEST_FILE);
   }
 
index fd5fe9dc6524e699b317eef930ec9f4798d92710..62c4652b05df6739ee17dbf6e5bd84a89c14998a 100644 (file)
@@ -34,15 +34,12 @@ import org.apache.ibatis.session.RowBounds;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.db.Dao;
-import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 import org.sonar.db.RowNotFoundException;
-import org.sonar.db.WildcardPosition;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
 import static java.util.Collections.emptyList;
-import static org.sonar.api.utils.Paging.offset;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
 
@@ -167,42 +164,7 @@ public class ComponentDao implements Dao {
   }
 
   /**
-   * Select the children of a base component, given by its UUID. The components that are not present in last
-   * analysis are ignored.
-   *
-   * An empty list is returned if the base component does not exist or if the base component is a leaf.
-   */
-  public List<ComponentDto> selectChildren(DbSession dbSession, ComponentTreeQuery query) {
-    Optional<ComponentDto> componentOpt = selectByUuid(dbSession, query.getBaseUuid());
-    if (!componentOpt.isPresent()) {
-      return emptyList();
-    }
-    ComponentDto component = componentOpt.get();
-    RowBounds rowBounds = new RowBounds(offset(query.getPage(), query.getPageSize()), query.getPageSize());
-    return mapper(dbSession).selectChildren(query, uuidPathForChildrenQuery(component), rowBounds);
-  }
-
-  /**
-   * Count the children of a base component, given by its UUID. The components that are not present in last
-   * analysis are ignored.
-   *
-   * Zero is returned if the base component does not exist or if the base component is a leaf.
-   */
-  public int countChildren(DbSession dbSession, ComponentTreeQuery query) {
-    Optional<ComponentDto> componentOpt = selectByUuid(dbSession, query.getBaseUuid());
-    if (!componentOpt.isPresent()) {
-      return 0;
-    }
-    ComponentDto component = componentOpt.get();
-    return mapper(dbSession).countChildren(query, uuidPathForChildrenQuery(component));
-  }
-
-  private static String uuidPathForChildrenQuery(ComponentDto component) {
-    return component.getUuidPath() + component.uuid() + ".";
-  }
-
-  /**
-   * Select the descendants of a base component, given by its UUID. The components that are not present in last
+   * Select the children or the leaves of a base component, given by its UUID. The components that are not present in last
    * analysis are ignored.
    *
    * An empty list is returned if the base component does not exist or if the base component is a leaf.
@@ -210,30 +172,10 @@ public class ComponentDao implements Dao {
   public List<ComponentDto> selectDescendants(DbSession dbSession, ComponentTreeQuery query) {
     Optional<ComponentDto> componentOpt = selectByUuid(dbSession, query.getBaseUuid());
     if (!componentOpt.isPresent()) {
-      return Collections.emptyList();
-    }
-    ComponentDto component = componentOpt.get();
-    RowBounds rowBounds = new RowBounds(offset(query.getPage(), query.getPageSize()), query.getPageSize());
-    return mapper(dbSession).selectDescendants(query, uuidPathForDescendantsQuery(component), rowBounds);
-  }
-
-  /**
-   * Count the descendants of a base component, given by its UUID. The components that are not present in last
-   * analysis are ignored.
-   *
-   * Zero is returned if the base component does not exist or if the base component is a leaf.
-   */
-  public int countDescendants(DbSession dbSession, ComponentTreeQuery query) {
-    Optional<ComponentDto> componentOpt = selectByUuid(dbSession, query.getBaseUuid());
-    if (!componentOpt.isPresent()) {
-      return 0;
+      return emptyList();
     }
     ComponentDto component = componentOpt.get();
-    return mapper(dbSession).countDescendants(query, uuidPathForDescendantsQuery(component));
-  }
-
-  private static String uuidPathForDescendantsQuery(ComponentDto component) {
-    return DatabaseUtils.buildLikeValue(component.getUuidPath() + component.uuid() + ".", WildcardPosition.AFTER);
+    return mapper(dbSession).selectDescendants(query, query.getUuidPath(component));
   }
 
   public ComponentDto selectOrFailByKey(DbSession session, String key) {
index 027a67fe5a10e3e1b206e895ba88317e13e4b8ae..7e41d5c97b480ca78717ea2a6b0239313a11a496 100644 (file)
@@ -64,13 +64,7 @@ public interface ComponentMapper {
 
   List<ComponentDto> selectAncestors(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike);
 
-  List<ComponentDto> selectChildren(@Param("query") ComponentTreeQuery query, @Param("baseUuidPath") String baseUuidPath, RowBounds rowBounds);
-
-  int countChildren(@Param("query") ComponentTreeQuery query, @Param("baseUuidPath") String baseUuidPath);
-
-  List<ComponentDto> selectDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike, RowBounds rowBounds);
-
-  int countDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike);
+  List<ComponentDto> selectDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuidPath") String baseUuidPath);
 
   /**
    * Return all project (PRJ/TRK) uuids
index b987ff479281b386a058ba32e3302ad98a9bc98d..c8f47f6cd75d73da472fbd3af613928115e0d270 100644 (file)
@@ -28,6 +28,7 @@ import java.util.stream.Collectors;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import org.sonar.db.WildcardPosition;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.Lists.newArrayList;
@@ -37,6 +38,11 @@ import static org.sonar.db.DatabaseUtils.buildLikeValue;
 import static org.sonar.db.WildcardPosition.AFTER;
 
 public class ComponentTreeQuery {
+
+  public enum Strategy {
+    CHILDREN, LEAVES
+  }
+
   @CheckForNull
   private final String nameOrKeyQuery;
   // SONAR-7681 a public implementation of List must be used in MyBatis - potential concurrency exceptions otherwise
@@ -49,6 +55,7 @@ public class ComponentTreeQuery {
   private final String baseUuid;
   private final String sqlSort;
   private final String direction;
+  private final Strategy strategy;
 
   private ComponentTreeQuery(Builder builder) {
     this.nameOrKeyQuery = builder.nameOrKeyQuery;
@@ -56,8 +63,9 @@ public class ComponentTreeQuery {
     this.page = builder.page;
     this.pageSize = builder.pageSize;
     this.baseUuid = builder.baseUuid;
+    this.strategy = requireNonNull(builder.strategy);
     this.direction = builder.asc ? "ASC" : "DESC";
-    this.sqlSort = sortFieldsToSqlSort(builder.sortFields, direction);
+    this.sqlSort = builder.sortFields != null ? sortFieldsToSqlSort(builder.sortFields, direction) : null;
   }
 
   public Collection<String> getQualifiers() {
@@ -74,10 +82,12 @@ public class ComponentTreeQuery {
     return nameOrKeyQuery == null ? null : buildLikeValue(nameOrKeyQuery, AFTER).toLowerCase(Locale.ENGLISH);
   }
 
+  @Deprecated
   public Integer getPage() {
     return page;
   }
 
+  @Deprecated
   public Integer getPageSize() {
     return pageSize;
   }
@@ -86,18 +96,36 @@ public class ComponentTreeQuery {
     return baseUuid;
   }
 
+  public Strategy getStrategy() {
+    return strategy;
+  }
+
+  @Deprecated
   public String getSqlSort() {
     return sqlSort;
   }
 
+  @Deprecated
   public String getDirection() {
     return direction;
   }
 
+  public String getUuidPath(ComponentDto component) {
+    switch (strategy) {
+      case CHILDREN:
+        return component.getUuidPath() + component.uuid() + ".";
+      case LEAVES:
+        return buildLikeValue(component.getUuidPath() + component.uuid() + ".", WildcardPosition.AFTER);
+      default:
+        throw new IllegalArgumentException("Unknown strategy : " + strategy);
+    }
+  }
+
   public static Builder builder() {
     return new Builder();
   }
 
+  @Deprecated
   private static String sortFieldsToSqlSort(List<String> sortFields, String direction) {
     return sortFields
       .stream()
@@ -117,6 +145,7 @@ public class ComponentTreeQuery {
     private String baseUuid;
     private List<String> sortFields;
     private boolean asc = true;
+    private Strategy strategy;
 
     private Builder() {
       // private constructor
@@ -124,7 +153,6 @@ public class ComponentTreeQuery {
 
     public ComponentTreeQuery build() {
       requireNonNull(baseUuid);
-      requireNonNull(sortFields);
       return new ComponentTreeQuery(this);
     }
 
@@ -138,11 +166,13 @@ public class ComponentTreeQuery {
       return this;
     }
 
+    @Deprecated
     public Builder setPage(int page) {
       this.page = page;
       return this;
     }
 
+    @Deprecated
     public Builder setPageSize(int pageSize) {
       this.pageSize = pageSize;
       return this;
@@ -153,18 +183,26 @@ public class ComponentTreeQuery {
       return this;
     }
 
+    public Builder setStrategy(Strategy strategy) {
+      this.strategy = requireNonNull(strategy);
+      return this;
+    }
+
+    @Deprecated
     public Builder setSortFields(List<String> sorts) {
       checkArgument(sorts != null && !sorts.isEmpty());
       this.sortFields = sorts;
       return this;
     }
 
+    @Deprecated
     public Builder setAsc(boolean asc) {
       this.asc = asc;
       return this;
     }
   }
 
+  @Deprecated
   private static class SortFieldToSqlSortFieldFunction implements Function<String, String> {
     private static final String PATTERN = "LOWER(p.%1$s) %2$s, p.%1$s %2$s";
 
index 0b5a2f4870059ba43cb7c421aa2a97d972177cd9..aba3ff7e7048d69c16aaef00840c7a142c9c413f 100644 (file)
@@ -34,6 +34,7 @@ import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTreeQuery;
+import org.sonar.db.component.ComponentTreeQuery.Strategy;
 
 import static java.util.Collections.emptyList;
 import static org.sonar.api.utils.DateUtils.dateToLong;
@@ -45,7 +46,6 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs;
 public class PurgeDao implements Dao {
   private static final Logger LOG = Loggers.get(PurgeDao.class);
   private static final String[] UNPROCESSED_STATUS = new String[] {"U"};
-  private static final List<String> UUID_FIELD_SORT = Collections.singletonList("uuid");
 
   private final ComponentDao componentDao;
   private final System2 system2;
@@ -112,9 +112,10 @@ public class PurgeDao implements Dao {
     List<String> componentWithoutHistoricalDataUuids = componentDao
       .selectDescendants(
         dbSession,
-        newComponentTreeQuery()
+        ComponentTreeQuery.builder()
           .setBaseUuid(rootUuid)
           .setQualifiers(Arrays.asList(scopesWithoutHistoricalData))
+          .setStrategy(Strategy.LEAVES)
           .build())
       .stream().map(ComponentDto::uuid)
       .collect(Collectors.toList());
@@ -122,16 +123,6 @@ public class PurgeDao implements Dao {
     purgeCommands.deleteComponentMeasures(analysisUuids, componentWithoutHistoricalDataUuids);
   }
 
-  /**
-   * Creates a new ComponentTreeQuery.Builder with properties that don't matter here but are mandatory populated.
-   */
-  private static ComponentTreeQuery.Builder newComponentTreeQuery() {
-    return ComponentTreeQuery.builder()
-      .setPage(1)
-      .setPageSize(Integer.MAX_VALUE)
-      .setSortFields(UUID_FIELD_SORT);
-  }
-
   private void purgeDisabledComponents(DbSession session, Collection<String> uuids, PurgeListener listener) {
     PurgeMapper mapper = mapper(session);
     executeLargeInputs(uuids,
index 52dd7c8820631ef15a25f4b13d62e02f446bb335..8a3aa4973a944de71e73b2c9916b8816b4a7a965 100644 (file)
     order by ${query.sqlSort}
   </select>
 
-  <!-- "p" is children -->
-  <select id="selectChildren" resultType="Component">
+  <select id="selectDescendants" resultType="Component">
     select
     <include refid="componentColumns"/>
-    <include refid="sqlChildren"/>
-    order by ${query.sqlSort}
+    <include refid="selectDescendantsQuery"/>
   </select>
 
-  <select id="countChildren" resultType="int">
-    select count(p.id)
-    <include refid="sqlChildren"/>
-  </select>
-
-  <sql id="sqlChildren">
+  <sql id="selectDescendantsQuery">
     from projects p
     inner join projects base on base.project_uuid = p.project_uuid and base.uuid = #{query.baseUuid}
-    where
-    p.enabled = ${_true}
-    and p.uuid_path = #{baseUuidPath}
-    <include refid="sqlTreeFilters"/>
+    <where>
+      <choose>
+        <when test="query.getStrategy().name() == 'CHILDREN'">
+          and p.uuid_path = #{baseUuidPath}
+        </when>
+        <otherwise>
+          and p.uuid_path like #{baseUuidPath} ESCAPE '/'
+        </otherwise>
+      </choose>
+      <include refid="selectDescendantsFilters"/>
+    </where>
   </sql>
 
-  <sql id="sqlTreeFilters">
+  <sql id="selectDescendantsFilters">
+    and p.enabled = ${_true}
     <if test="query.qualifiers != null">
       and p.qualifier in
       <foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=",">
     </if>
   </sql>
 
-  <!-- "p" is descendants -->
-  <select id="selectDescendants" resultType="Component">
-    select
-    <include refid="componentColumns"/>
-    <include refid="sqlDescendants"/>
-    order by ${query.sqlSort}
-  </select>
-
-  <select id="countDescendants" resultType="int">
-    select count(p.id)
-    <include refid="sqlDescendants"/>
-  </select>
-
-  <sql id="sqlDescendants">
-    from projects p
-    inner join projects base on base.project_uuid=p.project_uuid and base.uuid = #{query.baseUuid}
-    where
-    p.enabled = ${_true}
-    and p.uuid_path like #{baseUuidPathLike} ESCAPE '/'
-    <include refid="sqlTreeFilters"/>
-  </sql>
-
   <select id="countRootComponents" resultType="int">
     select count(p.id)
     from projects p
index 394d2a241e9061a9eb8e57f35c833a2d040598a0..70f3e206e232b60a90ae1dfa5156d069f878c6d7 100644 (file)
@@ -47,6 +47,8 @@ import static org.sonar.db.component.ComponentTesting.newProjectCopy;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.component.ComponentTesting.newSubView;
 import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
+import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
 
 public class ComponentDaoTest {
 
@@ -677,8 +679,7 @@ public class ComponentDaoTest {
       .setBModuleUuidPath("moduleUuidPath")
       .setBName("name")
       .setBPath("path")
-      .setBQualifier("qualifier")
-      );
+      .setBQualifier("qualifier"));
     dbSession.commit();
 
     Map<String, Object> row = selectBColumnsForUuid("U1");
@@ -840,7 +841,47 @@ public class ComponentDaoTest {
   }
 
   @Test
-  public void selectChildren() {
+  public void selectParent() {
+    // project -> module -> file
+    ComponentDto project = newProjectDto(PROJECT_UUID);
+    componentDb.insertProjectAndSnapshot(project);
+    ComponentDto module = newModuleDto(MODULE_UUID, project);
+    componentDb.insertComponent(module);
+    ComponentDto file = newFileDto(module, null, FILE_1_UUID);
+    componentDb.insertComponent(file);
+    db.commit();
+
+    assertThat(underTest.selectParent(dbSession, project)).isAbsent();
+    assertThat(underTest.selectParent(dbSession, module).get().uuid()).isEqualTo(PROJECT_UUID);
+    assertThat(underTest.selectParent(dbSession, file).get().uuid()).isEqualTo(MODULE_UUID);
+  }
+
+  @Test
+  public void selectAncestors() {
+    // project -> module -> file
+    ComponentDto project = newProjectDto(PROJECT_UUID);
+    componentDb.insertProjectAndSnapshot(project);
+    ComponentDto module = newModuleDto(MODULE_UUID, project);
+    componentDb.insertComponent(module);
+    ComponentDto file = newFileDto(module, null, FILE_1_UUID);
+    componentDb.insertComponent(file);
+    db.commit();
+
+    // ancestors of root
+    List<ComponentDto> ancestors = underTest.selectAncestors(dbSession, project);
+    assertThat(ancestors).isEmpty();
+
+    // ancestors of module
+    ancestors = underTest.selectAncestors(dbSession, module);
+    assertThat(ancestors).extracting("uuid").containsExactly(PROJECT_UUID);
+
+    // ancestors of file
+    ancestors = underTest.selectAncestors(dbSession, file);
+    assertThat(ancestors).extracting("uuid").containsExactly(PROJECT_UUID, MODULE_UUID);
+  }
+
+  @Test
+  public void select_descendants_with_children_stragegy() {
     // project has 2 children: module and file 1. Other files are part of module.
     ComponentDto project = newProjectDto(PROJECT_UUID);
     componentDb.insertProjectAndSnapshot(project);
@@ -857,133 +898,81 @@ public class ComponentDaoTest {
 
     // test children of root
     ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).build();
-    List<ComponentDto> children = underTest.selectChildren(dbSession, query);
-    assertThat(children).extracting("uuid").containsExactly(FILE_1_UUID, MODULE_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(2);
+    List<ComponentDto> children = underTest.selectDescendants(dbSession, query);
+    assertThat(children).extracting("uuid").containsOnly(FILE_1_UUID, MODULE_UUID);
 
     // test children of root, filtered by qualifier
     query = newTreeQuery(PROJECT_UUID).setQualifiers(asList(Qualifiers.MODULE)).build();
-    children = underTest.selectChildren(dbSession, query);
-    assertThat(children).extracting("uuid").containsExactly(MODULE_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(1);
+    children = underTest.selectDescendants(dbSession, query);
+    assertThat(children).extracting("uuid").containsOnly(MODULE_UUID);
 
     // test children of intermediate component (module here), default ordering by
     query = newTreeQuery(MODULE_UUID).build();
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsOnly(FILE_2_UUID, FILE_3_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(2);
+    assertThat(underTest.selectDescendants(dbSession, query)).extracting("uuid").containsOnly(FILE_2_UUID, FILE_3_UUID);
 
     // test children of leaf component (file here)
     query = newTreeQuery(FILE_1_UUID).build();
-    assertThat(underTest.selectChildren(dbSession, query)).isEmpty();
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(0);
+    assertThat(underTest.selectDescendants(dbSession, query)).isEmpty();
 
     // test children of root, matching name
     query = newTreeQuery(PROJECT_UUID).setNameOrKeyQuery("One").build();
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(1);
+    assertThat(underTest.selectDescendants(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
 
     // test children of root, matching case-insensitive name
     query = newTreeQuery(PROJECT_UUID).setNameOrKeyQuery("OnE").build();
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(1);
+    assertThat(underTest.selectDescendants(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
 
     // test children of root, matching key
     query = newTreeQuery(PROJECT_UUID).setNameOrKeyQuery("file-key-1").build();
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(1);
+    assertThat(underTest.selectDescendants(dbSession, query)).extracting("uuid").containsOnly(FILE_1_UUID);
 
     // test children of root, without matching name nor key
     query = newTreeQuery(PROJECT_UUID).setNameOrKeyQuery("does-not-exist").build();
-    assertThat(underTest.selectChildren(dbSession, query)).isEmpty();
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(0);
+    assertThat(underTest.selectDescendants(dbSession, query)).isEmpty();
 
     // test children of intermediate component (module here), matching name
     query = newTreeQuery(MODULE_UUID).setNameOrKeyQuery("Two").build();
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsOnly(FILE_2_UUID);
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(1);
+    assertThat(underTest.selectDescendants(dbSession, query)).extracting("uuid").containsOnly(FILE_2_UUID);
 
     // test children of intermediate component (module here), without matching name
     query = newTreeQuery(MODULE_UUID).setNameOrKeyQuery("does-not-exist").build();
-    assertThat(underTest.selectChildren(dbSession, query)).isEmpty();
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(0);
+    assertThat(underTest.selectDescendants(dbSession, query)).isEmpty();
 
     // test children of leaf component (file here)
     query = newTreeQuery(FILE_1_UUID).build();
-    assertThat(underTest.selectChildren(dbSession, query)).isEmpty();
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(0);
+    assertThat(underTest.selectDescendants(dbSession, query)).isEmpty();
 
     // test children of leaf component (file here), matching name
     query = newTreeQuery(FILE_1_UUID).setNameOrKeyQuery("Foo").build();
-    assertThat(underTest.selectChildren(dbSession, query)).isEmpty();
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(0);
-  }
-
-  @Test
-  public void selectChildren_with_pagination() {
-    ComponentDto project = newProjectDto(PROJECT_UUID);
-    componentDb.insertProjectAndSnapshot(project);
-    for (int i = 1; i <= 9; i++) {
-      componentDb.insertComponent(newFileDto(project, null, "file-uuid-" + i));
-    }
-    db.commit();
-
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID)
-      .setPage(2)
-      .setPageSize(3)
-      .setAsc(false)
-      .build();
-
-    assertThat(underTest.selectChildren(dbSession, query)).extracting("uuid").containsExactly("file-uuid-6", "file-uuid-5", "file-uuid-4");
-    assertThat(underTest.countChildren(dbSession, query)).isEqualTo(9);
+    assertThat(underTest.selectDescendants(dbSession, query)).isEmpty();
   }
 
   @Test
-  public void selectChildren_ordered_by_file_path() {
+  public void select_descendants_with_leaves_stragegy() {
     ComponentDto project = newProjectDto(PROJECT_UUID);
     componentDb.insertProjectAndSnapshot(project);
-    componentDb.insertComponent(newFileDto(project, null, "file-uuid-1").setName("file-name-1").setPath("3"));
-    componentDb.insertComponent(newFileDto(project, null, "file-uuid-2").setName("file-name-2").setPath("2"));
-    componentDb.insertComponent(newFileDto(project, null, "file-uuid-3").setName("file-name-3").setPath("1"));
+    componentDb.insertComponent(newModuleDto("module-1-uuid", project));
+    componentDb.insertComponent(newFileDto(project, null, "file-1-uuid"));
+    componentDb.insertComponent(newFileDto(project, null, "file-2-uuid"));
     db.commit();
     componentDb.indexAllComponents();
 
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID)
-      .setSortFields(singletonList("path"))
-      .setAsc(true)
-      .build();
+    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).setStrategy(LEAVES).build();
 
-    List<ComponentDto> result = underTest.selectChildren(dbSession, query);
-    assertThat(result).extracting("uuid").containsExactly("file-uuid-3", "file-uuid-2", "file-uuid-1");
+    List<ComponentDto> result = underTest.selectDescendants(dbSession, query);
+    assertThat(result).extracting("uuid").containsOnly("file-1-uuid", "file-2-uuid", "module-1-uuid");
   }
 
   @Test
-  public void selectChildren_returns_empty_list_if_base_component_does_not_exist() {
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).build();
+  public void select_descendants_returns_empty_list_if_base_component_does_not_exist() {
+    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).setStrategy(CHILDREN).build();
 
-    List<ComponentDto> result = underTest.selectChildren(dbSession, query);
+    List<ComponentDto> result = underTest.selectDescendants(dbSession, query);
     assertThat(result).isEmpty();
   }
 
   @Test
-  public void selectChildren_of_a_view() {
-    ComponentDto view = newView(A_VIEW_UUID);
-    componentDb.insertViewAndSnapshot(view);
-    // one subview
-    ComponentDto subView = newSubView(view, "subview-uuid", "subview-key").setName("subview-name");
-    componentDb.insertComponent(subView);
-    // one project and its copy linked to the view
-    ComponentDto project = newProjectDto(PROJECT_UUID).setName("project-name");
-    componentDb.insertProjectAndSnapshot(project);
-    componentDb.insertComponent(newProjectCopy("project-copy-uuid", project, view));
-    componentDb.indexAllComponents();
-    ComponentTreeQuery query = newTreeQuery(A_VIEW_UUID).build();
-
-    List<ComponentDto> components = underTest.selectChildren(dbSession, query);
-    assertThat(components).extracting("uuid").containsOnly("project-copy-uuid", "subview-uuid");
-  }
-
-  @Test
-  public void selectChildren_of_a_view_and_filter_by_name() {
+  public void select_descendants_of_a_view_and_filter_by_name() {
     ComponentDto view = newView(A_VIEW_UUID);
     componentDb.insertViewAndSnapshot(view);
     // one subview
@@ -994,113 +983,15 @@ public class ComponentDaoTest {
     componentDb.insertProjectAndSnapshot(project);
     componentDb.insertComponent(newProjectCopy("project-copy-uuid", project, view));
     componentDb.indexAllComponents();
-    ComponentTreeQuery dbQuery = newTreeQuery(A_VIEW_UUID).setNameOrKeyQuery("name").build();
+    ComponentTreeQuery dbQuery = newTreeQuery(A_VIEW_UUID).setNameOrKeyQuery("name").setStrategy(CHILDREN).build();
 
-    List<ComponentDto> components = underTest.selectChildren(dbSession, dbQuery);
+    List<ComponentDto> components = underTest.selectDescendants(dbSession, dbQuery);
     assertThat(components).extracting("uuid").containsOnly("project-copy-uuid", "subview-uuid");
   }
 
-  @Test
-  public void selectParent() {
-    // project -> module -> file
-    ComponentDto project = newProjectDto(PROJECT_UUID);
-    componentDb.insertProjectAndSnapshot(project);
-    ComponentDto module = newModuleDto(MODULE_UUID, project);
-    componentDb.insertComponent(module);
-    ComponentDto file = newFileDto(module, null, FILE_1_UUID);
-    componentDb.insertComponent(file);
-    db.commit();
-
-    assertThat(underTest.selectParent(dbSession, project)).isAbsent();
-    assertThat(underTest.selectParent(dbSession, module).get().uuid()).isEqualTo(PROJECT_UUID);
-    assertThat(underTest.selectParent(dbSession, file).get().uuid()).isEqualTo(MODULE_UUID);
-  }
-
-  @Test
-  public void selectAncestors() {
-    // project -> module -> file
-    ComponentDto project = newProjectDto(PROJECT_UUID);
-    componentDb.insertProjectAndSnapshot(project);
-    ComponentDto module = newModuleDto(MODULE_UUID, project);
-    componentDb.insertComponent(module);
-    ComponentDto file = newFileDto(module, null, FILE_1_UUID);
-    componentDb.insertComponent(file);
-    db.commit();
-
-    // ancestors of root
-    List<ComponentDto> ancestors = underTest.selectAncestors(dbSession, project);
-    assertThat(ancestors).isEmpty();
-
-    // ancestors of module
-    ancestors = underTest.selectAncestors(dbSession, module);
-    assertThat(ancestors).extracting("uuid").containsExactly(PROJECT_UUID);
-
-    // ancestors of file
-    ancestors = underTest.selectAncestors(dbSession, file);
-    assertThat(ancestors).extracting("uuid").containsExactly(PROJECT_UUID, MODULE_UUID);
-  }
-
-  @Test
-  public void selectDescendants() {
-    ComponentDto project = newProjectDto(PROJECT_UUID);
-    componentDb.insertProjectAndSnapshot(project);
-    componentDb.insertComponent(newModuleDto("module-1-uuid", project));
-    componentDb.insertComponent(newFileDto(project, null, "file-1-uuid"));
-    componentDb.insertComponent(newFileDto(project, null, "file-2-uuid"));
-    db.commit();
-    componentDb.indexAllComponents();
-
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).build();
-
-    List<ComponentDto> result = underTest.selectDescendants(dbSession, query);
-    assertThat(result).extracting("uuid").containsExactly("file-1-uuid", "file-2-uuid", "module-1-uuid");
-    int count = underTest.countDescendants(dbSession, query);
-    assertThat(count).isEqualTo(3);
-  }
-
-  @Test
-  public void selectDescendants_returns_empty_list_if_base_component_does_not_exist() {
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID).build();
-
-    List<ComponentDto> result = underTest.selectDescendants(dbSession, query);
-    assertThat(result).isEmpty();
-  }
-
-  @Test
-  public void selectDescendants_of_a_project_paginated_and_ordered() {
-    ComponentDto project = newProjectDto(PROJECT_UUID).setKey("project-key");
-    componentDb.insertProjectAndSnapshot(project);
-    componentDb.insertComponent(newModuleDto("module-1-uuid", project));
-    componentDb.insertComponent(newFileDto(project, null, "file-uuid-1").setName("file-name-1"));
-    componentDb.insertComponent(newFileDto(project, null, "another-uuid"));
-    for (int i = 2; i <= 9; i++) {
-      componentDb.insertComponent(newFileDto(project, null, "file-uuid-" + i).setName("file-name-" + i));
-    }
-    db.commit();
-    componentDb.indexAllComponents();
-
-    ComponentTreeQuery query = newTreeQuery(PROJECT_UUID)
-      .setQualifiers(newArrayList(Qualifiers.FILE))
-      .setPage(2)
-      .setPageSize(3)
-      .setNameOrKeyQuery("file-name")
-      .setSortFields(singletonList("name"))
-      .setAsc(false)
-      .build();
-
-    List<ComponentDto> result = underTest.selectDescendants(dbSession, query);
-    int count = underTest.countDescendants(dbSession, query);
-
-    assertThat(count).isEqualTo(9);
-    assertThat(result).extracting("uuid").containsExactly("file-uuid-6", "file-uuid-5", "file-uuid-4");
-  }
-
   private static ComponentTreeQuery.Builder newTreeQuery(String baseUuid) {
     return ComponentTreeQuery.builder()
-      .setPage(1)
-      .setPageSize(500)
       .setBaseUuid(baseUuid)
-      .setSortFields(singletonList("name"))
-      .setAsc(true);
+      .setStrategy(CHILDREN);
   }
 }