/* * 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.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; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.annotation.Nullable; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.db.Dao; import org.sonar.db.DbSession; import org.sonar.db.RowNotFoundException; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.Maps.newHashMapWithExpectedSize; import static java.util.Collections.emptyList; import static org.sonar.db.DatabaseUtils.executeLargeInputs; import static org.sonar.db.DatabaseUtils.executeLargeUpdates; public class ComponentDao implements Dao { public ComponentDto selectOrFailById(DbSession session, long id) { Optional componentDto = selectById(session, id); if (!componentDto.isPresent()) { throw new RowNotFoundException(String.format("Component id does not exist: %d", id)); } return componentDto.get(); } public Optional selectById(DbSession session, long id) { return Optional.fromNullable(mapper(session).selectById(id)); } public Optional selectByUuid(DbSession session, String uuid) { return Optional.fromNullable(mapper(session).selectByUuid(uuid)); } public ComponentDto selectOrFailByUuid(DbSession session, String uuid) { Optional componentDto = selectByUuid(session, uuid); if (!componentDto.isPresent()) { throw new RowNotFoundException(String.format("Component with uuid '%s' not found", uuid)); } return componentDto.get(); } public List selectByQuery(DbSession session, ComponentQuery query, int offset, int limit) { if (query.getComponentIds() != null && query.getComponentIds().isEmpty()) { return emptyList(); } return mapper(session).selectByQuery(query, new RowBounds(offset, limit)); } public int countByQuery(DbSession session, ComponentQuery query) { if (query.getComponentIds() != null && query.getComponentIds().isEmpty()) { return 0; } return mapper(session).countByQuery(query); } public List selectSubProjectsByComponentUuids(DbSession session, Collection keys) { if (keys.isEmpty()) { return emptyList(); } return mapper(session).selectSubProjectsByComponentUuids(keys); } public List selectDescendantModules(DbSession session, String rootComponentUuid) { return mapper(session).selectDescendantModules(rootComponentUuid, Scopes.PROJECT, false); } public List selectEnabledDescendantModules(DbSession session, String rootComponentUuid) { return mapper(session).selectDescendantModules(rootComponentUuid, Scopes.PROJECT, true); } public List selectEnabledDescendantFiles(DbSession session, String rootComponentUuid) { return mapper(session).selectDescendantFiles(rootComponentUuid, Scopes.FILE, true); } public List selectEnabledFilesFromProject(DbSession session, String rootComponentUuid) { return mapper(session).selectEnabledFilesFromProject(rootComponentUuid); } public List selectByIds(DbSession session, Collection ids) { return executeLargeInputs(ids, mapper(session)::selectByIds); } public List selectByUuids(DbSession session, Collection uuids) { return executeLargeInputs(uuids, mapper(session)::selectByUuids); } public List selectExistingUuids(DbSession session, Collection uuids) { return executeLargeInputs(uuids, mapper(session)::selectExistingUuids); } /** * Return all components of a project (including disable ones) */ public List selectAllComponentsFromProjectKey(DbSession session, String projectKey) { return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, null, false); } public List selectEnabledModulesFromProjectKey(DbSession session, String projectKey) { return mapper(session).selectComponentsFromProjectKeyAndScope(projectKey, Scopes.PROJECT, true); } public List selectByKeys(DbSession session, Collection keys) { return executeLargeInputs(keys, mapper(session)::selectByKeys); } public List selectComponentsHavingSameKeyOrderedById(DbSession session, String key) { return mapper(session).selectComponentsHavingSameKeyOrderedById(key); } /** * List of ancestors, ordered from root to parent. The list is empty * if the component is a tree root. Disabled components are excluded by design * as tree represents the more recent analysis. */ public List selectAncestors(DbSession dbSession, ComponentDto component) { if (component.isRoot()) { return Collections.emptyList(); } List ancestorUuids = component.getUuidPathAsList(); List ancestors = selectByUuids(dbSession, ancestorUuids); return Ordering.explicit(ancestorUuids).onResultOf(ComponentDto::uuid).immutableSortedCopy(ancestors); } /** * Select the children or the leaves of a base component, given by its UUID. The components that are not present in last * analysis are ignored. * * An empty list is returned if the base component does not exist or if the base component is a leaf. */ public List selectDescendants(DbSession dbSession, ComponentTreeQuery query) { Optional componentOpt = selectByUuid(dbSession, query.getBaseUuid()); if (!componentOpt.isPresent()) { return emptyList(); } ComponentDto component = componentOpt.get(); return mapper(dbSession).selectDescendants(query, componentOpt.get().uuid(), query.getUuidPath(component)); } public ComponentDto selectOrFailByKey(DbSession session, String key) { Optional component = selectByKey(session, key); if (!component.isPresent()) { throw new RowNotFoundException(String.format("Component key '%s' not found", key)); } return component.get(); } public Optional selectByKey(DbSession session, String key) { return Optional.fromNullable(mapper(session).selectByKey(key)); } public List selectAllViewsAndSubViews(DbSession session) { return mapper(session).selectUuidsForQualifiers(Qualifiers.VIEW, Qualifiers.SUBVIEW); } public List selectProjectsFromView(DbSession session, String viewUuid, String projectViewUuid) { return mapper(session).selectProjectsFromView("%." + viewUuid + ".%", projectViewUuid); } /** * Returns all projects (Scope {@link org.sonar.api.resources.Scopes#PROJECT} and qualifier * {@link org.sonar.api.resources.Qualifiers#PROJECT}) which are enabled. * * Used by Views. */ public List selectProjects(DbSession session) { return mapper(session).selectProjects(); } /** * Select all root components (projects and views), including disabled ones, for a given organization. */ public List selectAllRootsByOrganization(DbSession dbSession, String organizationUuid) { return mapper(dbSession).selectAllRootsByOrganization(organizationUuid); } public List selectProvisionedProjects(DbSession session, int offset, int limit, @Nullable String query) { Map parameters = newHashMapWithExpectedSize(2); addProjectQualifier(parameters); addPartialQueryParameterIfNotNull(parameters, query); return mapper(session).selectProvisionedProjects(parameters, new RowBounds(offset, limit)); } public int countProvisionedProjects(DbSession session, @Nullable String query) { Map parameters = newHashMapWithExpectedSize(2); addProjectQualifier(parameters); addPartialQueryParameterIfNotNull(parameters, query); return mapper(session).countProvisionedProjects(parameters); } public List selectGhostProjects(DbSession session, int offset, int limit, @Nullable String query) { Map parameters = newHashMapWithExpectedSize(2); addProjectQualifier(parameters); addPartialQueryParameterIfNotNull(parameters, query); return mapper(session).selectGhostProjects(parameters, new RowBounds(offset, limit)); } public long countGhostProjects(DbSession session, @Nullable String query) { Map parameters = newHashMapWithExpectedSize(2); addProjectQualifier(parameters); addPartialQueryParameterIfNotNull(parameters, query); return mapper(session).countGhostProjects(parameters); } /** * Selects all components that are relevant for indexing. The result is not returned (since it is usually too big), but handed over to the handler * @param session the database session * @param projectUuid the project uuid, which is selected with all of its children * @param handler the action to be applied to every result */ public void selectForIndexing(DbSession session, @Nullable String projectUuid, ResultHandler handler) { Objects.requireNonNull(handler); mapper(session).selectForIndexing(projectUuid, handler); } /** * Retrieves all components with a specific root project Uuid, no other filtering is done by this method. * * Used by Views plugin */ public List selectByProjectUuid(String projectUuid, DbSession dbSession) { return mapper(dbSession).selectByProjectUuid(projectUuid); } /** * Retrieve enabled components keys with given qualifiers * * Used by Views plugin */ public Set selectComponentsByQualifiers(DbSession dbSession, Set qualifiers) { checkArgument(!qualifiers.isEmpty(), "Qualifiers cannot be empty"); return new HashSet<>(mapper(dbSession).selectComponentsByQualifiers(qualifiers)); } private static void addPartialQueryParameterIfNotNull(Map parameters, @Nullable String keyOrNameFilter) { // TODO rely on resource_index table and match exactly the key if (keyOrNameFilter != null) { parameters.put("query", "%" + keyOrNameFilter.toUpperCase(Locale.ENGLISH) + "%"); } } private static void addProjectQualifier(Map parameters) { parameters.put("qualifier", Qualifiers.PROJECT); } public void insert(DbSession session, ComponentDto item) { mapper(session).insert(item); } public void insert(DbSession session, Collection items) { for (ComponentDto item : items) { insert(session, item); } } public void insert(DbSession session, ComponentDto item, ComponentDto... others) { insert(session, Lists.asList(item, others)); } public void update(DbSession session, ComponentUpdateDto component) { mapper(session).update(component); } public void updateBEnabledToFalse(DbSession session, Collection uuids) { executeLargeUpdates(uuids, mapper(session)::updateBEnabledToFalse); } public void applyBChangesForRootComponentUuid(DbSession session, String projectUuid) { mapper(session).applyBChangesForRootComponentUuid(projectUuid); } public void resetBChangedForRootComponentUuid(DbSession session, String projectUuid) { mapper(session).resetBChangedForRootComponentUuid(projectUuid); } public void delete(DbSession session, long componentId) { mapper(session).delete(componentId); } private static ComponentMapper mapper(DbSession session) { return session.getMapper(ComponentMapper.class); } }