aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db/src/main
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2016-06-22 16:37:21 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2016-06-27 18:19:46 +0200
commitd20ca1857a018a6b255a6eae7e5eeb71b31c22ab (patch)
tree9b6d91f085b23546e225a48f4d541870fe81bd19 /sonar-db/src/main
parent4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7 (diff)
downloadsonarqube-d20ca1857a018a6b255a6eae7e5eeb71b31c22ab.tar.gz
sonarqube-d20ca1857a018a6b255a6eae7e5eeb71b31c22ab.zip
SONAR-7800 add column PROJECTS.UUID_PATH
Diffstat (limited to 'sonar-db/src/main')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentDao.java101
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentDto.java107
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentMapper.java17
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentTreeQuery.java46
-rw-r--r--sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java14
-rw-r--r--sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java3
-rw-r--r--sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java95
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java5
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java10
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/Select.java2
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java1
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidPathColumnToProjects.java45
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidPathColumnNotNullOnProjects.java45
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidPathColumnOnProjects.java156
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/component/ComponentMapper.xml170
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml43
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql3
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl5
18 files changed, 744 insertions, 124 deletions
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 b531ef282a3..256e03ec2ce 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
@@ -21,7 +21,9 @@ package org.sonar.db.component;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -32,8 +34,10 @@ 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;
@@ -139,22 +143,99 @@ public class ComponentDao implements Dao {
return mapper(session).selectComponentsHavingSameKeyOrderedById(key);
}
- public List<ComponentDtoWithSnapshotId> selectDirectChildren(DbSession dbSession, ComponentTreeQuery componentQuery) {
- RowBounds rowBounds = new RowBounds(offset(componentQuery.getPage(), componentQuery.getPageSize()), componentQuery.getPageSize());
- return mapper(dbSession).selectDirectChildren(componentQuery, rowBounds);
+ /**
+ * Optional parent. It is absent if specified component is root.
+ */
+ public Optional<ComponentDto> selectParent(DbSession dbSession, ComponentDto component) {
+ if (component.isRoot()) {
+ return Optional.absent();
+ }
+ List<String> path = component.getUuidPathAsList();
+ String parentUuid = path.get(path.size() - 1);
+ return Optional.of(mapper(dbSession).selectByUuid(parentUuid));
}
- public List<ComponentDtoWithSnapshotId> selectAllChildren(DbSession dbSession, ComponentTreeQuery componentQuery) {
- RowBounds rowBounds = new RowBounds(offset(componentQuery.getPage(), componentQuery.getPageSize()), componentQuery.getPageSize());
- return mapper(dbSession).selectAllChildren(componentQuery, rowBounds);
+ /**
+ * List of ancestors, ordered from root to parent. The list is empty
+ * if the component is a tree root.
+ */
+ public List<ComponentDto> selectAncestors(DbSession dbSession, ComponentDto component) {
+ if (component.isRoot()) {
+ return Collections.emptyList();
+ }
+ List<String> ancestorUuids = component.getUuidPathAsList();
+ List<ComponentDto> ancestors = selectByUuids(dbSession, ancestorUuids);
+ return Ordering.explicit(ancestorUuids).onResultOf(ComponentDto::uuid).immutableSortedCopy(ancestors);
}
- public int countDirectChildren(DbSession dbSession, ComponentTreeQuery query) {
- return mapper(dbSession).countDirectChildren(query);
+ /**
+ * 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
+ * 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> 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;
+ }
+ ComponentDto component = componentOpt.get();
+ return mapper(dbSession).countDescendants(query, uuidPathForDescendantsQuery(component));
}
- public int countAllChildren(DbSession dbSession, ComponentTreeQuery query) {
- return mapper(dbSession).countAllChildren(query);
+ private static String uuidPathForDescendantsQuery(ComponentDto component) {
+ return DatabaseUtils.buildLikeValue(component.getUuidPath() + component.uuid() + ".", WildcardPosition.AFTER);
}
public ComponentDto selectOrFailByKey(DbSession session, String key) {
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentDto.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentDto.java
index 9df936cbf36..7652c76f6eb 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentDto.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentDto.java
@@ -19,33 +19,88 @@
*/
package org.sonar.db.component;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
import java.util.Date;
+import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.sonar.api.component.Component;
import org.sonar.api.resources.Scopes;
+import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.db.component.ComponentValidator.checkComponentKey;
import static org.sonar.db.component.ComponentValidator.checkComponentName;
public class ComponentDto implements Component {
- public static final String MODULE_UUID_PATH_SEP = ".";
+ public static final String UUID_PATH_SEPARATOR = ".";
+ public static final String UUID_PATH_OF_ROOT = UUID_PATH_SEPARATOR;
+ private static final Splitter UUID_PATH_SPLITTER = Splitter.on(UUID_PATH_SEPARATOR).omitEmptyStrings();
+ /**
+ * ID generated by database. Do not use.
+ */
private Long id;
- private String uuid;
+
+ /**
+ * Non-empty and unique functional key
+ */
private String kee;
- private String scope;
- private String qualifier;
+ /**
+ * Not empty . Max size is 50 (note that effective UUID values are 40 characters with
+ * the current algorithm in use). Obviously it is unique.
+ * It is generated by Compute Engine.
+ */
+ private String uuid;
+
+ /**
+ * Not empty path of ancestor UUIDS, including self. Value is suffixed by a dot in
+ * order to support LIKE conditions when requesting descendants of a component.
+ * Example:
+ * - on root module: UUID="1" UUID_PATH="1."
+ * - on module: UUID="2" UUID_PATH="1.2."
+ * - on directory: UUID="3" UUID_PATH="1.2.3."
+ * - on file: UUID="4" UUID_PATH="1.2.3.4."
+ * - on view: UUID="5" UUID_PATH="5."
+ * - on sub-view: UUID="6" UUID_PATH="5.6."
+ *
+ * @since 6.0
+ */
+ private String uuidPath;
+
+ /**
+ * Non-null UUID of root component. Equals UUID column on root components
+ * Example:
+ * - on root module: UUID="1" PROJECT_UUID="1"
+ * - on module: UUID="2" PROJECT_UUID="1"
+ * - on directory: UUID="3" PROJECT_UUID="1"
+ * - on file: UUID="4" PROJECT_UUID="1"
+ * - on view: UUID="5" PROJECT_UUID="5"
+ * - on sub-view: UUID="6" PROJECT_UUID="5"
+ */
private String projectUuid;
+
+ /**
+ * Badly named, it is not the root !
+ * - on root module: UUID="1" ROOT_UUID="1"
+ * - on modules, whatever depth, value is the root module: UUID="2" ROOT_UUID="1"
+ * - on directory, value is the closed module: UUID="3" ROOT_UUID="2"
+ * - on file, value is the closed module: UUID="4" ROOT_UUID="2"
+ * - on view: UUID="5" ROOT_UUID="5"
+ * - on sub-view: UUID="6" ROOT_UUID="5"
+ * @since 6.0
+ */
+ private String rootUuid;
+
private String moduleUuid;
private String moduleUuidPath;
- private String rootUuid;
private String copyComponentUuid;
private String developerUuid;
-
+ private String scope;
+ private String qualifier;
private String path;
private String deprecatedKey;
private String name;
@@ -75,6 +130,26 @@ public class ComponentDto implements Component {
return this;
}
+ /**
+ * No need to have public visibility as this field
+ * is supposed to be used only internally in SQL requests.
+ */
+ String getUuidPath() {
+ return uuidPath;
+ }
+
+ /**
+ * List of ancestor UUIDs, ordered by depth in tree.
+ */
+ List<String> getUuidPathAsList() {
+ return UUID_PATH_SPLITTER.splitToList(uuidPath);
+ }
+
+ public ComponentDto setUuidPath(String s) {
+ this.uuidPath = s;
+ return this;
+ }
+
@Override
public String key() {
return kee;
@@ -121,6 +196,10 @@ public class ComponentDto implements Component {
return this;
}
+ public boolean isRoot() {
+ return UUID_PATH_OF_ROOT.equals(uuidPath);
+ }
+
/**
* Return the direct module of a component. Will be null on projects
*/
@@ -270,7 +349,6 @@ public class ComponentDto implements Component {
return moduleUuid == null && Scopes.PROJECT.equals(scope);
}
- // FIXME equals/hashCode mean nothing on DTOs, especially when on id
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -280,16 +358,13 @@ public class ComponentDto implements Component {
return false;
}
ComponentDto that = (ComponentDto) o;
- if (id != null ? !id.equals(that.id) : that.id != null) {
- return false;
- }
- return true;
+ return uuid != null ? uuid.equals(that.uuid) : (that.uuid == null);
+
}
- // FIXME equals/hashCode mean nothing on DTOs, especially when on id
@Override
public int hashCode() {
- return id != null ? id.hashCode() : 0;
+ return uuid != null ? uuid.hashCode() : 0;
}
@Override
@@ -297,6 +372,7 @@ public class ComponentDto implements Component {
return new ToStringBuilder(this)
.append("id", id)
.append("uuid", uuid)
+ .append("uuidPath", uuidPath)
.append("kee", kee)
.append("scope", scope)
.append("qualifier", qualifier)
@@ -316,4 +392,9 @@ public class ComponentDto implements Component {
.toString();
}
+ public static String formatUuidPathFromParent(ComponentDto parent) {
+ checkArgument(!Strings.isNullOrEmpty(parent.getUuidPath()));
+ checkArgument(!Strings.isNullOrEmpty(parent.uuid()));
+ return parent.getUuidPath() + parent.uuid() + UUID_PATH_SEPARATOR;
+ }
}
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 bc5e291c5c7..9f69872a2fa 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
@@ -62,20 +62,15 @@ public interface ComponentMapper {
int countByQuery(@Param("query") ComponentQuery query);
- /**
- * Return direct children components
- */
- List<ComponentDtoWithSnapshotId> selectDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery, RowBounds rowBounds);
+ List<ComponentDto> selectAncestors(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike);
- int countDirectChildren(@Param("query") ComponentTreeQuery componentTreeQuery);
+ List<ComponentDto> selectChildren(@Param("query") ComponentTreeQuery query, @Param("baseUuidPath") String baseUuidPath, RowBounds rowBounds);
- /**
- * Return all children components.
- */
- List<ComponentDtoWithSnapshotId> selectAllChildren(@Param("query") ComponentTreeQuery componentTreeQuery,
- 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 countAllChildren(@Param("query") ComponentTreeQuery componentTreeQuery);
+ int countDescendants(@Param("query") ComponentTreeQuery query, @Param("baseUuidPathLike") String baseUuidPathLike);
/**
* 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 d7f1dee8746..5c0cae54a60 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
@@ -19,20 +19,19 @@
*/
package org.sonar.db.component;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
+import java.util.function.Function;
+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.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
+import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static org.sonar.db.DatabaseUtils.buildLikeValue;
import static org.sonar.db.WildcardPosition.AFTER;
@@ -47,8 +46,7 @@ public class ComponentTreeQuery {
private final Integer page;
@CheckForNull
private final Integer pageSize;
- private final SnapshotDto baseSnapshot;
- private final String baseSnapshotPath;
+ private final String baseUuid;
private final String sqlSort;
private final String direction;
@@ -57,8 +55,7 @@ public class ComponentTreeQuery {
this.qualifiers = builder.qualifiers == null ? null : newArrayList(builder.qualifiers);
this.page = builder.page;
this.pageSize = builder.pageSize;
- this.baseSnapshot = builder.baseSnapshot;
- this.baseSnapshotPath = buildBaseSnapshotPath(baseSnapshot);
+ this.baseUuid = builder.baseUuid;
this.direction = builder.asc ? "ASC" : "DESC";
this.sqlSort = sortFieldsToSqlSort(builder.sortFields, direction);
}
@@ -85,12 +82,8 @@ public class ComponentTreeQuery {
return pageSize;
}
- public SnapshotDto getBaseSnapshot() {
- return baseSnapshot;
- }
-
- public String getBaseSnapshotPath() {
- return baseSnapshotPath;
+ public String getBaseUuid() {
+ return baseUuid;
}
public String getSqlSort() {
@@ -106,15 +99,10 @@ public class ComponentTreeQuery {
}
private static String sortFieldsToSqlSort(List<String> sortFields, String direction) {
- List<String> sqlSortFields = from(sortFields)
- .transform(new SortFieldToSqlSortFieldFunction(direction)).toList();
-
- return Joiner.on(", ").join(sqlSortFields);
- }
-
- private static String buildBaseSnapshotPath(SnapshotDto baseSnapshot) {
- String existingSnapshotPath = baseSnapshot.getPath() == null ? "" : baseSnapshot.getPath();
- return buildLikeValue(existingSnapshotPath + baseSnapshot.getId() + ".", WildcardPosition.AFTER);
+ return sortFields
+ .stream()
+ .map(new SortFieldToSqlSortFieldFunction(direction)::apply)
+ .collect(Collectors.joining(", "));
}
public static class Builder {
@@ -126,7 +114,7 @@ public class ComponentTreeQuery {
private Integer page;
@CheckForNull
private Integer pageSize;
- private SnapshotDto baseSnapshot;
+ private String baseUuid;
private List<String> sortFields;
private boolean asc = true;
@@ -135,7 +123,7 @@ public class ComponentTreeQuery {
}
public ComponentTreeQuery build() {
- requireNonNull(baseSnapshot);
+ requireNonNull(baseUuid);
return new ComponentTreeQuery(this);
}
@@ -159,12 +147,12 @@ public class ComponentTreeQuery {
return this;
}
- public Builder setBaseSnapshot(SnapshotDto baseSnapshot) {
- this.baseSnapshot = baseSnapshot;
+ public Builder setBaseUuid(String uuid) {
+ this.baseUuid = uuid;
return this;
}
- public Builder setSortFields(@Nullable List<String> sorts) {
+ public Builder setSortFields(List<String> sorts) {
checkArgument(sorts != null && !sorts.isEmpty());
this.sortFields = sorts;
return this;
@@ -188,7 +176,7 @@ public class ComponentTreeQuery {
@Nonnull
@Override
public String apply(@Nonnull String input) {
- return String.format(PATTERN, input, direction);
+ return format(PATTERN, input, direction);
}
}
}
diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
index 738034f5d69..112808cde61 100644
--- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
@@ -21,6 +21,7 @@ package org.sonar.db.measure;
import com.google.common.collect.Lists;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.annotation.CheckForNull;
@@ -35,6 +36,19 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs;
public class MeasureDao implements Dao {
+ public List<MeasureDto> selectByQuery(DbSession dbSession, MeasureQuery query) {
+ if (query.returnsEmpty()) {
+ return Collections.emptyList();
+ }
+ if (query.getComponentUuids() == null) {
+ return mapper(dbSession).selectByQuery(query);
+ }
+ return executeLargeInputs(query.getComponentUuids(), componentUuids -> {
+ MeasureQuery pageQuery = MeasureQuery.copyWithSubsetOfComponentUuids(query, componentUuids);
+ return mapper(dbSession).selectByQuery(pageQuery);
+ });
+ }
+
public boolean existsByKey(DbSession session, String componentKey, String metricKey) {
return mapper(session).countByComponentAndMetric(componentKey, metricKey) > 0;
}
diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java
index 5c0910167c0..08423df79d7 100644
--- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java
+++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java
@@ -27,8 +27,9 @@ import org.apache.ibatis.annotations.Param;
public interface MeasureMapper {
- MeasureDto selectByKey(@Param("componentKey") String componentKey, @Param("metricKey") String metricKey);
+ List<MeasureDto> selectByQuery(@Param("query") MeasureQuery query);
+ MeasureDto selectByKey(@Param("componentKey") String componentKey, @Param("metricKey") String metricKey);
List<MeasureDto> selectByComponentAndMetrics(@Param("componentKey") String componentKey, @Param("metricKeys") List<String> metricKeys);
List<MeasureDto> selectBySnapshotAndMetricKeys(@Param("snapshotId") long snapshotId, @Param("metricKeys") List<String> metricKeys);
diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java
new file mode 100644
index 00000000000..b787570f161
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureQuery.java
@@ -0,0 +1,95 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.measure;
+
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class MeasureQuery {
+ private final List<String> componentUuids;
+ private final Collection<Integer> metricIds;
+ private final Long personId;
+
+ private MeasureQuery(Builder builder) {
+ this(builder.componentUuids, builder.metricIds, builder.personId);
+ }
+
+ private MeasureQuery(List<String> componentUuids, @CheckForNull Collection<Integer> metricIds, @Nullable Long personId) {
+ checkState(componentUuids != null, "Component UUIDs must be set");
+ this.componentUuids = componentUuids;
+ this.metricIds = metricIds;
+ this.personId = personId;
+ }
+
+ public List<String> getComponentUuids() {
+ return componentUuids;
+ }
+
+ @CheckForNull
+ public Collection<Integer> getMetricIds() {
+ return metricIds;
+ }
+
+ @CheckForNull
+ public Long getPersonId() {
+ return personId;
+ }
+
+ public boolean returnsEmpty() {
+ return componentUuids.isEmpty()
+ || (metricIds != null && metricIds.isEmpty());
+ }
+
+ public static MeasureQuery copyWithSubsetOfComponentUuids(MeasureQuery query, List<String> componentUuids) {
+ return new MeasureQuery(componentUuids, query.metricIds, query.personId);
+ }
+
+ public static final class Builder {
+ private List<String> componentUuids;
+ private Collection<Integer> metricIds;
+ private Long personId;
+
+ public Builder setComponentUuids(List<String> componentUuids) {
+ this.componentUuids = componentUuids;
+ return this;
+ }
+
+ /**
+ * All the measures are returned if parameter is {@code null}.
+ */
+ public Builder setMetricIds(@Nullable Collection<Integer> metricIds) {
+ this.metricIds = metricIds;
+ return this;
+ }
+
+ public Builder setPersonId(@Nullable Long l) {
+ this.personId = l;
+ return this;
+ }
+
+ public MeasureQuery build() {
+ return new MeasureQuery(this);
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
index 91a8b88a9c7..09e6fd5afd8 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseVersion.java
@@ -30,7 +30,7 @@ import org.sonar.db.MyBatis;
public class DatabaseVersion {
- public static final int LAST_VERSION = 1_254;
+ public static final int LAST_VERSION = 1_257;
/**
* The minimum supported version which can be upgraded. Lower
@@ -90,7 +90,8 @@ public class DatabaseVersion {
"user_roles",
"user_tokens",
"widgets",
- "widget_properties");
+ "widget_properties"
+ );
private MyBatis mybatis;
public DatabaseVersion(MyBatis mybatis) {
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
index 6e4314e9e16..5dd6b7e55e7 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
@@ -93,6 +93,7 @@ import org.sonar.db.version.v60.AddLastUsedColumnToRulesProfiles;
import org.sonar.db.version.v60.AddUuidColumnToSnapshots;
import org.sonar.db.version.v60.AddUuidColumnsToProjects;
import org.sonar.db.version.v60.AddUuidColumnsToResourceIndex;
+import org.sonar.db.version.v60.AddUuidPathColumnToProjects;
import org.sonar.db.version.v60.CleanEventsWithoutAnalysisUuid;
import org.sonar.db.version.v60.CleanEventsWithoutSnapshotId;
import org.sonar.db.version.v60.CleanOrphanRowsInProjects;
@@ -118,6 +119,7 @@ import org.sonar.db.version.v60.MakeComponentUuidNotNullOnMeasures;
import org.sonar.db.version.v60.MakeUuidColumnNotNullOnSnapshots;
import org.sonar.db.version.v60.MakeUuidColumnsNotNullOnProjects;
import org.sonar.db.version.v60.MakeUuidColumnsNotNullOnResourceIndex;
+import org.sonar.db.version.v60.MakeUuidPathColumnNotNullOnProjects;
import org.sonar.db.version.v60.PopulateAnalysisUuidColumnOnCeActivity;
import org.sonar.db.version.v60.PopulateAnalysisUuidOfDuplicationsIndex;
import org.sonar.db.version.v60.PopulateAnalysisUuidOnEvents;
@@ -128,6 +130,7 @@ import org.sonar.db.version.v60.PopulateLastUsedColumnOfRulesProfiles;
import org.sonar.db.version.v60.PopulateUuidColumnOnSnapshots;
import org.sonar.db.version.v60.PopulateUuidColumnsOfProjects;
import org.sonar.db.version.v60.PopulateUuidColumnsOfResourceIndex;
+import org.sonar.db.version.v60.PopulateUuidPathColumnOnProjects;
public class MigrationStepModule extends Module {
@Override
@@ -267,6 +270,11 @@ public class MigrationStepModule extends Module {
CleanEventsWithoutAnalysisUuid.class,
CleanEventsWithoutSnapshotId.class,
MakeAnalysisUuidNotNullOnEvents.class,
- DropSnapshotIdColumnFromEvents.class);
+ DropSnapshotIdColumnFromEvents.class,
+
+ // PROJECTS.UUID_PATH
+ AddUuidPathColumnToProjects.class,
+ PopulateUuidPathColumnOnProjects.class,
+ MakeUuidPathColumnNotNullOnProjects.class);
}
}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/Select.java b/sonar-db/src/main/java/org/sonar/db/version/Select.java
index 0d8b4a3698f..00714395082 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/Select.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/Select.java
@@ -126,6 +126,7 @@ public interface Select extends SqlStatement<Select> {
}
}
+ @FunctionalInterface
interface RowReader<T> {
T read(Row row) throws SQLException;
}
@@ -154,6 +155,7 @@ public interface Select extends SqlStatement<Select> {
RowReader<String> STRING_READER = new StringReader();
+ @FunctionalInterface
interface RowHandler {
void handle(Row row) throws SQLException;
}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java b/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java
index 71c77a0cd1f..0983a4145a0 100644
--- a/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java
+++ b/sonar-db/src/main/java/org/sonar/db/version/VarcharColumnDef.java
@@ -31,6 +31,7 @@ import static org.sonar.db.version.ColumnDefValidation.validateColumnName;
* Used to define VARCHAR column
*/
public class VarcharColumnDef extends AbstractColumnDef {
+ public static final int MAX_SIZE = 4_000;
public static final int UUID_VARCHAR_SIZE = 50;
private final int columnSize;
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidPathColumnToProjects.java b/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidPathColumnToProjects.java
new file mode 100644
index 00000000000..b9a46741d8a
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/AddUuidPathColumnToProjects.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.VarcharColumnDef.MAX_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class AddUuidPathColumnToProjects extends DdlChange {
+
+ private static final String TABLE_PROJECTS = "projects";
+
+ public AddUuidPathColumnToProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_PROJECTS)
+ .addColumn(newVarcharColumnDefBuilder().setColumnName("uuid_path").setLimit(MAX_SIZE).setIsNullable(true).build())
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidPathColumnNotNullOnProjects.java b/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidPathColumnNotNullOnProjects.java
new file mode 100644
index 00000000000..bcb3ce4c7e0
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/MakeUuidPathColumnNotNullOnProjects.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AlterColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.VarcharColumnDef.MAX_SIZE;
+import static org.sonar.db.version.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeUuidPathColumnNotNullOnProjects extends DdlChange {
+
+ private static final String TABLE_PROJECTS = "projects";
+
+ public MakeUuidPathColumnNotNullOnProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(getDialect(), TABLE_PROJECTS)
+ .updateColumn(newVarcharColumnDefBuilder().setColumnName("uuid_path").setLimit(MAX_SIZE).setIsNullable(false).build())
+ .build());
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidPathColumnOnProjects.java b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidPathColumnOnProjects.java
new file mode 100644
index 00000000000..ce12eb26bc5
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v60/PopulateUuidPathColumnOnProjects.java
@@ -0,0 +1,156 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.version.v60;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+import static java.util.stream.Collectors.toCollection;
+
+public class PopulateUuidPathColumnOnProjects extends BaseDataChange {
+
+ private static final Logger LOG = Loggers.get(PopulateUuidPathColumnOnProjects.class);
+ private static final Joiner PATH_JOINER = Joiner.on('.');
+ private static final Splitter PATH_SPLITTER = Splitter.on('.').omitEmptyStrings();
+ private static final String PATH_SEPARATOR = ".";
+ private static final String ROOT_PATH = PATH_SEPARATOR;
+
+ public PopulateUuidPathColumnOnProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ // group upgrades by tree of component
+ List<String> rootComponentUuids = context
+ .prepareSelect("select distinct project_uuid from projects where uuid_path is null")
+ .list(row -> row.getString(1));
+ for (String rootUuid : rootComponentUuids) {
+ handleRoot(rootUuid, context);
+ }
+
+ handleOrphans(context);
+ }
+
+ private void handleRoot(String rootComponentUuid, Context context) throws SQLException {
+ Relations relations = new Relations();
+ context
+ .prepareSelect("select s.id, s.path, s.component_uuid from snapshots s where s.root_component_uuid=? and s.islast=?")
+ .setString(1, rootComponentUuid)
+ .setBoolean(2, true)
+ .scroll(row -> {
+ long snapshotId = row.getLong(1);
+ String snapshotPath = row.getString(2);
+ String componentUuid = row.getString(3);
+ relations.add(new Snapshot(snapshotId, snapshotPath, componentUuid));
+ });
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select p.uuid, p.project_uuid from projects p where p.project_uuid=? and p.uuid_path is null").setString(1, rootComponentUuid);
+ massUpdate.update("update projects set uuid_path=? where uuid=? and uuid_path is null");
+ massUpdate.rowPluralName("components in tree of " + rootComponentUuid);
+ massUpdate.execute((row, update) -> handleComponent(relations, row, update));
+ }
+
+ private void handleOrphans(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select uuid, project_uuid from projects where uuid_path is null");
+ massUpdate.update("update projects set uuid_path=? where uuid=? and uuid_path is null");
+ massUpdate.rowPluralName("orphan components");
+ massUpdate.execute((row, update, updateIndex) -> {
+ String uuid = row.getString(1);
+ String rootUuid = row.getString(2);
+ String path = uuid.equals(rootUuid) ? ROOT_PATH : (rootUuid + PATH_SEPARATOR);
+ update.setString(1, path);
+ update.setString(2, uuid);
+ return true;
+ });
+ }
+
+ private boolean handleComponent(Relations relations, Select.Row row, SqlStatement update) throws SQLException {
+ String componentUuid = row.getString(1);
+ String rootComponentUuid = row.getString(2);
+
+ if (componentUuid.equals(rootComponentUuid)) {
+ // Root component, no need to use the table SNAPSHOTS.
+ // Moreover it allows to support provisioned projects (zero analysis)
+ update.setString(1, PATH_SEPARATOR);
+ update.setString(2, componentUuid);
+ return true;
+ }
+
+ Snapshot snapshot = relations.snapshotsByComponentUuid.get(componentUuid);
+ if (snapshot == null) {
+ LOG.trace("No UUID found for component UUID={}", componentUuid);
+ return false;
+ }
+
+ List<String> componentUuidPath = Arrays.stream(snapshot.snapshotPath)
+ .mapToObj(snapshotId -> relations.snapshotsById.get(snapshotId).componentUuid)
+ .collect(toCollection(() -> new ArrayList<>(snapshot.snapshotPath.length)));
+ update.setString(1, PATH_JOINER.join(componentUuidPath) + PATH_SEPARATOR);
+ update.setString(2, componentUuid);
+ return true;
+ }
+
+ private static final class Relations {
+ private final Map<String, Snapshot> snapshotsByComponentUuid = new HashMap<>();
+ private final Map<Long, Snapshot> snapshotsById = new HashMap<>();
+
+ void add(Snapshot snapshot) {
+ snapshotsByComponentUuid.put(snapshot.componentUuid, snapshot);
+ snapshotsById.put(snapshot.id, snapshot);
+ }
+ }
+
+ private static final class Snapshot {
+ private final long id;
+ private final long[] snapshotPath;
+ private final String componentUuid;
+
+ public Snapshot(long id, String snapshotPath, String componentUuid) {
+ this.id = id;
+ this.snapshotPath = parsePath(snapshotPath);
+ this.componentUuid = componentUuid;
+ }
+
+ // inputs: "", "1." or "1.2.3."
+ private long[] parsePath(String snapshotPath) {
+ return PATH_SPLITTER
+ .splitToList(snapshotPath)
+ .stream()
+ .mapToLong(Long::parseLong)
+ .toArray();
+ }
+ }
+}
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 5129d2e2681..49646233f04 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
@@ -5,6 +5,7 @@
<sql id="componentColumns">
p.id,
p.uuid as uuid,
+ p.uuid_path as uuidPath,
p.project_uuid as projectUuid,
p.module_uuid as moduleUuid,
p.module_uuid_path as moduleUuidPath,
@@ -29,6 +30,7 @@
p.id,
p.name,
p.uuid as uuid,
+ p.uuid_path as uuidPath,
p.kee as kee,
p.qualifier as qualifier,
p.scope as scope,
@@ -315,73 +317,101 @@
</where>
</sql>
- <select id="selectDirectChildren" resultType="ComponentWithSnapshot">
+ <!-- "p" is ancestors -->
+ <select id="selectAncestors" resultType="Component">
select
- <include refid="componentColumns"/>, s.id as snapshotId
- <include refid="sqlSelectByTreeQuery"/>
- and s.parent_snapshot_id = #{query.baseSnapshot.id}
+ <include refid="componentColumns"/>
+ from projects base
+ inner join projects p
+ where
+ base.uuid = #{query.baseUuid}
+ and p.project_uuid = base.project_uuid
+ and base.uuid_path like #{baseUuidPathLike}
+ and p.uuid != base.uuid
+ and p.enabled = ${_true}
order by ${query.sqlSort}
</select>
- <select id="countDirectChildren" resultType="int">
- select count(p.id)
- <include refid="sqlSelectByTreeQuery"/>
- and s.parent_snapshot_id = #{query.baseSnapshot.id}
- </select>
-
- <select id="selectAllChildren" resultType="ComponentWithSnapshot">
+ <!-- "p" is children -->
+ <select id="selectChildren" resultType="Component">
select
- <include refid="componentColumns"/>, s.id as snapshotId
- <include refid="sqlSelectAllChildren" />
+ <include refid="componentColumns"/>
+ <include refid="sqlChildren"/>
order by ${query.sqlSort}
</select>
- <select id="countAllChildren" resultType="int">
+ <select id="countChildren" resultType="int">
select count(p.id)
- <include refid="sqlSelectAllChildren"/>
+ <include refid="sqlChildren"/>
</select>
- <sql id="sqlSelectAllChildren">
- <include refid="sqlSelectByTreeQuery"/>
- <if test="query.baseSnapshot.rootId!=null">
- and s.root_snapshot_id = #{query.baseSnapshot.rootId}
- </if>
- <if test="query.baseSnapshot.rootId==null">
- and s.root_snapshot_id = #{query.baseSnapshot.id}
- </if>
- and s.path like #{query.baseSnapshotPath} ESCAPE '/'
- </sql>
-
- <sql id="sqlSelectByTreeQuery">
+ <sql id="sqlChildren">
from projects p
- inner join snapshots s on p.uuid = s.component_uuid
+ inner join projects base
+ inner join snapshots s
where
- p.enabled=${_true}
- <if test="query.qualifiers!=null">
- AND p.qualifier in
+ base.uuid = #{query.baseUuid}
+ and base.project_uuid = p.project_uuid
+ and s.component_uuid = base.project_uuid
+ and p.enabled = ${_true}
+ and s.islast = ${_true}
+ and p.uuid_path = #{baseUuidPath}
+ <include refid="sqlTreeFilters"/>
+ </sql>
+
+ <sql id="sqlTreeFilters">
+ <if test="query.qualifiers != null">
+ and p.qualifier in
<foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=",">
#{qualifier}
</foreach>
</if>
- <if test="query.nameOrKeyQuery!=null">
- AND (
- p.kee=#{query.nameOrKeyQuery}
- OR
- p.uuid IN (
- SELECT ri.component_uuid
- FROM resource_index ri
- WHERE ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/'
- )
- OR
- p.copy_component_uuid IN (
- SELECT ri.component_uuid
- FROM resource_index ri
- WHERE ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/'
- )
+ <if test="query.nameOrKeyQuery != null">
+ and (
+ p.kee=#{query.nameOrKeyQuery}
+ or
+ p.uuid IN (
+ SELECT ri.component_uuid
+ FROM resource_index ri
+ WHERE ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/'
+ )
+ or
+ p.copy_component_uuid IN (
+ SELECT ri.component_uuid
+ FROM resource_index ri
+ WHERE ri.kee like #{query.nameOrKeyQueryToSqlForResourceIndex} ESCAPE '/'
+ )
)
</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
+ inner join snapshots s
+ where
+ base.uuid = #{query.baseUuid}
+ and base.project_uuid=p.project_uuid
+ and p.enabled = ${_true}
+ and p.uuid_path like #{baseUuidPathLike} ESCAPE '/'
+ and s.component_uuid = base.project_uuid
+ and s.islast = ${_true}
+ <include refid="sqlTreeFilters"/>
+ </sql>
+
<select id="countRootComponents" resultType="int">
select count(p.id)
from projects p
@@ -491,17 +521,47 @@
</sql>
<sql id="insertSql">
- INSERT INTO projects (kee, deprecated_kee, uuid, project_uuid, module_uuid, module_uuid_path, name, long_name,
- qualifier, scope, language, description, root_uuid, path, copy_component_uuid, developer_uuid, enabled,
- created_at, authorization_updated_at)
- VALUES (#{kee,jdbcType=VARCHAR}, #{deprecatedKey,jdbcType=VARCHAR}, #{uuid,jdbcType=VARCHAR},
- #{projectUuid,jdbcType=VARCHAR}, #{moduleUuid,jdbcType=VARCHAR}, #{moduleUuidPath,jdbcType=VARCHAR},
- #{name,jdbcType=VARCHAR}, #{longName,jdbcType=VARCHAR}, #{qualifier,jdbcType=VARCHAR}, #{scope,jdbcType=VARCHAR},
- #{language,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
- #{rootUuid,jdbcType=VARCHAR}, #{path,jdbcType=VARCHAR}, #{copyComponentUuid,jdbcType=VARCHAR},
+ INSERT INTO projects (kee,
+ deprecated_kee,
+ uuid,
+ uuid_path,
+ project_uuid,
+ module_uuid,
+ module_uuid_path,
+ name,
+ long_name,
+ qualifier,
+ scope,
+ language,
+ description,
+ root_uuid,
+ path,
+ copy_component_uuid,
+ developer_uuid,
+ enabled,
+ created_at,
+ authorization_updated_at)
+ VALUES (
+ #{kee,jdbcType=VARCHAR},
+ #{deprecatedKey,jdbcType=VARCHAR},
+ #{uuid,jdbcType=VARCHAR},
+ #{uuidPath,jdbcType=VARCHAR},
+ #{projectUuid,jdbcType=VARCHAR},
+ #{moduleUuid,jdbcType=VARCHAR},
+ #{moduleUuidPath,jdbcType=VARCHAR},
+ #{name,jdbcType=VARCHAR},
+ #{longName,jdbcType=VARCHAR},
+ #{qualifier,jdbcType=VARCHAR},
+ #{scope,jdbcType=VARCHAR},
+ #{language,jdbcType=VARCHAR},
+ #{description,jdbcType=VARCHAR},
+ #{rootUuid,jdbcType=VARCHAR},
+ #{path,jdbcType=VARCHAR},
+ #{copyComponentUuid,jdbcType=VARCHAR},
#{developerUuid,jdbcType=VARCHAR},
#{enabled,jdbcType=BOOLEAN},
- #{createdAt,jdbcType=TIMESTAMP}, #{authorizationUpdatedAt,jdbcType=BIGINT})
+ #{createdAt,jdbcType=TIMESTAMP},
+ #{authorizationUpdatedAt,jdbcType=BIGINT})
</sql>
<insert id="insert" parameterType="Component" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
diff --git a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml
index f2e85bb036c..6641363bf0e 100644
--- a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml
+++ b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml
@@ -25,6 +25,49 @@
metric.name as metricKey
</sql>
+
+ <select id="selectByQuery" parameterType="map" resultType="Measure">
+ select
+ <include refid="measureColumns"/>
+ from project_measures pm
+ inner join snapshots s
+ where
+ s.id=pm.snapshot_id
+ and pm.component_uuid in
+ <foreach item="componentUuid" collection="query.getComponentUuids()" open="(" separator="," close=")">
+ #{componentUuid}
+ </foreach>
+ and s.islast=${_true}
+ <if test="query.getMetricIds() != null">
+ and pm.metric_id in
+ <foreach item="metricId" collection="query.getMetricIds()" open="(" separator="," close=")">#{metricId}</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="selectByComponentUuidsAndMetricIds" parameterType="map" resultType="Measure">
+ select
+ <include refid="measureColumns"/>
+ from project_measures pm
+ inner join snapshots s
+ where
+ s.id=pm.snapshot_id
+ and s.islast=${_true}
+ and pm.component_uuid in
+ <foreach item="componentUuid" collection="componentUuids" open="(" separator="," close=")">#{componentUuid}
+ </foreach>
+ and pm.person_id is null
+ and pm.metric_id in
+ <foreach item="metricId" collection="metricIds" open="(" separator="," close=")">#{metricId}</foreach>
+ </select>
+
<select id="selectByComponentAndMetric" parameterType="map" resultType="Measure">
SELECT metric.name as metric_name,
<include refid="extendedMeasureColumns"/>
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
index 5fb4ad24c2f..ce75f9372b6 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
+++ b/sonar-db/src/main/resources/org/sonar/db/version/rows-h2.sql
@@ -461,6 +461,9 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1251');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1252');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1253');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1254');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1255');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1256');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1257');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482');
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
index c94c26a17fa..72e3aa398c7 100644
--- a/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/sonar-db/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -217,6 +217,7 @@ CREATE TABLE "PROJECTS" (
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
"KEE" VARCHAR(400),
"UUID" VARCHAR(50) NOT NULL,
+ "UUID_PATH" VARCHAR(4000) NOT NULL,
"ROOT_UUID" VARCHAR(50) NOT NULL,
"PROJECT_UUID" VARCHAR(50),
"MODULE_UUID" VARCHAR(50),
@@ -583,6 +584,8 @@ CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID");
CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID");
+CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT" ON "SNAPSHOTS" ("ROOT_COMPONENT_UUID");
+
CREATE INDEX "RULES_PARAMETERS_RULE_ID" ON "RULES_PARAMETERS" ("RULE_ID");
CREATE INDEX "ACTIVE_DASHBOARDS_DASHBOARDID" ON "ACTIVE_DASHBOARDS" ("DASHBOARD_ID");
@@ -647,8 +650,6 @@ CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
-CREATE INDEX "SNAPSHOTS_ROOT_COMPONENT" ON "SNAPSHOTS" ("ROOT_COMPONENT_UUID");
-
CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES" ("GROUP_ID", "RESOURCE_ID", "ROLE");
CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY");