aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-db-migration
diff options
context:
space:
mode:
authorPierre <pierre.guillot@sonarsource.com>2022-07-15 11:50:31 +0200
committersonartech <sonartech@sonarsource.com>2022-07-21 20:03:05 +0000
commit4d4fa01b933038ba8e9639679fab4a36b6eff91a (patch)
tree7d5b99d4ffe2beab24dcb49a101d7e60127e1e0e /server/sonar-db-migration
parent5f4e6f7f86a2d780f92125a4fac844d1c2ca5a07 (diff)
downloadsonarqube-4d4fa01b933038ba8e9639679fab4a36b6eff91a.tar.gz
sonarqube-4d4fa01b933038ba8e9639679fab4a36b6eff91a.zip
SONAR-16613 Migrate users field 'sonarlint_ad_seen' to use the new notice mechanism
Diffstat (limited to 'server/sonar-db-migration')
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java2
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java51
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java31
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java2
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java34
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java66
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java54
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java78
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql28
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql42
10 files changed, 359 insertions, 29 deletions
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java
index 5bf051020f2..ba761526777 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnChange.java
@@ -44,7 +44,7 @@ public abstract class DropColumnChange extends DdlChange {
context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, columnName).build());
}
- private boolean checkIfUseManagedColumnExists() throws SQLException {
+ public boolean checkIfUseManagedColumnExists() throws SQLException {
try (var connection = getDatabase().getDataSource().getConnection()) {
if (DatabaseUtils.tableColumnExists(connection, tableName, columnName)) {
return true;
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java
new file mode 100644
index 00000000000..34d9dc1fe3b
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DropColumnWithConstraint.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.step;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder;
+
+public class DropColumnWithConstraint extends DropColumnChange {
+
+ private final String tableName;
+ private final String column;
+
+ public DropColumnWithConstraint(Database db, String tableName, String column) {
+ super(db, tableName, column);
+ this.tableName = tableName;
+ this.column = column;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ if (!checkIfUseManagedColumnExists()) {
+ return;
+ }
+
+ if (MsSql.ID.equals(getDatabase().getDialect().getId())) {
+ context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(tableName).setColumns(column).build());
+ }
+ context.execute(new DropColumnsBuilder(getDatabase().getDialect(), tableName, column).build());
+ }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java
index 05628583383..3cfcce03f27 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java
@@ -19,41 +19,16 @@
*/
package org.sonar.server.platform.db.migration.version.v91;
-import java.sql.SQLException;
import org.sonar.db.Database;
-import org.sonar.db.DatabaseUtils;
-import org.sonar.db.dialect.MsSql;
-import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder;
-import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder;
-import org.sonar.server.platform.db.migration.step.DdlChange;
+import org.sonar.server.platform.db.migration.step.DropColumnWithConstraint;
-public class DropUserManagedColumnFromMetricsTable extends DdlChange {
+public class DropUserManagedColumnFromMetricsTable extends DropColumnWithConstraint {
private static final String TABLE_NAME = "metrics";
private static final String COLUMN = "user_managed";
public DropUserManagedColumnFromMetricsTable(Database db) {
- super(db);
+ super(db, TABLE_NAME, COLUMN);
}
- @Override
- public void execute(Context context) throws SQLException {
- if (!checkIfUseManagedColumnExists()) {
- return;
- }
-
- if (MsSql.ID.equals(getDatabase().getDialect().getId())) {
- context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(TABLE_NAME).setColumns(COLUMN).build());
- }
- context.execute(new DropColumnsBuilder(getDatabase().getDialect(), TABLE_NAME, COLUMN).build());
- }
-
- private boolean checkIfUseManagedColumnExists() throws SQLException {
- try (var connection = getDatabase().getDataSource().getConnection()) {
- if (DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN)) {
- return true;
- }
- }
- return false;
- }
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java
index 4c3ad0be119..71692004e58 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DbVersion96.java
@@ -36,6 +36,8 @@ public class DbVersion96 implements DbVersion {
.add(6505, "Add column 'rule_description_context_key' to 'issues'", AddRuleDescriptionContextKeyInIssuesTable.class)
.add(6506, "Add column 'education_principles' to 'rules'", AddEducationPrinciplesColumnToRuleTable.class)
.add(6507, "Overwrite plugin file hash to force reloading rules", ForceReloadingOfAllPlugins.class)
+ .add(6508, "Migrate 'sonarlint_ad_seen' from users to properties", MigrateSonarlintAdSeenFromUsersToProperties.class)
+ .add(6509, "Drop column sonarlint_ad_seen in 'users'", DropSonarlintAdSeenColumnInUsersTable.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java
new file mode 100644
index 00000000000..fa8bc33dfd7
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTable.java
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v96;
+
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DropColumnWithConstraint;
+
+public class DropSonarlintAdSeenColumnInUsersTable extends DropColumnWithConstraint {
+
+ private static final String TABLE_NAME = "users";
+ private static final String COLUMN = "sonarlint_ad_seen";
+
+ public DropSonarlintAdSeenColumnInUsersTable(Database db) {
+ super(db, TABLE_NAME, COLUMN);
+ }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java
new file mode 100644
index 00000000000..99c52dfd358
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToProperties.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v96;
+
+import java.sql.SQLException;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class MigrateSonarlintAdSeenFromUsersToProperties extends DataChange {
+
+ public static final String USER_DISMISSED_NOTICES_SONARLINT_AD = "user.dismissedNotices.sonarlintAd";
+
+ private final UuidFactory uuidFactory;
+ private final System2 system2;
+
+ public MigrateSonarlintAdSeenFromUsersToProperties(Database db, UuidFactory uuidFactory, System2 system2) {
+ super(db);
+ this.uuidFactory = uuidFactory;
+ this.system2 = system2;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+
+ massUpdate.select("select u.uuid, u.sonarlint_ad_seen, p.uuid from users u" +
+ " left join properties p on u.uuid = p.user_uuid and p.prop_key = ?" +
+ " where u.sonarlint_ad_seen = ?" +
+ " and p.uuid is null")
+ .setString(1, USER_DISMISSED_NOTICES_SONARLINT_AD)
+ .setBoolean(2, true);
+
+ massUpdate.update("insert into properties (uuid,prop_key,user_uuid,is_empty,created_at) values (?, ?, ?, ?, ?)");
+
+ massUpdate.execute((row, update) -> {
+ update.setString(1, uuidFactory.create());
+ update.setString(2, USER_DISMISSED_NOTICES_SONARLINT_AD);
+ update.setString(3, row.getString(1));
+ update.setBoolean(4, true);
+ update.setLong(5, system2.now());
+
+ return true;
+ });
+
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java
new file mode 100644
index 00000000000..1dca64023a9
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v96;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropSonarlintAdSeenColumnInUsersTableTest {
+
+ private static final String COLUMN_NAME = "sonarlint_ad_seen";
+ private static final String TABLE_NAME = "users";
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(DropSonarlintAdSeenColumnInUsersTableTest.class, "schema.sql");
+
+ private final DdlChange underTest = new DropSonarlintAdSeenColumnInUsersTable(db.database());
+
+ @Test
+ public void migration_should_drop_column() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true);
+ underTest.execute();
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ }
+
+ @Test
+ public void migration_should_be_reentrant() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true);
+ underTest.execute();
+ // re-entrant
+ underTest.execute();
+ db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+ }
+} \ No newline at end of file
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java
new file mode 100644
index 00000000000..02d0c929ba1
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info 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.server.platform.db.migration.version.v96;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.core.util.UuidFactoryFast;
+import org.sonar.db.CoreDbTester;
+import org.sonar.server.platform.db.migration.step.DataChange;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.platform.db.migration.version.v96.MigrateSonarlintAdSeenFromUsersToProperties.USER_DISMISSED_NOTICES_SONARLINT_AD;
+
+public class MigrateSonarlintAdSeenFromUsersToPropertiesTest {
+
+ @Rule
+ public final CoreDbTester db = CoreDbTester.createForSchema(MigrateSonarlintAdSeenFromUsersToPropertiesTest.class, "schema.sql");
+
+ private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
+
+ private final System2 system2 = new System2();
+
+ private final DataChange underTest = new MigrateSonarlintAdSeenFromUsersToProperties(db.database(), uuidFactory, system2);
+
+ @Test
+ public void migrate_sonarlintAd_to_properties() throws SQLException {
+ insertUser(db, "uuid-user-1", "user1", "externalId1", "externalLogin1", true);
+ insertUser(db, "uuid-user-2", "user2", "externalId2", "externalLogin2", false);
+
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-1'")).isEqualTo(1);
+ assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-2'")).isZero();
+ }
+
+ @Test
+ public void migration_is_reentrant() throws SQLException {
+ insertUser(db, "uuid-user-1", "user1", "externalId1", "externalLogin1", true);
+
+ underTest.execute();
+ underTest.execute();
+
+ assertThat(db.countSql("select count(*) from properties where prop_key='" + USER_DISMISSED_NOTICES_SONARLINT_AD + "' and user_uuid='uuid-user-1'")).isEqualTo(1);
+ }
+
+ private void insertUser(CoreDbTester db, String userUuid, String login, String externalId, String externalLogin, boolean seen) {
+ db.executeInsert("users", "UUID", userUuid,
+ "login", login,
+ "external_identity_provider", "none",
+ "external_id", externalId,
+ "external_login", externalLogin,
+ "reset_password", false,
+ "sonarlint_ad_seen", seen
+ );
+ }
+
+
+} \ No newline at end of file
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql
new file mode 100644
index 00000000000..82da2b04f35
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/DropSonarlintAdSeenColumnInUsersTableTest/schema.sql
@@ -0,0 +1,28 @@
+CREATE TABLE "USERS"(
+ "UUID" CHARACTER VARYING(255) NOT NULL,
+ "LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "NAME" CHARACTER VARYING(200),
+ "EMAIL" CHARACTER VARYING(100),
+ "CRYPTED_PASSWORD" CHARACTER VARYING(100),
+ "SALT" CHARACTER VARYING(40),
+ "HASH_METHOD" CHARACTER VARYING(10),
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" CHARACTER VARYING(4000),
+ "EXTERNAL_LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "EXTERNAL_IDENTITY_PROVIDER" CHARACTER VARYING(100) NOT NULL,
+ "EXTERNAL_ID" CHARACTER VARYING(255) NOT NULL,
+ "USER_LOCAL" BOOLEAN,
+ "HOMEPAGE_TYPE" CHARACTER VARYING(40),
+ "HOMEPAGE_PARAMETER" CHARACTER VARYING(40),
+ "LAST_CONNECTION_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "RESET_PASSWORD" BOOLEAN NOT NULL,
+ "LAST_SONARLINT_CONNECTION" BIGINT,
+ "SONARLINT_AD_SEEN" BOOLEAN DEFAULT FALSE
+);
+ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST);
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST);
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql
new file mode 100644
index 00000000000..f5eda0e9df8
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v96/MigrateSonarlintAdSeenFromUsersToPropertiesTest/schema.sql
@@ -0,0 +1,42 @@
+CREATE TABLE "USERS"(
+ "UUID" CHARACTER VARYING(255) NOT NULL,
+ "LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "NAME" CHARACTER VARYING(200),
+ "EMAIL" CHARACTER VARYING(100),
+ "CRYPTED_PASSWORD" CHARACTER VARYING(100),
+ "SALT" CHARACTER VARYING(40),
+ "HASH_METHOD" CHARACTER VARYING(10),
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" CHARACTER VARYING(4000),
+ "EXTERNAL_LOGIN" CHARACTER VARYING(255) NOT NULL,
+ "EXTERNAL_IDENTITY_PROVIDER" CHARACTER VARYING(100) NOT NULL,
+ "EXTERNAL_ID" CHARACTER VARYING(255) NOT NULL,
+ "USER_LOCAL" BOOLEAN,
+ "HOMEPAGE_TYPE" CHARACTER VARYING(40),
+ "HOMEPAGE_PARAMETER" CHARACTER VARYING(40),
+ "LAST_CONNECTION_DATE" BIGINT,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT,
+ "RESET_PASSWORD" BOOLEAN NOT NULL,
+ "LAST_SONARLINT_CONNECTION" BIGINT,
+ "SONARLINT_AD_SEEN" BOOLEAN DEFAULT FALSE
+);
+ALTER TABLE "USERS" ADD CONSTRAINT "PK_USERS" PRIMARY KEY("UUID");
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS"("LOGIN" NULLS FIRST);
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS"("UPDATED_AT" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_ID" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_ID" NULLS FIRST);
+CREATE UNIQUE INDEX "UNIQ_EXTERNAL_LOGIN" ON "USERS"("EXTERNAL_IDENTITY_PROVIDER" NULLS FIRST, "EXTERNAL_LOGIN" NULLS FIRST);
+
+
+CREATE TABLE "PROPERTIES"(
+ "UUID" CHARACTER VARYING(40) NOT NULL,
+ "PROP_KEY" CHARACTER VARYING(512) NOT NULL,
+ "IS_EMPTY" BOOLEAN NOT NULL,
+ "TEXT_VALUE" CHARACTER VARYING(4000),
+ "CLOB_VALUE" CHARACTER LARGE OBJECT,
+ "CREATED_AT" BIGINT NOT NULL,
+ "COMPONENT_UUID" CHARACTER VARYING(40),
+ "USER_UUID" CHARACTER VARYING(255)
+);
+ALTER TABLE "PROPERTIES" ADD CONSTRAINT "PK_PROPERTIES" PRIMARY KEY("UUID");
+CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES"("PROP_KEY" NULLS FIRST);