@@ -44,6 +44,7 @@ import org.sonar.api.resources.Scopes; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.Pagination; | |||
import org.sonar.db.RowNotFoundException; | |||
import org.sonar.db.audit.AuditPersister; | |||
import org.sonar.db.audit.NoOpAuditPersister; | |||
@@ -79,6 +80,7 @@ import static org.sonar.api.resources.Qualifiers.PROJECT; | |||
import static org.sonar.api.resources.Qualifiers.SUBVIEW; | |||
import static org.sonar.api.resources.Qualifiers.VIEW; | |||
import static org.sonar.api.utils.DateUtils.parseDate; | |||
import static org.sonar.db.Pagination.forPage; | |||
import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; | |||
import static org.sonar.db.component.BranchType.BRANCH; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
@@ -931,35 +933,35 @@ public class ComponentDaoIT { | |||
SnapshotDto analyzedPortfolio = db.components().insertProjectAndSnapshot(ComponentTesting.newPortfolio()); | |||
Supplier<ComponentQuery.Builder> query = () -> ComponentQuery.builder().setQualifiers(PROJECT).setOnProvisionedOnly(true); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsOnly(provisionedProject.uuid()); | |||
// pagination | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), 2, 10)).isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(3).andSize(10))).isEmpty(); | |||
// filter on qualifiers | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers("XXX").build(), 0, 10)).isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, "XXX").build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers("XXX").build(), forPage(1).andSize(10))).isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, "XXX").build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsOnly(provisionedProject.uuid()); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setQualifiers(PROJECT, Qualifiers.VIEW).build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsOnly(provisionedProject.uuid(), provisionedPortfolio.uuid()); | |||
// match key | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery(provisionedProject.getKey()).build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery(provisionedProject.getKey()).build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsExactly(provisionedProject.uuid()); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("pROvisiONed.proJEcT").setPartialMatchOnKey(true).build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("pROvisiONed.proJEcT").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsExactly(provisionedProject.uuid()); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("missing").setPartialMatchOnKey(true).build(), 0, 10)).isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("to be escaped '\"\\%").setPartialMatchOnKey(true).build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("missing").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))).isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("to be escaped '\"\\%").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) | |||
.isEmpty(); | |||
// match name | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("ned proj").setPartialMatchOnKey(true).build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().setNameOrKeyQuery("ned proj").setPartialMatchOnKey(true).build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsExactly(provisionedProject.uuid()); | |||
} | |||
@@ -972,7 +974,7 @@ public class ComponentDaoIT { | |||
// the project does not have any analysis | |||
ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) | |||
.extracting(ComponentDto::uuid) | |||
.containsOnly(project.uuid()); | |||
@@ -981,7 +983,7 @@ public class ComponentDaoIT { | |||
ComponentDto branchWithoutAnalysis = db.components().insertProjectBranch(project); | |||
ComponentDto branchWithAnalysis = db.components().insertProjectBranch(project); | |||
db.components().insertSnapshot(branchWithAnalysis); | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), 0, 10)) | |||
assertThat(underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10))) | |||
.isEmpty(); | |||
} | |||
@@ -999,7 +1001,7 @@ public class ComponentDaoIT { | |||
.setQualifiers(PROJECT) | |||
.setOnProvisionedOnly(true); | |||
List<ComponentDto> results = underTest.selectByQuery(dbSession, query.get().build(), 0, 10); | |||
List<ComponentDto> results = underTest.selectByQuery(dbSession, query.get().build(), forPage(1).andSize(10)); | |||
assertThat(results) | |||
.extracting(ComponentDto::uuid) | |||
.containsExactly( | |||
@@ -1172,7 +1174,8 @@ public class ComponentDaoIT { | |||
private void assertThatSelectByQueryThrowsIAE(ComponentQuery.Builder query, String expectedMessage) { | |||
ComponentQuery componentQuery = query.build(); | |||
assertThatThrownBy(() -> underTest.selectByQuery(dbSession, componentQuery, 0, Integer.MAX_VALUE)) | |||
Pagination pagination = forPage(1).andSize(Integer.MAX_VALUE); | |||
assertThatThrownBy(() -> underTest.selectByQuery(dbSession, componentQuery, pagination)) | |||
.isInstanceOf(IllegalArgumentException.class) | |||
.hasMessage(expectedMessage); | |||
} | |||
@@ -1186,12 +1189,12 @@ public class ComponentDaoIT { | |||
} | |||
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("oJect").setQualifiers(PROJECT).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 1, 3); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(2).andSize(3)); | |||
int count = underTest.countByQuery(dbSession, query); | |||
assertThat(result).hasSize(3); | |||
assertThat(count).isEqualTo(9); | |||
assertThat(result).extracting(ComponentDto::name).containsExactly("project-2", "project-3", "project-4"); | |||
assertThat(result).extracting(ComponentDto::name).containsExactly("project-4", "project-5", "project-6"); | |||
} | |||
@Test | |||
@@ -1199,8 +1202,8 @@ public class ComponentDaoIT { | |||
ComponentDto main = db.components().insertPublicProject().getMainBranchComponent(); | |||
ComponentDto branch = db.components().insertProjectBranch(main); | |||
assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, 0, 2)).hasSize(1); | |||
assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, 0, 2).get(0).uuid()).isEqualTo(main.uuid()); | |||
assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, forPage(1).andSize(2))).hasSize(1); | |||
assertThat(underTest.selectByQuery(dbSession, ALL_PROJECTS_COMPONENT_QUERY, forPage(1).andSize(2)).get(0).uuid()).isEqualTo(main.uuid()); | |||
} | |||
@Test | |||
@@ -1216,7 +1219,7 @@ public class ComponentDaoIT { | |||
db.components().insertProjectAndSnapshot(newPrivateProjectDto().setName("project-\\_%/-name")); | |||
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("-\\_%/-").setQualifiers(PROJECT).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); | |||
assertThat(result).hasSize(1); | |||
assertThat(result.get(0).name()).isEqualTo("project-\\_%/-name"); | |||
@@ -1228,7 +1231,7 @@ public class ComponentDaoIT { | |||
db.components().insertProjectAndSnapshot(newPrivateProjectDto().setKey("project-key-that-does-not-match")); | |||
ComponentQuery query = ComponentQuery.builder().setNameOrKeyQuery("project-_%-key").setQualifiers(PROJECT).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); | |||
assertThat(result).hasSize(1); | |||
assertThat(result.get(0).getKey()).isEqualTo("project-_%-key"); | |||
@@ -1242,7 +1245,7 @@ public class ComponentDaoIT { | |||
.setNameOrKeyQuery("JECT-K") | |||
.setPartialMatchOnKey(true) | |||
.setQualifiers(PROJECT).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); | |||
assertThat(result).hasSize(1); | |||
assertThat(result.get(0).getKey()).isEqualTo("project-key"); | |||
@@ -1267,19 +1270,11 @@ public class ComponentDaoIT { | |||
.containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); | |||
// before date on any branch | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime))) | |||
.containsExactlyInAnyOrder(oldProject.uuid()); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(aLongTimeAgo))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(aLongTimeAgo))) | |||
.isEmpty(); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime + 1_000L))) | |||
.containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); | |||
// after date | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime - 1_000L))) | |||
.containsExactlyInAnyOrder(recentProject.uuid()); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime + 1_000L))) | |||
.isEmpty(); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(aLongTimeAgo))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime + 1_000L))) | |||
.containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); | |||
} | |||
@@ -1306,19 +1301,11 @@ public class ComponentDaoIT { | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnalyzedBefore(recentTime + 1_000L))).isEmpty(); | |||
// before date on any branch | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime))) | |||
.containsExactlyInAnyOrder(oldProject.uuid()); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(aLongTimeAgo))) | |||
.isEmpty(); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedBefore(recentTime + 1_000L))) | |||
.containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); | |||
// after date | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime - 1_000L))) | |||
.containsExactlyInAnyOrder(recentProject.uuid()); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(recentTime + 1_000L))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(aLongTimeAgo))) | |||
.isEmpty(); | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAnyBranchAnalyzedAfter(aLongTimeAgo))) | |||
assertThat(selectProjectUuidsByQuery(q -> q.setAllBranchesAnalyzedBefore(recentTime + 1_000L))) | |||
.containsExactlyInAnyOrder(oldProject.uuid(), recentProject.uuid()); | |||
} | |||
@@ -1387,7 +1374,7 @@ public class ComponentDaoIT { | |||
private List<String> selectUuidsByQuery(String qualifier, Consumer<ComponentQuery.Builder> query) { | |||
ComponentQuery.Builder builder = ComponentQuery.builder().setQualifiers(qualifier); | |||
query.accept(builder); | |||
return underTest.selectByQuery(dbSession, builder.build(), 0, 5) | |||
return underTest.selectByQuery(dbSession, builder.build(), forPage(1).andSize(5)) | |||
.stream() | |||
.map(ComponentDto::uuid) | |||
.toList(); | |||
@@ -1402,9 +1389,9 @@ public class ComponentDaoIT { | |||
ComponentQuery publicProjectsQuery = ComponentQuery.builder().setPrivate(false).setQualifiers(PROJECT).build(); | |||
ComponentQuery allProjectsQuery = ComponentQuery.builder().setPrivate(null).setQualifiers(PROJECT).build(); | |||
assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("private-key"); | |||
assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("public-key"); | |||
assertThat(underTest.selectByQuery(dbSession, allProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsOnly("public-key", "private-key"); | |||
assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsExactly("private-key"); | |||
assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsExactly("public-key"); | |||
assertThat(underTest.selectByQuery(dbSession, allProjectsQuery, forPage(1).andSize(10))).extracting(ComponentDto::getKey).containsOnly("public-key", "private-key"); | |||
} | |||
@Test | |||
@@ -1412,7 +1399,7 @@ public class ComponentDaoIT { | |||
db.components().insertPrivateProject().getMainBranchComponent(); | |||
ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentKeys(emptySet()).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, forPage(1).andSize(10)); | |||
int count = underTest.countByQuery(dbSession, dbQuery); | |||
assertThat(result).isEmpty(); | |||
@@ -1427,7 +1414,7 @@ public class ComponentDaoIT { | |||
ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT) | |||
.setComponentKeys(newHashSet(sonarqube.getKey(), jdk8.getKey())).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); | |||
assertThat(result).hasSize(2).extracting(ComponentDto::getKey) | |||
.containsExactlyInAnyOrder(sonarqube.getKey(), jdk8.getKey()) | |||
@@ -1439,7 +1426,7 @@ public class ComponentDaoIT { | |||
db.components().insertPrivateProject().getMainBranchComponent(); | |||
ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(PROJECT).setComponentUuids(emptySet()).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, dbQuery, forPage(1).andSize(10)); | |||
int count = underTest.countByQuery(dbSession, dbQuery); | |||
assertThat(result).isEmpty(); | |||
@@ -1454,7 +1441,7 @@ public class ComponentDaoIT { | |||
ComponentQuery query = ComponentQuery.builder().setQualifiers(PROJECT) | |||
.setComponentUuids(newHashSet(sonarqube.uuid(), jdk8.uuid())).build(); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, 0, 10); | |||
List<ComponentDto> result = underTest.selectByQuery(dbSession, query, forPage(1).andSize(10)); | |||
assertThat(result).hasSize(2).extracting(ComponentDto::uuid) | |||
.containsOnlyOnce(sonarqube.uuid(), jdk8.uuid()) |
@@ -31,10 +31,10 @@ import java.util.stream.Collectors; | |||
import java.util.stream.Stream; | |||
import javax.annotation.Nullable; | |||
import org.apache.ibatis.session.ResultHandler; | |||
import org.apache.ibatis.session.RowBounds; | |||
import org.sonar.api.resources.Qualifiers; | |||
import org.sonar.db.Dao; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.Pagination; | |||
import org.sonar.db.RowNotFoundException; | |||
import org.sonar.db.audit.AuditPersister; | |||
import org.sonar.db.audit.model.ComponentNewValue; | |||
@@ -104,8 +104,8 @@ public class ComponentDao implements Dao { | |||
* @throws IllegalArgumentException if parameter query#getComponentKeys() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values | |||
* @throws IllegalArgumentException if parameter query#getMainComponentUuids() has more than {@link org.sonar.db.DatabaseUtils#PARTITION_SIZE_FOR_ORACLE} values | |||
*/ | |||
public List<ComponentDto> selectByQuery(DbSession dbSession, ComponentQuery query, int offset, int limit) { | |||
return selectByQueryImpl(dbSession, query, offset, limit); | |||
public List<ComponentDto> selectByQuery(DbSession dbSession, ComponentQuery query, Pagination pagination) { | |||
return selectByQueryImpl(dbSession, query, pagination); | |||
} | |||
/** | |||
@@ -117,12 +117,12 @@ public class ComponentDao implements Dao { | |||
return countByQueryImpl(session, query); | |||
} | |||
private static List<ComponentDto> selectByQueryImpl(DbSession session, ComponentQuery query, int offset, int limit) { | |||
private static List<ComponentDto> selectByQueryImpl(DbSession session, ComponentQuery query, Pagination pagination) { | |||
if (query.hasEmptySetOfComponents()) { | |||
return emptyList(); | |||
} | |||
checkThatNotTooManyComponents(query); | |||
return mapper(session).selectByQuery(query, new RowBounds(offset, limit)); | |||
return mapper(session).selectByQuery(query, pagination); | |||
} | |||
private static int countByQueryImpl(DbSession session, ComponentQuery query) { |
@@ -26,7 +26,7 @@ import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.ibatis.annotations.Param; | |||
import org.apache.ibatis.session.ResultHandler; | |||
import org.apache.ibatis.session.RowBounds; | |||
import org.sonar.db.Pagination; | |||
public interface ComponentMapper { | |||
@CheckForNull | |||
@@ -54,7 +54,7 @@ public interface ComponentMapper { | |||
List<ComponentDto> selectComponentsByQualifiers(@Param("qualifiers") Collection<String> qualifiers); | |||
List<ComponentDto> selectByQuery(@Param("query") ComponentQuery query, RowBounds rowBounds); | |||
List<ComponentDto> selectByQuery(@Param("query") ComponentQuery query, @Param("pagination") Pagination pagination); | |||
int countByQuery(@Param("query") ComponentQuery query); | |||
@@ -38,8 +38,6 @@ public class ComponentQuery { | |||
private final Set<String> componentUuids; | |||
private final Set<String> componentKeys; | |||
private final Long analyzedBefore; | |||
private final Long anyBranchAnalyzedBefore; | |||
private final Long anyBranchAnalyzedAfter; | |||
private final Long allBranchesAnalyzedBefore; | |||
private final Date createdAfter; | |||
private final boolean onProvisionedOnly; | |||
@@ -52,8 +50,6 @@ public class ComponentQuery { | |||
this.componentKeys = builder.componentKeys; | |||
this.isPrivate = builder.isPrivate; | |||
this.analyzedBefore = builder.analyzedBefore; | |||
this.anyBranchAnalyzedBefore = builder.anyBranchAnalyzedBefore; | |||
this.anyBranchAnalyzedAfter = builder.anyBranchAnalyzedAfter; | |||
this.allBranchesAnalyzedBefore = builder.allBranchesAnalyzedBefore; | |||
this.createdAfter = builder.createdAfter; | |||
this.onProvisionedOnly = builder.onProvisionedOnly; | |||
@@ -103,16 +99,6 @@ public class ComponentQuery { | |||
return analyzedBefore; | |||
} | |||
@CheckForNull | |||
public Long getAnyBranchAnalyzedBefore() { | |||
return anyBranchAnalyzedBefore; | |||
} | |||
@CheckForNull | |||
public Long getAnyBranchAnalyzedAfter() { | |||
return anyBranchAnalyzedAfter; | |||
} | |||
@CheckForNull | |||
public Long getAllBranchesAnalyzedBefore() { | |||
return allBranchesAnalyzedBefore; | |||
@@ -144,8 +130,6 @@ public class ComponentQuery { | |||
private Set<String> componentUuids; | |||
private Set<String> componentKeys; | |||
private Long analyzedBefore; | |||
private Long anyBranchAnalyzedBefore; | |||
private Long anyBranchAnalyzedAfter; | |||
private Long allBranchesAnalyzedBefore; | |||
private Date createdAfter; | |||
private boolean onProvisionedOnly = false; | |||
@@ -193,21 +177,6 @@ public class ComponentQuery { | |||
return this; | |||
} | |||
/** | |||
* Filter on date of last analysis. On projects, all branches and pull requests are taken into | |||
* account. For example the analysis of a branch is included in the filter | |||
* even if the main branch has never been analyzed. | |||
*/ | |||
public Builder setAnyBranchAnalyzedAfter(@Nullable Long l) { | |||
this.anyBranchAnalyzedAfter = l; | |||
return this; | |||
} | |||
public Builder setAnyBranchAnalyzedBefore(@Nullable Long l) { | |||
this.anyBranchAnalyzedBefore = l; | |||
return this; | |||
} | |||
public Builder setCreatedAfter(@Nullable Date l) { | |||
this.createdAfter = l; | |||
return this; |
@@ -210,19 +210,34 @@ | |||
</select> | |||
<select id="selectByQuery" resultType="Component"> | |||
<include refid="mainBranchAndPortfoliosWith"/> | |||
select | |||
<include refid="componentColumns"/> | |||
<include refid="sqlSelectByQuery"/> | |||
ORDER BY LOWER(p.name), p.name, p.created_at | |||
offset (#{pagination.startRowNumber,jdbcType=INTEGER}-1) rows fetch next #{pagination.pageSize,jdbcType=INTEGER} rows only | |||
</select> | |||
<select id="countByQuery" resultType="int"> | |||
<include refid="mainBranchAndPortfoliosWith"/> | |||
select count(p.uuid) | |||
<include refid="sqlSelectByQuery"/> | |||
</select> | |||
<sql id="mainBranchAndPortfoliosWith"> | |||
WITH main_branch_and_portfolios AS (SELECT p.uuid as projectUuid, p.kee as kee, p.uuid | |||
from portfolios p | |||
UNION | |||
SELECT pb.project_uuid as projectUuid, p.kee as kee, pb.uuid | |||
FROM project_branches pb | |||
INNER JOIN projects p on pb.project_uuid = p.uuid | |||
where pb.is_main = ${_true} | |||
) | |||
</sql> | |||
<sql id="sqlSelectByQuery"> | |||
from components p | |||
from main_branch_and_portfolios mbp | |||
INNER JOIN components p on mbp.kee = p.kee and p.branch_uuid = mbp.uuid | |||
<if test="query.analyzedBefore!=null"> | |||
inner join snapshots sa on sa.root_component_uuid=p.uuid | |||
and sa.status='P' and sa.islast=${_true} and sa.created_at < #{query.analyzedBefore,jdbcType=BIGINT} | |||
@@ -230,8 +245,6 @@ | |||
left join project_branches pb on pb.uuid = p.branch_uuid | |||
where | |||
p.enabled=${_true} | |||
AND <include refid="mainBranchOrPortfolio"/> | |||
AND p.copy_component_uuid is null | |||
<if test="query.qualifiers!=null"> | |||
and p.qualifier in | |||
<foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=","> | |||
@@ -280,48 +293,6 @@ | |||
where pb2.project_uuid = pb.project_uuid | |||
) | |||
</if> | |||
<if test="query.anyBranchAnalyzedAfter != null"> | |||
and ( | |||
exists( | |||
-- branches of projects and applications | |||
select 1 from snapshots s | |||
inner join project_branches pb2 on s.root_component_uuid = pb2.uuid | |||
where pb2.project_uuid = pb.project_uuid | |||
and s.status='P' | |||
and s.islast = ${_true} | |||
and s.created_at >= #{query.anyBranchAnalyzedAfter,jdbcType=BIGINT} | |||
) | |||
or exists ( | |||
-- portfolios | |||
select 1 from snapshots s | |||
where s.root_component_uuid = p.uuid | |||
and s.status='P' | |||
and s.islast = ${_true} | |||
and s.created_at >= #{query.anyBranchAnalyzedAfter,jdbcType=BIGINT} | |||
) | |||
) | |||
</if> | |||
<if test="query.anyBranchAnalyzedBefore != null"> | |||
and ( | |||
exists( | |||
-- branches of projects and applications | |||
select 1 from snapshots s | |||
inner join project_branches pb2 on s.root_component_uuid = pb2.uuid | |||
where pb2.project_uuid = pb.project_uuid | |||
and s.status='P' | |||
and s.islast = ${_true} | |||
and s.created_at < #{query.anyBranchAnalyzedBefore,jdbcType=BIGINT} | |||
) | |||
or exists ( | |||
-- portfolios | |||
select 1 from snapshots s | |||
where s.root_component_uuid = p.uuid | |||
and s.status='P' | |||
and s.islast = ${_true} | |||
and s.created_at < #{query.anyBranchAnalyzedBefore,jdbcType=BIGINT} | |||
) | |||
) | |||
</if> | |||
<if test="query.allBranchesAnalyzedBefore != null"> | |||
and | |||
( |
@@ -54,38 +54,54 @@ | |||
and coalesce(pb.project_uuid, p.branch_uuid) = #{projectUuid,jdbcType=VARCHAR} | |||
</select> | |||
<select id="selectLastAnalysisDateByProjectUuids" resultType="org.sonar.db.component.ProjectLastAnalysisDateDto"> | |||
<select id="selectLastAnalysisDateByProjectUuids" resultType="org.sonar.db.component.ProjectLastAnalysisDateDto"> | |||
select | |||
result_with_duplicates.project_uuid as project_uuid , | |||
result_with_duplicates.project_uuid as project_uuid, | |||
max(result_with_duplicates.last_analysis_date) as last_analysis_date | |||
from | |||
FROM | |||
( | |||
select | |||
coalesce(pb.project_uuid, c.branch_uuid) as project_uuid, | |||
s.created_at as last_analysis_date | |||
from | |||
snapshots s | |||
inner join components c on s.root_component_uuid = c.branch_uuid | |||
left join project_branches pb on pb.uuid = c.branch_uuid | |||
where | |||
s.islast = ${_true} | |||
and ( | |||
<!-- case of an analysis of a project or app, with entries in project_branches --> | |||
(pb.uuid is not null and pb.project_uuid in | |||
<foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> | |||
#{projectUuid,jdbcType=VARCHAR} | |||
</foreach>) | |||
or | |||
<!-- case of an analysis of a portfolio, where there are no branches --> | |||
(pb.uuid is null and c.branch_uuid in | |||
<foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> | |||
#{projectUuid,jdbcType=VARCHAR} | |||
</foreach>) | |||
) | |||
<!-- cases of an analysis of a project or app, with entries in project_branches --> | |||
SELECT project_uuid, last_analysis_date | |||
FROM ( | |||
SELECT | |||
COALESCE(pb.project_uuid, c.branch_uuid) AS project_uuid, | |||
s.created_at AS last_analysis_date | |||
FROM snapshots s | |||
INNER JOIN components c ON s.root_component_uuid = c.branch_uuid | |||
LEFT JOIN project_branches pb ON pb.uuid = c.branch_uuid | |||
WHERE | |||
s.islast = ${_true} | |||
AND pb.uuid IS NOT NULL | |||
AND pb.project_uuid IN | |||
<foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> | |||
#{projectUuid,jdbcType=VARCHAR} | |||
</foreach> | |||
) project_case | |||
UNION | |||
<!-- cases of an analysis of a portfolio, where there are no branches --> | |||
SELECT project_uuid, last_analysis_date | |||
FROM ( | |||
SELECT | |||
COALESCE(pb.project_uuid, c.branch_uuid) AS project_uuid, | |||
s.created_at AS last_analysis_date | |||
FROM snapshots s | |||
INNER JOIN components c ON s.root_component_uuid = c.branch_uuid | |||
LEFT JOIN project_branches pb ON pb.uuid = c.branch_uuid | |||
WHERE | |||
s.islast = ${_true} | |||
AND pb.branch_type IS NULL | |||
AND pb.uuid IS NULL | |||
AND c.branch_uuid IN | |||
<foreach collection="projectUuids" item="projectUuid" separator="," open="(" close=")"> | |||
#{projectUuid,jdbcType=VARCHAR} | |||
</foreach> | |||
) portfolio_case | |||
) result_with_duplicates | |||
group by | |||
result_with_duplicates.project_uuid | |||
GROUP BY project_uuid | |||
</select> | |||
<select id="selectLastSnapshotByRootComponentUuid" resultType="Snapshot"> | |||
select <include refid="snapshotColumns" /> | |||
from snapshots s |
@@ -690,6 +690,7 @@ CREATE TABLE "PROJECT_BRANCHES"( | |||
); | |||
ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID"); | |||
CREATE UNIQUE INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE" NULLS FIRST, "PROJECT_UUID" NULLS FIRST, "KEE" NULLS FIRST); | |||
CREATE INDEX "PROJECT_BRANCHES_PROJECT_UUID" ON "PROJECT_BRANCHES"("PROJECT_UUID" NULLS FIRST); | |||
CREATE TABLE "PROJECT_LINKS"( | |||
"UUID" CHARACTER VARYING(40) NOT NULL, |
@@ -36,16 +36,14 @@ public class ComponentQueryTest { | |||
public void build_query() { | |||
ComponentQuery underTest = ComponentQuery.builder() | |||
.setNameOrKeyQuery("key") | |||
.setAnyBranchAnalyzedBefore(100L) | |||
.setAnyBranchAnalyzedAfter(200L) | |||
.setAllBranchesAnalyzedBefore(100L) | |||
.setCreatedAfter(new Date(300L)) | |||
.setQualifiers(PROJECT) | |||
.build(); | |||
assertThat(underTest.getNameOrKeyQuery()).isEqualTo("key"); | |||
assertThat(underTest.getQualifiers()).containsOnly(PROJECT); | |||
assertThat(underTest.getAnyBranchAnalyzedBefore()).isEqualTo(100L); | |||
assertThat(underTest.getAnyBranchAnalyzedAfter()).isEqualTo(200L); | |||
assertThat(underTest.getAllBranchesAnalyzedBefore()).isEqualTo(100L); | |||
assertThat(underTest.getCreatedAfter().getTime()).isEqualTo(300L); | |||
assertThat(underTest.isOnProvisionedOnly()).isFalse(); | |||
assertThat(underTest.isPartialMatchOnKey()).isFalse(); |
@@ -0,0 +1,33 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform.db.migration.version.v102; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.CreateIndexOnColumn; | |||
class CreateIndexProjectUuidInProjectBranches extends CreateIndexOnColumn { | |||
private static final String TABLE_NAME = "project_branches"; | |||
private static final String COLUMN_NAME = "project_uuid"; | |||
public CreateIndexProjectUuidInProjectBranches(Database db) { | |||
super(db, TABLE_NAME, COLUMN_NAME, false); | |||
} | |||
} |
@@ -25,7 +25,6 @@ import org.sonar.server.platform.db.migration.version.DbVersion; | |||
// ignoring bad number formatting, as it's indented that we align the migration numbers to SQ versions | |||
@SuppressWarnings("java:S3937") | |||
public class DbVersion102 implements DbVersion { | |||
/** | |||
* We use the start of the 10.X cycle as an opportunity to align migration numbers with the SQ version number. | |||
* Please follow this pattern: | |||
@@ -98,6 +97,7 @@ public class DbVersion102 implements DbVersion { | |||
.add(10_2_043, "Create 'previous_non_compliant_value' in 'new_code_periods' table", CreatePreviousNonCompliantValueInNewCodePeriods.class) | |||
.add(10_2_044, "Update column 'value' and populate column 'previous_non_compliant_value' in 'new_code_periods' table", | |||
UpdateValueAndPopulatePreviousNonCompliantValueInNewCodePeriods.class) | |||
.add(10_2_045, "Alter 'project_uuid' in 'user_dismissed_messages' - make it nullable", MakeProjectUuidNullableInUserDismissedMessages.class); | |||
.add(10_2_045, "Alter 'project_uuid' in 'user_dismissed_messages' - make it nullable", MakeProjectUuidNullableInUserDismissedMessages.class) | |||
.add(10_2_046, "Create index 'project_branches_project_uuid' in 'project_branches' table", CreateIndexProjectUuidInProjectBranches.class); | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2023 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
* License as published by the Free Software Foundation; either | |||
* version 3 of the License, or (at your option) any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
* Lesser General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Lesser General Public License | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
package org.sonar.server.platform.db.migration.version.v102; | |||
import java.sql.SQLException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.db.CoreDbTester; | |||
public class CreateIndexProjectUuidInProjectBranchesTest { | |||
@Rule | |||
public final CoreDbTester db = CoreDbTester.createForSchema(CreateIndexProjectUuidInProjectBranchesTest.class, "schema.sql"); | |||
private final CreateIndexProjectUuidInProjectBranches createIndex = new CreateIndexProjectUuidInProjectBranches(db.database()); | |||
@Test | |||
public void migration_should_create_index() throws SQLException { | |||
db.assertIndexDoesNotExist("project_branches", "project_branches_project_uuid"); | |||
createIndex.execute(); | |||
db.assertIndex("project_branches", "project_branches_project_uuid", "project_uuid"); | |||
} | |||
@Test | |||
public void migration_should_be_reentrant() throws SQLException { | |||
createIndex.execute(); | |||
createIndex.execute(); | |||
db.assertIndex("project_branches", "project_branches_project_uuid", "project_uuid"); | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
CREATE TABLE "PROJECT_BRANCHES"( | |||
"UUID" CHARACTER VARYING(50) NOT NULL, | |||
"PROJECT_UUID" CHARACTER VARYING(50) NOT NULL, | |||
"KEE" CHARACTER VARYING(255) NOT NULL, | |||
"BRANCH_TYPE" CHARACTER VARYING(12) NOT NULL, | |||
"MERGE_BRANCH_UUID" CHARACTER VARYING(50), | |||
"PULL_REQUEST_BINARY" BINARY LARGE OBJECT, | |||
"MANUAL_BASELINE_ANALYSIS_UUID" CHARACTER VARYING(40), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL, | |||
"EXCLUDE_FROM_PURGE" BOOLEAN DEFAULT FALSE NOT NULL, | |||
"NEED_ISSUE_SYNC" BOOLEAN NOT NULL | |||
); | |||
ALTER TABLE "PROJECT_BRANCHES" ADD CONSTRAINT "PK_PROJECT_BRANCHES" PRIMARY KEY("UUID"); | |||
CREATE UNIQUE INDEX "UNIQ_PROJECT_BRANCHES" ON "PROJECT_BRANCHES"("BRANCH_TYPE" NULLS FIRST, "PROJECT_UUID" NULLS FIRST, "KEE" NULLS FIRST); |
@@ -298,7 +298,7 @@ public class ActivityAction implements CeWsAction { | |||
.setNameOrKeyQuery(componentQuery) | |||
.setQualifiers(POSSIBLE_QUALIFIERS) | |||
.build(); | |||
List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, 0, CeTaskQuery.MAX_COMPONENT_UUIDS); | |||
List<ComponentDto> componentDtos = dbClient.componentDao().selectByQuery(dbSession, componentDtoQuery, forPage(1).andSize(CeTaskQuery.MAX_COMPONENT_UUIDS)); | |||
return dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet())); | |||
} | |||
@@ -37,6 +37,7 @@ import org.sonar.core.i18n.I18n; | |||
import org.sonar.db.DatabaseUtils; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.ce.CeTaskQuery; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentQuery; | |||
import org.sonar.db.entity.EntityDto; | |||
@@ -54,6 +55,7 @@ import static java.util.Collections.singleton; | |||
import static java.util.Objects.requireNonNull; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; | |||
import static org.sonar.db.Pagination.forPage; | |||
import static org.sonar.server.permission.PermissionPrivilegeChecker.checkGlobalAdmin; | |||
import static org.sonar.server.permission.ws.template.WsTemplateRef.newTemplateRef; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
@@ -156,7 +158,7 @@ public class BulkApplyTemplateAction implements PermissionsWsAction { | |||
checkGlobalAdmin(userSession); | |||
ComponentQuery componentQuery = buildDbQuery(request); | |||
List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, componentQuery, 0, Integer.MAX_VALUE); | |||
List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, componentQuery, forPage(1).andSize(CeTaskQuery.MAX_COMPONENT_UUIDS)); | |||
Set<String> entityUuids = components.stream() | |||
.map(ComponentDto::getKey) |
@@ -57,6 +57,7 @@ import static java.util.stream.Collectors.toSet; | |||
import static org.sonar.api.resources.Qualifiers.APP; | |||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||
import static org.sonar.api.resources.Qualifiers.VIEW; | |||
import static org.sonar.db.Pagination.forPage; | |||
import static org.sonar.server.project.ws.SearchAction.buildDbQuery; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002; | |||
@@ -150,7 +151,7 @@ public class BulkDeleteAction implements ProjectsWsAction { | |||
checkIfAnalyzedBeforeIsFutureDate(searchRequest); | |||
ComponentQuery query = buildDbQuery(searchRequest); | |||
Set<ComponentDto> componentDtos = new HashSet<>(dbClient.componentDao().selectByQuery(dbSession, query, 0, Integer.MAX_VALUE)); | |||
Set<ComponentDto> componentDtos = new HashSet<>(dbClient.componentDao().selectByQuery(dbSession, query, forPage(1).andSize(Integer.MAX_VALUE))); | |||
List<EntityDto> entities = dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet())); | |||
Set<String> entityUuids = entities.stream().map(EntityDto::getUuid).collect(toSet()); | |||
Map<String, String> mainBranchUuidByEntityUuid = dbClient.branchDao().selectMainBranchesByProjectUuids(dbSession, entityUuids).stream() |
@@ -53,6 +53,7 @@ import static org.sonar.api.resources.Qualifiers.PROJECT; | |||
import static org.sonar.api.resources.Qualifiers.VIEW; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; | |||
import static org.sonar.db.Pagination.forPage; | |||
import static org.sonar.server.project.Visibility.PRIVATE; | |||
import static org.sonar.server.project.Visibility.PUBLIC; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
@@ -167,7 +168,7 @@ public class SearchAction implements ProjectsWsAction { | |||
ComponentQuery query = buildDbQuery(request); | |||
Paging paging = buildPaging(dbSession, request, query); | |||
List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, query, paging.offset(), paging.pageSize()); | |||
List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, query, forPage(paging.pageIndex()).andSize(paging.pageSize())); | |||
Set<String> componentUuids = components.stream().map(ComponentDto::uuid).collect(Collectors.toSet()); | |||
List<BranchDto> branchDtos = dbClient.branchDao().selectByUuids(dbSession, componentUuids); | |||
Map<String, String> componentUuidToProjectUuid = branchDtos.stream().collect(Collectors.toMap(BranchDto::getUuid,BranchDto::getProjectUuid)); |