@@ -27,6 +27,7 @@ import java.util.List; | |||
import java.util.Optional; | |||
import org.sonar.db.Dao; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.version.Select; | |||
import static org.sonar.db.DatabaseUtils.executeLargeInputs; | |||
@@ -37,6 +38,19 @@ public class MeasureDao implements Dao { | |||
return Optional.ofNullable(Iterables.getOnlyElement(measures, null)); | |||
} | |||
/** | |||
* Selects the measures of either the last analysis (when {@link MeasureQuery#analysisUuid} is {@code null}) or of the | |||
* specified analysis (given by {@link MeasureQuery#analysisUuid}) for the component UUIDs specified in | |||
* {@link MeasureQuery#componentUuids}. | |||
* <p> | |||
* In addition, this method returns measures which are not associated to any developer, unless one is specified in | |||
* {@link MeasureQuery#personId}. | |||
* </p> | |||
* <p> | |||
* Returned measure can optionally be filtered metric (either by specifying {@link MeasureQuery#metricIds} | |||
* or {@link MeasureQuery#metricKeys}). | |||
* </p> | |||
*/ | |||
public List<MeasureDto> selectByQuery(DbSession dbSession, MeasureQuery query) { | |||
if (query.returnsEmpty()) { | |||
return Collections.emptyList(); | |||
@@ -50,6 +64,19 @@ public class MeasureDao implements Dao { | |||
}); | |||
} | |||
public List<MeasureDto> selectByQuery(DbSession dbSession, MeasureQuery query, Select.RowHandler rowHandler) { | |||
if (query.returnsEmpty()) { | |||
return Collections.emptyList(); | |||
} | |||
if (query.getComponentUuids() == null) { | |||
return mapper(dbSession).selectByQuery(query, rowHandler); | |||
} | |||
return executeLargeInputs(query.getComponentUuids(), componentUuids -> { | |||
MeasureQuery pageQuery = MeasureQuery.copyWithSubsetOfComponentUuids(query, componentUuids); | |||
return mapper(dbSession).selectByQuery(pageQuery, rowHandler); | |||
}); | |||
} | |||
public List<PastMeasureDto> selectPastMeasures(DbSession dbSession, | |||
String componentUuid, | |||
String analysisUuid, |
@@ -21,11 +21,14 @@ package org.sonar.db.measure; | |||
import java.util.List; | |||
import org.apache.ibatis.annotations.Param; | |||
import org.sonar.db.version.Select; | |||
public interface MeasureMapper { | |||
List<MeasureDto> selectByQuery(@Param("query") MeasureQuery query); | |||
List<MeasureDto> selectByQuery(@Param("query") MeasureQuery query, Select.RowHandler rowHandler); | |||
List<PastMeasureDto> selectPastMeasures(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid, @Param("metricIds") List<Integer> metricIds); | |||
void insert(MeasureDto measureDto); |
@@ -21,56 +21,51 @@ package org.sonar.db.measure; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static java.util.Collections.singleton; | |||
import static java.util.Collections.singletonList; | |||
import static java.util.Objects.requireNonNull; | |||
public class MeasureQuery { | |||
@CheckForNull | |||
private final String analysisUuid; | |||
private final List<String> componentUuids; | |||
@CheckForNull | |||
private final Collection<Integer> metricIds; | |||
@CheckForNull | |||
private final Collection<String> metricKeys; | |||
@CheckForNull | |||
private final Long personId; | |||
private MeasureQuery(Builder builder) { | |||
this(builder.componentUuids, builder.analysisUuid, builder.metricIds, builder.metricKeys, builder.personId); | |||
this(builder.analysisUuid, builder.componentUuids, builder.metricIds, builder.metricKeys, builder.personId); | |||
} | |||
private MeasureQuery(List<String> componentUuids, | |||
@Nullable String analysisUuid, | |||
private MeasureQuery(@Nullable String analysisUuid, | |||
List<String> componentUuids, | |||
@Nullable Collection<Integer> metricIds, | |||
@Nullable Collection<String> metricKeys, | |||
@Nullable Long personId) { | |||
checkState(componentUuids != null, "Component UUIDs must be set"); | |||
requireNonNull(componentUuids, "Component UUIDs must be set"); | |||
checkState(metricIds == null || metricKeys == null, "Metric IDs and keys must not be set both"); | |||
this.componentUuids = componentUuids; | |||
this.analysisUuid = analysisUuid; | |||
this.componentUuids = componentUuids; | |||
this.metricIds = metricIds; | |||
this.metricKeys = metricKeys; | |||
this.personId = personId; | |||
} | |||
public List<String> getComponentUuids() { | |||
return componentUuids; | |||
} | |||
@CheckForNull | |||
public String getAnalysisUuid() { | |||
return analysisUuid; | |||
} | |||
public List<String> getComponentUuids() { | |||
return componentUuids; | |||
} | |||
@CheckForNull | |||
public Collection<Integer> getMetricIds() { | |||
return metricIds; | |||
@@ -92,17 +87,38 @@ public class MeasureQuery { | |||
|| (metricKeys != null && metricKeys.isEmpty()); | |||
} | |||
@Override | |||
public boolean equals(@Nullable Object o) { | |||
if (this == o) { | |||
return true; | |||
} | |||
if (o == null || getClass() != o.getClass()) { | |||
return false; | |||
} | |||
MeasureQuery that = (MeasureQuery) o; | |||
return Objects.equals(analysisUuid, that.analysisUuid) && | |||
Objects.equals(componentUuids, that.componentUuids) && | |||
Objects.equals(metricIds, that.metricIds) && | |||
Objects.equals(metricKeys, that.metricKeys) && | |||
Objects.equals(personId, that.personId); | |||
} | |||
@Override | |||
public int hashCode() { | |||
return Objects.hash(analysisUuid, componentUuids, metricIds, metricKeys, personId); | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
static MeasureQuery copyWithSubsetOfComponentUuids(MeasureQuery query, List<String> componentUuids) { | |||
return new MeasureQuery(componentUuids, query.analysisUuid, query.metricIds, query.metricKeys, query.personId); | |||
return new MeasureQuery(query.analysisUuid, componentUuids, query.metricIds, query.metricKeys, query.personId); | |||
} | |||
public static final class Builder { | |||
private List<String> componentUuids; | |||
private String analysisUuid; | |||
private List<String> componentUuids; | |||
private Collection<Integer> metricIds; | |||
private Collection<String> metricKeys; | |||
private Long personId; | |||
@@ -111,6 +127,11 @@ public class MeasureQuery { | |||
// see MeasureQuery#builder() | |||
} | |||
public Builder setAnalysisUuid(String analysisUuid) { | |||
this.analysisUuid = analysisUuid; | |||
return this; | |||
} | |||
public Builder setComponentUuids(List<String> componentUuids) { | |||
this.componentUuids = componentUuids; | |||
return this; | |||
@@ -121,11 +142,6 @@ public class MeasureQuery { | |||
return this; | |||
} | |||
public Builder setAnalysisUuid(String s) { | |||
this.analysisUuid = s; | |||
return this; | |||
} | |||
/** | |||
* All the measures are returned if parameter is {@code null}. | |||
*/ |
@@ -26,37 +26,43 @@ | |||
</sql> | |||
<select id="selectByQuery" parameterType="map" resultType="Measure"> | |||
select <include refid="measureColumns"/> | |||
from project_measures pm | |||
inner join snapshots analysis on analysis.uuid = pm.analysis_uuid | |||
select | |||
<include refid="measureColumns"/> | |||
from | |||
project_measures pm | |||
inner join snapshots analysis on analysis.uuid = pm.analysis_uuid | |||
<if test="query.getMetricKeys() != null"> | |||
inner join metrics m on m.id = pm.metric_id | |||
</if> | |||
where | |||
pm.component_uuid in | |||
<foreach item="componentUuid" collection="query.getComponentUuids()" open="(" separator="," close=")"> | |||
#{componentUuid} | |||
</foreach> | |||
<choose> | |||
<when test="query.getAnalysisUuid() != null"> | |||
and analysis.uuid in <foreach item="analysisUuid" collection="query.getAnalysisUuid()" open="(" separator="," close=")">#{analysisUuid}</foreach> | |||
</when> | |||
<otherwise> | |||
and analysis.islast=${_true} | |||
</otherwise> | |||
</choose> | |||
<if test="query.getMetricIds() != null"> | |||
and pm.metric_id in | |||
<foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId}</foreach> | |||
</if> | |||
<if test="query.getMetricKeys() != null"> | |||
and pm.metric_id in (select id from metrics where name in <foreach item="metricKey" collection="query.getMetricKeys()" open="(" separator="," close=")">#{metricKey}</foreach>) | |||
</if> | |||
<choose> | |||
<when test="query.getPersonId() != null"> | |||
and person_id = #{query.personId} | |||
</when> | |||
<otherwise> | |||
and person_id is null | |||
</otherwise> | |||
</choose> | |||
<if test="query.getAnalysisUuid() == null"> | |||
analysis.islast=${_true} | |||
</if> | |||
<if test="query.getAnalysisUuid() != null"> | |||
analysis.uuid = #{query.analysisUuid} | |||
</if> | |||
and pm.component_uuid in | |||
<foreach item="componentUuid" collection="query.getComponentUuids()" open="(" separator="," close=")"> | |||
#{componentUuid} | |||
</foreach> | |||
<if test="query.getMetricIds() != null"> | |||
and pm.metric_id in | |||
<foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId}</foreach> | |||
</if> | |||
<if test="query.getMetricKeys() != null"> | |||
and m.name in | |||
<foreach item="metricKey" collection="query.getMetricKeys()" open="(" separator="," close=")"> | |||
#{metricKey} | |||
</foreach> | |||
</if> | |||
<choose> | |||
<when test="query.getPersonId() != null"> | |||
and person_id = #{query.personId} | |||
</when> | |||
<otherwise> | |||
and person_id is null | |||
</otherwise> | |||
</choose> | |||
</select> | |||
<select id="selectPastMeasures" parameterType="map" resultType="org.sonar.db.measure.PastMeasureDto"> |
@@ -38,6 +38,8 @@ public class MeasureDaoTest { | |||
private static final int COMPLEXITY_METRIC_ID = 11; | |||
private static final int NCLOC_METRIC_ID = 12; | |||
private static final long A_PERSON_ID = 444L; | |||
public static final String LAST_ANALYSIS_UUID = "A1"; | |||
public static final String OTHER_ANALYSIS_UUID = "A2"; | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@@ -49,10 +51,10 @@ public class MeasureDaoTest { | |||
@Test | |||
public void test_inserted_and_selected_columns() { | |||
insertAnalysis("A1", true); | |||
insertAnalysis(LAST_ANALYSIS_UUID, true); | |||
MeasureDto inserted = new MeasureDto() | |||
.setAnalysisUuid("A1") | |||
.setAnalysisUuid(LAST_ANALYSIS_UUID) | |||
.setMetricId(2) | |||
.setDeveloperId(3L) | |||
.setComponentUuid("C4") | |||
@@ -90,47 +92,76 @@ public class MeasureDaoTest { | |||
@Test | |||
public void selectByQuery() { | |||
insertAnalysis("A1", false); | |||
insertAnalysis("A2", true); | |||
insertAnalysis(LAST_ANALYSIS_UUID, true); | |||
insertAnalysis(OTHER_ANALYSIS_UUID, false); | |||
// component C1 | |||
insertMeasure("M1", "A1", "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M2", "A2", "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M3", "A2", "C1", COVERAGE_METRIC_ID); | |||
insertMeasureOnPerson("M4", "A2", "C1", NCLOC_METRIC_ID, A_PERSON_ID); | |||
insertMeasure("M1", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M3", LAST_ANALYSIS_UUID, "C1", COVERAGE_METRIC_ID); | |||
insertMeasureOnPerson("M4", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, A_PERSON_ID); | |||
insertMeasureOnPerson("M5", OTHER_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID, 123L); | |||
// component C2 | |||
insertMeasure("M5", "A2", "C2", NCLOC_METRIC_ID); | |||
insertMeasure("M6", LAST_ANALYSIS_UUID, "C2", NCLOC_METRIC_ID); | |||
db.commit(); | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuids(emptyList())); | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("MISSING_COMPONENT")); | |||
// all measures of component C1 | |||
// all measures of component C1 of last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1"), "M2", "M3"); | |||
// all measures of component C1 of non last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1"); | |||
// all measures of component C1 of last analysis by UUID | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3"); | |||
// ncloc measure of component C1 | |||
// ncloc measure of component C1 of last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(NCLOC_METRIC_ID), "M2"); | |||
// ncloc measure of component C1 of non last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M1"); | |||
// ncloc measure of component C1 of last analysis by UUID | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(NCLOC_METRIC_ID), "M2"); | |||
// multiple measures of component C1 | |||
// multiple measures of component C1 of last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3"); | |||
// multiple measures of component C1 of non last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M1"); | |||
// multiple measures of component C1 of last analysis by UUID | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricIds(asList(NCLOC_METRIC_ID, COVERAGE_METRIC_ID)), "M2", "M3"); | |||
// missing measure of component C1 | |||
// missing measure of component C1 of last analysis | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setMetricId(COMPLEXITY_METRIC_ID)); | |||
// ncloc measures of components C1, C2 and C3 (which does not exist) | |||
verifyMeasures(MeasureQuery.builder().setComponentUuids(asList("C1", "C2", "C3")), "M2", "M3", "M5"); | |||
// measures of missing developer of component C1 | |||
// missing measure of component C1 of non last analysis | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID)); | |||
// missing measure of component C1 of last analysis by UUID | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setMetricId(COMPLEXITY_METRIC_ID)); | |||
// ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuids(asList("C1", "C2", "C3")), "M2", "M3", "M6"); | |||
// ncloc measures of components C1, C2 and C3 (which does not exist) of non last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuids(asList("C1", "C2", "C3")).setAnalysisUuid(OTHER_ANALYSIS_UUID), "M1"); | |||
// ncloc measures of components C1, C2 and C3 (which does not exist) of last analysis by UUID | |||
verifyMeasures(MeasureQuery.builder().setComponentUuids(asList("C1", "C2", "C3")).setAnalysisUuid(LAST_ANALYSIS_UUID), "M2", "M3", "M6"); | |||
// measures of missing developer of component C1 of last analysis | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(123L)); | |||
// measures of missing developer of component C1 of non last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(123L), "M5"); | |||
// measures of missing developer of component C1 of last analysis by UUID | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(123L)); | |||
// developer measures of component C1 | |||
// developer measures of component C1 of last analysis | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setPersonId(A_PERSON_ID), "M4"); | |||
// developer measures of component C1 of non last analysis | |||
verifyZeroMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(OTHER_ANALYSIS_UUID).setPersonId(A_PERSON_ID)); | |||
// developer measures of component C1 of last analysis by UUID | |||
verifyMeasures(MeasureQuery.builder().setComponentUuid("C1").setAnalysisUuid(LAST_ANALYSIS_UUID).setPersonId(A_PERSON_ID), "M4"); | |||
} | |||
@Test | |||
public void selectSingle() { | |||
insertAnalysis("A1", true); | |||
insertMeasure("M1", "A1", "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M2", "A1", "C1", COMPLEXITY_METRIC_ID); | |||
insertAnalysis(LAST_ANALYSIS_UUID, true); | |||
insertMeasure("M1", LAST_ANALYSIS_UUID, "C1", NCLOC_METRIC_ID); | |||
insertMeasure("M2", LAST_ANALYSIS_UUID, "C1", COMPLEXITY_METRIC_ID); | |||
db.commit(); | |||
assertThat(selectSingle(MeasureQuery.builder().setComponentUuids(emptyList()))).isNotPresent(); |