Merge changes from topic "sql-for-group-db-access-in-schema-upgrades"

* changes:
  Use SQL for group related tables in schema upgrade tests
  Use SQL for group related tables in schema upgrades
This commit is contained in:
Dave Borowitz
2017-10-17 17:20:02 +00:00
committed by Gerrit Code Review
3 changed files with 128 additions and 41 deletions

View File

@@ -14,17 +14,17 @@
package com.google.gerrit.server.schema;
import com.google.common.collect.Streams;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit.Key;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -36,21 +36,45 @@ public class Schema_151 extends SchemaVersion {
}
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException {
List<AccountGroup> accountGroups = db.accountGroups().all().toList();
for (AccountGroup accountGroup : accountGroups) {
ResultSet<AccountGroupMemberAudit> groupMemberAudits =
db.accountGroupMembersAudit().byGroup(accountGroup.getId());
Optional<Timestamp> firstTimeMentioned =
Streams.stream(groupMemberAudits)
.map(AccountGroupMemberAudit::getKey)
.map(Key::getAddedOn)
.min(Comparator.naturalOrder());
Timestamp createdOn =
firstTimeMentioned.orElseGet(() -> AccountGroup.auditCreationInstantTs());
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
try (PreparedStatement groupUpdate =
prepareStatement(db, "UPDATE account_groups SET created_on = ? WHERE group_id = ?");
PreparedStatement addedOnRetrieval =
prepareStatement(
db,
"SELECT added_on FROM account_group_members_audit WHERE group_id = ?"
+ " ORDER BY added_on ASC")) {
List<AccountGroup.Id> accountGroups = getAllGroupIds(db);
for (AccountGroup.Id groupId : accountGroups) {
Optional<Timestamp> firstTimeMentioned = getFirstTimeMentioned(addedOnRetrieval, groupId);
Timestamp createdOn = firstTimeMentioned.orElseGet(AccountGroup::auditCreationInstantTs);
accountGroup.setCreatedOn(createdOn);
groupUpdate.setTimestamp(1, createdOn);
groupUpdate.setInt(2, groupId.get());
groupUpdate.executeUpdate();
}
}
}
private static Optional<Timestamp> getFirstTimeMentioned(
PreparedStatement addedOnRetrieval, AccountGroup.Id groupId) throws SQLException {
addedOnRetrieval.setInt(1, groupId.get());
try (ResultSet resultSet = addedOnRetrieval.executeQuery()) {
if (resultSet.first()) {
return Optional.of(resultSet.getTimestamp(1));
}
}
return Optional.empty();
}
private static List<AccountGroup.Id> getAllGroupIds(ReviewDb db) throws SQLException {
try (Statement stmt = newStatement(db);
ResultSet rs = stmt.executeQuery("SELECT group_id FROM account_groups")) {
List<AccountGroup.Id> groupIds = new ArrayList<>();
while (rs.next()) {
groupIds.add(new AccountGroup.Id(rs.getInt(1)));
}
return groupIds;
}
db.accountGroups().update(accountGroups);
}
}

View File

@@ -20,11 +20,12 @@ import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
public class Schema_87 extends SchemaVersion {
@@ -35,16 +36,37 @@ public class Schema_87 extends SchemaVersion {
@Override
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
for (AccountGroup.Id id : scanSystemGroups(db)) {
AccountGroup group = db.accountGroups().get(id);
if (group != null && SystemGroupBackend.isSystemGroup(group.getGroupUUID())) {
db.accountGroups().delete(Collections.singleton(group));
db.accountGroupNames().deleteKeys(Collections.singleton(group.getNameKey()));
try (PreparedStatement uuidRetrieval =
prepareStatement(db, "SELECT group_uuid FROM account_groups WHERE group_id = ?");
PreparedStatement groupDeletion =
prepareStatement(db, "DELETE FROM account_groups WHERE group_id = ?");
PreparedStatement groupNameDeletion =
prepareStatement(db, "DELETE FROM account_group_names WHERE group_id = ?")) {
for (AccountGroup.Id id : scanSystemGroups(db)) {
Optional<AccountGroup.UUID> groupUuid = getUuid(uuidRetrieval, id);
if (groupUuid.filter(SystemGroupBackend::isSystemGroup).isPresent()) {
groupDeletion.setInt(1, id.get());
groupDeletion.executeUpdate();
groupNameDeletion.setInt(1, id.get());
groupNameDeletion.executeUpdate();
}
}
}
}
private Set<AccountGroup.Id> scanSystemGroups(ReviewDb db) throws SQLException {
private static Optional<AccountGroup.UUID> getUuid(
PreparedStatement uuidRetrieval, AccountGroup.Id id) throws SQLException {
uuidRetrieval.setInt(1, id.get());
try (ResultSet uuidResults = uuidRetrieval.executeQuery()) {
if (uuidResults.first()) {
Optional.of(new AccountGroup.UUID(uuidResults.getString(1)));
}
}
return Optional.empty();
}
private static Set<AccountGroup.Id> scanSystemGroups(ReviewDb db) throws SQLException {
try (Statement stmt = newStatement(db);
ResultSet rs =
stmt.executeQuery("SELECT group_id FROM account_groups WHERE group_type = 'SYSTEM'")) {

View File

@@ -15,27 +15,29 @@
package com.google.gerrit.server.schema;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
import com.google.common.collect.ImmutableList;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.api.groups.GroupInput;
import com.google.gerrit.extensions.common.GroupInfo;
import com.google.gerrit.extensions.restapi.TopLevelResource;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.AccountGroup.Id;
import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.group.CreateGroup;
import com.google.gerrit.testutil.SchemaUpgradeTestEnvironment;
import com.google.gerrit.testutil.TestUpdateUI;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.gwtorm.jdbc.JdbcSchema;
import com.google.inject.Inject;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneOffset;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -48,11 +50,40 @@ public class Schema_150_to_151_Test {
@Inject private Schema_151 schema151;
private ReviewDb db;
private Connection connection;
private PreparedStatement createdOnRetrieval;
private PreparedStatement createdOnUpdate;
private PreparedStatement auditEntryDeletion;
@Before
public void setUp() throws Exception {
testEnv.getInjector().injectMembers(this);
db = testEnv.getDb();
assume().that(db instanceof JdbcSchema).isTrue();
connection = ((JdbcSchema) db).getConnection();
createdOnRetrieval =
connection.prepareStatement("SELECT created_on FROM account_groups WHERE group_id = ?");
createdOnUpdate =
connection.prepareStatement("UPDATE account_groups SET created_on = ? WHERE group_id = ?");
auditEntryDeletion =
connection.prepareStatement("DELETE FROM account_group_members_audit WHERE group_id = ?");
}
@After
public void tearDown() throws Exception {
if (auditEntryDeletion != null) {
auditEntryDeletion.close();
}
if (createdOnUpdate != null) {
createdOnUpdate.close();
}
if (createdOnRetrieval != null) {
createdOnRetrieval.close();
}
if (connection != null) {
connection.close();
}
}
@Test
@@ -63,8 +94,8 @@ public class Schema_150_to_151_Test {
schema151.migrateData(db, new TestUpdateUI());
AccountGroup group = db.accountGroups().get(groupId);
assertThat(group.getCreatedOn()).isAtLeast(testStartTime);
Timestamp createdOn = getCreatedOn(groupId);
assertThat(createdOn).isAtLeast(testStartTime);
}
@Test
@@ -75,8 +106,8 @@ public class Schema_150_to_151_Test {
schema151.migrateData(db, new TestUpdateUI());
AccountGroup group = db.accountGroups().get(groupId);
assertThat(group.getCreatedOn()).isEqualTo(AccountGroup.auditCreationInstantTs());
Timestamp createdOn = getCreatedOn(groupId);
assertThat(createdOn).isEqualTo(AccountGroup.auditCreationInstantTs());
}
private AccountGroup.Id createGroup(String name) throws Exception {
@@ -87,16 +118,26 @@ public class Schema_150_to_151_Test {
return new Id(groupInfo.groupId);
}
private void setCreatedOnToVeryOldTimestamp(Id groupId) throws OrmException {
AccountGroup group = db.accountGroups().get(groupId);
private Timestamp getCreatedOn(Id groupId) throws Exception {
createdOnRetrieval.setInt(1, groupId.get());
try (ResultSet results = createdOnRetrieval.executeQuery()) {
if (results.first()) {
return results.getTimestamp(1);
}
}
return null;
}
private void setCreatedOnToVeryOldTimestamp(Id groupId) throws Exception {
createdOnUpdate.setInt(1, groupId.get());
Instant instant = LocalDateTime.of(1800, Month.JANUARY, 1, 0, 0).toInstant(ZoneOffset.UTC);
group.setCreatedOn(Timestamp.from(instant));
db.accountGroups().update(ImmutableList.of(group));
createdOnUpdate.setTimestamp(1, Timestamp.from(instant));
createdOnUpdate.setInt(2, groupId.get());
createdOnUpdate.executeUpdate();
}
private void removeAuditEntriesFor(AccountGroup.Id groupId) throws Exception {
ResultSet<AccountGroupMemberAudit> groupMemberAudits =
db.accountGroupMembersAudit().byGroup(groupId);
db.accountGroupMembersAudit().delete(groupMemberAudits);
auditEntryDeletion.setInt(1, groupId.get());
auditEntryDeletion.executeUpdate();
}
}