All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.db.version.v60.PopulateUuidPathColumnOnProjects Maven / Gradle / Ivy

There is a newer version: 6.3.1
Show newest version
/*
 * 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 javax.annotation.Nullable;
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 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 : (PATH_SEPARATOR + 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 componentUuidPath = Arrays.stream(snapshot.snapshotPath)
      .mapToObj(snapshotId -> relations.snapshotsById.get(snapshotId).componentUuid)
      .collect(toCollection(() -> new ArrayList<>(snapshot.snapshotPath.length)));
    update.setString(1, PATH_SEPARATOR + PATH_JOINER.join(componentUuidPath) + PATH_SEPARATOR);
    update.setString(2, componentUuid);
    return true;
  }

  private static final class Relations {
    private final Map snapshotsByComponentUuid = new HashMap<>();
    private final Map snapshotsById = new HashMap<>();

    void add(Snapshot snapshot) {
      snapshotsByComponentUuid.put(snapshot.componentUuid, snapshot);
      snapshotsById.put(snapshot.id, snapshot);
    }
  }

  private static final class Snapshot {
    private static final long[] EMPTY_PATH = new long[0];
    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: null (on Oracle), "", "1." or "1.2.3."
    private long[] parsePath(@Nullable String snapshotPath) {
      if (snapshotPath == null) {
        return EMPTY_PATH;
      }
      return PATH_SPLITTER
        .splitToList(snapshotPath)
        .stream()
        .mapToLong(Long::parseLong)
        .toArray();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy