]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9085 Clean orphan rows in GROUPS_USERS
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 13 Apr 2017 07:17:12 +0000 (09:17 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 13 Apr 2017 09:51:55 +0000 (11:51 +0200)
server/sonar-db-core/src/main/resources/org/sonar/db/version/rows-h2.sql
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsers.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/DbVersion64Test.java
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest/initial.sql [new file with mode: 0644]

index 24ba7f0fd59d71ecf79c544c7cf584f2e5496f1d..c3ba8396b64bf4544b703cc113356ad90902c91f 100644 (file)
@@ -559,6 +559,7 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1622');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1623');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1624');
 INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1625');
+INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('1626');
 
 INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, EXTERNAL_IDENTITY, EXTERNAL_IDENTITY_PROVIDER, USER_LOCAL, CRYPTED_PASSWORD, SALT, IS_ROOT, CREATED_AT, UPDATED_AT) VALUES (1, 'admin', 'Administrator', '', 'admin', 'sonarqube', true, 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', false, '1418215735482', '1418215735482');
 ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsers.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsers.java
new file mode 100644 (file)
index 0000000..85095b2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.v64;
+
+import java.sql.SQLException;
+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 CleanOrphanRowsInGroupsUsers extends DataChange {
+
+  public CleanOrphanRowsInGroupsUsers(Database db) {
+    super(db);
+  }
+
+  @Override
+  protected void execute(Context context) throws SQLException {
+    MassUpdate massUpdate = context.prepareMassUpdate().rowPluralName("orphan user group membership");
+    massUpdate.select("SELECT gu.user_id, gu.group_id FROM groups_users gu " +
+      "LEFT OUTER JOIN users u ON u.id=gu.user_id " +
+      "WHERE u.id IS NULL or u.active=?")
+      .setBoolean(1, false);
+    massUpdate.update("DELETE FROM groups_users WHERE user_id=? AND group_id=?");
+    massUpdate.execute((row, update) -> {
+      update.setLong(1, row.getLong(1));
+      update.setLong(2, row.getLong(2));
+      return true;
+    });
+  }
+
+}
index 2784cb84469bdef4ab6927f6c61a2a8d81075ee0..6af218c54a58eab950233a2a77f51c26332a8001 100644 (file)
@@ -53,6 +53,7 @@ public class DbVersion64 implements DbVersion {
       .add(1622, "Create 'Members' group in each organization", CreateMembersGroupsInEachOrganization.class)
       .add(1623, "Set organization members into 'Members' group", SetOrganizationMembersIntoMembersGroup.class)
       .add(1624, "Add ORGANIZATIONS.DEFAULT_GROUP_ID", AddDefaultGroupIdToOrganizations.class)
-      .add(1625, "Populate column ORGANIZATIONS.DEFAULT_GROUP_ID", PopulateColumnDefaultGroupIdOfOrganizations.class);
+      .add(1625, "Populate column ORGANIZATIONS.DEFAULT_GROUP_ID", PopulateColumnDefaultGroupIdOfOrganizations.class)
+      .add(1626, "Clean orphan rows in table GROUPS_USERS", CleanOrphanRowsInGroupsUsers.class);
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest.java
new file mode 100644 (file)
index 0000000..164875b
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.v64;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.CoreDbTester;
+
+import static java.lang.String.format;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class CleanOrphanRowsInGroupsUsersTest {
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(CleanOrphanRowsInGroupsUsersTest.class, "initial.sql");
+
+  private System2 system2 = mock(System2.class);
+
+  private CleanOrphanRowsInGroupsUsers underTest = new CleanOrphanRowsInGroupsUsers(db.database());
+
+  @Test
+  public void remove_orphans_when_users_id_does_not_exist() throws Exception {
+    insertUserGroups(1L, 10L);
+    insertUserGroups(1L, 11L);
+    insertUserGroups(2L, 12L);
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("groups_users")).isZero();
+  }
+
+  @Test
+  public void remove_orphans_when_user_is_disabled() throws Exception {
+    long user1 = insertUser("user1", false);
+    insertUserGroups(user1, 10L);
+    insertUserGroups(user1, 11L);
+
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("groups_users")).isZero();
+  }
+
+  @Test
+  public void does_not_remove_group_membership_on_active_users() throws Exception {
+    long user1 = insertUser("user1", true);
+    insertUserGroups(user1, 10L);
+    insertUserGroups(user1, 11L);
+    long user2 = insertUser("user2", true);
+    insertUserGroups(user2, 10L);
+
+    underTest.execute();
+
+    checkUserGroups(user1, 10L, 11L);
+    checkUserGroups(user2, 10L);
+  }
+
+  @Test
+  public void migration_is_reentrant() throws Exception {
+    insertUserGroups(1L, 10L);
+    insertUserGroups(2L, 10L);
+
+    underTest.execute();
+    assertThat(db.countRowsOfTable("groups_users")).isZero();
+
+    underTest.execute();
+    assertThat(db.countRowsOfTable("groups_users")).isZero();
+  }
+
+  private void checkUserGroups(long userId, Long... expectedGroupIds) {
+    List<Long> groups = db.select(format("select gu.group_id from groups_users gu where gu.user_id=%s", userId)).stream()
+      .map(map -> (Long) map.get("GROUP_ID"))
+      .collect(Collectors.toList());
+    assertThat(groups).containsOnly(expectedGroupIds);
+  }
+
+  private void insertUserGroups(long userId, Long... groupIds) {
+    Arrays.stream(groupIds).forEach(groupId -> db.executeInsert(
+      "GROUPS_USERS",
+      "USER_ID", userId,
+      "GROUP_ID", groupId));
+  }
+
+  private long insertUser(String login, boolean enabled) {
+    db.executeInsert(
+      "USERS",
+      "LOGIN", login,
+      "ACTIVE", Boolean.toString(enabled),
+      "IS_ROOT", "false",
+      "CREATED_AT", "1000",
+      "UPDATED_AT", "1000");
+    return (Long) db.selectFirst(format("select id from users where login='%s'", login)).get("ID");
+  }
+}
index 2d1c5c541309f7984523ae974329b2f49b214b5e..59cdff0639ea58c414e0fb5a2cd057975a8febe3 100644 (file)
@@ -35,7 +35,7 @@ public class DbVersion64Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 26);
+    verifyMigrationCount(underTest, 27);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest/initial.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v64/CleanOrphanRowsInGroupsUsersTest/initial.sql
new file mode 100644 (file)
index 0000000..3ea20b3
--- /dev/null
@@ -0,0 +1,26 @@
+CREATE TABLE "GROUPS_USERS" (
+  "USER_ID" INTEGER,
+  "GROUP_ID" INTEGER
+);
+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 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),
+  "ACTIVE" BOOLEAN DEFAULT TRUE,
+  "SCM_ACCOUNTS" VARCHAR(4000),
+  "EXTERNAL_IDENTITY" VARCHAR(255),
+  "EXTERNAL_IDENTITY_PROVIDER" VARCHAR(100),
+  "IS_ROOT" BOOLEAN NOT NULL,
+  "USER_LOCAL" BOOLEAN,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
+);
+CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN");
+CREATE INDEX "USERS_UPDATED_AT" ON "USERS" ("UPDATED_AT");