diff options
author | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-11-04 11:28:44 +0100 |
---|---|---|
committer | Julien Lancelot <julien.lancelot@sonarsource.com> | 2016-11-08 11:12:51 +0100 |
commit | b67b21e7324e78bab3876f460cfe426455b2d367 (patch) | |
tree | 940698429a5416afbe404757d1c68712892b5e53 | |
parent | 617497c27c42a9ce4d782d1bfa8249c3a0d94083 (diff) | |
download | sonarqube-b67b21e7324e78bab3876f460cfe426455b2d367.tar.gz sonarqube-b67b21e7324e78bab3876f460cfe426455b2d367.zip |
SONAR-8089 Merge ComponentDao#selectChildren and selectDescendants
10 files changed, 170 insertions, 339 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java index 3ddb250ea0b..93db78f2ab7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java @@ -19,26 +19,6 @@ */ 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"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java index 5ffd9591cd5..38e1f2ab914 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java @@ -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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java index 8a2d059c215..04f5c44c397 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java @@ -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"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStepTest.java index f19049bcabc..15bb327294c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStepTest.java @@ -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); } diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java index fd5fe9dc652..62c4652b05d 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java @@ -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) { diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java index 027a67fe5a1..7e41d5c97b4 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java @@ -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 diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java index b987ff47928..c8f47f6cd75 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java @@ -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"; diff --git a/sonar-db/src/main/java/org/sonar/db/purge/PurgeDao.java b/sonar-db/src/main/java/org/sonar/db/purge/PurgeDao.java index 0b5a2f48700..aba3ff7e704 100644 --- a/sonar-db/src/main/java/org/sonar/db/purge/PurgeDao.java +++ b/sonar-db/src/main/java/org/sonar/db/purge/PurgeDao.java @@ -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, diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 52dd7c88206..8a3aa4973a9 100644 --- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -331,29 +331,30 @@ 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=","> @@ -379,28 +380,6 @@ </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 diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java index 394d2a241e9..70f3e206e23 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -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); } } |