--- /dev/null
+#
+# 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.
+#
+
+#
+# SonarQube 5.6.4
+# SONAR-8454
+#
+class CleanUsurperRootComponents < ActiveRecord::Migration
+
+ def self.up
+ execute_java_migration('org.sonar.db.version.v564.FixProjectUuidOfDeveloperProjects')
+ execute_java_migration('org.sonar.db.version.v564.CleanUsurperRootComponents')
+ end
+
+end
public class DatabaseVersion {
- public static final int LAST_VERSION = 1_153;
+ public static final int LAST_VERSION = 1_154;
/**
* The minimum supported version which can be upgraded. Lower
import java.sql.Connection;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.sonar.core.util.ProgressLogger;
import org.sonar.db.Database;
+import static com.google.common.base.Preconditions.checkState;
+
public class MassUpdate {
+ @FunctionalInterface
public interface Handler {
/**
* Convert some column values of a given row.
boolean handle(Select.Row row, SqlStatement update) throws SQLException;
}
+ @FunctionalInterface
+ public interface MultiHandler {
+ /**
+ * Convert some column values of a given row.
+ *
+ * @param updateIndex 0-based
+ * @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, int updateIndex) throws SQLException;
+ }
+
private final Database db;
private final Connection readConnection;
private final Connection writeConnection;
private final ProgressLogger progress = ProgressLogger.create(getClass(), counter);
private Select select;
- private Upsert update;
+ private List<UpsertImpl> updates = new ArrayList<>(1);
MassUpdate(Database db, Connection readConnection, Connection writeConnection) {
this.db = db;
}
public MassUpdate update(String sql) throws SQLException {
- this.update = UpsertImpl.create(writeConnection, sql);
+ this.updates.add(UpsertImpl.create(writeConnection, sql));
return this;
}
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");
+ public void execute(Handler handler) throws SQLException {
+ checkState(select != null && !updates.isEmpty(), "SELECT or UPDATE requests are not defined");
+ checkState(updates.size() == 1, "There should be only one update when using a " + Handler.class.getName());
+
+ progress.start();
+ try {
+ select.scroll(row -> callSingleHandler(handler, updates.iterator().next(), row));
+ closeUpdates();
+
+ // log the total number of processed rows
+ progress.log();
+ } finally {
+ progress.stop();
}
+ }
+
+ public void execute(MultiHandler handler) throws SQLException {
+ checkState(select != null && !updates.isEmpty(), "SELECT or UPDATE(s) 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();
+ select.scroll(row -> callMultiHandler(handler, updates, row));
+ closeUpdates();
// log the total number of processed rows
progress.log();
}
}
+ private void callSingleHandler(Handler handler, Upsert update, Select.Row row) throws SQLException {
+ if (handler.handle(row, update)) {
+ update.addBatch();
+ }
+ counter.getAndIncrement();
+ }
+
+ private void callMultiHandler(MultiHandler handler, List<UpsertImpl> updates, Select.Row row) throws SQLException {
+ int i = 0;
+ for (UpsertImpl update : updates) {
+ if (handler.handle(row, update, i)) {
+ update.addBatch();
+ }
+ i++;
+ }
+ counter.getAndIncrement();
+ }
+
+ private void closeUpdates() throws SQLException {
+ for (UpsertImpl update : updates) {
+ if (update.getBatchCount() > 0L) {
+ update.execute().commit();
+ }
+ update.close();
+ }
+ }
+
}
import org.sonar.db.version.v56.FixLengthOfIssuesMessageOnOracle;
import org.sonar.db.version.v56.FixTypeOfRuleTypeOnMysql;
import org.sonar.db.version.v56.UpdateUsersExternalIdentityWhenEmpty;
+import org.sonar.db.version.v564.CleanUsurperRootComponents;
+import org.sonar.db.version.v564.FixProjectUuidOfDeveloperProjects;
public class MigrationStepModule extends Module {
@Override
// 5.6
FixTypeOfRuleTypeOnMysql.class,
FixLengthOfIssuesMessageOnOracle.class,
- UpdateUsersExternalIdentityWhenEmpty.class);
+ UpdateUsersExternalIdentityWhenEmpty.class,
+
+ // 5.6.4
+ FixProjectUuidOfDeveloperProjects.class,
+ CleanUsurperRootComponents.class
+ );
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v564;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+
+public class CleanUsurperRootComponents extends BaseDataChange {
+
+ public CleanUsurperRootComponents(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ // fix snapshots which don't have the scope and/or qualifier of their associated component
+ fixSnapshotScopeAndQualifier(context);
+ // delete components declaring themselves as root in table PROJECTS but which don't have a root scope and/or qualifier
+ cleanUsurperRootComponents(context);
+ // components which has snapshots reference a component as root which is not a root
+ cleanSnapshotWithIncorrectRoot(context);
+ }
+
+ private static void fixSnapshotScopeAndQualifier(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select" +
+ " sn.id,p.scope,p.qualifier" +
+ " from" +
+ " snapshots sn, projects p" +
+ " where" +
+ " p.id = sn.project_id" +
+ " and (p.qualifier<>sn.qualifier or p.scope<>sn.scope)");
+ massUpdate.update("update snapshots set scope=?,qualifier=? where id=?");
+ massUpdate.rowPluralName("snapshots with inconsistent scope or qualifier");
+ massUpdate.execute((row, update) -> {
+ long snapshotId = row.getLong(1);
+ String scope = row.getString(2);
+ String qualifier = row.getString(3);
+
+ update.setString(1, scope);
+ update.setString(2, qualifier);
+ update.setLong(3, snapshotId);
+
+ return true;
+ });
+ }
+
+ private static void cleanUsurperRootComponents(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select p.id,p.uuid from projects p " +
+ " where" +
+ " p.project_uuid = p.uuid" +
+ " and not (" +
+ " p.scope = 'PRJ'" +
+ " and p.qualifier in ('TRK', 'VW', 'DEV')" +
+ " )");
+ massUpdate.update("delete from duplications_index where snapshot_id in (select id from snapshots where project_id=?)");
+ massUpdate.update("delete from project_measures where project_id=?");
+ massUpdate.update("delete from ce_activity where component_uuid=?");
+ massUpdate.update("delete from events where component_uuid=?");
+ massUpdate.update("delete from project_links where component_uuid=?");
+ massUpdate.update("delete from snapshots where project_id=? or root_project_id=?");
+ massUpdate.update("delete from issues where component_uuid=? or project_uuid=?");
+ massUpdate.update("delete from file_sources where file_uuid=? or project_uuid=?");
+ massUpdate.update("delete from group_roles where resource_id=?");
+ massUpdate.update("delete from user_roles where resource_id=?");
+ massUpdate.update("delete from properties where resource_id=?");
+ massUpdate.update("delete from widgets where resource_id=?");
+ massUpdate.update("delete from projects where uuid=?");
+ massUpdate.rowPluralName("usurper root components");
+ massUpdate.execute((row, update, updateIndex) -> {
+ long componentId = row.getLong(1);
+ String componentUuid = row.getString(2);
+ switch (updateIndex) {
+ case 0:
+ case 1:
+ update.setLong(1, componentId);
+ return true;
+ case 2:
+ case 3:
+ case 4:
+ update.setString(1, componentUuid);
+ return true;
+ case 5:
+ update.setLong(1, componentId);
+ update.setLong(2, componentId);
+ return true;
+ case 6:
+ case 7:
+ update.setString(1, componentUuid);
+ update.setString(2, componentUuid);
+ return true;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ update.setLong(1, componentId);
+ return true;
+ case 12:
+ update.setString(1, componentUuid);
+ return true;
+ default:
+ throw new IllegalArgumentException("Unsupported update index " + updateIndex);
+ }
+ });
+ }
+
+ private static void cleanSnapshotWithIncorrectRoot(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select" +
+ " sn.id" +
+ " from " +
+ " projects p, snapshots sn" +
+ " where" +
+ " p.id = sn.root_project_id" +
+ " and not (" +
+ " p.scope = 'PRJ'" +
+ " and p.qualifier in ('TRK', 'VW', 'DEV')" +
+ " )");
+ massUpdate.update("DELETE from ce_activity WHERE snapshot_id=?");
+ massUpdate.update("DELETE from events WHERE snapshot_id=?");
+ massUpdate.update("DELETE from project_measures WHERE snapshot_id=?");
+ massUpdate.update("DELETE from duplications_index WHERE project_snapshot_id=?");
+ massUpdate.update("DELETE from snapshots WHERE id=?");
+ massUpdate.rowPluralName("snapshots with incorrect root");
+ massUpdate.execute((row, update, updateIndex) -> {
+ long snapshotId = row.getLong(1);
+ switch (updateIndex) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ update.setLong(1, snapshotId);
+ return true;
+ default:
+ throw new IllegalArgumentException("Unsupported update index " + updateIndex);
+ }
+ });
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v564;
+
+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 FixProjectUuidOfDeveloperProjects extends BaseDataChange {
+
+ public FixProjectUuidOfDeveloperProjects(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select distinct p.person_id,d.uuid from projects p, projects d where p.qualifier = 'DEV_PRJ' and p.project_uuid != d.uuid and d.id = p.person_id");
+ massUpdate.update("update projects set project_uuid = ? where person_id = ? and qualifier = 'DEV_PRJ' and project_uuid != ?");
+ massUpdate.rowPluralName("developers with incorrect project_uuid");
+ massUpdate.execute(FixProjectUuidOfDeveloperProjects::handleComponent);
+ }
+
+ private static boolean handleComponent(Select.Row row, SqlStatement update) throws SQLException {
+ long personId = row.getLong(1);
+ String developerUuid = row.getString(2);
+ update.setString(1, developerUuid);
+ update.setLong(2, personId);
+ update.setString(3, developerUuid);
+
+ return true;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.db.version.v564;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1151');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1152');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1153');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1154');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '1418215735482', '1418215735482', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Clob;
return client;
}
- public void executeUpdateSql(String sql, String... params) {
- try (Connection connection = db.getDatabase().getDataSource().getConnection()) {
+ public void executeUpdateSql(String sql, Object... params) {
+ try (Connection connection = getConnection()) {
new QueryRunner().update(connection, sql, params);
+ } catch (SQLException e) {
+ SQLException nextException = e.getNextException();
+ if (nextException != null) {
+ throw new IllegalStateException("Fail to execute sql: " + sql,
+ new SQLException(e.getMessage(), nextException.getSQLState(), nextException.getErrorCode(), nextException));
+ }
+ throw new IllegalStateException("Fail to execute sql: " + sql, e);
} catch (Exception e) {
throw new IllegalStateException("Fail to execute sql: " + sql, e);
}
}
+
/**
* Very simple helper method to insert some data into a table.
* It's the responsibility of the caller to convert column values to string.
*/
- public void executeInsert(String table, Map<String, String> valuesByColumn) {
+ public void executeInsert(String table, String firstColumn, Object... others) {
+ executeInsert(table, mapOf(firstColumn, others));
+ }
+
+ private static Map<String, Object> mapOf(String firstColumn, Object... values) {
+ ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
+ List<Object> args = Lists.asList(firstColumn, values);
+ for (int i = 0; i < args.size(); i++) {
+ String key = args.get(i).toString();
+ Object value = args.get(i + 1);
+ if (value != null) {
+ builder.put(key, value);
+ }
+ i++;
+ }
+ return builder.build();
+ }
+
+ /**
+ * Very simple helper method to insert some data into a table.
+ * It's the responsibility of the caller to convert column values to string.
+ */
+ public void executeInsert(String table, Map<String, Object> valuesByColumn) {
if (valuesByColumn.isEmpty()) {
throw new IllegalArgumentException("Values cannot be empty");
}
") values (" +
COMMA_JOINER.join(Collections.nCopies(valuesByColumn.size(), '?')) +
")";
- executeUpdateSql(sql, valuesByColumn.values().toArray(new String[valuesByColumn.size()]));
+ executeUpdateSql(sql, valuesByColumn.values().toArray(new Object[valuesByColumn.size()]));
}
/**
return db.getDatabase().getDataSource().getConnection();
}
+ private Connection getConnection() throws SQLException {
+ return db.getDatabase().getDataSource().getConnection();
+ }
+
@Deprecated
public Database database() {
return db.getDatabase();
public void verify_count_of_added_MigrationStep_types() {
ComponentContainer container = new ComponentContainer();
new MigrationStepModule().configure(container);
- assertThat(container.size()).isEqualTo(66);
+ assertThat(container.size()).isEqualTo(68);
}
}
}
private void insertUser(String login, @Nullable String externalIdentity, @Nullable String externalIdentityProvider, long updatedAt) {
- Map<String, String> params = newHashMap(ImmutableMap.of(
+ Map<String, Object> params = newHashMap(ImmutableMap.of(
"LOGIN", login,
"CREATED_AT", Long.toString(PAST),
"UPDATED_AT", Long.toString(updatedAt)));
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v564;
+
+import com.google.common.collect.ImmutableList;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.Long.parseLong;
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CleanUsurperRootComponentsTest {
+
+ private static final List<String> TABLES = ImmutableList.of(
+ "duplications_index", "project_measures", "ce_activity", "events", "snapshots",
+ "project_links", "project_measures", "issues", "file_sources", "group_roles",
+ "user_roles", "properties", "widgets", "projects");
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, CleanUsurperRootComponentsTest.class,
+ "complete_schema.sql");
+
+ private CleanUsurperRootComponents underTest = new CleanUsurperRootComponents(db.database());
+
+ @Test
+ public void migration_has_no_effect_on_empty_db() throws SQLException {
+ underTest.execute();
+
+ TABLES.forEach(tableName -> assertThat(db.countRowsOfTable(tableName)).isEqualTo(0));
+ }
+
+ @Test
+ public void execute_fixes_scope_and_qualifier_of_snapshot_inconsistent_with_component() throws SQLException {
+ long[] componentIds = {
+ parseLong(insertComponent("sc1", "qu1")),
+ parseLong(insertComponent("sc2", "qu2")),
+ parseLong(insertComponent("sc3", "qu3")),
+ parseLong(insertComponent("sc4", "qu4"))
+ };
+ Long[] snapshotIds = {
+ insertSnapshot(componentIds[0], "sc1", "qu1"),
+ insertSnapshot(componentIds[1], "sc2", "quW"),
+ insertSnapshot(componentIds[2], "scX", "qu3"),
+ insertSnapshot(componentIds[3], "scY", "quZ"),
+ };
+
+ underTest.execute();
+
+ assertSnapshot(snapshotIds[0], "sc1", "qu1");
+ assertSnapshot(snapshotIds[1], "sc2", "qu2");
+ assertSnapshot(snapshotIds[2], "sc3", "qu3");
+ assertSnapshot(snapshotIds[3], "sc4", "qu4");
+ }
+
+ @Test
+ public void executes_deletes_usurper_root_components() throws SQLException {
+ String[] componentUuids = {
+ insertRootComponent(Scopes.PROJECT, Qualifiers.PROJECT),
+ insertRootComponent(Scopes.PROJECT, Qualifiers.MODULE),
+ insertRootComponent(Scopes.DIRECTORY, Qualifiers.DIRECTORY),
+ insertRootComponent(Scopes.FILE, Qualifiers.FILE),
+ insertRootComponent(Scopes.PROJECT, Qualifiers.VIEW),
+ insertRootComponent(Scopes.PROJECT, Qualifiers.SUBVIEW),
+ insertRootComponent(Scopes.FILE, Qualifiers.PROJECT),
+ insertRootComponent(Scopes.PROJECT, "DEV"),
+ insertRootComponent(Scopes.PROJECT, "DEV_PRJ"),
+ };
+
+ underTest.execute();
+
+ assertUuidsInTableProjects("projects", componentUuids[0], componentUuids[4], componentUuids[7]);
+ }
+
+ @Test
+ public void executes_deletes_data_in_all_children_tables_of_component_for_usurper_root_components() throws SQLException {
+ long usurperId = 12L;
+ String usurperUuid = "usurper_uuid";
+ insertComponent(Scopes.PROJECT, Qualifiers.MODULE, usurperId, usurperUuid, usurperUuid);
+ Long snapshotId = insertSnapshot(usurperId, usurperId);
+ insertDuplicationsIndex(snapshotId);
+ insertProjectMeasures(usurperId, dontCareLong());
+ insertCeActivity(usurperUuid, dontCareLong());
+ insertEvent(usurperUuid, dontCareLong());
+ insertSnapshot(usurperId, dontCare());
+ insertSnapshot(dontCare(), usurperId);
+ insertProjectLinks(usurperUuid);
+ insertIssue(usurperUuid, null);
+ insertIssue(null, usurperUuid);
+ insertIssue(usurperUuid, usurperUuid);
+ insertFileSource(null, usurperUuid);
+ insertFileSource(usurperUuid, null);
+ insertFileSource(usurperUuid, usurperUuid);
+ insertGroupRole(usurperId);
+ insertUserRole(usurperId);
+ insertProperties(usurperId);
+ insertWidget(usurperId);
+
+ TABLES.forEach(s -> assertThat(db.countRowsOfTable(s)).describedAs("table " + s).isGreaterThanOrEqualTo(1));
+
+ underTest.execute();
+
+ TABLES.forEach(s -> assertThat(db.countRowsOfTable(s)).describedAs("table " + s).isEqualTo(0));
+ }
+
+ @Test
+ public void execute_deletes_snapshots_which_root_is_not_root() throws SQLException {
+ long[] componentIds = {
+ parseLong(insertRootComponent(Scopes.PROJECT, Qualifiers.PROJECT)),
+ parseLong(insertComponent(Scopes.PROJECT, Qualifiers.MODULE)),
+ parseLong(insertComponent(Scopes.DIRECTORY, Qualifiers.DIRECTORY)),
+ parseLong(insertComponent(Scopes.FILE, Qualifiers.FILE)),
+ parseLong(insertComponent(Scopes.PROJECT, Qualifiers.VIEW)),
+ parseLong(insertComponent(Scopes.PROJECT, Qualifiers.SUBVIEW)),
+ parseLong(insertComponent(Scopes.FILE, Qualifiers.PROJECT)),
+ parseLong(insertComponent(Scopes.PROJECT, "DEV")),
+ parseLong(insertComponent(Scopes.PROJECT, "DEV_PRJ"))
+ };
+ Long[] snapshotIds = {
+ insertSnapshot(dontCare(), componentIds[0]),
+ insertSnapshot(dontCare(), componentIds[1]),
+ insertSnapshot(dontCare(), componentIds[2]),
+ insertSnapshot(dontCare(), componentIds[3]),
+ insertSnapshot(dontCare(), componentIds[4]),
+ insertSnapshot(dontCare(), componentIds[5]),
+ insertSnapshot(dontCare(), componentIds[6]),
+ insertSnapshot(dontCare(), componentIds[7]),
+ insertSnapshot(dontCare(), componentIds[8])
+ };
+
+ underTest.execute();
+
+ assertIdsInTableProjects("snapshots", snapshotIds[0], snapshotIds[4], snapshotIds[7]);
+ }
+
+ @Test
+ public void execute_deletes_children_tables_of_snapshots_when_root_of_snapshot_is_not_root() throws SQLException {
+ long usurperId = 12L;
+ String componentUuid = insertComponent(Scopes.FILE, Scopes.FILE, usurperId, "U1", "U2");
+ Long snapshotId = insertSnapshot(dontCare(), usurperId);
+ insertProjectMeasures(usurperId, snapshotId);
+ insertCeActivity(componentUuid, snapshotId);
+ insertEvent(componentUuid, snapshotId);
+
+ underTest.execute();
+
+ TABLES.stream()
+ .filter(s1 -> !s1.equals("projects"))
+ .forEach(s -> assertThat(db.countRowsOfTable(s)).describedAs("table " + s).isEqualTo(0));
+ }
+
+ private void insertDuplicationsIndex(long snapshotId) {
+ db.executeInsert(
+ "duplications_index",
+ "PROJECT_SNAPSHOT_ID", valueOf(dontCareLong()),
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "HASH", dontCare(),
+ "INDEX_IN_FILE", valueOf(0),
+ "START_LINE", valueOf(0),
+ "END_LINE", valueOf(0));
+ db.commit();
+ }
+
+ private void insertProjectMeasures(long componentId, long snapshotId) {
+ db.executeInsert(
+ "project_measures",
+ "METRIC_ID", valueOf(123L),
+ "PROJECT_ID", componentId,
+ "SNAPSHOT_ID", valueOf(snapshotId));
+ db.commit();
+ }
+
+ private void insertCeActivity(String componentUuid, Long snapshotId) {
+ db.executeInsert(
+ "ce_activity",
+ "UUID", dontCare(),
+ "TASK_TYPE", dontCare(),
+ "COMPONENT_UUID", componentUuid,
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "STATUS", dontCare(),
+ "IS_LAST", "true",
+ "IS_LAST_KEY", dontCare(),
+ "SUBMITTED_AT", valueOf(121L),
+ "CREATED_AT", valueOf(122L),
+ "UPDATED_AT", valueOf(123L));
+ db.commit();
+ }
+
+ private void insertEvent(String componentUuid, Long snapshotId) {
+ db.executeInsert(
+ "events",
+ "SNAPSHOT_ID", valueOf(snapshotId),
+ "COMPONENT_UUID", componentUuid,
+ "CREATED_AT", valueOf(122L),
+ "EVENT_DATE", valueOf(123L));
+ db.commit();
+ }
+
+ private Long insertSnapshot(long componentId, long rootComponentId) {
+ Long id = idGenerator++;
+ db.executeInsert(
+ "snapshots",
+ "ID", valueOf(id),
+ "PROJECT_ID", componentId,
+ "ROOT_PROJECT_ID", rootComponentId);
+ db.commit();
+ return id;
+ }
+
+ private void insertProjectLinks(String componentUuid) {
+ db.executeInsert(
+ "project_links",
+ "COMPONENT_UUID", componentUuid,
+ "HREF", dontCare());
+ db.commit();
+ }
+
+ private void insertIssue(@Nullable String componentUuid, @Nullable String projectUuid) {
+ db.executeInsert(
+ "issues",
+ "COMPONENT_UUID", componentUuid == null ? dontCare() : componentUuid,
+ "PROJECT_UUID", projectUuid == null ? dontCare() : projectUuid,
+ "KEE", "kee_" + componentUuid + projectUuid,
+ "MANUAL_SEVERITY", valueOf(true));
+ db.commit();
+ }
+
+ private void insertFileSource(@Nullable String fileUuid, @Nullable String projectUuid) {
+ db.executeInsert(
+ "file_sources",
+ "FILE_UUID", fileUuid == null ? dontCare() : fileUuid,
+ "PROJECT_UUID", projectUuid == null ? dontCare() : projectUuid,
+ "CREATED_AT", valueOf(122L),
+ "UPDATED_AT", valueOf(123L));
+ db.commit();
+ }
+
+ private void insertGroupRole(long componentId) {
+ db.executeInsert(
+ "group_roles",
+ "RESOURCE_ID", valueOf(componentId),
+ "ROLE", dontCare());
+ db.commit();
+ }
+
+ private void insertUserRole(long componentId) {
+ db.executeInsert(
+ "user_roles",
+ "RESOURCE_ID", valueOf(componentId),
+ "ROLE", dontCare());
+ db.commit();
+ }
+
+ private void insertProperties(long componentId) {
+ db.executeInsert(
+ "properties",
+ "RESOURCE_ID", valueOf(componentId));
+ db.commit();
+ }
+
+ private void insertWidget(long componentId) {
+ db.executeInsert(
+ "widgets",
+ "DASHBOARD_ID", valueOf(95),
+ "WIDGET_KEY", dontCare(),
+ "RESOURCE_ID", valueOf(componentId));
+ db.commit();
+ }
+
+ private long idGenerator = 0;
+
+ private String insertComponent(String scope, String qualifier) {
+ long id = idGenerator++;
+ String uuid = "" + id;
+ return insertComponent(scope, qualifier, id, uuid, String.valueOf(dontCare()));
+ }
+
+ private String insertRootComponent(String scope, String qualifier) {
+ long id = idGenerator++;
+ String uuid = "" + id;
+ return insertComponent(scope, qualifier, id, uuid, uuid);
+ }
+
+ private String insertComponent(String scope, String qualifier, long id, String uuid, String projectUuid) {
+ db.executeInsert(
+ "projects",
+ "ID", valueOf(id),
+ "UUID", uuid,
+ "PROJECT_UUID", projectUuid,
+ "SCOPE", scope,
+ "QUALIFIER", qualifier);
+ db.commit();
+ return uuid;
+ }
+
+ private Long insertSnapshot(long componentId, String scope, String qualifier) {
+ long id = idGenerator++;
+
+ db.executeInsert(
+ "snapshots",
+ "id", valueOf(id),
+ "project_id", componentId,
+ "root_project_id", dontCare(),
+ "scope", scope,
+ "qualifier", qualifier);
+ db.commit();
+ return id;
+ }
+
+ private void assertSnapshot(Long snapshotId, String scope, String qualifier) {
+ List<Map<String, Object>> rows = db.select("select SCOPE, QUALIFIER from snapshots where ID=" + snapshotId);
+ assertThat(rows).hasSize(1);
+ Map<String, Object> row = rows.get(0);
+ assertThat(row.get("SCOPE")).isEqualTo(scope);
+ assertThat(row.get("QUALIFIER")).isEqualTo(qualifier);
+ }
+
+ private void assertIdsInTableProjects(String tableName, Long... expected) {
+ assertThat(db.select("select id from " + tableName)
+ .stream()
+ .map(stringObjectMap -> (Long) stringObjectMap.entrySet().iterator().next().getValue()))
+ .containsOnly(expected);
+ }
+
+ private void assertUuidsInTableProjects(String tableName, String... expected) {
+ assertThat(db.select("select uuid from " + tableName)
+ .stream()
+ .map(stringObjectMap -> (String) stringObjectMap.entrySet().iterator().next().getValue()))
+ .containsOnly(expected);
+ }
+
+ private long dontCareGenerator = 0;
+
+ private long dontCare() {
+ return 999_999_999L + dontCareGenerator++;
+ }
+
+ private Long dontCareLong() {
+ return dontCareGenerator++;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v564;
+
+import java.sql.SQLException;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FixProjectUuidOfDeveloperProjectsTest {
+
+ private static final String TABLE_PROJECTS = "projects";
+ private static final String PROJECT_UUID = "U1";
+ private static final String FILE_UUID = "U2";
+ private static final String DEVELOPER_UUID = "U3";
+ private static final String DEV1_IN_PROJECT_UUID = "U4";
+ private static final String DEV2_IN_PROJECT_UUID = "U5";
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FixProjectUuidOfDeveloperProjectsTest.class,
+ "projects_5.6.sql");
+
+ private FixProjectUuidOfDeveloperProjects underTest = new FixProjectUuidOfDeveloperProjects(db.database());
+
+ @Test
+ public void migration_has_no_effect_on_empty_tables() throws SQLException {
+ underTest.execute();
+
+ assertThat(db.countRowsOfTable(TABLE_PROJECTS)).isEqualTo(0);
+ }
+
+ @Test
+ public void migration_fixes_project_uuid_of_rows_with_qualifier_DEV_PRJ() throws SQLException {
+ insertComponents();
+
+ underTest.execute();
+
+ verifyComponents();
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ insertComponents();
+
+ underTest.execute();
+ verifyComponents();
+
+ underTest.execute();
+ verifyComponents();
+ }
+
+ private void verifyComponents() {
+ verifyProjectUuid(PROJECT_UUID, PROJECT_UUID);
+ verifyProjectUuid(FILE_UUID, PROJECT_UUID);
+ verifyProjectUuid(DEVELOPER_UUID, DEVELOPER_UUID);
+ verifyProjectUuid(DEV1_IN_PROJECT_UUID, DEVELOPER_UUID);
+ verifyProjectUuid(DEV2_IN_PROJECT_UUID, DEVELOPER_UUID);
+ }
+
+ private void insertComponents() {
+ // regular project
+ insert(PROJECT_UUID, "TRK", null, PROJECT_UUID);
+ insert(FILE_UUID, "FIL", null, PROJECT_UUID);
+ // developer
+ Long personId = insert(DEVELOPER_UUID, "DEV", null, DEVELOPER_UUID);
+ insert(DEV1_IN_PROJECT_UUID, "DEV_PRJ", personId, /* not correct */PROJECT_UUID);
+ insert(DEV2_IN_PROJECT_UUID, "DEV_PRJ", personId, /* not correct */PROJECT_UUID);
+ db.commit();
+ }
+
+ private void verifyProjectUuid(String uuid, @Nullable String expectedProjectUuid) {
+ Map<String, Object> rows = db.selectFirst("select project_uuid as \"projectUuid\" from projects where uuid='" + uuid + "'");
+ assertThat(rows.get("projectUuid")).isEqualTo(expectedProjectUuid);
+ }
+
+ private Long insert(String uuid, String qualifier, @Nullable Long personId, String projectUuid) {
+ db.executeInsert(
+ TABLE_PROJECTS,
+ "UUID", uuid,
+ "PERSON_ID", personId == null ? null : valueOf(personId),
+ "PROJECT_UUID", projectUuid,
+ "QUALIFIER", qualifier);
+ db.commit();
+ return db.select("select ID from projects where UUID='" + uuid + "'").stream()
+ .findFirst()
+ .map(f -> (Long) f.get("ID"))
+ .orElseThrow(() -> new IllegalStateException("NO ID??"));
+ }
+}
--- /dev/null
+CREATE TABLE "GROUPS_USERS" (
+ "USER_ID" INTEGER,
+ "GROUP_ID" INTEGER
+);
+
+CREATE TABLE "RULES_PARAMETERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "RULE_ID" INTEGER NOT NULL,
+ "NAME" VARCHAR(128) NOT NULL,
+ "PARAM_TYPE" VARCHAR(512) NOT NULL,
+ "DEFAULT_VALUE" VARCHAR(4000),
+ "DESCRIPTION" VARCHAR(4000)
+);
+
+CREATE TABLE "RULES_PROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "LANGUAGE" VARCHAR(20),
+ "KEE" VARCHAR(255) NOT NULL,
+ "PARENT_KEE" VARCHAR(255),
+ "RULES_UPDATED_AT" VARCHAR(100),
+ "IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PROJECT_QPROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "PROFILE_KEY" VARCHAR(255) NOT NULL
+);
+
+CREATE TABLE "WIDGETS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "DASHBOARD_ID" INTEGER NOT NULL,
+ "WIDGET_KEY" VARCHAR(256) NOT NULL,
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(1000),
+ "COLUMN_INDEX" INTEGER,
+ "ROW_INDEX" INTEGER,
+ "CONFIGURED" BOOLEAN,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "RESOURCE_ID" INTEGER
+);
+
+CREATE TABLE "GROUPS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(500),
+ "DESCRIPTION" VARCHAR(200),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);
+
+CREATE TABLE "SCHEMA_MIGRATIONS" (
+"VERSION" VARCHAR(256) NOT NULL
+);
+
+CREATE TABLE "GROUP_ROLES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "GROUP_ID" INTEGER,
+ "RESOURCE_ID" INTEGER,
+ "ROLE" VARCHAR(64) NOT NULL
+);
+
+CREATE TABLE "RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+ "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+ "DESCRIPTION" VARCHAR(16777215),
+ "DESCRIPTION_FORMAT" VARCHAR(20),
+ "PRIORITY" INTEGER,
+ "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+ "TEMPLATE_ID" INTEGER,
+ "PLUGIN_CONFIG_KEY" VARCHAR(200),
+ "NAME" VARCHAR(200),
+ "STATUS" VARCHAR(40),
+ "LANGUAGE" VARCHAR(20),
+ "NOTE_DATA" CLOB(2147483647),
+ "NOTE_USER_LOGIN" VARCHAR(255),
+ "NOTE_CREATED_AT" TIMESTAMP,
+ "NOTE_UPDATED_AT" TIMESTAMP,
+ "REMEDIATION_FUNCTION" VARCHAR(20),
+ "DEF_REMEDIATION_FUNCTION" VARCHAR(20),
+ "REMEDIATION_GAP_MULT" VARCHAR(20),
+ "DEF_REMEDIATION_GAP_MULT" VARCHAR(20),
+ "REMEDIATION_BASE_EFFORT" VARCHAR(20),
+ "DEF_REMEDIATION_BASE_EFFORT" VARCHAR(20),
+ "GAP_DESCRIPTION" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "SYSTEM_TAGS" VARCHAR(4000),
+ "RULE_TYPE" TINYINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+
+
+CREATE TABLE "WIDGET_PROPERTIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "WIDGET_ID" INTEGER NOT NULL,
+ "KEE" VARCHAR(100),
+ "TEXT_VALUE" VARCHAR(4000)
+);
+
+CREATE TABLE "EVENTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(400),
+ "COMPONENT_UUID" VARCHAR(50),
+ "SNAPSHOT_ID" INTEGER,
+ "CATEGORY" VARCHAR(50),
+ "EVENT_DATE" BIGINT NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "DESCRIPTION" VARCHAR(4000),
+ "EVENT_DATA" VARCHAR(4000)
+);
+
+CREATE TABLE "QUALITY_GATES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+);
+
+CREATE TABLE "QUALITY_GATE_CONDITIONS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "QGATE_ID" INTEGER,
+ "METRIC_ID" INTEGER,
+ "OPERATOR" VARCHAR(3),
+ "VALUE_ERROR" VARCHAR(64),
+ "VALUE_WARNING" VARCHAR(64),
+ "PERIOD" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+);
+
+CREATE TABLE "PROPERTIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROP_KEY" VARCHAR(512),
+ "RESOURCE_ID" INTEGER,
+ "TEXT_VALUE" CLOB(2147483647),
+ "USER_ID" INTEGER
+);
+
+CREATE TABLE "PROJECT_LINKS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "COMPONENT_UUID" VARCHAR(50),
+ "LINK_TYPE" VARCHAR(20),
+ "NAME" VARCHAR(128),
+ "HREF" VARCHAR(2048) NOT NULL
+);
+
+CREATE TABLE "DUPLICATIONS_INDEX" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_SNAPSHOT_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER NOT NULL,
+ "HASH" VARCHAR(50) NOT NULL,
+ "INDEX_IN_FILE" INTEGER NOT NULL,
+ "START_LINE" INTEGER NOT NULL,
+ "END_LINE" INTEGER NOT NULL
+);
+
+CREATE TABLE "PROJECT_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "VALUE" DOUBLE,
+ "METRIC_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "RULES_CATEGORY_ID" INTEGER,
+ "TEXT_VALUE" VARCHAR(4000),
+ "TENDENCY" INTEGER,
+ "MEASURE_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER,
+ "ALERT_STATUS" VARCHAR(5),
+ "ALERT_TEXT" VARCHAR(4000),
+ "URL" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(4000),
+ "RULE_PRIORITY" INTEGER,
+ "CHARACTERISTIC_ID" INTEGER,
+ "PERSON_ID" INTEGER,
+ "VARIATION_VALUE_1" DOUBLE,
+ "VARIATION_VALUE_2" DOUBLE,
+ "VARIATION_VALUE_3" DOUBLE,
+ "VARIATION_VALUE_4" DOUBLE,
+ "VARIATION_VALUE_5" DOUBLE,
+ "MEASURE_DATA" BINARY(167772150)
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(2000),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "MANUAL_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "METRIC_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "VALUE" DOUBLE,
+ "TEXT_VALUE" VARCHAR(4000),
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "ACTIVE_RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROFILE_ID" INTEGER NOT NULL,
+ "RULE_ID" INTEGER NOT NULL,
+ "FAILURE_LEVEL" INTEGER NOT NULL,
+ "INHERITANCE" VARCHAR(10),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "NOTIFICATIONS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "DATA" BLOB(167772150)
+);
+
+CREATE TABLE "USER_ROLES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_ID" INTEGER,
+ "RESOURCE_ID" INTEGER,
+ "ROLE" VARCHAR(64) NOT NULL
+);
+
+CREATE TABLE "ACTIVE_DASHBOARDS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "DASHBOARD_ID" INTEGER NOT NULL,
+ "USER_ID" INTEGER,
+ "ORDER_INDEX" INTEGER
+);
+
+CREATE TABLE "ACTIVE_RULE_PARAMETERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ACTIVE_RULE_ID" INTEGER NOT NULL,
+ "RULES_PARAMETER_ID" INTEGER NOT NULL,
+ "RULES_PARAMETER_KEY" VARCHAR(128),
+ "VALUE" VARCHAR(4000)
+);
+
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255),
+ "NAME" VARCHAR(200),
+ "EMAIL" VARCHAR(100),
+ "CRYPTED_PASSWORD" VARCHAR(40),
+ "SALT" VARCHAR(40),
+ "REMEMBER_TOKEN" VARCHAR(500),
+ "REMEMBER_TOKEN_EXPIRES_AT" TIMESTAMP,
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" VARCHAR(4000),
+ "EXTERNAL_IDENTITY" VARCHAR(255),
+ "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100),
+ "USER_LOCAL" BOOLEAN,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "DASHBOARDS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_ID" INTEGER,
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(1000),
+ "COLUMN_LAYOUT" VARCHAR(20),
+ "SHARED" BOOLEAN,
+ "IS_GLOBAL" BOOLEAN,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "METRICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(64) NOT NULL,
+ "DESCRIPTION" VARCHAR(255),
+ "DIRECTION" INTEGER NOT NULL DEFAULT 0,
+ "DOMAIN" VARCHAR(64),
+ "SHORT_NAME" VARCHAR(64),
+ "QUALITATIVE" BOOLEAN NOT NULL DEFAULT FALSE,
+ "VAL_TYPE" VARCHAR(8),
+ "USER_MANAGED" BOOLEAN DEFAULT FALSE,
+ "ENABLED" BOOLEAN DEFAULT TRUE,
+ "WORST_VALUE" DOUBLE,
+ "BEST_VALUE" DOUBLE,
+ "OPTIMIZED_BEST_VALUE" BOOLEAN,
+ "HIDDEN" BOOLEAN,
+ "DELETE_HISTORICAL_DATA" BOOLEAN,
+ "DECIMAL_SCALE" INTEGER
+);
+
+CREATE TABLE "LOADED_TEMPLATES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(200),
+ "TEMPLATE_TYPE" VARCHAR(15)
+);
+
+CREATE TABLE "RESOURCE_INDEX" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400) NOT NULL,
+ "POSITION" INTEGER NOT NULL,
+ "NAME_SIZE" INTEGER NOT NULL,
+ "RESOURCE_ID" INTEGER NOT NULL,
+ "ROOT_PROJECT_ID" INTEGER NOT NULL,
+ "QUALIFIER" VARCHAR(10) NOT NULL
+);
+
+CREATE TABLE "AUTHORS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PERSON_ID" INTEGER,
+ "LOGIN" VARCHAR(255),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "MEASURE_FILTERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "SHARED" BOOLEAN NOT NULL DEFAULT FALSE,
+ "USER_ID" INTEGER,
+ "DESCRIPTION" VARCHAR(4000),
+ "DATA" CLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "MEASURE_FILTER_FAVOURITES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_ID" INTEGER NOT NULL,
+ "MEASURE_FILTER_ID" INTEGER NOT NULL,
+ "CREATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "GAP" DOUBLE,
+ "EFFORT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" BIGINT,
+ "ISSUE_CLOSE_DATE" BIGINT,
+ "ISSUE_UPDATE_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "LOCATIONS" BLOB(167772150),
+ "ISSUE_TYPE" TINYINT
+);
+
+CREATE TABLE "ISSUE_CHANGES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50),
+ "ISSUE_KEY" VARCHAR(50) NOT NULL,
+ "USER_LOGIN" VARCHAR(255),
+ "CHANGE_TYPE" VARCHAR(40),
+ "CHANGE_DATA" VARCHAR(16777215),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "ISSUE_CHANGE_CREATION_DATE" BIGINT
+);
+
+CREATE TABLE "ISSUE_FILTERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "SHARED" BOOLEAN NOT NULL DEFAULT FALSE,
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "DATA" CLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "ISSUE_FILTER_FAVOURITES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_LOGIN" VARCHAR(255) NOT NULL,
+ "ISSUE_FILTER_ID" INTEGER NOT NULL,
+ "CREATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PERMISSION_TEMPLATES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "KEE" VARCHAR(100) NOT NULL,
+ "DESCRIPTION" VARCHAR(4000),
+ "KEY_PATTERN" VARCHAR(500),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PERM_TEMPLATES_USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_ID" INTEGER NOT NULL,
+ "TEMPLATE_ID" INTEGER NOT NULL,
+ "PERMISSION_REFERENCE" VARCHAR(64) NOT NULL,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PERM_TEMPLATES_GROUPS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "GROUP_ID" INTEGER,
+ "TEMPLATE_ID" INTEGER NOT NULL,
+ "PERMISSION_REFERENCE" VARCHAR(64) NOT NULL,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+
+CREATE TABLE "ACTIVITIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOG_KEY" VARCHAR(250),
+ "CREATED_AT" TIMESTAMP,
+ "USER_LOGIN" VARCHAR(255),
+ "LOG_TYPE" VARCHAR(250),
+ "LOG_ACTION" VARCHAR(250),
+ "LOG_MESSAGE" VARCHAR(250),
+ "DATA_FIELD" CLOB(2147483647)
+);
+
+CREATE TABLE "FILE_SOURCES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "FILE_UUID" VARCHAR(50) NOT NULL,
+ "LINE_HASHES" CLOB(2147483647),
+ "BINARY_DATA" BLOB(167772150),
+ "DATA_TYPE" VARCHAR(20),
+ "DATA_HASH" VARCHAR(50),
+ "SRC_HASH" VARCHAR(50),
+ "REVISION" VARCHAR(100),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+
+CREATE TABLE "CE_QUEUE" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "SUBMITTER_LOGIN" VARCHAR(255) NULL,
+ "STARTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+
+CREATE TABLE "CE_ACTIVITY" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(40) NOT NULL,
+ "TASK_TYPE" VARCHAR(15) NOT NULL,
+ "COMPONENT_UUID" VARCHAR(40) NULL,
+ "SNAPSHOT_ID" INTEGER NULL,
+ "STATUS" VARCHAR(15) NOT NULL,
+ "IS_LAST" BOOLEAN NOT NULL,
+ "IS_LAST_KEY" VARCHAR(55) NOT NULL,
+ "SUBMITTER_LOGIN" VARCHAR(255) NULL,
+ "SUBMITTED_AT" BIGINT NOT NULL,
+ "STARTED_AT" BIGINT NULL,
+ "EXECUTED_AT" BIGINT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "EXECUTION_TIME_MS" BIGINT NULL
+);
+
+CREATE TABLE "USER_TOKENS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255) NOT NULL,
+ "NAME" VARCHAR(100) NOT NULL,
+ "TOKEN_HASH" VARCHAR(255) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL
+);
+
+-- ----------------------------------------------
+-- DDL Statements for indexes
+-- ----------------------------------------------
+
+CREATE UNIQUE INDEX "ACTIVITIES_LOG_KEY" ON "ACTIVITIES" ("LOG_KEY");
+
+CREATE INDEX "GROUP_ROLES_RESOURCE" ON "GROUP_ROLES" ("RESOURCE_ID");
+
+CREATE INDEX "USER_ROLES_RESOURCE" ON "USER_ROLES" ("RESOURCE_ID");
+
+CREATE INDEX "USER_ROLES_USER" ON "USER_ROLES" ("USER_ID");
+
+CREATE INDEX "DUPLICATIONS_INDEX_HASH" ON "DUPLICATIONS_INDEX" ("HASH");
+
+CREATE INDEX "DUPLICATIONS_INDEX_SID" ON "DUPLICATIONS_INDEX" ("SNAPSHOT_ID");
+
+CREATE INDEX "INDEX_GROUPS_USERS_ON_GROUP_ID" ON "GROUPS_USERS" ("GROUP_ID");
+
+CREATE INDEX "INDEX_GROUPS_USERS_ON_USER_ID" ON "GROUPS_USERS" ("USER_ID");
+
+CREATE UNIQUE INDEX "GROUPS_USERS_UNIQUE" ON "GROUPS_USERS" ("GROUP_ID", "USER_ID");
+
+CREATE INDEX "MEASURES_SID_METRIC" ON "PROJECT_MEASURES" ("SNAPSHOT_ID", "METRIC_ID");
+
+CREATE INDEX "MEASURES_PERSON" ON "PROJECT_MEASURES" ("PERSON_ID");
+
+CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS" ("NAME");
+
+CREATE INDEX "EVENTS_SNAPSHOT_ID" ON "EVENTS" ("SNAPSHOT_ID");
+
+CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS" ("COMPONENT_UUID");
+
+CREATE INDEX "WIDGETS_WIDGETKEY" ON "WIDGETS" ("WIDGET_KEY");
+
+CREATE INDEX "WIDGETS_DASHBOARDS" ON "WIDGETS" ("DASHBOARD_ID");
+
+CREATE INDEX "SNAPSHOTS_QUALIFIER" ON "SNAPSHOTS" ("QUALIFIER");
+
+CREATE INDEX "SNAPSHOTS_ROOT" ON "SNAPSHOTS" ("ROOT_SNAPSHOT_ID");
+
+CREATE INDEX "SNAPSHOTS_PARENT" ON "SNAPSHOTS" ("PARENT_SNAPSHOT_ID");
+
+CREATE INDEX "SNAPSHOT_PROJECT_ID" ON "SNAPSHOTS" ("PROJECT_ID");
+
+CREATE INDEX "RULES_PARAMETERS_RULE_ID" ON "RULES_PARAMETERS" ("RULE_ID");
+
+CREATE INDEX "ACTIVE_DASHBOARDS_DASHBOARDID" ON "ACTIVE_DASHBOARDS" ("DASHBOARD_ID");
+
+CREATE INDEX "ACTIVE_DASHBOARDS_USERID" ON "ACTIVE_DASHBOARDS" ("USER_ID");
+
+CREATE INDEX "UNIQUE_SCHEMA_MIGRATIONS" ON "SCHEMA_MIGRATIONS" ("VERSION");
+
+CREATE INDEX "WIDGET_PROPERTIES_WIDGETS" ON "WIDGET_PROPERTIES" ("WIDGET_ID");
+
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
+
+CREATE INDEX "MANUAL_MEASURES_COMPONENT_UUID" ON "MANUAL_MEASURES" ("COMPONENT_UUID");
+
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+
+CREATE INDEX "PROJECTS_ROOT_ID" ON "PROJECTS" ("ROOT_ID");
+
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
+
+CREATE INDEX "RESOURCE_INDEX_KEY" ON "RESOURCE_INDEX" ("KEE");
+
+CREATE INDEX "RESOURCE_INDEX_RID" ON "RESOURCE_INDEX" ("RESOURCE_ID");
+
+CREATE UNIQUE INDEX "UNIQ_AUTHOR_LOGINS" ON "AUTHORS" ("LOGIN");
+
+CREATE INDEX "MEASURE_FILTERS_NAME" ON "MEASURE_FILTERS" ("NAME");
+
+CREATE INDEX "MEASURE_FILTER_FAVS_USERID" ON "MEASURE_FILTER_FAVOURITES" ("USER_ID");
+
+CREATE UNIQUE INDEX "ISSUES_KEE" ON "ISSUES" ("KEE");
+
+CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID");
+
+CREATE INDEX "ISSUES_PROJECT_UUID" ON "ISSUES" ("PROJECT_UUID");
+
+CREATE INDEX "ISSUES_RULE_ID" ON "ISSUES" ("RULE_ID");
+
+CREATE INDEX "ISSUES_RESOLUTION" ON "ISSUES" ("RESOLUTION");
+
+CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES" ("ASSIGNEE");
+
+CREATE INDEX "ISSUES_CREATION_DATE" ON "ISSUES" ("ISSUE_CREATION_DATE");
+
+CREATE INDEX "ISSUES_UPDATED_AT" ON "ISSUES" ("UPDATED_AT");
+
+CREATE INDEX "ISSUE_CHANGES_KEE" ON "ISSUE_CHANGES" ("KEE");
+
+CREATE INDEX "ISSUE_CHANGES_ISSUE_KEY" ON "ISSUE_CHANGES" ("ISSUE_KEY");
+
+CREATE INDEX "ISSUE_FILTERS_NAME" ON "ISSUE_FILTERS" ("NAME");
+
+CREATE INDEX "ISSUE_FILTER_FAVS_USER" ON "ISSUE_FILTER_FAVOURITES" ("USER_LOGIN");
+
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");
+
+CREATE INDEX "SNAPSHOTS_ROOT_PROJECT_ID" ON "SNAPSHOTS" ("ROOT_PROJECT_ID");
+
+CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES" ("GROUP_ID", "RESOURCE_ID", "ROLE");
+
+CREATE UNIQUE INDEX "RULES_REPO_KEY" ON "RULES" ("PLUGIN_NAME", "PLUGIN_RULE_KEY");
+
+CREATE UNIQUE INDEX "UNIQ_QUALITY_GATES" ON "QUALITY_GATES" ("NAME");
+
+CREATE UNIQUE INDEX "ACTIVE_RULES_UNIQUE" ON "ACTIVE_RULES" ("PROFILE_ID","RULE_ID");
+
+CREATE UNIQUE INDEX "UNIQ_QPROF_KEY" ON "RULES_PROFILES" ("KEE");
+
+CREATE INDEX "FILE_SOURCES_PROJECT_UUID" ON "FILE_SOURCES" ("PROJECT_UUID");
+
+CREATE UNIQUE INDEX "FILE_SOURCES_UUID_TYPE" ON "FILE_SOURCES" ("FILE_UUID", "DATA_TYPE");
+
+CREATE INDEX "FILE_SOURCES_UPDATED_AT" ON "FILE_SOURCES" ("UPDATED_AT");
+
+CREATE UNIQUE INDEX "UNIQ_PROJECT_QPROFILES" ON "PROJECT_QPROFILES" ("PROJECT_UUID", "PROFILE_KEY");
+
+CREATE UNIQUE INDEX "CE_QUEUE_UUID" ON "CE_QUEUE" ("UUID");
+
+CREATE INDEX "CE_QUEUE_COMPONENT_UUID" ON "CE_QUEUE" ("COMPONENT_UUID");
+
+CREATE INDEX "CE_QUEUE_STATUS" ON "CE_QUEUE" ("STATUS");
+
+CREATE UNIQUE INDEX "CE_ACTIVITY_UUID" ON "CE_ACTIVITY" ("UUID");
+
+CREATE INDEX "CE_ACTIVITY_COMPONENT_UUID" ON "CE_ACTIVITY" ("COMPONENT_UUID");
+
+CREATE UNIQUE INDEX "USER_TOKENS_TOKEN_HASH" ON "USER_TOKENS" ("TOKEN_HASH");
+
+CREATE UNIQUE INDEX "USER_TOKENS_LOGIN_NAME" ON "USER_TOKENS" ("LOGIN", "NAME");
+
+CREATE INDEX "CE_ACTIVITY_ISLASTKEY" ON "CE_ACTIVITY" ("IS_LAST_KEY");
+
+CREATE INDEX "CE_ACTIVITY_ISLAST_STATUS" ON "CE_ACTIVITY" ("IS_LAST", "STATUS");
--- /dev/null
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(2000),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+