Merge "Reimplement is:watched to inline filter query trees"
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 513669c..b89cb7e 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
@@ -45,7 +45,7 @@
*/
public class ChangeField {
/** Increment whenever making schema changes. */
- public static final int SCHEMA_VERSION = 9;
+ public static final int SCHEMA_VERSION = 10;
/** Legacy change ID. */
public static final FieldDef<ChangeData, Integer> LEGACY_ID =
@@ -203,6 +203,27 @@
}
};
+ /** List of labels on the current patch set. */
+ public static final FieldDef<ChangeData, Iterable<String>> LABEL =
+ new FieldDef.Repeatable<ChangeData, String>(
+ ChangeQueryBuilder.FIELD_LABEL, FieldType.EXACT, false) {
+ @Override
+ public Iterable<String> get(ChangeData input, FillArgs args)
+ throws OrmException {
+ Set<String> distinctApprovals = Sets.newHashSet();
+ for (PatchSetApproval a : input.currentApprovals(args.db)) {
+ if (a.getValue() != 0) {
+ distinctApprovals.add(formatLabel(a.getLabel(), a.getValue()));
+ }
+ }
+ return distinctApprovals;
+ }
+ };
+
+ public static String formatLabel(String label, int value) {
+ return label.toLowerCase() + (value >= 0 ? "+" : "") + value;
+ }
+
public static final ImmutableMap<String, FieldDef<ChangeData, ?>> ALL;
static {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
new file mode 100644
index 0000000..b838ca5
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -0,0 +1,139 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.query.change;
+
+import com.google.gerrit.common.data.LabelType;
+import com.google.gerrit.common.data.LabelTypes;
+import com.google.gerrit.common.data.Permission;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.index.ChangeField;
+import com.google.gerrit.server.index.IndexPredicate;
+import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.gerrit.server.project.ProjectState;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Provider;
+
+class EqualsLabelPredicate extends IndexPredicate<ChangeData> {
+ private final ProjectCache projectCache;
+ private final ChangeControl.GenericFactory ccFactory;
+ private final IdentifiedUser.GenericFactory userFactory;
+ private final Provider<ReviewDb> dbProvider;
+ private final String label;
+ private final int expVal;
+
+ EqualsLabelPredicate(ProjectCache projectCache,
+ ChangeControl.GenericFactory ccFactory,
+ IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
+ String label, int expVal) {
+ super(ChangeField.LABEL, ChangeField.formatLabel(label, expVal));
+ this.ccFactory = ccFactory;
+ this.projectCache = projectCache;
+ this.userFactory = userFactory;
+ this.dbProvider = dbProvider;
+ this.label = label;
+ this.expVal = expVal;
+ }
+
+ @Override
+ public boolean match(ChangeData object) throws OrmException {
+ Change c = object.change(dbProvider);
+ if (c == null) {
+ // The change has disappeared.
+ //
+ return false;
+ }
+ ProjectState project = projectCache.get(c.getDest().getParentKey());
+ if (project == null) {
+ // The project has disappeared.
+ //
+ return false;
+ }
+ LabelType labelType = type(project.getLabelTypes(), label);
+ boolean hasVote = false;
+ for (PatchSetApproval p : object.currentApprovals(dbProvider)) {
+ if (labelType.matches(p)) {
+ hasVote = true;
+ if (match(c, p.getValue(), p.getAccountId(), labelType)) {
+ return true;
+ }
+ }
+ }
+
+ if (!hasVote && expVal == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static LabelType type(LabelTypes types, String toFind) {
+ if (types.byLabel(toFind) != null) {
+ return types.byLabel(toFind);
+ }
+
+ for (LabelType lt : types.getLabelTypes()) {
+ if (toFind.equalsIgnoreCase(lt.getName())) {
+ return lt;
+ }
+ }
+
+ for (LabelType lt : types.getLabelTypes()) {
+ if (toFind.equalsIgnoreCase(lt.getAbbreviation())) {
+ return lt;
+ }
+ }
+
+ return LabelType.withDefaultValues(toFind);
+ }
+
+ private boolean match(Change change, int value, Account.Id approver,
+ LabelType type) throws OrmException {
+ int psVal = value;
+ if (psVal == expVal) {
+ // Double check the value is still permitted for the user.
+ //
+ try {
+ ChangeControl cc = ccFactory.controlFor(change, //
+ userFactory.create(dbProvider, approver));
+ if (!cc.isVisible(dbProvider.get())) {
+ // The user can't see the change anymore.
+ //
+ return false;
+ }
+ psVal = cc.getRange(Permission.forLabel(type.getName())).squash(psVal);
+ } catch (NoSuchChangeException e) {
+ // The project has disappeared.
+ //
+ return false;
+ }
+
+ if (psVal == expVal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getCost() {
+ return 1;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
index b0d02f8..a3c972a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -14,84 +14,106 @@
package com.google.gerrit.server.query.change;
-import com.google.gerrit.common.data.LabelType;
-import com.google.gerrit.common.data.LabelTypes;
-import com.google.gerrit.common.data.Permission;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSetApproval;
+import com.google.common.collect.Lists;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.ProjectCache;
-import com.google.gerrit.server.project.ProjectState;
-import com.google.gerrit.server.query.OperatorPredicate;
-import com.google.gwtorm.server.OrmException;
+import com.google.gerrit.server.query.OrPredicate;
+import com.google.gerrit.server.query.Predicate;
import com.google.inject.Provider;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-class LabelPredicate extends OperatorPredicate<ChangeData> {
+public class LabelPredicate extends OrPredicate<ChangeData> {
+ private static final int MAX_LABEL_VALUE = 4;
+
private static enum Test {
- EQ {
- @Override
- public boolean match(int psValue, int expValue) {
- return psValue == expValue;
- }
- },
- GT_EQ {
- @Override
- public boolean match(int psValue, int expValue) {
- return psValue >= expValue;
- }
- },
- LT_EQ {
- @Override
- public boolean match(int psValue, int expValue) {
- return psValue <= expValue;
- }
- };
+ EQ, GT_EQ, LT_EQ;
- abstract boolean match(int psValue, int expValue);
+ boolean isEq() {
+ return EQ.equals(this);
+ }
+
+ boolean isGtEq() {
+ return GT_EQ.equals(this);
+ }
+
+ static Test op(String op) {
+ if ("=".equals(op)) {
+ return EQ;
+
+ } else if (">=".equals(op)) {
+ return GT_EQ;
+
+ } else if ("<=".equals(op)) {
+ return LT_EQ;
+
+ } else {
+ throw new IllegalArgumentException("Unsupported operation " + op);
+ }
+ }
}
- private static LabelType type(LabelTypes types, String toFind) {
- if (types.byLabel(toFind) != null) {
- return types.byLabel(toFind);
- }
+ private final String value;
- for (LabelType lt : types.getLabelTypes()) {
- if (toFind.equalsIgnoreCase(lt.getName())) {
- return lt;
- }
- }
-
- for (LabelType lt : types.getLabelTypes()) {
- if (toFind.equalsIgnoreCase(lt.getAbbreviation())) {
- return lt;
- }
- }
-
- return LabelType.withDefaultValues(toFind);
+ LabelPredicate(ProjectCache projectCache,
+ ChangeControl.GenericFactory ccFactory,
+ IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
+ String value) {
+ super(predicates(projectCache, ccFactory, userFactory,
+ dbProvider, value));
+ this.value = value;
}
- private static Test op(String op) {
- if ("=".equals(op)) {
- return Test.EQ;
+ private static List<Predicate<ChangeData>> predicates(
+ ProjectCache projectCache, ChangeControl.GenericFactory ccFactory,
+ IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
+ String value) {
+ String label;
+ Test test;
+ int expVal;
+ Matcher m1 = Pattern.compile("(=|>=|<=)([+-]?\\d+)$").matcher(value);
+ Matcher m2 = Pattern.compile("([+-]\\d+)$").matcher(value);
+ if (m1.find()) {
+ label = value.substring(0, m1.start());
+ test = Test.op(m1.group(1));
+ expVal = value(m1.group(2));
- } else if (">=".equals(op)) {
- return Test.GT_EQ;
-
- } else if ("<=".equals(op)) {
- return Test.LT_EQ;
+ } else if (m2.find()) {
+ label = value.substring(0, m2.start());
+ test = Test.EQ;
+ expVal = value(m2.group(1));
} else {
- throw new IllegalArgumentException("Unsupported operation " + op);
+ label = value;
+ test = Test.EQ;
+ expVal = 1;
}
+
+ List<Predicate<ChangeData>> r = Lists.newArrayListWithCapacity(2 * MAX_LABEL_VALUE);
+ if (test.isEq()) {
+ if (expVal != 0) {
+ r.add(equalsLabelPredicate(projectCache, ccFactory, userFactory,
+ dbProvider, label, expVal));
+ } else {
+ r.add(noLabelQuery(projectCache, ccFactory, userFactory,
+ dbProvider, label));
+ }
+ } else {
+ for (int i = test.isGtEq() ? expVal : neg(expVal); i <= MAX_LABEL_VALUE; i++) {
+ if (i != 0) {
+ r.add(equalsLabelPredicate(projectCache, ccFactory, userFactory,
+ dbProvider, label, test.isGtEq() ? i : neg(i)));
+ } else {
+ r.add(noLabelQuery(projectCache, ccFactory, userFactory,
+ dbProvider, label));
+ }
+ }
+ }
+ return r;
}
private static int value(String value) {
@@ -101,113 +123,31 @@
return Integer.parseInt(value);
}
- private final ProjectCache projectCache;
- private final ChangeControl.GenericFactory ccFactory;
- private final IdentifiedUser.GenericFactory userFactory;
- private final Provider<ReviewDb> dbProvider;
- private final Test test;
- private final String type;
- private final int expVal;
+ private static int neg(int value) {
+ return -1 * value;
+ }
- LabelPredicate(ProjectCache projectCache,
- ChangeControl.GenericFactory ccFactory,
- IdentifiedUser.GenericFactory userFactory,
- Provider<ReviewDb> dbProvider,
- String value) {
- super(ChangeQueryBuilder.FIELD_LABEL, value);
- this.ccFactory = ccFactory;
- this.projectCache = projectCache;
- this.userFactory = userFactory;
- this.dbProvider = dbProvider;
-
- Matcher m1 = Pattern.compile("(=|>=|<=)([+-]?\\d+)$").matcher(value);
- Matcher m2 = Pattern.compile("([+-]\\d+)$").matcher(value);
- if (m1.find()) {
- type = value.substring(0, m1.start());
- test = op(m1.group(1));
- expVal = value(m1.group(2));
-
- } else if (m2.find()) {
- type = value.substring(0, m2.start());
- test = Test.EQ;
- expVal = value(m2.group(1));
-
- } else {
- type = value;
- test = Test.EQ;
- expVal = 1;
+ private static Predicate<ChangeData> noLabelQuery(ProjectCache projectCache, ChangeControl.GenericFactory ccFactory,
+ IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider, String label) {
+ List<Predicate<ChangeData>> r =
+ Lists.newArrayListWithCapacity(2 * MAX_LABEL_VALUE);
+ for (int i = 1; i <= MAX_LABEL_VALUE; i++) {
+ r.add(not(equalsLabelPredicate(projectCache, ccFactory, userFactory,
+ dbProvider, label, i)));
+ r.add(not(equalsLabelPredicate(projectCache, ccFactory, userFactory,
+ dbProvider, label, neg(i))));
}
+ return and(r);
+ }
+
+ private static Predicate<ChangeData> equalsLabelPredicate(ProjectCache projectCache, ChangeControl.GenericFactory ccFactory,
+ IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider, String label, int expVal) {
+ return new EqualsLabelPredicate(projectCache, ccFactory, userFactory,
+ dbProvider, label, expVal);
}
@Override
- public boolean match(final ChangeData object) throws OrmException {
- final Change c = object.change(dbProvider);
- if (c == null) {
- // The change has disappeared.
- //
- return false;
- }
- final ProjectState project = projectCache.get(c.getDest().getParentKey());
- if (project == null) {
- // The project has disappeared.
- //
- return false;
- }
- final LabelType labelType = type(project.getLabelTypes(), type);
- final Set<Account.Id> allApprovers = new HashSet<Account.Id>();
- final Set<Account.Id> approversThatVotedInCategory = new HashSet<Account.Id>();
- for (PatchSetApproval p : object.currentApprovals(dbProvider)) {
- allApprovers.add(p.getAccountId());
- if (labelType.matches(p)) {
- approversThatVotedInCategory.add(p.getAccountId());
- if (match(c, p.getValue(), p.getAccountId(), labelType)) {
- return true;
- }
- }
- }
-
- final Set<Account.Id> approversThatDidNotVoteInCategory = new HashSet<Account.Id>(allApprovers);
- approversThatDidNotVoteInCategory.removeAll(approversThatVotedInCategory);
- for (Account.Id a : approversThatDidNotVoteInCategory) {
- if (match(c, 0, a, labelType)) {
- return true;
- }
- }
-
- return false;
- }
-
- private boolean match(final Change change, final int value,
- final Account.Id approver, final LabelType type)
- throws OrmException {
- int psVal = value;
- if (test.match(psVal, expVal)) {
- // Double check the value is still permitted for the user.
- //
- try {
- ChangeControl cc = ccFactory.controlFor(change, //
- userFactory.create(dbProvider, approver));
- if (!cc.isVisible(dbProvider.get())) {
- // The user can't see the change anymore.
- //
- return false;
- }
- psVal = cc.getRange(Permission.forLabel(type.getName())).squash(psVal);
- } catch (NoSuchChangeException e) {
- // The project has disappeared.
- //
- return false;
- }
-
- if (test.match(psVal, expVal)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public int getCost() {
- return 2;
+ public String toString() {
+ return ChangeQueryBuilder.FIELD_LABEL + ":" + value;
}
}