Merge changes I7c096bfb,If0dd9547

* changes:
  Fall back to ReviewDb for removing approvals
  DeleteReviewer#updateChange: Use database from context
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index 0fc0d14..6340801 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -65,6 +65,7 @@
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.config.AnonymousCowardNameProvider;
 import com.google.gerrit.server.git.ProjectConfig;
@@ -701,6 +702,19 @@
 
   @Test
   public void removeReviewerNoVotes() throws Exception {
+    ProjectConfig cfg = projectCache.checkedGet(project).getConfig();
+
+    LabelType verified = category("Verified", value(1, "Passes"),
+        value(0, "No score"), value(-1, "Failed"));
+    cfg.getLabelSections().put(verified.getName(), verified);
+
+    AccountGroup.UUID registeredUsers =
+        SystemGroupBackend.getGroup(REGISTERED_USERS).getUUID();
+    String heads = RefNames.REFS_HEADS + "*";
+    Util.allow(cfg, Permission.forLabel(Util.verified().getName()), -1, 1,
+        registeredUsers, heads);
+    saveProjectConfig(project, cfg);
+
     PushOneCommit.Result r = createChange();
     String changeId = r.getChangeId();
     gApi.changes()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
index be5c75d..bdefa93 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteReviewer.java
@@ -28,11 +28,11 @@
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
-import com.google.gerrit.reviewdb.client.LabelId;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.reviewdb.server.ReviewDbUtil;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.ChangeUtil;
@@ -43,9 +43,11 @@
 import com.google.gerrit.server.git.BatchUpdate;
 import com.google.gerrit.server.git.BatchUpdate.ChangeContext;
 import com.google.gerrit.server.git.BatchUpdate.Context;
+import com.google.gerrit.server.git.BatchUpdateReviewDb;
 import com.google.gerrit.server.git.UpdateException;
 import com.google.gerrit.server.mail.DeleteReviewerSender;
 import com.google.gerrit.server.notedb.ChangeUpdate;
+import com.google.gerrit.server.notedb.NotesMigration;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
@@ -56,10 +58,8 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 @Singleton
 public class DeleteReviewer implements RestModifyView<ReviewerResource, Input> {
@@ -78,6 +78,7 @@
   private final ReviewerDeleted reviewerDeleted;
   private final Provider<IdentifiedUser> user;
   private final DeleteReviewerSender.Factory deleteReviewerSenderFactory;
+  private final NotesMigration migration;
 
   @Inject
   DeleteReviewer(Provider<ReviewDb> dbProvider,
@@ -88,7 +89,8 @@
       IdentifiedUser.GenericFactory userFactory,
       ReviewerDeleted reviewerDeleted,
       Provider<IdentifiedUser> user,
-      DeleteReviewerSender.Factory deleteReviewerSenderFactory) {
+      DeleteReviewerSender.Factory deleteReviewerSenderFactory,
+      NotesMigration migration) {
     this.dbProvider = dbProvider;
     this.approvalsUtil = approvalsUtil;
     this.psUtil = psUtil;
@@ -98,6 +100,7 @@
     this.reviewerDeleted = reviewerDeleted;
     this.user = user;
     this.deleteReviewerSenderFactory = deleteReviewerSenderFactory;
+    this.migration = migration;
   }
 
   @Override
@@ -136,21 +139,18 @@
         throw new ResourceNotFoundException();
       }
       currChange = ctx.getChange();
-      currPs = psUtil.current(dbProvider.get(), ctx.getNotes());
+      currPs = psUtil.current(ctx.getDb(), ctx.getNotes());
 
-      Set<String> placeholders = new HashSet<>();
       LabelTypes labelTypes = ctx.getControl().getLabelTypes();
       // removing a reviewer will remove all her votes
       for (LabelType lt : labelTypes.getLabelTypes()) {
         newApprovals.put(lt.getName(), (short) 0);
-        placeholders.add(lt.getName());
       }
 
       StringBuilder msg = new StringBuilder();
       for (PatchSetApproval a : approvals(ctx, reviewerId)) {
         if (ctx.getControl().canRemoveReviewer(a)) {
           del.add(a);
-          placeholders.remove(a.getLabel());
           if (a.getPatchSetId().equals(currPs.getId()) && a.getValue() != 0) {
             oldApprovals.put(a.getLabel(), a.getValue());
             if (msg.length() == 0) {
@@ -167,14 +167,6 @@
         }
       }
 
-      for (String label : placeholders) {
-        PatchSetApproval a = new PatchSetApproval(
-            new PatchSetApproval.Key(currPs.getId(), reviewerId, new LabelId(label)),
-            (short) 0,
-            TimeUtil.nowTs());
-        del.add(a);
-      }
-
       ctx.getDb().patchSetApprovals().delete(del);
       ChangeUpdate update = ctx.getUpdate(currPs.getId());
       update.removeReviewer(reviewerId);
@@ -207,8 +199,26 @@
 
     private Iterable<PatchSetApproval> approvals(ChangeContext ctx,
         final Account.Id accountId) throws OrmException {
+      Change.Id changeId = ctx.getNotes().getChangeId();
+      Iterable<PatchSetApproval> approvals;
+
+      if (migration.readChanges()) {
+        // Because NoteDb and ReviewDb have different semantics for zero-value
+        // approvals, we must fall back to ReviewDb as the source of truth here.
+        ReviewDb db = ctx.getDb();
+
+        if (db instanceof BatchUpdateReviewDb) {
+          db = ((BatchUpdateReviewDb) db).unsafeGetDelegate();
+        }
+        db = ReviewDbUtil.unwrapDb(db);
+        approvals = db.patchSetApprovals().byChange(changeId);
+      } else {
+        approvals =
+            approvalsUtil.byChange(ctx.getDb(), ctx.getNotes()).values();
+      }
+
       return Iterables.filter(
-          approvalsUtil.byChange(ctx.getDb(), ctx.getNotes()).values(),
+          approvals,
           new Predicate<PatchSetApproval>() {
             @Override
             public boolean apply(PatchSetApproval input) {