diff options
Diffstat (limited to 'sonar-db/src/main/java/org/sonar/db/version')
52 files changed, 4968 insertions, 0 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java new file mode 100644 index 00000000000..ab6543fffe7 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java @@ -0,0 +1,146 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Preconditions; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.db.dialect.Dialect; +import org.sonar.db.dialect.MsSql; +import org.sonar.db.dialect.Oracle; +import org.sonar.db.dialect.PostgreSql; + +import static com.google.common.collect.Lists.newArrayList; + +public class AddColumnsBuilder { + + private final Dialect dialect; + private final String tableName; + private List<ColumnDef> columnDefs = newArrayList(); + + public AddColumnsBuilder(Dialect dialect, String tableName) { + this.tableName = tableName; + this.dialect = dialect; + } + + public AddColumnsBuilder addColumn(ColumnDef columnDef) { + columnDefs.add(columnDef); + return this; + } + + public String build() { + StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" "); + switch (dialect.getId()) { + case PostgreSql.ID: + addColumns(sql, "ADD COLUMN "); + break; + case MsSql.ID: + sql.append("ADD "); + addColumns(sql, ""); + break; + default: + sql.append("ADD ("); + addColumns(sql, ""); + sql.append(")"); + } + return sql.toString(); + } + + private void addColumns(StringBuilder sql, String columnPrefix) { + for (int i = 0; i < columnDefs.size(); i++) { + sql.append(columnPrefix); + addColumn(sql, columnDefs.get(i)); + if (i < columnDefs.size() - 1) { + sql.append(", "); + } + } + } + + private void addColumn(StringBuilder sql, ColumnDef columnDef) { + sql.append(columnDef.getName()).append(" ").append(typeToSql(columnDef)); + Integer limit = columnDef.getLimit(); + if (limit != null) { + sql.append(" (").append(Integer.toString(limit)).append(")"); + } + sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL"); + } + + private String typeToSql(ColumnDef columnDef) { + switch (columnDef.getType()) { + case STRING: + return "VARCHAR"; + case BIG_INTEGER: + return !dialect.getId().equals(Oracle.ID) ? "BIGINT" : "NUMBER (38)"; + default: + throw new IllegalArgumentException("Unsupported type : " + columnDef.getType()); + } + } + + public static class ColumnDef { + private String name; + private Type type; + private boolean isNullable; + private Integer limit; + + public enum Type { + STRING, BIG_INTEGER + } + + public ColumnDef setNullable(boolean isNullable) { + this.isNullable = isNullable; + return this; + } + + public ColumnDef setLimit(@Nullable Integer limit) { + this.limit = limit; + return this; + } + + public ColumnDef setName(String name) { + Preconditions.checkArgument(CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).matchesAllOf(name), "Column name should only contains lowercase and _ characters"); + this.name = name; + return this; + } + + public ColumnDef setType(Type type) { + this.type = type; + return this; + } + + public boolean isNullable() { + return isNullable; + } + + @CheckForNull + public Integer getLimit() { + return limit; + } + + public String getName() { + return name; + } + + public Type getType() { + return type; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java b/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java new file mode 100644 index 00000000000..800e66a7e34 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java @@ -0,0 +1,65 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.db.Database; + +public abstract class BaseDataChange implements DataChange, MigrationStep { + + private final Database db; + + public BaseDataChange(Database db) { + this.db = db; + } + + @Override + public final void execute() throws SQLException { + Connection readConnection = null; + Connection writeConnection = null; + try { + readConnection = openConnection(); + + writeConnection = db.getDataSource().getConnection(); + writeConnection.setAutoCommit(false); + Context context = new Context(db, readConnection, writeConnection); + execute(context); + + } finally { + DbUtils.closeQuietly(readConnection); + DbUtils.closeQuietly(writeConnection); + } + } + + /** + * Do not forget to close it ! + */ + protected Connection openConnection() throws SQLException { + Connection connection = db.getDataSource().getConnection(); + connection.setAutoCommit(false); + if (connection.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)) { + connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); + } + return connection; + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java b/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java new file mode 100644 index 00000000000..f302311bb2d --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java @@ -0,0 +1,105 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Date; +import javax.annotation.Nullable; +import org.apache.commons.dbutils.DbUtils; + +class BaseSqlStatement<CHILD extends SqlStatement> implements SqlStatement<CHILD> { + protected PreparedStatement pstmt; + + protected BaseSqlStatement(PreparedStatement pstmt) { + this.pstmt = pstmt; + } + + @Override + public CHILD close() { + DbUtils.closeQuietly(pstmt); + pstmt = null; + return (CHILD) this; + } + + @Override + public CHILD setString(int columnIndex, @Nullable String value) throws SQLException { + pstmt.setString(columnIndex, value); + return (CHILD) this; + } + + @Override + public CHILD setBytes(int columnIndex, @Nullable byte[] value) throws SQLException { + pstmt.setBytes(columnIndex, value); + return (CHILD) this; + } + + @Override + public CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.INTEGER); + } else { + pstmt.setInt(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.BIGINT); + } else { + pstmt.setLong(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.BOOLEAN); + } else { + pstmt.setBoolean(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.DECIMAL); + } else { + pstmt.setDouble(columnIndex, value); + } + return (CHILD) this; + } + + @Override + public CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException { + if (value == null) { + pstmt.setNull(columnIndex, Types.TIMESTAMP); + } else { + pstmt.setTimestamp(columnIndex, new Timestamp(value.getTime())); + } + return (CHILD) this; + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/DataChange.java b/sonar-db/src/main/java/org/sonar/db/version/DataChange.java new file mode 100644 index 00000000000..d06e4d7b093 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/DataChange.java @@ -0,0 +1,53 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; + +public interface DataChange { + + class Context { + private final Database db; + private final Connection readConnection; + private final Connection writeConnection; + + public Context(Database db, Connection readConnection, Connection writeConnection) { + this.db = db; + this.readConnection = readConnection; + this.writeConnection = writeConnection; + } + + public Select prepareSelect(String sql) throws SQLException { + return SelectImpl.create(db, readConnection, sql); + } + + public Upsert prepareUpsert(String sql) throws SQLException { + return UpsertImpl.create(writeConnection, sql); + } + + public MassUpdate prepareMassUpdate() throws SQLException { + return new MassUpdate(db, readConnection, writeConnection); + } + } + + void execute(Context context) throws SQLException; +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java new file mode 100644 index 00000000000..1d00155e13b --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java @@ -0,0 +1,70 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.util.Date; +import javax.annotation.CheckForNull; + +public interface DatabaseMigration { + enum Status { + NONE, RUNNING, FAILED, SUCCEEDED + } + + /** + * Starts the migration status and returns immediately. + * <p> + * Migration can not be started twice but calling this method wont raise an error. + * On the other hand, calling this method when no migration is needed will start the process anyway. + * </p> + * <p> + * <strong>This method should be named {@code start} but it can not be because it will be called by the pico container + * and this will cause unwanted behavior</strong> + * </p> + */ + void startIt(); + + /** + * The time and day the last migration was started. + * <p> + * If no migration was ever started, the returned date is {@code null}. This value is reset when {@link #startIt()} is + * called. + * </p> + * + * @return a {@link Date} or {@code null} + */ + @CheckForNull + Date startedAt(); + + /** + * Current status of the migration. + */ + Status status(); + + /** + * The error of the last migration if it failed. + * <p> + * This value is reset when {@link #startIt()} is called. + * </p> + * @return a {@link Throwable} or {@code null} + */ + @CheckForNull + Throwable failureError(); + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java b/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java new file mode 100644 index 00000000000..1e2bcb47582 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java @@ -0,0 +1,67 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.SQLException; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.db.Database; + +public abstract class DdlChange implements MigrationStep { + + private final Database db; + + public DdlChange(Database db) { + this.db = db; + } + + @Override + public final void execute() throws SQLException { + Connection writeConnection = null; + try { + writeConnection = db.getDataSource().getConnection(); + writeConnection.setAutoCommit(false); + Context context = new Context(writeConnection); + execute(context); + + } finally { + DbUtils.closeQuietly(writeConnection); + } + } + + public static class Context { + private final Connection writeConnection; + + public Context(Connection writeConnection) { + this.writeConnection = writeConnection; + } + + public void execute(String sql) throws SQLException { + try { + UpsertImpl.create(writeConnection, sql).execute().commit(); + } catch (Exception e) { + throw new IllegalStateException(String.format("Fail to execute %s", sql), e); + } + } + } + + public abstract void execute(Context context) throws SQLException; + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java new file mode 100644 index 00000000000..61a0fbc45c8 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java @@ -0,0 +1,72 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import org.sonar.db.dialect.Dialect; +import org.sonar.db.dialect.MsSql; +import org.sonar.db.dialect.MySql; +import org.sonar.db.dialect.Oracle; +import org.sonar.db.dialect.PostgreSql; + +public class DropColumnsBuilder { + + private final Dialect dialect; + private final String tableName; + private final String[] columns; + + public DropColumnsBuilder(Dialect dialect, String tableName, String... columns) { + this.tableName = tableName; + this.dialect = dialect; + this.columns = columns; + } + + public String build() { + StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" "); + switch (dialect.getId()) { + case PostgreSql.ID: + case MySql.ID: + dropColumns(sql, "DROP COLUMN "); + break; + case MsSql.ID: + sql.append("DROP COLUMN "); + dropColumns(sql, ""); + break; + case Oracle.ID: + sql.append("DROP ("); + dropColumns(sql, ""); + sql.append(")"); + break; + default: + throw new IllegalStateException(String.format("Unsupported database '%s'", dialect.getId())); + } + return sql.toString(); + } + + private void dropColumns(StringBuilder sql, String columnPrefix) { + for (int i = 0; i < columns.length; i++) { + sql.append(columnPrefix); + sql.append(columns[i]); + if (i < columns.length - 1) { + sql.append(", "); + } + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java new file mode 100644 index 00000000000..a09ed828a5c --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java @@ -0,0 +1,97 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicLong; +import org.sonar.core.util.ProgressLogger; +import org.sonar.db.Database; + +public class MassUpdate { + + public interface Handler { + /** + * Convert some column values of a given row. + * + * @return true if the row must be updated, else false. If false, then the update parameter must not be touched. + */ + boolean handle(Select.Row row, SqlStatement update) throws SQLException; + } + + private final Database db; + private final Connection readConnection; + private final Connection writeConnection; + private final AtomicLong counter = new AtomicLong(0L); + private final ProgressLogger progress = ProgressLogger.create(getClass(), counter); + + private Select select; + private Upsert update; + + MassUpdate(Database db, Connection readConnection, Connection writeConnection) { + this.db = db; + this.readConnection = readConnection; + this.writeConnection = writeConnection; + } + + public SqlStatement select(String sql) throws SQLException { + this.select = SelectImpl.create(db, readConnection, sql); + return this.select; + } + + public MassUpdate update(String sql) throws SQLException { + this.update = UpsertImpl.create(writeConnection, sql); + return this; + } + + public MassUpdate rowPluralName(String s) { + this.progress.setPluralLabel(s); + return this; + } + + public void execute(final Handler handler) throws SQLException { + if (select == null || update == null) { + throw new IllegalStateException("SELECT or UPDATE requests are not defined"); + } + + progress.start(); + try { + select.scroll(new Select.RowHandler() { + @Override + public void handle(Select.Row row) throws SQLException { + if (handler.handle(row, update)) { + update.addBatch(); + } + counter.getAndIncrement(); + } + }); + if (((UpsertImpl) update).getBatchCount() > 0L) { + update.execute().commit(); + } + update.close(); + + // log the total number of processed rows + progress.log(); + } finally { + progress.stop(); + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java new file mode 100644 index 00000000000..f34fdcffcd8 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java @@ -0,0 +1,32 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.SQLException; + +/** + * Java alternative of ActiveRecord::Migration. Do not forget to declare implementation classes in {@link MigrationStepModule} + * @since 3.7 + */ +public interface MigrationStep { + + void execute() throws SQLException; + +} 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 new file mode 100644 index 00000000000..4e1ea5cd811 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java @@ -0,0 +1,108 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import org.sonar.core.platform.Module; +import org.sonar.db.version.v451.AddMissingCustomRuleParametersMigrationStep; +import org.sonar.db.version.v451.DeleteUnescapedActivities; +import org.sonar.db.version.v50.FeedFileSources; +import org.sonar.db.version.v50.FeedIssueLongDates; +import org.sonar.db.version.v50.FeedSnapshotSourcesUpdatedAt; +import org.sonar.db.version.v50.InsertProjectsAuthorizationUpdatedAtMigrationStep; +import org.sonar.db.version.v50.PopulateProjectsUuidColumnsMigrationStep; +import org.sonar.db.version.v50.RemoveSortFieldFromIssueFiltersMigrationStep; +import org.sonar.db.version.v50.ReplaceIssueFiltersProjectKeyByUuid; +import org.sonar.db.version.v51.AddIssuesColumns; +import org.sonar.db.version.v51.AddNewCharacteristics; +import org.sonar.db.version.v51.CopyScmAccountsFromAuthorsToUsers; +import org.sonar.db.version.v51.DropIssuesColumns; +import org.sonar.db.version.v51.FeedAnalysisReportsLongDates; +import org.sonar.db.version.v51.FeedEventsLongDates; +import org.sonar.db.version.v51.FeedFileSourcesBinaryData; +import org.sonar.db.version.v51.FeedIssueChangesLongDates; +import org.sonar.db.version.v51.FeedIssueComponentUuids; +import org.sonar.db.version.v51.FeedIssueTags; +import org.sonar.db.version.v51.FeedIssuesLongDates; +import org.sonar.db.version.v51.FeedManualMeasuresLongDates; +import org.sonar.db.version.v51.FeedSnapshotsLongDates; +import org.sonar.db.version.v51.FeedUsersLongDates; +import org.sonar.db.version.v51.RemovePermissionsOnModulesMigrationStep; +import org.sonar.db.version.v51.RenameComponentRelatedParamsInIssueFilters; +import org.sonar.db.version.v51.UpdateProjectsModuleUuidPath; +import org.sonar.db.version.v52.AddManualMeasuresComponentUuidColumn; +import org.sonar.db.version.v52.FeedEventsComponentUuid; +import org.sonar.db.version.v52.FeedFileSourcesDataType; +import org.sonar.db.version.v52.FeedManualMeasuresComponentUuid; +import org.sonar.db.version.v52.FeedMetricsBooleans; +import org.sonar.db.version.v52.FeedProjectLinksComponentUuid; +import org.sonar.db.version.v52.MoveProjectProfileAssociation; +import org.sonar.db.version.v52.RemoveComponentLibraries; +import org.sonar.db.version.v52.RemoveDuplicatedComponentKeys; +import org.sonar.db.version.v52.RemoveSnapshotLibraries; + +public class MigrationStepModule extends Module { + @Override + protected void configureModule() { + add( + // 4.5.1 + AddMissingCustomRuleParametersMigrationStep.class, + DeleteUnescapedActivities.class, + + // 5.0 + InsertProjectsAuthorizationUpdatedAtMigrationStep.class, + PopulateProjectsUuidColumnsMigrationStep.class, + ReplaceIssueFiltersProjectKeyByUuid.class, + FeedSnapshotSourcesUpdatedAt.class, + FeedFileSources.class, + FeedIssueLongDates.class, + RemoveSortFieldFromIssueFiltersMigrationStep.class, + + // 5.1 + FeedIssueTags.class, + FeedUsersLongDates.class, + RenameComponentRelatedParamsInIssueFilters.class, + CopyScmAccountsFromAuthorsToUsers.class, + FeedIssueChangesLongDates.class, + FeedAnalysisReportsLongDates.class, + UpdateProjectsModuleUuidPath.class, + FeedIssueComponentUuids.class, + FeedSnapshotsLongDates.class, + FeedIssuesLongDates.class, + FeedFileSourcesBinaryData.class, + FeedManualMeasuresLongDates.class, + FeedEventsLongDates.class, + AddNewCharacteristics.class, + RemovePermissionsOnModulesMigrationStep.class, + AddIssuesColumns.class, + DropIssuesColumns.class, + + // 5.2 + FeedProjectLinksComponentUuid.class, + FeedEventsComponentUuid.class, + MoveProjectProfileAssociation.class, + FeedFileSourcesDataType.class, + FeedMetricsBooleans.class, + AddManualMeasuresComponentUuidColumn.class, + FeedManualMeasuresComponentUuid.class, + RemoveSnapshotLibraries.class, + RemoveComponentLibraries.class, + RemoveDuplicatedComponentKeys.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 new file mode 100644 index 00000000000..ab9433d25e6 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/Select.java @@ -0,0 +1,167 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.util.Date; +import java.util.List; +import javax.annotation.CheckForNull; + +public interface Select extends SqlStatement<Select> { + + class Row { + private final ResultSet rs; + + Row(ResultSet rs) { + this.rs = rs; + } + + @CheckForNull + public Long getNullableLong(int columnIndex) throws SQLException { + long l = rs.getLong(columnIndex); + return rs.wasNull() ? null : l; + } + + public long getLong(int columnIndex) throws SQLException { + return rs.getLong(columnIndex); + } + + @CheckForNull + public Double getNullableDouble(int columnIndex) throws SQLException { + double d = rs.getDouble(columnIndex); + return rs.wasNull() ? null : d; + } + + public double getDouble(int columnIndex) throws SQLException { + return rs.getDouble(columnIndex); + } + + @CheckForNull + public Integer getNullableInt(int columnIndex) throws SQLException { + int i = rs.getInt(columnIndex); + return rs.wasNull() ? null : i; + } + + public int getInt(int columnIndex) throws SQLException { + return rs.getInt(columnIndex); + } + + @CheckForNull + public Boolean getNullableBoolean(int columnIndex) throws SQLException { + boolean b = rs.getBoolean(columnIndex); + return rs.wasNull() ? null : b; + } + + public boolean getBoolean(int columnIndex) throws SQLException { + return rs.getBoolean(columnIndex); + } + + @CheckForNull + public String getNullableString(int columnIndex) throws SQLException { + String s = rs.getString(columnIndex); + return rs.wasNull() ? null : s; + } + + public String getString(int columnIndex) throws SQLException { + return rs.getString(columnIndex); + } + + @CheckForNull + public Date getNullableDate(int columnIndex) throws SQLException { + Timestamp t = rs.getTimestamp(columnIndex); + return rs.wasNull() ? null : t; + } + + public Date getDate(int columnIndex) throws SQLException { + return rs.getTimestamp(columnIndex); + } + + @CheckForNull + public byte[] getNullableBytes(int columnIndex) throws SQLException { + byte[] b = rs.getBytes(columnIndex); + return rs.wasNull() ? null : b; + } + + public byte[] getBytes(int columnIndex) throws SQLException { + return rs.getBytes(columnIndex); + } + + @Override + public String toString() { + try { + ResultSetMetaData rsMetaData = rs.getMetaData(); + StringBuilder sb = new StringBuilder(); + for (int i = 1; i <= rsMetaData.getColumnCount(); i++) { + if (i > 1) { + sb.append(","); + } + sb.append(rsMetaData.getColumnLabel(i).toLowerCase()); + sb.append("="); + sb.append(rs.getObject(i)); + } + return sb.toString(); + } catch (Exception e) { + return "Unavailable: " + e.getMessage(); + } + } + } + + interface RowReader<T> { + T read(Row row) throws SQLException; + } + + class LongReader implements RowReader<Long> { + private LongReader() { + } + + @Override + public Long read(Row row) throws SQLException { + return row.getNullableLong(1); + } + } + + RowReader<Long> LONG_READER = new LongReader(); + + class StringReader implements RowReader<String> { + private StringReader() { + } + + @Override + public String read(Row row) throws SQLException { + return row.getNullableString(1); + } + } + + RowReader<String> STRING_READER = new StringReader(); + + interface RowHandler { + void handle(Row row) throws SQLException; + } + + <T> List<T> list(RowReader<T> reader) throws SQLException; + + @CheckForNull + <T> T get(RowReader<T> reader) throws SQLException; + + void scroll(RowHandler handler) throws SQLException; +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java b/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java new file mode 100644 index 00000000000..90031326979 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java @@ -0,0 +1,98 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.db.Database; + +class SelectImpl extends BaseSqlStatement<Select> implements Select { + + private SelectImpl(PreparedStatement pstmt) { + super(pstmt); + } + + @Override + public <T> List<T> list(Select.RowReader<T> reader) throws SQLException { + ResultSet rs = pstmt.executeQuery(); + Select.Row row = new Select.Row(rs); + try { + List<T> rows = new ArrayList<>(); + while (rs.next()) { + rows.add(reader.read(row)); + } + return rows; + } catch (Exception e) { + throw newExceptionWithRowDetails(row, e); + } finally { + DbUtils.closeQuietly(rs); + close(); + } + } + + @Override + public <T> T get(Select.RowReader<T> reader) throws SQLException { + ResultSet rs = pstmt.executeQuery(); + Select.Row row = new Select.Row(rs); + try { + if (rs.next()) { + return reader.read(row); + } + return null; + } catch (Exception e) { + throw newExceptionWithRowDetails(row, e); + } finally { + DbUtils.closeQuietly(rs); + close(); + } + } + + @Override + public void scroll(Select.RowHandler handler) throws SQLException { + ResultSet rs = pstmt.executeQuery(); + Select.Row row = new Select.Row(rs); + try { + while (rs.next()) { + handler.handle(row); + } + } catch (Exception e) { + throw newExceptionWithRowDetails(row, e); + } finally { + DbUtils.closeQuietly(rs); + close(); + } + } + + private IllegalStateException newExceptionWithRowDetails(Select.Row row, Exception e) { + return new IllegalStateException("Error during processing of row: [" + row + "]", e); + } + + static SelectImpl create(Database db, Connection connection, String sql) throws SQLException { + // TODO use DbClient#newScrollingSelectStatement() + PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + pstmt.setFetchSize(db.getDialect().getScrollDefaultFetchSize()); + return new SelectImpl(pstmt); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java b/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java new file mode 100644 index 00000000000..4418f659b16 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java @@ -0,0 +1,42 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.SQLException; +import java.util.Date; +import javax.annotation.Nullable; + +public interface SqlStatement<CHILD extends SqlStatement> { + CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException; + + CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException; + + CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException; + + CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException; + + CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException; + + CHILD setString(int columnIndex, @Nullable String value) throws SQLException; + + CHILD setBytes(int columnIndex, @Nullable byte[] data) throws SQLException; + + CHILD close(); +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/Upsert.java b/sonar-db/src/main/java/org/sonar/db/version/Upsert.java new file mode 100644 index 00000000000..31068959a1b --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/Upsert.java @@ -0,0 +1,33 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.SQLException; + +/** + * INSERT, UPDATE or DELETE + */ +public interface Upsert extends SqlStatement<Upsert> { + Upsert addBatch() throws SQLException; + + Upsert execute() throws SQLException; + + Upsert commit() throws SQLException; +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java b/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java new file mode 100644 index 00000000000..7f58429fac3 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java @@ -0,0 +1,70 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import org.sonar.db.BatchSession; + +public class UpsertImpl extends BaseSqlStatement<Upsert> implements Upsert { + + private long batchCount = 0L; + + private UpsertImpl(PreparedStatement pstmt) { + super(pstmt); + } + + @Override + public Upsert addBatch() throws SQLException { + pstmt.addBatch(); + pstmt.clearParameters(); + batchCount++; + if (batchCount % BatchSession.MAX_BATCH_SIZE == 0L) { + pstmt.executeBatch(); + pstmt.getConnection().commit(); + } + return this; + } + + @Override + public Upsert execute() throws SQLException { + if (batchCount == 0L) { + pstmt.execute(); + } else { + pstmt.executeBatch(); + } + return this; + } + + public long getBatchCount() { + return batchCount; + } + + @Override + public Upsert commit() throws SQLException { + pstmt.getConnection().commit(); + return this; + } + + static UpsertImpl create(Connection connection, String sql) throws SQLException { + return new UpsertImpl(connection.prepareStatement(sql)); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java new file mode 100644 index 00000000000..ec8aa9cdbe6 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java @@ -0,0 +1,143 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v451; + +import com.google.common.base.Predicate; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nullable; +import org.sonar.api.utils.System2; +import org.sonar.core.util.ProgressLogger; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.version.MigrationStep; +import org.sonar.db.version.v45.Migration45Mapper; +import org.sonar.db.version.v45.Rule; +import org.sonar.db.version.v45.RuleParameter; + +/** + * See http://jira.sonarsource.com/browse/SONAR-5575 + * + * Add missing parameters (with no value) on each custom rules + * + * @since 4.5.1 + */ +public class AddMissingCustomRuleParametersMigrationStep implements MigrationStep { + + private final DbClient db; + private final System2 system; + private final AtomicLong counter = new AtomicLong(0L); + + public AddMissingCustomRuleParametersMigrationStep(DbClient db, System2 system) { + this.db = db; + this.system = system; + } + + @Override + public void execute() { + ProgressLogger progress = ProgressLogger.create(getClass(), counter); + progress.start(); + + DbSession session = db.openSession(false); + try { + Migration45Mapper mapper = session.getMapper(Migration45Mapper.class); + + List<RuleParameter> templateRuleParams = mapper.selectAllTemplateRuleParameters(); + Multimap<Integer, RuleParameter> templateRuleParamsByRuleId = ArrayListMultimap.create(); + for (RuleParameter templateRuleParam : templateRuleParams) { + templateRuleParamsByRuleId.put(templateRuleParam.getRuleId(), templateRuleParam); + } + + List<Rule> customRules = mapper.selectAllCustomRules(); + Multimap<Integer, Integer> customRuleIdsByTemplateRuleId = HashMultimap.create(); + for (Rule customRule : customRules) { + customRuleIdsByTemplateRuleId.put(customRule.getTemplateId(), customRule.getId()); + } + + List<RuleParameter> customRuleParams = mapper.selectAllCustomRuleParameters(); + Multimap<Integer, RuleParameter> customRuleParamsByRuleId = ArrayListMultimap.create(); + for (RuleParameter customRuleParam : customRuleParams) { + customRuleParamsByRuleId.put(customRuleParam.getRuleId(), customRuleParam); + } + + // For each parameters of template rules, verify that each custom rules has the parameter + for (Integer templateRuleId : templateRuleParamsByRuleId.keySet()) { + for (RuleParameter templateRuleParam : templateRuleParamsByRuleId.get(templateRuleId)) { + // Each custom rule should have this parameter + insertCustomRuleParameterIfNotAlreadyExisting(templateRuleParam, templateRuleId, customRuleIdsByTemplateRuleId, customRuleParamsByRuleId, session); + } + } + + session.commit(); + + // log the total number of process rows + progress.log(); + } finally { + session.close(); + progress.stop(); + } + } + + private void insertCustomRuleParameterIfNotAlreadyExisting(RuleParameter templateRuleParam, Integer templateRuleId, + Multimap<Integer, Integer> customRuleIdsByTemplateRuleId, + Multimap<Integer, RuleParameter> customRuleParamsByRuleId, + DbSession session) { + for (Integer customRuleId : customRuleIdsByTemplateRuleId.get(templateRuleId)) { + if (!hasParameter(templateRuleParam.getName(), customRuleParamsByRuleId.get(customRuleId))) { + // Insert new custom rule parameter + session.getMapper(Migration45Mapper.class).insertRuleParameter(new RuleParameter() + .setRuleId(customRuleId) + .setRuleTemplateId(templateRuleId) + .setName(templateRuleParam.getName()) + .setDescription(templateRuleParam.getDescription()) + .setType(templateRuleParam.getType()) + ); + + // Update updated at date of custom rule in order to allow E/S indexation + session.getMapper(Migration45Mapper.class).updateRuleUpdateAt(customRuleId, new Date(system.now())); + + counter.getAndIncrement(); + } + } + } + + private boolean hasParameter(String parameter, Collection<RuleParameter> customRuleParams) { + return Iterables.any(customRuleParams, new MatchParameter(parameter)); + } + + private static class MatchParameter implements Predicate<RuleParameter> { + private final String parameter; + + public MatchParameter(String parameter) { + this.parameter = parameter; + } + + @Override + public boolean apply(@Nullable RuleParameter input) { + return input != null && input.getName().equals(parameter); + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java b/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java new file mode 100644 index 00000000000..d22ba822f47 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java @@ -0,0 +1,71 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v451; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +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; + +/** + * See http://jira.sonarsource.com/browse/SONAR-5758 + * + * @since 4.5.1 + */ +public class DeleteUnescapedActivities extends BaseDataChange { + + public DeleteUnescapedActivities(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select id,data_field from activities where log_type='QPROFILE'"); + massUpdate.update("delete from activities where id=?"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + String csv = row.getNullableString(2); + if (isUnescaped(csv)) { + update.setLong(1, row.getNullableLong(1)); + return true; + } + return false; + } + }); + } + + static boolean isUnescaped(@Nullable String csv) { + if (csv != null) { + String[] splits = StringUtils.split(csv, ';'); + for (String split : splits) { + if (StringUtils.countMatches(split, "=") != 1) { + return true; + } + } + } + return false; + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java new file mode 100644 index 00000000000..5a4c3921bf5 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java @@ -0,0 +1,283 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.util.Date; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.Select.RowReader; +import org.sonar.db.version.SqlStatement; + +/** + * Used in the Active Record Migration 714 + * + * @since 5.0 + */ +public class FeedFileSources extends BaseDataChange { + + private static final String SELECT_FILES_AND_MEASURES_SQL = "SELECT " + + "p.uuid, " + + "f.uuid, " + + "ss.data, " + + "ss.updated_at, " + + + // revisions_by_line + "m1.text_value, " + + "m1.measure_data, " + + + // authors_by_line + "m2.text_value, " + + "m2.measure_data, " + + + // dates_by_line + "m3.text_value, " + + "m3.measure_data, " + + + // hits_by_line + "m4.text_value, " + + "m4.measure_data, " + + + // cond_by_line + "m5.text_value, " + + "m5.measure_data, " + + + // cover_cond_by_line + "m6.text_value, " + + "m6.measure_data, " + + + // it_hits_by_line + "m7.text_value, " + + "m7.measure_data, " + + + // it_cond_by_line + "m8.text_value, " + + "m8.measure_data, " + + + // it_cover_cond_by_line + "m9.text_value, " + + "m9.measure_data, " + + + // overall_hits_by_line + "m10.text_value, " + + "m10.measure_data, " + + + // overall_cond_by_line + "m11.text_value, " + + "m11.measure_data, " + + + // overall_cover_cond_by_line + "m12.text_value, " + + "m12.measure_data, " + + + // duplication_data + "m13.text_value, " + + "m13.measure_data " + + + "FROM snapshots s " + + "JOIN snapshot_sources ss " + + "ON s.id = ss.snapshot_id AND s.islast = ? " + + "JOIN projects p " + + "ON s.root_project_id = p.id " + + "JOIN projects f " + + "ON s.project_id = f.id " + + "LEFT JOIN file_sources fs " + + "ON fs.file_uuid = f.uuid " + + "LEFT JOIN project_measures m1 " + + "ON m1.snapshot_id = s.id AND m1.metric_id = ? " + + "LEFT JOIN project_measures m2 " + + "ON m2.snapshot_id = s.id AND m2.metric_id = ? " + + "LEFT JOIN project_measures m3 " + + "ON m3.snapshot_id = s.id AND m3.metric_id = ? " + + "LEFT JOIN project_measures m4 " + + "ON m4.snapshot_id = s.id AND m4.metric_id = ? " + + "LEFT JOIN project_measures m5 " + + "ON m5.snapshot_id = s.id AND m5.metric_id = ? " + + "LEFT JOIN project_measures m6 " + + "ON m6.snapshot_id = s.id AND m6.metric_id = ? " + + "LEFT JOIN project_measures m7 " + + "ON m7.snapshot_id = s.id AND m7.metric_id = ? " + + "LEFT JOIN project_measures m8 " + + "ON m8.snapshot_id = s.id AND m8.metric_id = ? " + + "LEFT JOIN project_measures m9 " + + "ON m9.snapshot_id = s.id AND m9.metric_id = ? " + + "LEFT JOIN project_measures m10 " + + "ON m10.snapshot_id = s.id AND m10.metric_id = ? " + + "LEFT JOIN project_measures m11 " + + "ON m11.snapshot_id = s.id AND m11.metric_id = ? " + + "LEFT JOIN project_measures m12 " + + "ON m12.snapshot_id = s.id AND m12.metric_id = ? " + + "LEFT JOIN project_measures m13 " + + "ON m13.snapshot_id = s.id AND m13.metric_id = ? " + + "WHERE " + + "f.enabled = ? " + + "AND f.scope = 'FIL' " + + "AND p.scope = 'PRJ' AND p.qualifier = 'TRK' " + + "AND fs.file_uuid IS NULL"; + + private static final class FileSourceBuilder implements MassUpdate.Handler { + private final long now; + + public FileSourceBuilder(System2 system) { + now = system.now(); + } + + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + String projectUuid = row.getNullableString(1); + String fileUuid = row.getNullableString(2); + String source = StringUtils.defaultIfBlank(row.getNullableString(3), ""); + Date updatedAt = row.getNullableDate(4); + byte[] shortRevisions = row.getNullableBytes(5); + byte[] longRevisions = row.getNullableBytes(6); + byte[] shortAuthors = row.getNullableBytes(7); + byte[] longAuthors = row.getNullableBytes(8); + byte[] shortDates = row.getNullableBytes(9); + byte[] longDates = row.getNullableBytes(10); + byte[] shortUtHits = row.getNullableBytes(11); + byte[] longUtHits = row.getNullableBytes(12); + byte[] shortUtCond = row.getNullableBytes(13); + byte[] longUtCond = row.getNullableBytes(14); + byte[] shortUtCovCond = row.getNullableBytes(15); + byte[] longUtCovCond = row.getNullableBytes(16); + byte[] shortItHits = row.getNullableBytes(17); + byte[] longItHits = row.getNullableBytes(18); + byte[] shortItCond = row.getNullableBytes(19); + byte[] longItCond = row.getNullableBytes(20); + byte[] shortItCovCond = row.getNullableBytes(21); + byte[] longItCovCond = row.getNullableBytes(22); + byte[] shortOverallHits = row.getNullableBytes(23); + byte[] longOverallHits = row.getNullableBytes(24); + byte[] shortOverallCond = row.getNullableBytes(25); + byte[] longOverallCond = row.getNullableBytes(26); + byte[] shortOverallCovCond = row.getNullableBytes(27); + byte[] longOverallCovCond = row.getNullableBytes(28); + byte[] shortDuplicationData = row.getNullableBytes(29); + byte[] longDuplicationData = row.getNullableBytes(30); + + String[] sourceData = new FileSourceDto(source, + ofNullableBytes(shortRevisions, longRevisions), + ofNullableBytes(shortAuthors, longAuthors), + ofNullableBytes(shortDates, longDates), + ofNullableBytes(shortUtHits, longUtHits), + ofNullableBytes(shortUtCond, longUtCond), + ofNullableBytes(shortUtCovCond, longUtCovCond), + ofNullableBytes(shortItHits, longItHits), + ofNullableBytes(shortItCond, longItCond), + ofNullableBytes(shortItCovCond, longItCovCond), + ofNullableBytes(shortOverallHits, longOverallHits), + ofNullableBytes(shortOverallCond, longOverallCond), + ofNullableBytes(shortOverallCovCond, longOverallCovCond), + ofNullableBytes(shortDuplicationData, longDuplicationData) + ).getSourceData(); + + update.setString(1, projectUuid) + .setString(2, fileUuid) + .setLong(3, now) + .setLong(4, updatedAt == null ? now : updatedAt.getTime()) + .setString(5, sourceData[0]) + .setString(6, sourceData[1]) + .setString(7, ""); + + return true; + } + } + + private static String ofNullableBytes(@Nullable byte[] shortBytes, @Nullable byte[] longBytes) { + byte[] result; + if (shortBytes == null) { + if (longBytes == null) { + return ""; + } else { + result = longBytes; + } + } else { + result = shortBytes; + } + return new String(result, StandardCharsets.UTF_8); + } + + private final System2 system; + + public FeedFileSources(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + RowReader<Long> simpleLongReader = new RowReader<Long>() { + @Override + public Long read(Row row) throws SQLException { + Long longValue = row.getNullableLong(1); + return longValue == null ? Long.valueOf(0L) : longValue; + } + }; + Long revisionMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'revisions_by_line'").get(simpleLongReader); + Long authorMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'authors_by_line'").get(simpleLongReader); + Long datesMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'last_commit_datetimes_by_line'").get(simpleLongReader); + Long utCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'coverage_line_hits_data'").get(simpleLongReader); + Long utConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'conditions_by_line'").get(simpleLongReader); + Long utCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'covered_conditions_by_line'").get(simpleLongReader); + Long itCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_coverage_line_hits_data'").get(simpleLongReader); + Long itConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_conditions_by_line'").get(simpleLongReader); + Long itCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_covered_conditions_by_line'").get(simpleLongReader); + Long overallCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_coverage_line_hits_data'").get(simpleLongReader); + Long overallConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_conditions_by_line'").get(simpleLongReader); + Long overallCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_covered_conditions_by_line'").get(simpleLongReader); + Long duplicationDataMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'duplications_data'").get(simpleLongReader); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select(SELECT_FILES_AND_MEASURES_SQL) + .setBoolean(1, true) + .setLong(2, zeroIfNull(revisionMetricId)) + .setLong(3, zeroIfNull(authorMetricId)) + .setLong(4, zeroIfNull(datesMetricId)) + .setLong(5, zeroIfNull(utCoverageHitsByLineMetricId)) + .setLong(6, zeroIfNull(utConditionsByLineMetricId)) + .setLong(7, zeroIfNull(utCoveredConditionsByLineMetricId)) + .setLong(8, zeroIfNull(itCoverageHitsByLineMetricId)) + .setLong(9, zeroIfNull(itConditionsByLineMetricId)) + .setLong(10, zeroIfNull(itCoveredConditionsByLineMetricId)) + .setLong(11, zeroIfNull(overallCoverageHitsByLineMetricId)) + .setLong(12, zeroIfNull(overallConditionsByLineMetricId)) + .setLong(13, zeroIfNull(overallCoveredConditionsByLineMetricId)) + .setLong(14, zeroIfNull(duplicationDataMetricId)) + .setBoolean(15, true); + + massUpdate.update("INSERT INTO file_sources" + + "(project_uuid, file_uuid, created_at, updated_at, data, line_hashes, data_hash)" + + "VALUES " + + "(?, ?, ?, ?, ?, ?, ?)"); + massUpdate.rowPluralName("files"); + + massUpdate.execute(new FileSourceBuilder(system)); + } + + private static long zeroIfNull(@Nullable Long value) { + return value == null ? 0L : value.longValue(); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java new file mode 100644 index 00000000000..c9fd8cfbebe --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java @@ -0,0 +1,71 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedIssueLongDates extends BaseDataChange { + + private final System2 system; + + public FeedIssueLongDates(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system.now(); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT i.id, i.created_at, i.updated_at FROM issues i WHERE created_at_ms IS NULL"); + massUpdate.update("UPDATE issues SET created_at_ms=?, updated_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("issues"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + Date createdAt = row.getNullableDate(2); + Date updatedAt = row.getNullableDate(3); + + if (createdAt == null) { + update.setLong(1, now); + } else { + update.setLong(1, Math.min(now, createdAt.getTime())); + } + if (updatedAt == null) { + update.setLong(2, now); + } else { + update.setLong(2, Math.min(now, updatedAt.getTime())); + } + update.setLong(3, id); + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java new file mode 100644 index 00000000000..cc1e6859b5f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java @@ -0,0 +1,60 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import java.sql.SQLException; +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; + +/** + * Used in the Active Record Migration 712 + * + * @since 5.0 + */ +public class FeedSnapshotSourcesUpdatedAt extends BaseDataChange { + + public FeedSnapshotSourcesUpdatedAt(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT ss.id, s.build_date " + + "FROM snapshot_sources ss " + + "INNER JOIN snapshots s ON s.id = ss.snapshot_id " + + "WHERE ss.updated_at IS NULL"); + massUpdate.update("UPDATE snapshot_sources " + + "SET updated_at=? " + + "WHERE id=?"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setDate(1, row.getNullableDate(2)); + update.setLong(2, row.getNullableLong(1)); + return true; + } + }); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java new file mode 100644 index 00000000000..ca1683ffa2d --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java @@ -0,0 +1,212 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.io.StringReader; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.lang.StringUtils; +import org.codehaus.stax2.XMLInputFactory2; +import org.codehaus.staxmate.SMInputFactory; +import org.codehaus.staxmate.in.SMHierarchicCursor; +import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.utils.KeyValueFormat; +import org.sonar.api.utils.text.CsvWriter; + +import static java.nio.charset.StandardCharsets.UTF_8; + +class FileSourceDto { + + private static final String SPACE_CHARS = "\t\n\r "; + + private Iterator<String> sourceSplitter; + + private final Map<Integer, String> revisions; + private final Map<Integer, String> authors; + private final Map<Integer, String> dates; + private final Map<Integer, String> utHits; + private final Map<Integer, String> utConditions; + private final Map<Integer, String> utCoveredConditions; + private final Map<Integer, String> itHits; + private final Map<Integer, String> itConditions; + private final Map<Integer, String> itCoveredConditions; + private final Map<Integer, String> overallHits; + private final Map<Integer, String> overallConditions; + private final Map<Integer, String> overallCoveredConditions; + private final List<List<Block>> duplicationGroups; + + FileSourceDto(String source, String revisions, String authors, String dates, + String utHits, String utConditions, String utCoveredConditions, + String itHits, String itConditions, String itCoveredConditions, + String overallHits, String overallConditions, String overallCoveredConditions, String duplicationData) { + sourceSplitter = Splitter.onPattern("\r?\n|\r").split(source).iterator(); + this.revisions = KeyValueFormat.parseIntString(revisions); + this.authors = KeyValueFormat.parseIntString(authors); + this.dates = KeyValueFormat.parseIntString(dates); + this.utHits = KeyValueFormat.parseIntString(utHits); + this.utConditions = KeyValueFormat.parseIntString(utConditions); + this.utCoveredConditions = KeyValueFormat.parseIntString(utCoveredConditions); + this.itHits = KeyValueFormat.parseIntString(itHits); + this.itConditions = KeyValueFormat.parseIntString(itConditions); + this.itCoveredConditions = KeyValueFormat.parseIntString(itCoveredConditions); + this.overallHits = KeyValueFormat.parseIntString(overallHits); + this.overallConditions = KeyValueFormat.parseIntString(overallConditions); + this.overallCoveredConditions = KeyValueFormat.parseIntString(overallCoveredConditions); + this.duplicationGroups = StringUtils.isNotBlank(duplicationData) ? parseDuplicationData(duplicationData) : Collections.<List<Block>>emptyList(); + } + + String[] getSourceData() { + String highlighting = ""; + String symbolRefs = ""; + Map<Integer, String> duplicationsPerLine = computeDuplicationsPerLine(duplicationGroups); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + int line = 0; + String sourceLine = null; + CsvWriter csv = CsvWriter.of(new OutputStreamWriter(output, UTF_8)); + StringBuilder lineHashes = new StringBuilder(); + while (sourceSplitter.hasNext()) { + line++; + sourceLine = sourceSplitter.next(); + lineHashes.append(lineChecksum(sourceLine)).append("\n"); + csv.values(revisions.get(line), authors.get(line), dates.get(line), + utHits.get(line), utConditions.get(line), utCoveredConditions.get(line), + itHits.get(line), itConditions.get(line), itCoveredConditions.get(line), + overallHits.get(line), overallConditions.get(line), overallCoveredConditions.get(line), + highlighting, symbolRefs, duplicationsPerLine.get(line), sourceLine); + } + csv.close(); + return new String[] {new String(output.toByteArray(), UTF_8), lineHashes.toString()}; + } + + public static String lineChecksum(String line) { + String reducedLine = StringUtils.replaceChars(line, SPACE_CHARS, ""); + if (reducedLine.isEmpty()) { + return ""; + } + return DigestUtils.md5Hex(reducedLine); + } + + private Map<Integer, String> computeDuplicationsPerLine(List<List<Block>> duplicationGroups) { + Map<Integer, String> result = new HashMap<>(); + if (duplicationGroups.isEmpty()) { + return result; + } + Map<Integer, StringBuilder> dupPerLine = new HashMap<>(); + int blockId = 1; + for (List<Block> group : duplicationGroups) { + Block originBlock = group.get(0); + addBlock(blockId, originBlock, dupPerLine); + blockId++; + for (int i = 1; i < group.size(); i++) { + Block duplicate = group.get(i); + if (duplicate.resourceKey.equals(originBlock.resourceKey)) { + addBlock(blockId, duplicate, dupPerLine); + blockId++; + } + } + } + for (Map.Entry<Integer, StringBuilder> entry : dupPerLine.entrySet()) { + result.put(entry.getKey(), entry.getValue().toString()); + } + return result; + } + + private void addBlock(int blockId, Block block, Map<Integer, StringBuilder> dupPerLine) { + int currentLine = block.start; + for (int i = 0; i < block.length; i++) { + if (dupPerLine.get(currentLine) == null) { + dupPerLine.put(currentLine, new StringBuilder()); + } + if (dupPerLine.get(currentLine).length() > 0) { + dupPerLine.get(currentLine).append(','); + } + dupPerLine.get(currentLine).append(blockId); + currentLine++; + } + + } + + /** + * Parses data of {@link CoreMetrics#DUPLICATIONS_DATA}. + */ + private static List<List<Block>> parseDuplicationData(String data) { + ImmutableList.Builder<List<Block>> groups = ImmutableList.builder(); + try { + StringReader reader = new StringReader(data); + SMInputFactory inputFactory = initStax(); + SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader); + // <duplications> + rootC.advance(); + SMInputCursor groupsCursor = rootC.childElementCursor("g"); + while (groupsCursor.getNext() != null) { + // <g> + SMInputCursor blocksCursor = groupsCursor.childElementCursor("b"); + ImmutableList.Builder<Block> group = ImmutableList.builder(); + while (blocksCursor.getNext() != null) { + // <b> + String resourceKey = blocksCursor.getAttrValue("r"); + int firstLine = getAttrIntValue(blocksCursor, "s"); + int numberOfLines = getAttrIntValue(blocksCursor, "l"); + + group.add(new Block(resourceKey, firstLine, numberOfLines)); + } + groups.add(group.build()); + } + } catch (Exception e) { + // SONAR-6174 Ignore any issue while parsing duplication measure. There is nothing user can do and things will get solved after + // next analysis anyway + } + return groups.build(); + } + + private static int getAttrIntValue(SMInputCursor cursor, String attrName) throws XMLStreamException { + return cursor.getAttrIntValue(cursor.findAttrIndex(null, attrName)); + } + + private static SMInputFactory initStax() { + XMLInputFactory xmlFactory = XMLInputFactory2.newInstance(); + return new SMInputFactory(xmlFactory); + } + + private static class Block { + + final String resourceKey; + final int start; + final int length; + + public Block(String resourceKey, int s, int l) { + this.resourceKey = resourceKey; + this.start = s; + this.length = l; + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java new file mode 100644 index 00000000000..d088be4f190 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java @@ -0,0 +1,63 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import java.sql.SQLException; +import org.sonar.api.utils.System2; +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; + +/** + * Used in the Active Record Migration 716 + * + * @since 5.0 + */ +public class InsertProjectsAuthorizationUpdatedAtMigrationStep extends BaseDataChange { + + private final System2 system; + + public InsertProjectsAuthorizationUpdatedAtMigrationStep(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT p.id FROM projects p WHERE p.scope=? AND p.enabled=? and p.authorization_updated_at IS NULL").setString(1, "PRJ").setBoolean(2, true); + massUpdate.update("UPDATE projects SET authorization_updated_at=? WHERE id=?"); + massUpdate.rowPluralName("projects"); + massUpdate.execute(new MigrationHandler()); + } + + private class MigrationHandler implements MassUpdate.Handler { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + update.setLong(1, system.now()); + update.setLong(2, id); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java new file mode 100644 index 00000000000..181d0aeebf3 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java @@ -0,0 +1,194 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.ibatis.session.ResultContext; +import org.apache.ibatis.session.ResultHandler; +import org.sonar.api.resources.Scopes; +import org.sonar.api.utils.internal.Uuids; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.core.util.ProgressLogger; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.version.MigrationStep; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Maps.newHashMap; + +/** + * Used in the Active Record Migration 705 + * + * @since 5.0 + */ +public class PopulateProjectsUuidColumnsMigrationStep implements MigrationStep { + + private static final Logger LOG = Loggers.get(PopulateProjectsUuidColumnsMigrationStep.class); + + private final DbClient db; + private final AtomicLong counter = new AtomicLong(0L); + + public PopulateProjectsUuidColumnsMigrationStep(DbClient db) { + this.db = db; + } + + @Override + public void execute() { + ProgressLogger progress = ProgressLogger.create(getClass(), counter); + progress.start(); + + final DbSession readSession = db.openSession(false); + final DbSession writeSession = db.openSession(true); + try { + readSession.select("org.sonar.db.version.v50.Migration50Mapper.selectRootProjects", new ResultHandler() { + @Override + public void handleResult(ResultContext context) { + Component project = (Component) context.getResultObject(); + List<Component> components = readSession.getMapper(Migration50Mapper.class).selectComponentChildrenForProjects(project.getId()); + MigrationContext migrationContext = new MigrationContext(readSession, writeSession, project, components); + migrateEnabledComponents(migrationContext); + migrateDisabledComponents(migrationContext); + } + }); + writeSession.commit(true); + readSession.commit(true); + + migrateComponentsWithoutUuid(readSession, writeSession); + writeSession.commit(true); + + // log the total number of process rows + progress.log(); + } finally { + readSession.close(); + writeSession.close(); + progress.stop(); + } + } + + private void migrateEnabledComponents(MigrationContext migrationContext) { + saveComponent(migrationContext.writeSession, migrationContext.project); + for (Component component : migrationContext.componentsToMigrate) { + migrationContext.updateComponent(component); + if (Strings.isNullOrEmpty(component.getModuleUuidPath())) { + LOG.warn(String.format("Ignoring component id '%s' because the module uuid path could not be created", component.getId())); + } else { + migrationContext.updateComponent(component); + saveComponent(migrationContext.writeSession, component); + } + } + } + + private void migrateDisabledComponents(MigrationContext migrationContext) { + for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledDirectComponentChildrenForProjects(migrationContext.project.getId())) { + migrationContext.updateComponent(component); + saveComponent(migrationContext.writeSession, component); + } + for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledNoneDirectComponentChildrenForProjects( + migrationContext.project.getId())) { + migrationContext.updateComponent(component); + saveComponent(migrationContext.writeSession, component); + } + } + + private void migrateComponentsWithoutUuid(DbSession readSession, DbSession writeSession) { + for (Component component : readSession.getMapper(Migration50Mapper.class).selectComponentsWithoutUuid()) { + String uuid = Uuids.create(); + component.setUuid(uuid); + component.setProjectUuid(uuid); + saveComponent(writeSession, component); + } + } + + private void saveComponent(DbSession writeSession, Component component) { + writeSession.getMapper(Migration50Mapper.class).updateComponentUuids(component); + counter.getAndIncrement(); + } + + private static class MigrationContext { + private final DbSession readSession; + private final DbSession writeSession; + private final Component project; + private final Map<Long, Component> componentsBySnapshotId = newHashMap(); + private final Map<Long, String> uuidByComponentId = newHashMap(); + private final List<Component> componentsToMigrate = newArrayList(); + + private MigrationContext(DbSession readSession, DbSession writeSession, Component project, List<Component> components) { + this.readSession = readSession; + this.writeSession = writeSession; + this.project = project; + + project.setUuid(getOrCreateUuid(project)); + project.setProjectUuid(project.getUuid()); + + componentsBySnapshotId.put(project.getSnapshotId(), project); + for (Component component : components) { + componentsBySnapshotId.put(component.getSnapshotId(), component); + if (component.getUuid() == null) { + componentsToMigrate.add(component); + } + } + } + + public void updateComponent(Component component) { + component.setUuid(getOrCreateUuid(component)); + component.setProjectUuid(getOrCreateUuid(project)); + + String snapshotPath = component.getSnapshotPath(); + StringBuilder moduleUuidPath = new StringBuilder(); + String lastModuleUuid = null; + if (!Strings.isNullOrEmpty(snapshotPath)) { + for (String s : Splitter.on(".").omitEmptyStrings().split(snapshotPath)) { + Long snapshotId = Long.valueOf(s); + Component currentComponent = componentsBySnapshotId.get(snapshotId); + if (currentComponent != null && currentComponent.getScope().equals(Scopes.PROJECT)) { + lastModuleUuid = getOrCreateUuid(currentComponent); + moduleUuidPath.append(lastModuleUuid).append("."); + } + } + } + + if (moduleUuidPath.length() > 0 && lastModuleUuid != null) { + // Remove last '.' + moduleUuidPath.deleteCharAt(moduleUuidPath.length() - 1); + + component.setModuleUuidPath(moduleUuidPath.toString()); + component.setModuleUuid(lastModuleUuid); + } + } + + private String getOrCreateUuid(Component component) { + String existingUuid = component.getUuid(); + String uuid = existingUuid == null ? uuidByComponentId.get(component.getId()) : existingUuid; + if (uuid == null) { + String newUuid = Uuids.create(); + uuidByComponentId.put(component.getId(), newUuid); + return newUuid; + } + return uuid; + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java new file mode 100644 index 00000000000..50b1bd4c445 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java @@ -0,0 +1,88 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import com.google.common.collect.Lists; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +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; + +// TODO could be refactored to benefit from existing code of ReplaceIssueFiltersProjectKeyByUuid +// -> make any change of issue_filters easier +public class RemoveSortFieldFromIssueFiltersMigrationStep extends BaseDataChange { + + private static final char FIELD_SEPARATOR = '|'; + private static final String SORT_KEY = "sort="; + private static final String ASC_KEY = "asc="; + + private final System2 system; + + public RemoveSortFieldFromIssueFiltersMigrationStep(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select id,data from issue_filters where data like '%" + SORT_KEY + "%' or data like '%" + ASC_KEY + "%'"); + massUpdate.update("update issue_filters set data=?, updated_at=? where id=?"); + massUpdate.rowPluralName("issue filters"); + massUpdate.execute(new FilterHandler(new Date(system.now()))); + } + + private static class FilterHandler implements MassUpdate.Handler { + private final Date now; + + private FilterHandler(Date now) { + this.now = now; + } + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + String data = row.getNullableString(2); + String[] fields = StringUtils.split(data, FIELD_SEPARATOR); + + boolean found = false; + List<String> fieldsToKeep = Lists.newArrayList(); + for (String field : fields) { + if (field.startsWith(SORT_KEY) || field.startsWith(ASC_KEY)) { + found = true; + } else { + fieldsToKeep.add(field); + } + } + if (found) { + // data without 'sort' field + update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR)); + update.setDate(2, now); + update.setLong(3, row.getNullableLong(1)); + } + return found; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java new file mode 100644 index 00000000000..16e2ca6a2f3 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java @@ -0,0 +1,122 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v50; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Date; +import javax.annotation.Nullable; +import org.apache.commons.dbutils.DbUtils; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.Select; +import org.sonar.db.version.SqlStatement; + +/** + * Used in the Active Record Migration 710 + * + * @since 5.0 + */ +public class ReplaceIssueFiltersProjectKeyByUuid extends BaseDataChange { + + private final System2 system; + + public ReplaceIssueFiltersProjectKeyByUuid(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(final Context context) throws SQLException { + final Date now = new Date(system.now()); + + Connection connection = null; + PreparedStatement pstmt = null; + try { + connection = openConnection(); + pstmt = connection.prepareStatement("SELECT p.uuid as uuid FROM projects p WHERE p.kee=?"); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT f.id, f.data FROM issue_filters f WHERE f.data like '%componentRoots=%'"); + massUpdate.update("UPDATE issue_filters SET data=?, updated_at=? WHERE id=?"); + final PreparedStatement finalPstmt = pstmt; + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + String data = row.getNullableString(2); + if (data == null) { + return false; + } + update.setString(1, convertData(finalPstmt, data)); + update.setDate(2, now); + update.setLong(3, id); + return true; + } + }); + } finally { + DbUtils.closeQuietly(connection); + DbUtils.closeQuietly(pstmt); + } + } + + private String convertData(PreparedStatement pstmt, String data) throws SQLException { + StringBuilder newFields = new StringBuilder(); + String[] fields = data.split("\\|"); + for (int i = 0; i < fields.length; i++) { + String field = fields[i]; + if (field.contains("componentRoots")) { + String[] componentRootValues = field.split("="); + append(pstmt, newFields, componentRootValues.length == 2 ? componentRootValues[1] : null); + } else { + newFields.append(field); + } + if (i < fields.length - 1) { + newFields.append("|"); + } + } + return newFields.toString(); + } + + private static void append(PreparedStatement pstmt, StringBuilder newFields, @Nullable String projectKey) throws SQLException { + if (projectKey != null) { + pstmt.setString(1, projectKey); + ResultSet rs = null; + try { + rs = pstmt.executeQuery(); + if (rs.next()) { + String projectUuid = DatabaseUtils.getString(rs, "uuid"); + if (projectUuid != null) { + newFields.append("projectUuids=").append(projectUuid); + } + } + } finally { + DbUtils.closeQuietly(rs); + } + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java new file mode 100644 index 00000000000..09ffbdb94a8 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java @@ -0,0 +1,92 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.AddColumnsBuilder; +import org.sonar.db.version.DdlChange; + +/** + * Add the following columns to the issues table : + * - issue_creation_date_ms + * - issue_update_date_ms + * - issue_close_date_ms + * - tags + * - component_uuid + * - project_uuid + */ +public class AddIssuesColumns extends DdlChange { + + private final Database db; + + public AddIssuesColumns(Database db) { + super(db); + this.db = db; + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + context.execute(generateSql()); + } + + private String generateSql() { + return new AddColumnsBuilder(db.getDialect(), "issues") + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("issue_creation_date_ms") + .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setNullable(true) + ) + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("issue_update_date_ms") + .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setNullable(true) + ) + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("issue_close_date_ms") + .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER) + .setNullable(true) + ) + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("tags") + .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setLimit(4000) + .setNullable(true)) + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("component_uuid") + .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setLimit(50) + .setNullable(true)) + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("project_uuid") + .setType(AddColumnsBuilder.ColumnDef.Type.STRING) + .setLimit(50) + .setNullable(true)) + .build(); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java b/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java new file mode 100644 index 00000000000..2623dcb75e0 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java @@ -0,0 +1,330 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.System2; +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.Select; + +/** + * See http://jira.sonarsource.com/browse/SONAR-6187 + * + * Add a new Characteristic 'Usability' with 2 sub-characteristics 'Accessibility' and 'Ease of Use' + * and add a new sub-characteristic 'Compliance' for all characteristics. + * + * Nothing will be done if there's no characteristics in db, as they're all gonna be created by {@link org.sonar.server.startup.RegisterDebtModel} + * + * Before 4.3 the characteristics table contains requirements, then when selecting characteristics we should not forget to exclude them (with a filter on rule_id IS NULL) + * + */ +public class AddNewCharacteristics extends BaseDataChange { + + private static final Logger LOGGER = Loggers.get(AddNewCharacteristics.class); + + private static final String COMPLIANCE_NAME = "Compliance"; + private static final String COMPLIANCE_KEY_SUFFIX = "_COMPLIANCE"; + + private static final String SECURITY_KEY = "SECURITY"; + private static final String USABILITY_KEY = "USABILITY"; + + private static final String ERROR_SUFFIX = "Please restore your DB backup, start the previous version of SonarQube " + + "and update your SQALE model to fix this issue before trying again to run the migration."; + + private final System2 system; + + public AddNewCharacteristics(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + CharacteristicsContext characteristicsContext = new CharacteristicsContext(context, system); + + // On an empty DB, there are no characteristics, they're all gonna be created after in RegisterDebtModel + if (!characteristicsContext.characteristics().isEmpty()) { + int usabilityOder = moveCharacteristicsDownToBeAbleToInsertUsability(characteristicsContext); + createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(characteristicsContext, usabilityOder); + + createSubCharacteristic(characteristicsContext, "REUSABILITY" + COMPLIANCE_KEY_SUFFIX, "Reusability " + COMPLIANCE_NAME, "REUSABILITY"); + createSubCharacteristic(characteristicsContext, "PORTABILITY" + COMPLIANCE_KEY_SUFFIX, "Portability " + COMPLIANCE_NAME, "PORTABILITY"); + createSubCharacteristic(characteristicsContext, "MAINTAINABILITY" + COMPLIANCE_KEY_SUFFIX, "Maintainability " + COMPLIANCE_NAME, "MAINTAINABILITY"); + createSubCharacteristic(characteristicsContext, SECURITY_KEY + COMPLIANCE_KEY_SUFFIX, "Security " + COMPLIANCE_NAME, SECURITY_KEY); + createSubCharacteristic(characteristicsContext, "EFFICIENCY" + COMPLIANCE_KEY_SUFFIX, "Efficiency " + COMPLIANCE_NAME, "EFFICIENCY"); + createSubCharacteristic(characteristicsContext, "CHANGEABILITY" + COMPLIANCE_KEY_SUFFIX, "Changeability " + COMPLIANCE_NAME, "CHANGEABILITY"); + createSubCharacteristic(characteristicsContext, "RELIABILITY" + COMPLIANCE_KEY_SUFFIX, "Reliability " + COMPLIANCE_NAME, "RELIABILITY"); + createSubCharacteristic(characteristicsContext, "TESTABILITY" + COMPLIANCE_KEY_SUFFIX, "Testability " + COMPLIANCE_NAME, "TESTABILITY"); + } + } + + /** + * If the characteristic 'Security' exists, the new characteristic 'Usability' should be inserted just below it, + * so every existing characteristics below Security should move down. + * + * If the characteristic 'Security' does not exists, the new characteristic 'Usability' should be the first one, + * so every existing characteristics should move down. + * + * If the characteristic 'Usability' is already at the right place, nothing will be done. + */ + private static int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException { + Characteristic security = characteristicsContext.findCharacteristicByKey(SECURITY_KEY); + Characteristic usability = characteristicsContext.findCharacteristicByKey(USABILITY_KEY); + + int usabilityOder = 1; + int indexToStart = 0; + if (security != null) { + indexToStart = characteristicsContext.characteristics().indexOf(security) + 1; + usabilityOder = security.getOrder() + 1; + } + + if (usability == null || usability.getOrder() != usabilityOder) { + // Move root characteristics one step lower + for (int i = indexToStart; i < characteristicsContext.characteristics().size(); i++) { + Characteristic characteristic = characteristicsContext.characteristics().get(i); + if (characteristic.getParentId() == null) { + characteristicsContext.updateCharacteristicOrder(characteristic.getKey(), characteristic.getOrder() + 1); + } + } + } + return usabilityOder; + } + + private void createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(CharacteristicsContext characteristicsContext, int newUsabilityOrder) + throws SQLException { + String usabilityKey = USABILITY_KEY; + Characteristic usability = characteristicsContext.findCharacteristicByKey(usabilityKey); + if (usability != null) { + if (usability.getOrder() != newUsabilityOrder) { + usability.setOrder(newUsabilityOrder); + characteristicsContext.updateCharacteristicOrder(usability.getKey(), usability.getOrder()); + } + } else { + usability = new Characteristic().setKey(usabilityKey).setName("Usability").setOrder(newUsabilityOrder); + characteristicsContext.insertCharacteristic(usability); + } + + createSubCharacteristic(characteristicsContext, "USABILITY_ACCESSIBILITY", "Accessibility", usabilityKey); + createSubCharacteristic(characteristicsContext, "USABILITY_EASE_OF_USE", "Ease of Use", usabilityKey); + createSubCharacteristic(characteristicsContext, USABILITY_KEY + COMPLIANCE_KEY_SUFFIX, "Usability " + COMPLIANCE_NAME, usabilityKey); + } + + private void createSubCharacteristic(CharacteristicsContext characteristicsContext, + String subCharacteristicKey, String subCharacteristicName, String parentKey) throws SQLException { + Characteristic parent = characteristicsContext.findCharacteristicByKey(parentKey); + if (parent != null) { + Characteristic subCharacteristic = characteristicsContext.findSubCharacteristicByKey(subCharacteristicKey, parent); + if (subCharacteristic == null) { + characteristicsContext.insertCharacteristic(new Characteristic().setKey(subCharacteristicKey).setName(subCharacteristicName).setParentId(parent.getId())); + } + } + // If the characteristic parent does not exits, the sub-characteristic is not added + } + + private static class Characteristic { + private Integer id; + private String key; + private String name; + private Integer order; + private Integer parentId; + + public Integer getId() { + return id; + } + + public Characteristic setId(Integer id) { + this.id = id; + return this; + } + + public String getKey() { + return key; + } + + public Characteristic setKey(String key) { + this.key = key; + return this; + } + + public String getName() { + return name; + } + + public Characteristic setName(String name) { + this.name = name; + return this; + } + + /** + * On a characteristic, the order can never be null + */ + public Integer getOrder() { + return parentId == null && order != null ? order : null; + } + + public Characteristic setOrder(@Nullable Integer order) { + this.order = order; + return this; + } + + @CheckForNull + public Integer getParentId() { + return parentId; + } + + public Characteristic setParentId(@Nullable Integer parentId) { + this.parentId = parentId; + return this; + } + } + + private static class CharacteristicsContext { + private final System2 system; + Context context; + Date now; + List<Characteristic> characteristics; + + public CharacteristicsContext(Context context, System2 system) throws SQLException { + this.context = context; + this.system = system; + init(); + } + + private void init() throws SQLException { + now = new Date(system.now()); + characteristics = selectEnabledCharacteristics(); + } + + public List<Characteristic> characteristics() { + return characteristics; + } + + @CheckForNull + public Characteristic findCharacteristicByKey(final String key) { + Characteristic characteristic = Iterables.find(characteristics, new CharacteristicKey(key), null); + if (characteristic != null && characteristic.getParentId() != null) { + throw MessageException.of(String.format("'%s' must be a characteristic. " + ERROR_SUFFIX, characteristic.getName())); + } + return characteristic; + } + + @CheckForNull + public Characteristic findSubCharacteristicByKey(final String key, Characteristic parent) { + Characteristic characteristic = Iterables.find(characteristics, new CharacteristicKey(key), null); + if (characteristic != null) { + Integer parentId = characteristic.getParentId(); + if (parentId == null) { + throw MessageException.of(String.format("'%s' must be a sub-characteristic. " + ERROR_SUFFIX, characteristic.getName())); + } else if (!parentId.equals(parent.getId())) { + throw MessageException.of(String.format("'%s' must be defined under '%s'. " + ERROR_SUFFIX, characteristic.getName(), parent.getName())); + } + } + return characteristic; + } + + private List<Characteristic> selectEnabledCharacteristics() throws SQLException { + return context.prepareSelect( + // Exclude requirements (to not fail when coming from a version older than 4.3) + "SELECT c.id, c.kee, c.name, c.characteristic_order, c.parent_id FROM characteristics c WHERE c.enabled=? AND c.rule_id IS NULL ORDER BY c.characteristic_order") + .setBoolean(1, true) + .list(new CharacteristicReader()); + } + + private int selectCharacteristicId(String key) throws SQLException { + Long id = context.prepareSelect( + "SELECT c.id FROM characteristics c WHERE c.kee = ? AND c.enabled=?") + .setString(1, key) + .setBoolean(2, true) + .get(Select.LONG_READER); + if (id != null) { + return id.intValue(); + } else { + throw new IllegalStateException(String.format("Characteristic '%s' could not be inserted", key)); + } + } + + public void insertCharacteristic(Characteristic characteristic) throws SQLException { + if (characteristic.getParentId() == null) { + LOGGER.info("Insert new characteristic '{}'", characteristic.getKey()); + } else { + LOGGER.info("Insert new sub characteristic '{}'", characteristic.getKey()); + } + + context.prepareUpsert("INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at) VALUES (?, ?, ?, ?, ?, ?)") + .setString(1, characteristic.getKey()) + .setString(2, characteristic.getName()) + .setInt(3, characteristic.getParentId()) + .setInt(4, characteristic.getOrder()) + .setBoolean(5, true) + .setDate(6, now) + .execute() + .commit(); + characteristic.setId(selectCharacteristicId(characteristic.getKey())); + + characteristics.add(characteristic); + } + + public void updateCharacteristicOrder(String key, Integer order) throws SQLException { + LOGGER.info("Update characteristic '{}' order to {}", key, order); + + context.prepareUpsert("UPDATE characteristics SET characteristic_order=?, updated_at=? WHERE kee=?") + .setInt(1, order) + .setDate(2, now) + .setString(3, key) + .execute() + .commit(); + } + + private static class CharacteristicReader implements Select.RowReader<Characteristic> { + @Override + public Characteristic read(Select.Row row) throws SQLException { + return new Characteristic() + .setId(row.getInt(1)) + .setKey(row.getString(2)) + .setName(row.getString(3)) + .setOrder(row.getNullableInt(4)) + .setParentId(row.getNullableInt(5)); + } + } + } + + private static class CharacteristicKey implements Predicate<Characteristic> { + private final String key; + + public CharacteristicKey(String key) { + this.key = key; + } + + @Override + public boolean apply(@Nullable Characteristic input) { + return input != null && input.key.equals(key); + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java b/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java new file mode 100644 index 00000000000..0ae65deb79f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java @@ -0,0 +1,172 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.base.Joiner; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import java.sql.SQLException; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.CheckForNull; +import org.sonar.api.utils.System2; +import org.sonar.core.util.ProgressLogger; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.Select; +import org.sonar.db.version.Upsert; +import org.sonar.db.version.UpsertImpl; + +import static com.google.common.collect.Lists.newArrayList; + +public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange { + + private static final char SCM_ACCOUNTS_SEPARATOR = '\n'; + + private final System2 system; + private final AtomicLong counter = new AtomicLong(0L); + + public CopyScmAccountsFromAuthorsToUsers(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(final Context context) throws SQLException { + ProgressLogger progress = ProgressLogger.create(getClass(), counter); + progress.start(); + final Long now = system.now(); + + try { + final Multimap<Long, String> authorsByPersonId = ArrayListMultimap.create(); + context.prepareSelect("SELECT a.person_id, a.login FROM authors a," + + " (SELECT person_id, COUNT(*) AS nb FROM authors GROUP BY person_id HAVING COUNT(*) > 1) group_by_person" + + " WHERE a.person_id = group_by_person.person_id " + ).scroll(new AuthorsByPersonIdHandler(authorsByPersonId)); + + Upsert update = context.prepareUpsert("UPDATE users SET scm_accounts = ?, updated_at = ? WHERE id = ?"); + for (Long personId : authorsByPersonId.keySet()) { + List<String> authors = newArrayList(authorsByPersonId.get(personId)); + List<User> users = selectUsersFromLoginOrEmail(context, authors); + if (users.size() == 1) { + User user = users.get(0); + if (authors.contains(user.login)) { + authors.remove(user.login); + } + if (authors.contains(user.email)) { + authors.remove(user.email); + } + if (!authors.isEmpty()) { + update + .setString(1, encodeScmAccounts(authors)) + .setLong(2, now) + .setLong(3, user.id) + .addBatch(); + counter.getAndIncrement(); + } + } + } + if (((UpsertImpl) update).getBatchCount() > 0L) { + update.execute().commit(); + } + update.close(); + + progress.log(); + } finally { + progress.stop(); + } + } + + private List<User> selectUsersFromLoginOrEmail(Context context, Collection<String> authors) throws SQLException { + final List<User> users = newArrayList(); + StringBuilder sql = new StringBuilder("SELECT u.id, u.login, u.email, u.scm_accounts FROM users u WHERE u.active=? AND ("); + for (int i = 0; i < authors.size(); i++) { + if (i < authors.size() - 1) { + sql.append("u.login=? OR u.email=? OR "); + } else { + sql.append("u.login=? OR u.email=?)"); + } + } + Select select = context.prepareSelect(sql.toString()); + select.setBoolean(1, true); + int currentIndex = 1; + for (String author : authors) { + currentIndex++; + select.setString(currentIndex, author); + currentIndex++; + select.setString(currentIndex, author); + } + + select.scroll(new UsersHandler(users)); + return users; + } + + @CheckForNull + private static String encodeScmAccounts(List<String> scmAccounts) { + if (scmAccounts.isEmpty()) { + return null; + } + return SCM_ACCOUNTS_SEPARATOR + Joiner.on(SCM_ACCOUNTS_SEPARATOR).join(scmAccounts) + SCM_ACCOUNTS_SEPARATOR; + } + + private static class User { + Long id; + String login; + String email; + String scmAccounts; + + User(Long id, String login, String email, String scmAccounts) { + this.id = id; + this.login = login; + this.email = email; + this.scmAccounts = scmAccounts; + } + } + + private static class AuthorsByPersonIdHandler implements Select.RowHandler { + + private final Multimap<Long, String> authorsByPersonId; + + private AuthorsByPersonIdHandler(Multimap<Long, String> authorsByPersonId) { + this.authorsByPersonId = authorsByPersonId; + } + + @Override + public void handle(Select.Row row) throws SQLException { + authorsByPersonId.put(row.getNullableLong(1), row.getNullableString(2)); + } + } + + private static class UsersHandler implements Select.RowHandler { + + private final List<User> users; + + private UsersHandler(List<User> users) { + this.users = users; + } + + @Override + public void handle(Select.Row row) throws SQLException { + users.add(new User(row.getNullableLong(1), row.getNullableString(2), row.getNullableString(3), row.getNullableString(4))); + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java new file mode 100644 index 00000000000..a8a0557e6a8 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.annotations.VisibleForTesting; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.DdlChange; +import org.sonar.db.version.DropColumnsBuilder; + +/** + * Drop the following columns from the issues table : + * - issue_creation_date + * - issue_update_date + * - issue_close_date + * - component_id + * - root_component_id + */ +public class DropIssuesColumns extends DdlChange { + + private final Database db; + + public DropIssuesColumns(Database db) { + super(db); + this.db = db; + } + + @Override + public void execute(Context context) throws SQLException { + context.execute(generateSql()); + } + + @VisibleForTesting + String generateSql() { + return new DropColumnsBuilder(db.getDialect(), "issues", + "issue_creation_date", "issue_update_date", "issue_close_date", "component_id", "root_component_id") + .build(); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java new file mode 100644 index 00000000000..42434d8d727 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java @@ -0,0 +1,68 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedAnalysisReportsLongDates extends BaseDataChange { + + private final System2 system; + + public FeedAnalysisReportsLongDates(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system.now(); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT a.created_at, a.updated_at, a.started_at, a.finished_at, a.id FROM analysis_reports a WHERE created_at_ms IS NULL"); + massUpdate.update("UPDATE analysis_reports SET created_at_ms=?, updated_at_ms=?, started_at_ms=?, finished_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("analysis_reports"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Date createdAt = row.getNullableDate(1); + Date updatedAt = row.getNullableDate(2); + Date startedAt = row.getNullableDate(3); + Date finishedAt = row.getNullableDate(4); + Long id = row.getNullableLong(5); + + update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime())); + update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime())); + update.setLong(3, startedAt == null ? null : startedAt.getTime()); + update.setLong(4, finishedAt == null ? null : finishedAt.getTime()); + update.setLong(5, id); + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java new file mode 100644 index 00000000000..151fa7c9cff --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java @@ -0,0 +1,75 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedEventsLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedEventsLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT e.event_date, e.created_at, e.id FROM events e WHERE event_date_ms IS NULL"); + massUpdate + .update("UPDATE events SET event_date_ms=?, created_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("events"); + massUpdate.execute(new EventDateHandler(system2.now())); + } + + private static class EventDateHandler implements MassUpdate.Handler { + + private final long now; + + public EventDateHandler(long now) { + this.now = now; + } + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Date eventDate = row.getNullableDate(1); + long eventTime = eventDate == null ? now : Math.min(now, eventDate.getTime()); + update.setLong(1, eventTime); + Date createdAt = row.getNullableDate(2); + update.setLong(2, createdAt == null ? eventTime : Math.min(now, createdAt.getTime())); + + Long id = row.getNullableLong(3); + update.setLong(3, id); + + return true; + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java new file mode 100644 index 00000000000..7e3d5e5ef74 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java @@ -0,0 +1,163 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.base.Function; +import com.google.common.base.Splitter; +import com.google.common.collect.Iterables; +import java.sql.SQLException; +import java.util.Iterator; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.DateUtils; +import org.sonar.db.Database; +import org.sonar.db.source.FileSourceDto; +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 org.sonar.server.source.db.FileSourceDb; + +public class FeedFileSourcesBinaryData extends BaseDataChange { + + public FeedFileSourcesBinaryData(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("issues"); + update.select("SELECT id,data FROM file_sources WHERE binary_data is null"); + update.update("UPDATE file_sources SET binary_data=? WHERE id=?"); + update.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long fileSourceId = row.getNullableLong(1); + update.setBytes(1, toBinary(fileSourceId, row.getNullableString(2))); + update.setLong(2, fileSourceId); + return true; + } + }); + } + + private byte[] toBinary(Long fileSourceId, @Nullable String data) { + FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder(); + CSVParser parser = null; + try { + if (data != null) { + parser = CSVParser.parse(data, CSVFormat.DEFAULT); + Iterator<CSVRecord> rows = parser.iterator(); + int line = 1; + while (rows.hasNext()) { + CSVRecord row = rows.next(); + if (row.size() == 16) { + + FileSourceDb.Line.Builder lineBuilder = dataBuilder.addLinesBuilder(); + lineBuilder.setLine(line); + String s = row.get(0); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmRevision(s); + } + s = row.get(1); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmAuthor(s); + } + s = row.get(2); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setScmDate(DateUtils.parseDateTimeQuietly(s).getTime()); + } + s = row.get(3); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtLineHits(Integer.parseInt(s)); + } + s = row.get(4); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtConditions(Integer.parseInt(s)); + } + s = row.get(5); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setUtCoveredConditions(Integer.parseInt(s)); + } + s = row.get(6); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItLineHits(Integer.parseInt(s)); + } + s = row.get(7); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItConditions(Integer.parseInt(s)); + } + s = row.get(8); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setItCoveredConditions(Integer.parseInt(s)); + } + s = row.get(9); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallLineHits(Integer.parseInt(s)); + } + s = row.get(10); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallConditions(Integer.parseInt(s)); + } + s = row.get(11); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setOverallCoveredConditions(Integer.parseInt(s)); + } + s = row.get(12); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setHighlighting(s); + } + s = row.get(13); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.setSymbols(s); + } + s = row.get(14); + if (StringUtils.isNotEmpty(s)) { + lineBuilder.addAllDuplication(splitIntegers(s)); + } + s = row.get(15); + if (s != null) { + lineBuilder.setSource(s); + } + } + line++; + } + } + return FileSourceDto.encodeSourceData(dataBuilder.build()); + } catch (Exception e) { + throw new IllegalStateException("Invalid FILE_SOURCES.DATA on row with ID " + fileSourceId + ": " + data, e); + } finally { + IOUtils.closeQuietly(parser); + } + } + + private static Iterable<Integer> splitIntegers(String s) { + return Iterables.transform(Splitter.on(',').omitEmptyStrings().trimResults().split(s), new Function<String, Integer>() { + @Override + public Integer apply(@Nonnull String input) { + return Integer.parseInt(input); + } + }); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java new file mode 100644 index 00000000000..78c6a81b334 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedIssueChangesLongDates extends BaseDataChange { + + private final System2 system; + + public FeedIssueChangesLongDates(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system.now(); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT i.created_at, i.updated_at, i.issue_change_creation_date, i.id FROM issue_changes i WHERE created_at_ms IS NULL"); + massUpdate.update("UPDATE issue_changes SET created_at_ms=?, updated_at_ms=?, issue_change_creation_date_ms=? WHERE id=?"); + massUpdate.rowPluralName("issue_changes"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Date createdAt = row.getNullableDate(1); + Date updatedAt = row.getNullableDate(2); + Date functionalCreatedAt = row.getNullableDate(3); + Long id = row.getNullableLong(4); + + update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime())); + update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime())); + update.setLong(3, functionalCreatedAt == null ? null : functionalCreatedAt.getTime()); + update.setLong(4, id); + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java new file mode 100644 index 00000000000..0bfe58afd52 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java @@ -0,0 +1,59 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.MassUpdate.Handler; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.SqlStatement; + +public class FeedIssueComponentUuids extends BaseDataChange { + + public FeedIssueComponentUuids(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("issues"); + update.select( + "SELECT c.uuid, c.project_uuid, i.id " + + "FROM issues i " + + "INNER JOIN projects c ON i.component_id=c.id " + + "WHERE i.component_uuid is null"); + update.update("UPDATE issues SET component_uuid=?, project_uuid=? WHERE id=?"); + update.execute(new SqlRowHandler()); + } + + private static class SqlRowHandler implements Handler { + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + update.setString(1, row.getNullableString(1)); + update.setString(2, row.getNullableString(2)); + update.setLong(3, row.getNullableLong(3)); + + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java new file mode 100644 index 00000000000..f84dd6b3688 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java @@ -0,0 +1,84 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.base.Joiner; +import com.google.common.collect.Maps; +import java.sql.SQLException; +import java.util.Map; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.MassUpdate.Handler; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.Select.RowHandler; +import org.sonar.db.version.SqlStatement; + +/** + * SONAR-5897 + */ +public class FeedIssueTags extends BaseDataChange { + + private static final Joiner TAG_JOINER = Joiner.on(',').skipNulls(); + + private final long now; + + public FeedIssueTags(Database db, System2 system) { + super(db); + this.now = system.now(); + } + + @Override + public void execute(Context context) throws SQLException { + + final Map<Integer, String> tagsByRuleId = Maps.newHashMap(); + context.prepareSelect("SELECT id, system_tags, tags FROM rules").scroll(new RowHandler() { + @Override + public void handle(Row row) throws SQLException { + Integer id = row.getNullableInt(1); + tagsByRuleId.put(id, StringUtils.trimToNull(TAG_JOINER.join( + StringUtils.trimToNull(row.getNullableString(2)), + StringUtils.trimToNull(row.getNullableString(3))))); + } + }); + + MassUpdate update = context.prepareMassUpdate().rowPluralName("issues"); + update.select("SELECT id, rule_id FROM issues WHERE tags IS NULL"); + update.update("UPDATE issues SET tags = ?, updated_at = ? WHERE id = ?"); + update.execute(new Handler() { + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + Integer ruleId = row.getNullableInt(2); + boolean updated = false; + if (tagsByRuleId.get(ruleId) != null) { + updated = true; + update.setString(1, tagsByRuleId.get(ruleId)); + update.setLong(2, now); + update.setLong(3, id); + } + return updated; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java new file mode 100644 index 00000000000..66ba13b9248 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedIssuesLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedIssuesLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT i.issue_creation_date, i.issue_update_date, i.issue_close_date, i.id FROM issues i WHERE issue_creation_date_ms IS NULL"); + massUpdate + .update("UPDATE issues SET issue_creation_date_ms=?, issue_update_date_ms=?, issue_close_date_ms=? WHERE id=?"); + massUpdate.rowPluralName("issues"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + for (int i = 1; i <= 3; i++) { + Date date = row.getNullableDate(i); + update.setLong(i, date == null ? null : Math.min(now, date.getTime())); + } + + Long id = row.getNullableLong(4); + update.setLong(4, id); + + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java new file mode 100644 index 00000000000..cb250c840d0 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedManualMeasuresLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedManualMeasuresLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT m.created_at, m.updated_at, m.id FROM manual_measures m WHERE created_at_ms IS NULL"); + massUpdate + .update("UPDATE manual_measures SET created_at_ms=?, updated_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("manual measures"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + for (int i = 1; i <= 2; i++) { + Date date = row.getNullableDate(i); + update.setLong(i, date == null ? null : Math.min(now, date.getTime())); + } + + Long id = row.getNullableLong(3); + update.setLong(3, id); + + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java new file mode 100644 index 00000000000..5e5ae4f149b --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java @@ -0,0 +1,66 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedSnapshotsLongDates extends BaseDataChange { + + private final System2 system2; + + public FeedSnapshotsLongDates(Database db, System2 system2) { + super(db); + this.system2 = system2; + } + + @Override + public void execute(Context context) throws SQLException { + final long now = system2.now(); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate + .select("SELECT s.created_at, s.build_date, s.period1_date, s.period2_date, s.period3_date, s.period4_date, s.period5_date, s.id FROM snapshots s WHERE created_at_ms IS NULL"); + massUpdate + .update("UPDATE snapshots SET created_at_ms=?, build_date_ms=?, period1_date_ms=?, period2_date_ms=?, period3_date_ms=?, period4_date_ms=?, period5_date_ms=? WHERE id=?"); + massUpdate.rowPluralName("snapshots"); + massUpdate.execute(new MassUpdate.Handler() { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + for (int i = 1; i <= 7; i++) { + Date date = row.getNullableDate(i); + update.setLong(i, date == null ? null : Math.min(now, date.getTime())); + } + + Long id = row.getNullableLong(8); + update.setLong(8, id); + + return true; + } + }); + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java new file mode 100644 index 00000000000..0196e62fc57 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java @@ -0,0 +1,78 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import java.util.Date; +import org.sonar.api.utils.System2; +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; + +public class FeedUsersLongDates extends BaseDataChange { + + private final System2 system; + + public FeedUsersLongDates(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT u.id, u.created_at, u.updated_at FROM users u WHERE created_at_ms IS NULL"); + massUpdate.update("UPDATE users SET created_at_ms=?, updated_at_ms=? WHERE id=?"); + massUpdate.rowPluralName("users"); + massUpdate.execute(new RowHandler(system.now())); + } + + private static class RowHandler implements MassUpdate.Handler { + + private final long now; + + private RowHandler(long now) { + this.now = now; + } + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + Date createdAt = row.getNullableDate(2); + Date updatedAt = row.getNullableDate(3); + + if (createdAt == null) { + update.setLong(1, now); + } else { + update.setLong(1, Math.min(now, createdAt.getTime())); + } + if (updatedAt == null) { + update.setLong(2, now); + } else { + update.setLong(2, Math.min(now, updatedAt.getTime())); + } + update.setLong(3, id); + return true; + } + } + +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java new file mode 100644 index 00000000000..bb70c544e89 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java @@ -0,0 +1,70 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +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; + +/** + * See http://jira.sonarsource.com/browse/SONAR-5596 + * + * It's no possible to set permission on a module or a sub-view, but the batch was setting default permission on it on their creation. + * As now it's no more the case, we need to purge this useless data. + * + * @since 5.1 + */ +public class RemovePermissionsOnModulesMigrationStep extends BaseDataChange { + + public RemovePermissionsOnModulesMigrationStep(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + removeUserRolePermissions(context, "user_roles", "user roles"); + removeUserRolePermissions(context, "group_roles", "group roles"); + } + + private void removeUserRolePermissions(Context context, String tableName, String pluralName) throws SQLException { + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT r.id " + + "FROM " + tableName + " r " + + " INNER JOIN projects ON projects.id = r.resource_id " + + "WHERE projects.module_uuid IS NOT NULL"); + massUpdate.update("DELETE FROM " + tableName + " WHERE id=?"); + massUpdate.rowPluralName(pluralName); + massUpdate.execute(MigrationHandler.INSTANCE); + } + + private enum MigrationHandler implements MassUpdate.Handler { + INSTANCE; + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setLong(1, row.getLong(1)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java b/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java new file mode 100644 index 00000000000..73e8e8b9a79 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java @@ -0,0 +1,89 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import com.google.common.collect.Lists; +import java.sql.SQLException; +import java.util.Date; +import java.util.List; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +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; + +public class RenameComponentRelatedParamsInIssueFilters extends BaseDataChange { + + private static final char FIELD_SEPARATOR = '|'; + private static final String LIKE_PREFIX = "data like '%"; + private static final String LIKE_SUFFIX = "%' or "; + private static final String COMPONENT_UUIDS = "componentUuids="; + private static final String COMPONENT_ROOT_UUIDS = "componentRootUuids="; + + private final System2 system; + + public RenameComponentRelatedParamsInIssueFilters(Database db, System2 system) { + super(db); + this.system = system; + } + + @Override + public void execute(Context context) throws SQLException { + final Date now = new Date(system.now()); + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("select id,data from issue_filters where " + + LIKE_PREFIX + COMPONENT_UUIDS + LIKE_SUFFIX + + LIKE_PREFIX + COMPONENT_ROOT_UUIDS + "%'"); + massUpdate.update("update issue_filters set data=?, updated_at=? where id=?"); + massUpdate.rowPluralName("issue filters"); + massUpdate.execute(new RenameComponentRelatedParamsHandler(now)); + } + + private static final class RenameComponentRelatedParamsHandler implements MassUpdate.Handler { + private final Date now; + + private RenameComponentRelatedParamsHandler(Date now) { + this.now = now; + } + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + String data = row.getNullableString(2); + String[] fields = StringUtils.split(data, FIELD_SEPARATOR); + + List<String> fieldsToKeep = Lists.newArrayList(); + for (String field : fields) { + if (field.startsWith(COMPONENT_UUIDS) || field.startsWith(COMPONENT_ROOT_UUIDS)) { + fieldsToKeep.add( + field.replace(COMPONENT_UUIDS, "fileUuids=") + .replace(COMPONENT_ROOT_UUIDS, "moduleUuids=")); + } else { + fieldsToKeep.add(field); + } + } + update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR)); + update.setDate(2, now); + update.setLong(3, row.getNullableLong(1)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java b/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java new file mode 100644 index 00000000000..d07f9acc2f8 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java @@ -0,0 +1,113 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v51; + +import java.sql.SQLException; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.MassUpdate.Handler; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.SqlStatement; + +/** + * SONAR-6054 + * SONAR-6119 + */ +public class UpdateProjectsModuleUuidPath extends BaseDataChange { + + private static final String SEP = "."; + + public UpdateProjectsModuleUuidPath(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("components"); + update.select("SELECT p.id, p.module_uuid_path, p.uuid, p.scope, p.qualifier FROM projects p"); + update.update("UPDATE projects SET module_uuid_path=? WHERE id=?"); + update.execute(new ModuleUuidPathUpdateHandler()); + } + + private static final class ModuleUuidPathUpdateHandler implements Handler { + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + Long id = row.getNullableLong(1); + String moduleUuidPath = row.getNullableString(2); + String uuid = row.getNullableString(3); + String scope = row.getNullableString(4); + String qualifier = row.getNullableString(5); + + boolean needUpdate = false; + String newModuleUuidPath = moduleUuidPath; + + if (needUpdateForEmptyPath(newModuleUuidPath)) { + newModuleUuidPath = SEP + uuid + SEP; + needUpdate = true; + } + + if (needUpdateForSeparators(newModuleUuidPath)) { + newModuleUuidPath = newModuleUuidPathWithSeparators(newModuleUuidPath); + needUpdate = true; + } + + if (needUpdateToIncludeItself(newModuleUuidPath, uuid, scope, qualifier)) { + newModuleUuidPath = newModuleUuidPathIncludingItself(newModuleUuidPath, uuid); + needUpdate = true; + } + + if (needUpdate) { + update.setString(1, newModuleUuidPath); + update.setLong(2, id); + } + return needUpdate; + } + + private static boolean needUpdateForEmptyPath(@Nullable String moduleUuidPath) { + return StringUtils.isEmpty(moduleUuidPath) || SEP.equals(moduleUuidPath); + } + + private static boolean needUpdateForSeparators(String moduleUuidPath) { + return !(moduleUuidPath.startsWith(SEP) && moduleUuidPath.endsWith(SEP)); + } + + private static String newModuleUuidPathWithSeparators(String oldModuleUuidPath) { + StringBuilder newModuleUuidPath = new StringBuilder(oldModuleUuidPath); + newModuleUuidPath.insert(0, SEP); + newModuleUuidPath.append(SEP); + return newModuleUuidPath.toString(); + } + + private static boolean needUpdateToIncludeItself(String moduleUuidPath, @Nullable String uuid, @Nullable String scope, @Nullable String qualifier) { + return "PRJ".equals(scope) && !("DEV_PRJ".equals(qualifier)) && !(moduleUuidPath.contains(uuid)); + } + + private static String newModuleUuidPathIncludingItself(String moduleUuidPath, String uuid) { + StringBuilder newModuleUuidPath = new StringBuilder(moduleUuidPath); + newModuleUuidPath.append(uuid); + newModuleUuidPath.append(SEP); + return newModuleUuidPath.toString(); + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java new file mode 100644 index 00000000000..686db41ae2f --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +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.AddColumnsBuilder.ColumnDef.Type.STRING; + +/** + * Add the following column to the manual_measures table : + * - component_uuid + */ +public class AddManualMeasuresComponentUuidColumn extends DdlChange { + private final Database db; + + public AddManualMeasuresComponentUuidColumn(Database db) { + super(db); + this.db = db; + } + + @Override + public void execute(DdlChange.Context context) throws SQLException { + context.execute(generateSql()); + } + + private String generateSql() { + return new AddColumnsBuilder(db.getDialect(), "manual_measures") + .addColumn( + new AddColumnsBuilder.ColumnDef() + .setName("component_uuid") + .setType(STRING) + .setLimit(50) + .setNullable(true) + ) + .build(); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java new file mode 100644 index 00000000000..a6139e1e946 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +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; + +public class FeedEventsComponentUuid extends BaseDataChange { + + public FeedEventsComponentUuid(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("events"); + update.select( + "SELECT p.uuid, event.id " + + "FROM events event " + + "INNER JOIN projects p ON p.id=event.resource_id " + + "WHERE event.component_uuid is null"); + update.update("UPDATE events SET component_uuid=? WHERE id=?"); + update.execute(MigrationHandler.INSTANCE); + } + + private enum MigrationHandler implements MassUpdate.Handler { + INSTANCE; + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setString(1, row.getString(1)); + update.setLong(2, row.getLong(2)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java new file mode 100644 index 00000000000..b37e60054b3 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java @@ -0,0 +1,37 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; + +public class FeedFileSourcesDataType extends BaseDataChange { + + public FeedFileSourcesDataType(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.prepareUpsert("update file_sources set data_type = 'SOURCE'").execute().commit(); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java new file mode 100644 index 00000000000..36b14ac2e83 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java @@ -0,0 +1,56 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +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; + +public class FeedManualMeasuresComponentUuid extends BaseDataChange { + + public FeedManualMeasuresComponentUuid(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("manual measures"); + update.select( + "SELECT p.uuid, mm.resource_id " + + "FROM manual_measures mm " + + "INNER JOIN projects p ON mm.resource_id = p.id " + + "WHERE mm.component_uuid IS NULL"); + update.update("UPDATE manual_measures SET component_uuid=? WHERE resource_id=?"); + update.execute(new SqlRowHandler()); + } + + private static class SqlRowHandler implements MassUpdate.Handler { + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setString(1, row.getString(1)); + update.setLong(2, row.getLong(2)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java new file mode 100644 index 00000000000..f28bc096e5b --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java @@ -0,0 +1,43 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; + +public class FeedMetricsBooleans extends BaseDataChange { + + public FeedMetricsBooleans(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + context.prepareUpsert("update metrics set optimized_best_value=?, hidden=?, delete_historical_data=? " + + "where user_managed=? or optimized_best_value is null or hidden is null or delete_historical_data is null") + .setBoolean(1, false) + .setBoolean(2, false) + .setBoolean(3, false) + .setBoolean(4, true) + .execute().commit(); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java new file mode 100644 index 00000000000..4162aacb2ca --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java @@ -0,0 +1,58 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.MassUpdate; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.SqlStatement; + +public class FeedProjectLinksComponentUuid extends BaseDataChange { + + public FeedProjectLinksComponentUuid(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("project links"); + update.select( + "SELECT p.uuid, link.id " + + "FROM project_links link " + + "INNER JOIN projects p ON p.id=link.project_id " + + "WHERE link.component_uuid is null"); + update.update("UPDATE project_links SET component_uuid=? WHERE id=?"); + update.execute(MigrationHandler.INSTANCE); + } + + private enum MigrationHandler implements MassUpdate.Handler { + INSTANCE; + + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + update.setString(1, row.getString(1)); + update.setLong(2, row.getLong(2)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java b/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java new file mode 100644 index 00000000000..d533aa73ef4 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java @@ -0,0 +1,136 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import java.sql.SQLException; +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.MassUpdate.Handler; +import org.sonar.db.version.Select; +import org.sonar.db.version.Select.Row; +import org.sonar.db.version.Select.RowReader; +import org.sonar.db.version.SqlStatement; +import org.sonar.db.version.Upsert; + +/** + * SonarQube 5.2 + * SONAR-6328 + * + */ +public class MoveProjectProfileAssociation extends BaseDataChange { + + private static final class ProjectProfileAssociationHandler implements Handler { + private final Upsert setDefaultProfile; + private final Upsert associateProjectToProfile; + private final Table<String, String, String> profileKeysByLanguageThenName; + + private ProjectProfileAssociationHandler(Upsert setDefaultProfile, Upsert associateProjectToProfile, Table<String, String, String> profileKeysByLanguageThenName) { + this.setDefaultProfile = setDefaultProfile; + this.associateProjectToProfile = associateProjectToProfile; + this.profileKeysByLanguageThenName = profileKeysByLanguageThenName; + } + + @Override + public boolean handle(Row row, SqlStatement update) throws SQLException { + Long id = row.getLong(1); + String profileLanguage = extractLanguage(row.getString(2)); + String profileName = row.getString(3); + Long projectId = row.getNullableLong(4); + String projectUuid = row.getString(5); + + if (profileKeysByLanguageThenName.contains(profileLanguage, profileName)) { + String profileKey = profileKeysByLanguageThenName.get(profileLanguage, profileName); + + if (projectUuid == null) { + if (projectId == null) { + setDefaultProfile.setBoolean(1, true).setString(2, profileKey).execute(); + } else { + LOGGER.warn(String.format("Profile with language '%s' and name '%s' is associated with unknown project '%d', ignored", profileLanguage, profileName, projectId)); + } + } else { + associateProjectToProfile.setString(1, projectUuid).setString(2, profileKey).execute(); + } + } else { + LOGGER.warn(String.format("Unable to find profile with language '%s' and name '%s', ignored", profileLanguage, profileName)); + } + + update.setLong(1, id); + return true; + } + } + + private static final Logger LOGGER = Loggers.get(MoveProjectProfileAssociation.class); + + public MoveProjectProfileAssociation(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + + final Table<String, String, String> profileKeysByLanguageThenName = getProfileKeysByLanguageThenName(context); + + MassUpdate massUpdate = context.prepareMassUpdate(); + massUpdate.select("SELECT prop.id, prop.prop_key, prop.text_value, prop.resource_id, proj.uuid " + + "FROM properties prop " + + "LEFT OUTER JOIN projects proj ON prop.resource_id = proj.id " + + "WHERE prop.prop_key LIKE 'sonar.profile.%'" + ); + massUpdate.update("DELETE FROM properties WHERE id = ?"); + + final Upsert setDefaultProfile = context.prepareUpsert("UPDATE rules_profiles SET is_default = ? WHERE kee = ?"); + final Upsert associateProjectToProfile = context.prepareUpsert("INSERT INTO project_qprofiles (project_uuid, profile_key) VALUES (?, ?)"); + + try { + massUpdate.execute(new ProjectProfileAssociationHandler(setDefaultProfile, associateProjectToProfile, profileKeysByLanguageThenName)); + } finally { + associateProjectToProfile.close(); + setDefaultProfile.close(); + } + } + + private static String extractLanguage(String propertyKey) { + return propertyKey.substring("sonar.profile.".length()); + } + + private Table<String, String, String> getProfileKeysByLanguageThenName(final Context context) throws SQLException { + final Table<String, String, String> profilesByLanguageAndName = HashBasedTable.create(); + + Select selectProfiles = context.prepareSelect("SELECT kee, name, language FROM rules_profiles"); + try { + selectProfiles.list(new RowReader<Void>() { + @Override + public Void read(Row row) throws SQLException { + profilesByLanguageAndName.put(row.getString(3), row.getString(2), row.getString(1)); + return null; + } + }); + } finally { + selectProfiles.close(); + } + + return profilesByLanguageAndName; + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java new file mode 100644 index 00000000000..11945b60316 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java @@ -0,0 +1,56 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +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; + +/** + * Remove all components having qualifier 'LIB' + */ +public class RemoveComponentLibraries extends BaseDataChange { + + public RemoveComponentLibraries(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("component libraries"); + update.select("SELECT p.id FROM projects p WHERE p.qualifier='LIB'"); + update.update("DELETE FROM projects WHERE id=?"); + update.execute(MigrationHandler.INSTANCE); + } + + private enum MigrationHandler implements MassUpdate.Handler { + INSTANCE; + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setLong(1, row.getLong(1)); + return true; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java new file mode 100644 index 00000000000..557c562fca0 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java @@ -0,0 +1,163 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import java.sql.SQLException; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; +import javax.annotation.Nonnull; +import org.sonar.core.util.ProgressLogger; +import org.sonar.db.Database; +import org.sonar.db.version.BaseDataChange; +import org.sonar.db.version.Select; +import org.sonar.db.version.Upsert; + +/** + * Remove all duplicated component that have the same keys. + * For each duplicated component key : + * <ul> + * <li>Only the enabled one or last one (with the latest id) is kept</li> + * <li>When deleting a component, all its issues are linked to the remaining component</li> + * </ul> + */ +public class RemoveDuplicatedComponentKeys extends BaseDataChange { + + private final AtomicLong counter = new AtomicLong(0L); + + public RemoveDuplicatedComponentKeys(Database db) { + super(db); + } + + @Override + public void execute(final Context context) throws SQLException { + Upsert componentUpdate = context.prepareUpsert("DELETE FROM projects WHERE id=?"); + Upsert issuesUpdate = context.prepareUpsert("UPDATE issues SET component_uuid=?, project_uuid=? WHERE component_uuid=?"); + + ProgressLogger progress = ProgressLogger.create(getClass(), counter); + progress.start(); + try { + RemoveDuplicatedComponentHandler handler = new RemoveDuplicatedComponentHandler(context, componentUpdate, issuesUpdate); + context.prepareSelect( + "SELECT p.kee, COUNT(p.kee) FROM projects p " + + "GROUP BY p.kee " + + "HAVING COUNT(p.kee) > 1") + .scroll(handler); + if (!handler.isEmpty) { + componentUpdate.execute().commit(); + issuesUpdate.execute().commit(); + } + progress.log(); + } finally { + progress.stop(); + componentUpdate.close(); + issuesUpdate.close(); + } + } + + private class RemoveDuplicatedComponentHandler implements Select.RowHandler { + private final Context context; + private final Upsert componentUpdate; + private final Upsert issuesUpdate; + + private boolean isEmpty = true; + + public RemoveDuplicatedComponentHandler(Context context, Upsert componentUpdate, Upsert issuesUpdate) { + this.context = context; + this.componentUpdate = componentUpdate; + this.issuesUpdate = issuesUpdate; + } + + @Override + public void handle(Select.Row row) throws SQLException { + List<Component> components = context + .prepareSelect("SELECT p.id, p.uuid, p.project_uuid, p.enabled FROM projects p WHERE p.kee=? ORDER BY id") + .setString(1, row.getString(1)) + .list(ComponentRowReader.INSTANCE); + // We keep the enabled component or the last component of the list + Component refComponent = FluentIterable.from(components).firstMatch(EnabledComponent.INSTANCE).or(components.get(components.size() - 1)); + for (Component componentToRemove : FluentIterable.from(components).filter(Predicates.not(new MatchComponentId(refComponent.id)))) { + componentUpdate + .setLong(1, componentToRemove.id) + .addBatch(); + issuesUpdate + .setString(1, refComponent.uuid) + .setString(2, refComponent.projectUuid) + .setString(3, componentToRemove.uuid) + .addBatch(); + counter.getAndIncrement(); + isEmpty = false; + } + } + + public boolean isEmpty() { + return isEmpty; + } + } + + private enum EnabledComponent implements Predicate<Component> { + INSTANCE; + + @Override + public boolean apply(@Nonnull Component input) { + return input.enabled; + } + } + + private static class MatchComponentId implements Predicate<Component> { + + private final long id; + + public MatchComponentId(long id) { + this.id = id; + } + + @Override + public boolean apply(@Nonnull Component input) { + return input.id == this.id; + } + } + + private enum ComponentRowReader implements Select.RowReader<Component> { + INSTANCE; + + @Override + public Component read(Select.Row row) throws SQLException { + return new Component(row.getLong(1), row.getString(2), row.getString(3), row.getBoolean(4)); + } + } + + private static class Component { + private final long id; + private final String uuid; + private final String projectUuid; + private final boolean enabled; + + public Component(long id, String uuid, String projectUuid, boolean enabled) { + this.id = id; + this.uuid = uuid; + this.projectUuid = projectUuid; + this.enabled = enabled; + } + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java new file mode 100644 index 00000000000..108200dac7e --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java @@ -0,0 +1,56 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.v52; + +import java.sql.SQLException; +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; + +/** + * Remove all components having qualifier 'LIB' + */ +public class RemoveSnapshotLibraries extends BaseDataChange { + + public RemoveSnapshotLibraries(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + MassUpdate update = context.prepareMassUpdate().rowPluralName("snapshot libraries"); + update.select("SELECT s.id FROM snapshots s WHERE s.qualifier='LIB'"); + update.update("DELETE FROM snapshots WHERE id=?"); + update.execute(MigrationHandler.INSTANCE); + } + + private enum MigrationHandler implements MassUpdate.Handler { + INSTANCE; + + @Override + public boolean handle(Select.Row row, SqlStatement update) throws SQLException { + update.setLong(1, row.getLong(1)); + return true; + } + } +} |