Fix all ChangeFields to properly handle null values
Many ChangeData methods can return null values, typically indicating
a change is corrupt. Handle these gracefully within ChangeFields by
just not emitting the field values.
Fix a few ChangeData methods to return null that were previously
throwing exceptions, for consistency.
Testing this is hard. We get a little coverage for missing patch sets
by trying to reindex the changes in CheckIT; unfortunately this won't
work for testing a missing change.
Change-Id: Ia9b2722bb07d0025ca45631648ef060ff5b4f117
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 96b737f..3049a98 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -42,6 +42,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MetaDataUpdate;
import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.Util;
import com.google.gerrit.testutil.ConfigSuite;
@@ -107,6 +108,9 @@
@Inject
protected GitRepositoryManager repoManager;
+ @Inject
+ protected ChangeIndexer indexer;
+
protected Git git;
protected GerritServer server;
protected TestAccount admin;
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/CheckIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/CheckIT.java
index db45e4c..ab6bc42 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/CheckIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/CheckIT.java
@@ -40,6 +40,7 @@
PushOneCommit.Result r = createChange();
Change c = getChange(r);
db.patchSets().deleteKeys(Collections.singleton(c.currentPatchSetId()));
+ indexer.index(db, c);
List<ProblemInfo> problems = gApi.changes()
.id(r.getChangeId())
@@ -65,6 +66,7 @@
Change c = getChange(r);
c.setStatus(Change.Status.NEW);
db.changes().update(Collections.singleton(c));
+ indexer.index(db, c);
ChangeInfo info = gApi.changes()
.id(r.getChangeId())
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index d8fb7db..9f90e37 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -14,9 +14,11 @@
package com.google.gerrit.server.index;
+import static com.google.common.base.MoreObjects.firstNonNull;
+
import com.google.common.base.Function;
-import com.google.common.base.MoreObjects;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -27,7 +29,6 @@
import com.google.gerrit.reviewdb.client.PatchLineComment;
import com.google.gerrit.reviewdb.client.PatchSet;
import com.google.gerrit.reviewdb.client.PatchSetApproval;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeData.ChangedLines;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
@@ -37,6 +38,8 @@
import com.google.gwtorm.server.OrmException;
import com.google.protobuf.CodedOutputStream;
+import org.eclipse.jgit.revwalk.FooterLine;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.Timestamp;
@@ -70,7 +73,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getKey().get();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getKey().get();
}
};
@@ -81,8 +88,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return ChangeStatusPredicate.canonicalize(
- input.change().getStatus());
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return ChangeStatusPredicate.canonicalize(c.getStatus());
}
};
@@ -93,7 +103,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getProject().get();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getProject().get();
}
};
@@ -104,7 +118,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getProject().get();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getProject().get();
}
};
@@ -115,7 +133,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getDest().get();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getDest().get();
}
};
@@ -126,7 +148,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return MoreObjects.firstNonNull(input.change().getTopic(), "");
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return firstNonNull(c.getTopic(), "");
}
};
@@ -137,7 +163,11 @@
@Override
public Timestamp get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getLastUpdatedOn();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getLastUpdatedOn();
}
};
@@ -149,14 +179,19 @@
@Override
public Iterable<String> get(ChangeData input, FillArgs args)
throws OrmException {
- return input.currentFilePaths();
+ return firstNonNull(input.currentFilePaths(),
+ ImmutableList.<String> of());
}
};
public static Set<String> getFileParts(ChangeData cd) throws OrmException {
+ List<String> paths = cd.currentFilePaths();
+ if (paths == null) {
+ return ImmutableSet.of();
+ }
Splitter s = Splitter.on('/').omitEmptyStrings();
Set<String> r = Sets.newHashSet();
- for (String path : cd.currentFilePaths()) {
+ for (String path : paths) {
for (String part : s.split(path)) {
r.add(part);
}
@@ -201,7 +236,11 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- return input.change().getOwner().get();
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return c.getOwner().get();
}
};
@@ -212,9 +251,12 @@
@Override
public Iterable<Integer> get(ChangeData input, FillArgs args)
throws OrmException {
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
Set<Integer> r = Sets.newHashSet();
- if (!args.allowsDrafts &&
- input.change().getStatus() == Change.Status.DRAFT) {
+ if (!args.allowsDrafts && c.getStatus() == Change.Status.DRAFT) {
return r;
}
for (PatchSetApproval a : input.approvals().values()) {
@@ -249,9 +291,13 @@
public Iterable<String> get(ChangeData input, FillArgs args)
throws OrmException {
try {
- return Sets.newHashSet(args.trackingFooters.extract(
- input.commitFooters()).values());
- } catch (NoSuchChangeException | IOException e) {
+ List<FooterLine> footers = input.commitFooters();
+ if (footers == null) {
+ return null;
+ }
+ return Sets.newHashSet(
+ args.trackingFooters.extract(footers).values());
+ } catch (IOException e) {
throw new OrmException(e);
}
}
@@ -305,7 +351,11 @@
@Override
public byte[] get(ChangeData input, FieldDef.FillArgs args)
throws OrmException {
- return CODEC.encodeToByteArray(input.change());
+ Change c = input.change();
+ if (c == null) {
+ return null;
+ }
+ return CODEC.encodeToByteArray(c);
}
}
@@ -352,7 +402,7 @@
public String get(ChangeData input, FillArgs args) throws OrmException {
try {
return input.commitMessage();
- } catch (NoSuchChangeException | IOException e) {
+ } catch (IOException e) {
throw new OrmException(e);
}
}
@@ -384,7 +434,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.isMergeable() ? "1" : null;
+ Boolean m = input.isMergeable();
+ if (m == null) {
+ return null;
+ }
+ return m ? "1" : null;
}
};
@@ -395,7 +449,11 @@
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
- return input.isMergeable() ? "1" : "0";
+ Boolean m = input.isMergeable();
+ if (m == null) {
+ return null;
+ }
+ return m ? "1" : "0";
}
};
@@ -406,7 +464,8 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- return input.changedLines().insertions;
+ ChangedLines lines = input.changedLines();
+ return lines != null ? lines.insertions : null;
}
};
@@ -417,7 +476,8 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- return input.changedLines().deletions;
+ ChangedLines lines = input.changedLines();
+ return lines != null ? lines.deletions : null;
}
};
@@ -428,8 +488,11 @@
@Override
public Integer get(ChangeData input, FillArgs args)
throws OrmException {
- ChangedLines changedLines = input.changedLines();
- return changedLines.insertions + changedLines.deletions;
+ ChangedLines lines = input.changedLines();
+ if (lines == null) {
+ return null;
+ }
+ return lines.insertions + lines.deletions;
}
};
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index 0f1a7bb..dcb4924 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -473,29 +473,31 @@
currentApprovals = approvals;
}
- public String commitMessage() throws NoSuchChangeException, IOException,
- OrmException {
+ public String commitMessage() throws IOException, OrmException {
if (commitMessage == null) {
- loadCommitData();
+ if (!loadCommitData()) {
+ return null;
+ }
}
return commitMessage;
}
- public List<FooterLine> commitFooters() throws NoSuchChangeException,
- IOException, OrmException {
+ public List<FooterLine> commitFooters() throws IOException, OrmException {
if (commitFooters == null) {
- loadCommitData();
+ if (!loadCommitData()) {
+ return null;
+ }
}
return commitFooters;
}
- private void loadCommitData() throws NoSuchChangeException, OrmException,
+ private boolean loadCommitData() throws OrmException,
RepositoryNotFoundException, IOException, MissingObjectException,
IncorrectObjectTypeException {
PatchSet.Id psId = change().currentPatchSetId();
PatchSet ps = db.patchSets().get(psId);
if (ps == null) {
- throw new NoSuchChangeException(legacyId);
+ return false;
}
String sha1 = ps.getRevision().get();
Repository repo = repoManager.openRepository(change().getProject());
@@ -511,6 +513,7 @@
} finally {
repo.close();
}
+ return true;
}
/**
@@ -587,15 +590,18 @@
this.mergeable = mergeable;
}
- public boolean isMergeable() throws OrmException {
+ public Boolean isMergeable() throws OrmException {
if (mergeable == null) {
Change c = change();
+ if (c == null) {
+ return null;
+ }
if (c.getStatus() == Change.Status.MERGED) {
mergeable = true;
} else {
PatchSet ps = currentPatchSet();
if (ps == null) {
- throw new OrmException("Missing patch set for mergeability check");
+ return null;
}
Repository repo = null;
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
index 8f33c01..52846ed 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/OutputStreamQuery.java
@@ -24,7 +24,6 @@
import com.google.gerrit.server.data.QueryStatsAttribute;
import com.google.gerrit.server.events.EventFactory;
import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.QueryParseException;
import com.google.gson.Gson;
@@ -266,11 +265,6 @@
ErrorMessage m = new ErrorMessage();
m.message = e.getMessage();
show(m);
- } catch (NoSuchChangeException e) {
- log.error("Missing change: " + e.getMessage(), e);
- ErrorMessage m = new ErrorMessage();
- m.message = "missing change " + e.getMessage();
- show(m);
}
} finally {
try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
index bdf32fa..4f2a8d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/TrackingIdPredicate.java
@@ -18,13 +18,14 @@
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.IndexPredicate;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gwtorm.server.OrmException;
+import org.eclipse.jgit.revwalk.FooterLine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.List;
class TrackingIdPredicate extends IndexPredicate<ChangeData> {
private static final Logger log = LoggerFactory.getLogger(TrackingIdPredicate.class);
@@ -41,9 +42,10 @@
Change c = object.change();
if (c != null) {
try {
- return trackingFooters.extract(object.commitFooters())
- .values().contains(getValue());
- } catch (NoSuchChangeException | IOException e) {
+ List<FooterLine> footers = object.commitFooters();
+ return footers != null && trackingFooters.extract(
+ object.commitFooters()).values().contains(getValue());
+ } catch (IOException e) {
log.warn("Cannot extract footers from " + c.getChangeId(), e);
}
}