diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-06-22 16:37:21 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2016-06-27 18:19:46 +0200 |
commit | d20ca1857a018a6b255a6eae7e5eeb71b31c22ab (patch) | |
tree | 9b6d91f085b23546e225a48f4d541870fe81bd19 /sonar-db/src/main | |
parent | 4ecb6fc3ec08ee37a1bb5c19d8ca44e2b832b5a7 (diff) | |
download | sonarqube-d20ca1857a018a6b255a6eae7e5eeb71b31c22ab.tar.gz sonarqube-d20ca1857a018a6b255a6eae7e5eeb71b31c22ab.zip |
SONAR-7800 add column PROJECTS.UUID_PATH
Diffstat (limited to 'sonar-db/src/main')
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"); |