Don't run SQL rewrites when secondary index is enabled
If the secondary index is enabled on a server the index handles all queries. There is no reason to run the SQL rewrites or to leave predicates in the predicate tree without being wrapped by the IndexedChangeQuery predicate. Change-Id: I18defed2a5a6003b9ae6ba227e29864b5c912e7b
This commit is contained in:
@@ -16,8 +16,8 @@ package com.google.gerrit.lucene;
|
||||
|
||||
import static com.google.gerrit.lucene.IndexVersionCheck.SCHEMA_VERSIONS;
|
||||
import static com.google.gerrit.lucene.IndexVersionCheck.gerritIndexConfig;
|
||||
import static com.google.gerrit.server.query.change.IndexRewriteImpl.CLOSED_STATUSES;
|
||||
import static com.google.gerrit.server.query.change.IndexRewriteImpl.OPEN_STATUSES;
|
||||
import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES;
|
||||
import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -34,11 +34,11 @@ import com.google.gerrit.server.index.ChangeIndex;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.FieldDef.FillArgs;
|
||||
import com.google.gerrit.server.index.FieldType;
|
||||
import com.google.gerrit.server.index.IndexRewriteImpl;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeDataSource;
|
||||
import com.google.gerrit.server.query.change.IndexRewriteImpl;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
|
||||
@@ -258,6 +258,11 @@ public class LuceneChangeIndex implements ChangeIndex, LifecycleListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return query.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
IndexSearcher[] searchers = new IndexSearcher[indexes.size()];
|
||||
|
||||
@@ -121,8 +121,8 @@ public class QueryBuilder {
|
||||
private static Query sortKeyQuery(SortKeyPredicate p) {
|
||||
return NumericRangeQuery.newLongRange(
|
||||
p.getField().getName(),
|
||||
p.getMinValue(),
|
||||
p.getMaxValue(),
|
||||
p.getMinValue() != Long.MIN_VALUE ? p.getMinValue() : null,
|
||||
p.getMaxValue() != Long.MAX_VALUE ? p.getMaxValue() : null,
|
||||
true, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@ import com.google.gerrit.server.project.ProjectNode;
|
||||
import com.google.gerrit.server.project.ProjectState;
|
||||
import com.google.gerrit.server.project.SectionSortCache;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||
import com.google.gerrit.server.ssh.SshAddressesModule;
|
||||
import com.google.gerrit.server.tools.ToolsCatalog;
|
||||
import com.google.gerrit.server.util.IdGenerator;
|
||||
@@ -172,7 +171,6 @@ public class GerritGlobalModule extends FactoryModule {
|
||||
install(ThreadLocalRequestContext.module());
|
||||
|
||||
bind(AccountResolver.class);
|
||||
bind(ChangeQueryRewriter.class);
|
||||
|
||||
factory(AccountInfoCacheFactory.Factory.class);
|
||||
factory(AddReviewerSender.Factory.class);
|
||||
|
||||
@@ -19,8 +19,7 @@ import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.WorkQueue;
|
||||
import com.google.gerrit.server.git.WorkQueue.Executor;
|
||||
import com.google.gerrit.server.query.change.IndexRewrite;
|
||||
import com.google.gerrit.server.query.change.IndexRewriteImpl;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
@@ -56,7 +55,8 @@ public class IndexModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ChangeIndexer.class).to(ChangeIndexerImpl.class);
|
||||
bind(IndexRewrite.class).to(IndexRewriteImpl.class);
|
||||
bind(ChangeQueryRewriter.class).to(IndexRewriteImpl.class);
|
||||
bind(IndexRewriteImpl.BasicRewritesImpl.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -37,12 +37,4 @@ public abstract class IndexPredicate<I> extends OperatorPredicate<I> {
|
||||
public FieldType<?> getType() {
|
||||
return def.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this predicate can only be satisfied by looking at the
|
||||
* secondary index, i.e. it cannot be expressed as a query over the DB.
|
||||
*/
|
||||
public boolean isIndexOnly() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
// 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.index;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.client.Change.Status;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.query.AndPredicate;
|
||||
import com.google.gerrit.server.query.NotPredicate;
|
||||
import com.google.gerrit.server.query.OrPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gerrit.server.query.QueryRewriter;
|
||||
import com.google.gerrit.server.query.change.AndSource;
|
||||
import com.google.gerrit.server.query.change.BasicChangeRewrites;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||
import com.google.gerrit.server.query.change.ChangeStatusPredicate;
|
||||
import com.google.gerrit.server.query.change.OrSource;
|
||||
import com.google.gerrit.server.query.change.SqlRewriterImpl;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** Rewriter that pushes boolean logic into the secondary index. */
|
||||
public class IndexRewriteImpl implements ChangeQueryRewriter {
|
||||
/** Set of all open change statuses. */
|
||||
public static final Set<Change.Status> OPEN_STATUSES;
|
||||
|
||||
/** Set of all closed change statuses. */
|
||||
public static final Set<Change.Status> CLOSED_STATUSES;
|
||||
|
||||
static {
|
||||
EnumSet<Change.Status> open = EnumSet.noneOf(Change.Status.class);
|
||||
EnumSet<Change.Status> closed = EnumSet.noneOf(Change.Status.class);
|
||||
for (Change.Status s : Change.Status.values()) {
|
||||
if (s.isOpen()) {
|
||||
open.add(s);
|
||||
} else {
|
||||
closed.add(s);
|
||||
}
|
||||
}
|
||||
OPEN_STATUSES = Sets.immutableEnumSet(open);
|
||||
CLOSED_STATUSES = Sets.immutableEnumSet(closed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of statuses that changes matching the given predicate may have.
|
||||
*
|
||||
* @param in predicate
|
||||
* @return the maximal set of statuses that any changes matching the input
|
||||
* predicates may have, based on examining boolean and
|
||||
* {@link ChangeStatusPredicate}s.
|
||||
*/
|
||||
public static EnumSet<Change.Status> getPossibleStatus(Predicate<ChangeData> in) {
|
||||
EnumSet<Change.Status> s = extractStatus(in);
|
||||
return s != null ? s : EnumSet.allOf(Change.Status.class);
|
||||
}
|
||||
|
||||
private static EnumSet<Change.Status> extractStatus(Predicate<ChangeData> in) {
|
||||
if (in instanceof ChangeStatusPredicate) {
|
||||
return EnumSet.of(((ChangeStatusPredicate) in).getStatus());
|
||||
} else if (in instanceof NotPredicate) {
|
||||
EnumSet<Status> s = extractStatus(in.getChild(0));
|
||||
return s != null ? EnumSet.complementOf(s) : null;
|
||||
} else if (in instanceof OrPredicate) {
|
||||
EnumSet<Change.Status> r = null;
|
||||
int childrenWithStatus = 0;
|
||||
for (int i = 0; i < in.getChildCount(); i++) {
|
||||
EnumSet<Status> c = extractStatus(in.getChild(i));
|
||||
if (c != null) {
|
||||
if (r == null) {
|
||||
r = EnumSet.noneOf(Change.Status.class);
|
||||
}
|
||||
r.addAll(c);
|
||||
childrenWithStatus++;
|
||||
}
|
||||
}
|
||||
if (r != null && childrenWithStatus < in.getChildCount()) {
|
||||
// At least one child supplied a status but another did not.
|
||||
// Assume all statuses for the children that did not feed a
|
||||
// status at this part of the tree. This matches behavior if
|
||||
// the child was used at the root of a query.
|
||||
return EnumSet.allOf(Change.Status.class);
|
||||
}
|
||||
return r;
|
||||
} else if (in instanceof AndPredicate) {
|
||||
EnumSet<Change.Status> r = null;
|
||||
for (int i = 0; i < in.getChildCount(); i++) {
|
||||
EnumSet<Change.Status> c = extractStatus(in.getChild(i));
|
||||
if (c != null) {
|
||||
if (r == null) {
|
||||
r = EnumSet.allOf(Change.Status.class);
|
||||
}
|
||||
r.retainAll(c);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final ChangeIndex index;
|
||||
private final Provider<ReviewDb> db;
|
||||
private final BasicRewritesImpl basicRewrites;
|
||||
|
||||
@Inject
|
||||
IndexRewriteImpl(ChangeIndex index,
|
||||
Provider<ReviewDb> db,
|
||||
BasicRewritesImpl basicRewrites) {
|
||||
this.index = index;
|
||||
this.db = db;
|
||||
this.basicRewrites = basicRewrites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in) {
|
||||
in = basicRewrites.rewrite(in);
|
||||
|
||||
Predicate<ChangeData> out = rewriteImpl(in);
|
||||
if (in == out || out instanceof IndexPredicate) {
|
||||
return query(out);
|
||||
} else if (out == null /* cannot rewrite */) {
|
||||
return in;
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite a single predicate subtree.
|
||||
*
|
||||
* @param in predicate to rewrite.
|
||||
* @return {@code null} if no part of this subtree can be queried in the
|
||||
* index directly. {@code in} if this subtree and all its children can be
|
||||
* queried directly in the index. Otherwise, a predicate that is
|
||||
* semantically equivalent, with some of its subtrees wrapped to query the
|
||||
* index directly.
|
||||
*/
|
||||
private Predicate<ChangeData> rewriteImpl(Predicate<ChangeData> in) {
|
||||
if (in instanceof IndexPredicate) {
|
||||
return in;
|
||||
} else if (!isRewritePossible(in)) {
|
||||
return null; // magic to indicate "in" cannot be rewritten
|
||||
}
|
||||
|
||||
int n = in.getChildCount();
|
||||
BitSet isIndexed = new BitSet(n);
|
||||
BitSet notIndexed = new BitSet(n);
|
||||
BitSet rewritten = new BitSet(n);
|
||||
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Predicate<ChangeData> c = in.getChild(i);
|
||||
Predicate<ChangeData> nc = rewriteImpl(c);
|
||||
if (nc == c) {
|
||||
isIndexed.set(i);
|
||||
newChildren.add(c);
|
||||
} else if (nc == null /* cannot rewrite c */) {
|
||||
notIndexed.set(i);
|
||||
newChildren.add(c);
|
||||
} else {
|
||||
rewritten.set(i);
|
||||
newChildren.add(nc);
|
||||
}
|
||||
}
|
||||
|
||||
if (isIndexed.cardinality() == n) {
|
||||
return in; // All children are indexed, leave as-is for parent.
|
||||
} else if (notIndexed.cardinality() == n) {
|
||||
return null; // Can't rewrite any children, so cannot rewrite in.
|
||||
} else if (rewritten.cardinality() == n) {
|
||||
return in.copy(newChildren); // All children were rewritten.
|
||||
}
|
||||
return partitionChildren(in, newChildren, isIndexed);
|
||||
}
|
||||
|
||||
private Predicate<ChangeData> partitionChildren(
|
||||
Predicate<ChangeData> in,
|
||||
List<Predicate<ChangeData>> newChildren,
|
||||
BitSet isIndexed) {
|
||||
if (isIndexed.cardinality() == 1) {
|
||||
int i = isIndexed.nextSetBit(0);
|
||||
newChildren.add(0, query(newChildren.remove(i)));
|
||||
return copy(in, newChildren);
|
||||
}
|
||||
|
||||
// Group all indexed predicates into a wrapped subtree.
|
||||
List<Predicate<ChangeData>> indexed =
|
||||
Lists.newArrayListWithCapacity(isIndexed.cardinality());
|
||||
|
||||
List<Predicate<ChangeData>> all =
|
||||
Lists.newArrayListWithCapacity(
|
||||
newChildren.size() - isIndexed.cardinality() + 1);
|
||||
|
||||
for (int i = 0; i < newChildren.size(); i++) {
|
||||
Predicate<ChangeData> c = newChildren.get(i);
|
||||
if (isIndexed.get(i)) {
|
||||
indexed.add(c);
|
||||
} else {
|
||||
all.add(c);
|
||||
}
|
||||
}
|
||||
all.add(0, query(in.copy(indexed)));
|
||||
return copy(in, all);
|
||||
}
|
||||
|
||||
private Predicate<ChangeData> copy(
|
||||
Predicate<ChangeData> in,
|
||||
List<Predicate<ChangeData>> all) {
|
||||
if (in instanceof AndPredicate) {
|
||||
return new AndSource(db, all);
|
||||
} else if (in instanceof OrPredicate) {
|
||||
return new OrSource(all);
|
||||
}
|
||||
return in.copy(all);
|
||||
}
|
||||
|
||||
private IndexedChangeQuery query(Predicate<ChangeData> p) {
|
||||
try {
|
||||
return new IndexedChangeQuery(index, p);
|
||||
} catch (QueryParseException e) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to convert " + p + " to index predicate", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isRewritePossible(Predicate<ChangeData> p) {
|
||||
return p.getChildCount() > 0 && (
|
||||
p instanceof AndPredicate
|
||||
|| p instanceof OrPredicate
|
||||
|| p instanceof NotPredicate);
|
||||
}
|
||||
|
||||
static class BasicRewritesImpl extends BasicChangeRewrites {
|
||||
private static final QueryRewriter.Definition<ChangeData, BasicRewritesImpl> mydef =
|
||||
new QueryRewriter.Definition<ChangeData, BasicRewritesImpl>(
|
||||
BasicRewritesImpl.class, SqlRewriterImpl.BUILDER);
|
||||
@Inject
|
||||
BasicRewritesImpl(Provider<ReviewDb> db) {
|
||||
super(mydef, db);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
@@ -35,17 +36,35 @@ import java.util.List;
|
||||
* the secondary index; such predicates must also implement
|
||||
* {@link ChangeDataSource} to be chosen by the query processor.
|
||||
*/
|
||||
public class PredicateWrapper extends Predicate<ChangeData> implements
|
||||
ChangeDataSource {
|
||||
public class IndexedChangeQuery extends Predicate<ChangeData>
|
||||
implements ChangeDataSource {
|
||||
private final Predicate<ChangeData> pred;
|
||||
private final ChangeDataSource source;
|
||||
|
||||
public PredicateWrapper(ChangeIndex index, Predicate<ChangeData> pred)
|
||||
public IndexedChangeQuery(ChangeIndex index, Predicate<ChangeData> pred)
|
||||
throws QueryParseException {
|
||||
this.pred = pred;
|
||||
this.source = index.getSource(pred);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> getChild(int i) {
|
||||
if (i == 0) {
|
||||
return pred;
|
||||
}
|
||||
throw new ArrayIndexOutOfBoundsException(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Predicate<ChangeData>> getChildren() {
|
||||
return ImmutableList.of(pred);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return source.getCardinality();
|
||||
@@ -115,11 +134,11 @@ public class PredicateWrapper extends Predicate<ChangeData> implements
|
||||
public boolean equals(Object other) {
|
||||
return other != null
|
||||
&& getClass() == other.getClass()
|
||||
&& pred.equals(((PredicateWrapper) other).pred);
|
||||
&& pred.equals(((IndexedChangeQuery) other).pred);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "index(" + pred + ")";
|
||||
return "index(" + source + ")";
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import com.google.gerrit.server.query.change.IndexRewrite;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryRewriter;
|
||||
import com.google.gerrit.server.query.change.SqlRewriterImpl;
|
||||
import com.google.inject.AbstractModule;
|
||||
|
||||
public class NoIndexModule extends AbstractModule {
|
||||
@@ -26,6 +27,6 @@ public class NoIndexModule extends AbstractModule {
|
||||
protected void configure() {
|
||||
bind(ChangeIndex.class).toInstance(ChangeIndex.DISABLED);
|
||||
bind(ChangeIndexer.class).toInstance(ChangeIndexer.DISABLED);
|
||||
bind(IndexRewrite.class).toInstance(IndexRewrite.DISABLED);
|
||||
bind(ChangeQueryRewriter.class).to(SqlRewriterImpl.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server.query;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -27,7 +28,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -65,19 +66,13 @@ public abstract class QueryRewriter<T> {
|
||||
private final List<RewriteRule<T>> rewriteRules;
|
||||
|
||||
public Definition(Class<R> clazz, QueryBuilder<T> qb) {
|
||||
rewriteRules = new ArrayList<RewriteRule<T>>();
|
||||
rewriteRules = Lists.newArrayList();
|
||||
|
||||
Class<?> c = clazz;
|
||||
while (c != QueryRewriter.class) {
|
||||
final Method[] declared = c.getDeclaredMethods();
|
||||
Arrays.sort(declared, new Comparator<Method>() {
|
||||
@Override
|
||||
public int compare(Method o1, Method o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
Method[] declared = c.getDeclaredMethods();
|
||||
for (Method m : declared) {
|
||||
final Rewrite rp = m.getAnnotation(Rewrite.class);
|
||||
Rewrite rp = m.getAnnotation(Rewrite.class);
|
||||
if ((m.getModifiers() & Modifier.ABSTRACT) != Modifier.ABSTRACT
|
||||
&& (m.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC
|
||||
&& rp != null) {
|
||||
@@ -86,6 +81,7 @@ public abstract class QueryRewriter<T> {
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
Collections.sort(rewriteRules);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,7 +336,7 @@ public abstract class QueryRewriter<T> {
|
||||
}
|
||||
|
||||
/** Applies a rewrite rule to a Predicate. */
|
||||
protected interface RewriteRule<T> {
|
||||
protected interface RewriteRule<T> extends Comparable<RewriteRule<T>> {
|
||||
/**
|
||||
* Apply a rewrite rule to the Predicate.
|
||||
*
|
||||
@@ -463,6 +459,15 @@ public abstract class QueryRewriter<T> {
|
||||
final String msg = "Cannot apply " + method.getName();
|
||||
return new IllegalArgumentException(msg, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RewriteRule<T> in) {
|
||||
if (in instanceof MethodRewrite) {
|
||||
return method.getName().compareTo(
|
||||
((MethodRewrite<T>) in).method.getName());
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> Predicate<T> removeDuplicates(Predicate<T> in) {
|
||||
|
||||
@@ -34,7 +34,8 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
class AndSource extends AndPredicate<ChangeData> implements ChangeDataSource {
|
||||
public class AndSource extends AndPredicate<ChangeData>
|
||||
implements ChangeDataSource {
|
||||
private static final Comparator<Predicate<ChangeData>> CMP =
|
||||
new Comparator<Predicate<ChangeData>>() {
|
||||
@Override
|
||||
@@ -75,7 +76,8 @@ class AndSource extends AndPredicate<ChangeData> implements ChangeDataSource {
|
||||
private final Provider<ReviewDb> db;
|
||||
private int cardinality = -1;
|
||||
|
||||
AndSource(Provider<ReviewDb> db, Collection<? extends Predicate<ChangeData>> that) {
|
||||
public AndSource(Provider<ReviewDb> db,
|
||||
Collection<? extends Predicate<ChangeData>> that) {
|
||||
super(sort(that));
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// 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.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.query.IntPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryRewriter;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
public abstract class BasicChangeRewrites extends QueryRewriter<ChangeData> {
|
||||
protected static final ChangeQueryBuilder BUILDER = new ChangeQueryBuilder(
|
||||
new ChangeQueryBuilder.Arguments( //
|
||||
new InvalidProvider<ReviewDb>(), //
|
||||
new InvalidProvider<ChangeQueryRewriter>(), //
|
||||
null, null, null, null, null, //
|
||||
null, null, null, null, null), null);
|
||||
|
||||
protected final Provider<ReviewDb> dbProvider;
|
||||
|
||||
protected BasicChangeRewrites(
|
||||
Definition<ChangeData, ? extends QueryRewriter<ChangeData>> def,
|
||||
Provider<ReviewDb> dbProvider) {
|
||||
super(def);
|
||||
this.dbProvider = dbProvider;
|
||||
}
|
||||
|
||||
@Rewrite("-status:open")
|
||||
@NoCostComputation
|
||||
public Predicate<ChangeData> r00_notOpen() {
|
||||
return ChangeStatusPredicate.closed(dbProvider);
|
||||
}
|
||||
|
||||
@Rewrite("-status:closed")
|
||||
@NoCostComputation
|
||||
public Predicate<ChangeData> r00_notClosed() {
|
||||
return ChangeStatusPredicate.open(dbProvider);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("-status:merged")
|
||||
public Predicate<ChangeData> r00_notMerged() {
|
||||
return or(ChangeStatusPredicate.open(dbProvider),
|
||||
new ChangeStatusPredicate(dbProvider, Change.Status.ABANDONED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("-status:abandoned")
|
||||
public Predicate<ChangeData> r00_notAbandoned() {
|
||||
return or(ChangeStatusPredicate.open(dbProvider),
|
||||
new ChangeStatusPredicate(dbProvider, Change.Status.MERGED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("sortkey_before:z A=(age:*)")
|
||||
public Predicate<ChangeData> r00_ageToSortKey(@Named("A") AgePredicate a) {
|
||||
String cut = ChangeUtil.sortKey(a.getCut(), Integer.MAX_VALUE);
|
||||
return and(new SortKeyPredicate.Before(dbProvider, cut), a);
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(limit:*) B=(limit:*)")
|
||||
public Predicate<ChangeData> r00_smallestLimit(
|
||||
@Named("A") IntPredicate<ChangeData> a,
|
||||
@Named("B") IntPredicate<ChangeData> b) {
|
||||
return a.intValue() <= b.intValue() ? a : b;
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(sortkey_before:*) B=(sortkey_before:*)")
|
||||
public Predicate<ChangeData> r00_oldestSortKey(
|
||||
@Named("A") SortKeyPredicate.Before a,
|
||||
@Named("B") SortKeyPredicate.Before b) {
|
||||
return a.getValue().compareTo(b.getValue()) <= 0 ? a : b;
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(sortkey_after:*) B=(sortkey_after:*)")
|
||||
public Predicate<ChangeData> r00_newestSortKey(
|
||||
@Named("A") SortKeyPredicate.After a, @Named("B") SortKeyPredicate.After b) {
|
||||
return a.getValue().compareTo(b.getValue()) >= 0 ? a : b;
|
||||
}
|
||||
|
||||
private static final class InvalidProvider<T> implements Provider<T> {
|
||||
@Override
|
||||
public T get() {
|
||||
throw new OutOfScopeException("Not available at init");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,7 +110,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
||||
new QueryBuilder.Definition<ChangeData, ChangeQueryBuilder>(
|
||||
ChangeQueryBuilder.class);
|
||||
|
||||
static class Arguments {
|
||||
@VisibleForTesting
|
||||
public static class Arguments {
|
||||
final Provider<ReviewDb> dbProvider;
|
||||
final Provider<ChangeQueryRewriter> rewriter;
|
||||
final IdentifiedUser.GenericFactory userFactory;
|
||||
@@ -125,7 +126,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
||||
final ChangeIndex index;
|
||||
|
||||
@Inject
|
||||
Arguments(Provider<ReviewDb> dbProvider,
|
||||
@VisibleForTesting
|
||||
public Arguments(Provider<ReviewDb> dbProvider,
|
||||
Provider<ChangeQueryRewriter> rewriter,
|
||||
IdentifiedUser.GenericFactory userFactory,
|
||||
CapabilityControl.Factory capabilityControlFactory,
|
||||
@@ -161,7 +163,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
||||
private boolean allowFileRegex;
|
||||
|
||||
@Inject
|
||||
ChangeQueryBuilder(Arguments args, @Assisted CurrentUser currentUser) {
|
||||
public ChangeQueryBuilder(Arguments args, @Assisted CurrentUser currentUser) {
|
||||
super(mydef);
|
||||
this.args = args;
|
||||
this.currentUser = currentUser;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
// 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.
|
||||
@@ -14,701 +14,8 @@
|
||||
|
||||
package com.google.gerrit.server.query.change;
|
||||
|
||||
import com.google.gerrit.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.server.ChangeAccess;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.query.IntPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryRewriter;
|
||||
import com.google.gerrit.server.query.RewritePredicate;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.OutOfScopeException;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ChangeQueryRewriter extends QueryRewriter<ChangeData> {
|
||||
private static final QueryRewriter.Definition<ChangeData, ChangeQueryRewriter> mydef =
|
||||
new QueryRewriter.Definition<ChangeData, ChangeQueryRewriter>(
|
||||
ChangeQueryRewriter.class, new ChangeQueryBuilder(
|
||||
new ChangeQueryBuilder.Arguments( //
|
||||
new InvalidProvider<ReviewDb>(), //
|
||||
new InvalidProvider<ChangeQueryRewriter>(), //
|
||||
null, null, null, null, null, //
|
||||
null, null, null, null, null), null));
|
||||
|
||||
private final Provider<ReviewDb> dbProvider;
|
||||
private final IndexRewrite indexRewrite;
|
||||
|
||||
@Inject
|
||||
ChangeQueryRewriter(Provider<ReviewDb> dbProvider,
|
||||
IndexRewrite indexRewrite) {
|
||||
super(mydef);
|
||||
this.dbProvider = dbProvider;
|
||||
this.indexRewrite = indexRewrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> and(Collection<? extends Predicate<ChangeData>> l) {
|
||||
return hasSource(l) ? new AndSource(dbProvider, l) : super.and(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> or(Collection<? extends Predicate<ChangeData>> l) {
|
||||
return hasSource(l) ? new OrSource(l) : super.or(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> preRewrite(Predicate<ChangeData> in) {
|
||||
return indexRewrite.rewrite(in);
|
||||
}
|
||||
|
||||
@Rewrite("-status:open")
|
||||
@NoCostComputation
|
||||
public Predicate<ChangeData> r00_notOpen() {
|
||||
return ChangeStatusPredicate.closed(dbProvider);
|
||||
}
|
||||
|
||||
@Rewrite("-status:closed")
|
||||
@NoCostComputation
|
||||
public Predicate<ChangeData> r00_notClosed() {
|
||||
return ChangeStatusPredicate.open(dbProvider);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("-status:merged")
|
||||
public Predicate<ChangeData> r00_notMerged() {
|
||||
return or(ChangeStatusPredicate.open(dbProvider),
|
||||
new ChangeStatusPredicate(dbProvider, Change.Status.ABANDONED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("-status:abandoned")
|
||||
public Predicate<ChangeData> r00_notAbandoned() {
|
||||
return or(ChangeStatusPredicate.open(dbProvider),
|
||||
new ChangeStatusPredicate(dbProvider, Change.Status.MERGED));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoCostComputation
|
||||
@Rewrite("sortkey_before:z A=(age:*)")
|
||||
public Predicate<ChangeData> r00_ageToSortKey(@Named("A") AgePredicate a) {
|
||||
String cut = ChangeUtil.sortKey(a.getCut(), Integer.MAX_VALUE);
|
||||
return and(new SortKeyPredicate.Before(dbProvider, cut), a);
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(limit:*) B=(limit:*)")
|
||||
public Predicate<ChangeData> r00_smallestLimit(
|
||||
@Named("A") IntPredicate<ChangeData> a,
|
||||
@Named("B") IntPredicate<ChangeData> b) {
|
||||
return a.intValue() <= b.intValue() ? a : b;
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(sortkey_before:*) B=(sortkey_before:*)")
|
||||
public Predicate<ChangeData> r00_oldestSortKey(
|
||||
@Named("A") SortKeyPredicate.Before a,
|
||||
@Named("B") SortKeyPredicate.Before b) {
|
||||
return a.getValue().compareTo(b.getValue()) <= 0 ? a : b;
|
||||
}
|
||||
|
||||
@NoCostComputation
|
||||
@Rewrite("A=(sortkey_after:*) B=(sortkey_after:*)")
|
||||
public Predicate<ChangeData> r00_newestSortKey(
|
||||
@Named("A") SortKeyPredicate.After a, @Named("B") SortKeyPredicate.After b) {
|
||||
return a.getValue().compareTo(b.getValue()) >= 0 ? a : b;
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) B=(ref:*)")
|
||||
public Predicate<ChangeData> r05_byBranchOpen(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b) {
|
||||
return new ChangeSource(500) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a)
|
||||
throws OrmException {
|
||||
return a.byBranchOpenAll(
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen()
|
||||
&& p.match(cd)
|
||||
&& b.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) B=(ref:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r05_byBranchMergedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byBranchClosedPrev(Change.Status.MERGED.getCode(), //
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& b.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) B=(ref:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r05_byBranchMergedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byBranchClosedNext(Change.Status.MERGED.getCode(), //
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& b.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectOpenPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(500, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectOpenPrev(p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() //
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectOpenNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(500, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectOpenNext(p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() //
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectMergedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedPrev(Change.Status.MERGED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectMergedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedNext(Change.Status.MERGED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:abandoned P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectAbandonedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedPrev(Change.Status.ABANDONED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:abandoned P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectAbandonedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedNext(Change.Status.ABANDONED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byOpenPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(2000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allOpenPrev(key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byOpenNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(2000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allOpenNext(key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:merged S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byMergedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byMergedPrev", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedPrev(Change.Status.MERGED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:merged S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byMergedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byMergedNext", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedNext(Change.Status.MERGED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:abandoned S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byAbandonedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byAbandonedPrev", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedPrev(Change.Status.ABANDONED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:abandoned S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byAbandonedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byAbandonedNext", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedNext(Change.Status.ABANDONED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byClosedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return or(r20_byMergedPrev(s, l), r20_byAbandonedPrev(s, l));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byClosedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return or(r20_byMergedNext(s, l), r20_byAbandonedNext(s, l));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:open O=(owner:*)")
|
||||
public Predicate<ChangeData> r25_byOwnerOpen(
|
||||
@Named("O") final OwnerPredicate o) {
|
||||
return new ChangeSource(50) {
|
||||
{
|
||||
init("r25_byOwnerOpen", o);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byOwnerOpen(o.getAccountId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && o.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed O=(owner:*)")
|
||||
public Predicate<ChangeData> r25_byOwnerClosed(
|
||||
@Named("O") final OwnerPredicate o) {
|
||||
return new ChangeSource(5000) {
|
||||
{
|
||||
init("r25_byOwnerClosed", o);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byOwnerClosedAll(o.getAccountId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isClosed() && o.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("O=(owner:*)")
|
||||
public Predicate<ChangeData> r26_byOwner(@Named("O") OwnerPredicate o) {
|
||||
return or(r25_byOwnerOpen(o), r25_byOwnerClosed(o));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:open R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r30_byReviewerOpen(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return new Source() {
|
||||
{
|
||||
init("r30_byReviewerOpen", r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.patchSetApproval(dbProvider.get()
|
||||
.patchSetApprovals().openByUser(r.getAccountId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
Change change = cd.change(dbProvider);
|
||||
return change != null && change.getStatus().isOpen() && r.match(cd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.APPROVALS_SCAN, getCardinality());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r30_byReviewerClosed(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return new Source() {
|
||||
{
|
||||
init("r30_byReviewerClosed", r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.patchSetApproval(dbProvider.get()
|
||||
.patchSetApprovals().closedByUserAll(r.getAccountId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
Change change = cd.change(dbProvider);
|
||||
return change != null && change.getStatus().isClosed() && r.match(cd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return 5000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.APPROVALS_SCAN, getCardinality());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r31_byReviewer(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return or(r30_byReviewerOpen(r), r30_byReviewerClosed(r));
|
||||
}
|
||||
|
||||
@Rewrite("status:submitted")
|
||||
public Predicate<ChangeData> r99_allSubmitted() {
|
||||
return new ChangeSource(50) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.allSubmitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.SUBMITTED;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("P=(project:*)")
|
||||
public Predicate<ChangeData> r99_byProject(
|
||||
@Named("P") final ProjectPredicate p) {
|
||||
return new ChangeSource(1000000) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byProject(p.getValueKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return p.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean hasSource(Collection<? extends Predicate<ChangeData>> l) {
|
||||
for (Predicate<ChangeData> p : l) {
|
||||
if (p instanceof ChangeDataSource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private abstract static class Source extends RewritePredicate<ChangeData>
|
||||
implements ChangeDataSource {
|
||||
@Override
|
||||
public boolean hasChange() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ChangeSource extends Source {
|
||||
private final int cardinality;
|
||||
|
||||
ChangeSource(int card) {
|
||||
this.cardinality = card;
|
||||
}
|
||||
|
||||
abstract ResultSet<Change> scan(ChangeAccess a) throws OrmException;
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.change(scan(dbProvider.get().changes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChange() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return cardinality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.CHANGES_SCAN, getCardinality());
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class PaginatedSource extends ChangeSource implements
|
||||
Paginated {
|
||||
private final String startKey;
|
||||
private final int limit;
|
||||
|
||||
PaginatedSource(int card, String start, int lim) {
|
||||
super(card);
|
||||
this.startKey = start;
|
||||
this.limit = lim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return scan(a, startKey, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> restart(ChangeData last) throws OrmException {
|
||||
return ChangeDataResultSet.change(scan(dbProvider.get().changes(), //
|
||||
last.change(dbProvider).getSortKey(), //
|
||||
limit));
|
||||
}
|
||||
|
||||
abstract ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException;
|
||||
}
|
||||
|
||||
private static final class InvalidProvider<T> implements Provider<T> {
|
||||
@Override
|
||||
public T get() {
|
||||
throw new OutOfScopeException("Not available at init");
|
||||
}
|
||||
}
|
||||
public interface ChangeQueryRewriter {
|
||||
Predicate<ChangeData> rewrite(Predicate<ChangeData> in);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public final class ChangeStatusPredicate extends IndexPredicate<ChangeData> {
|
||||
VALUES = values.build();
|
||||
}
|
||||
|
||||
static Predicate<ChangeData> open(Provider<ReviewDb> dbProvider) {
|
||||
public static Predicate<ChangeData> open(Provider<ReviewDb> dbProvider) {
|
||||
List<Predicate<ChangeData>> r = new ArrayList<Predicate<ChangeData>>(4);
|
||||
for (final Change.Status e : Change.Status.values()) {
|
||||
if (e.isOpen()) {
|
||||
@@ -57,7 +57,7 @@ public final class ChangeStatusPredicate extends IndexPredicate<ChangeData> {
|
||||
return r.size() == 1 ? r.get(0) : or(r);
|
||||
}
|
||||
|
||||
static Predicate<ChangeData> closed(Provider<ReviewDb> dbProvider) {
|
||||
public static Predicate<ChangeData> closed(Provider<ReviewDb> dbProvider) {
|
||||
List<Predicate<ChangeData>> r = new ArrayList<Predicate<ChangeData>>(4);
|
||||
for (final Change.Status e : Change.Status.values()) {
|
||||
if (e.isClosed()) {
|
||||
@@ -83,7 +83,7 @@ public final class ChangeStatusPredicate extends IndexPredicate<ChangeData> {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
Change.Status getStatus() {
|
||||
public Change.Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -55,9 +55,4 @@ class CommentPredicate extends IndexPredicate<ChangeData> {
|
||||
public int getCost() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,9 +54,4 @@ class EqualsFilePredicate extends IndexPredicate<ChangeData> {
|
||||
public int getCost() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// 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.server.query.Predicate;
|
||||
|
||||
public interface IndexRewrite {
|
||||
/** Instance indicating secondary index is disabled. */
|
||||
public static final IndexRewrite DISABLED = new IndexRewrite() {
|
||||
@Override
|
||||
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in) {
|
||||
return in;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Rewrite a predicate to push as much boolean logic as possible into the
|
||||
* secondary index query system.
|
||||
*
|
||||
* @param in predicate to rewrite.
|
||||
* @return a predicate with some subtrees replaced with predicates that are
|
||||
* also sources that query the index directly.
|
||||
*/
|
||||
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in);
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
// 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.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.index.ChangeIndex;
|
||||
import com.google.gerrit.server.index.IndexPredicate;
|
||||
import com.google.gerrit.server.index.PredicateWrapper;
|
||||
import com.google.gerrit.server.query.AndPredicate;
|
||||
import com.google.gerrit.server.query.NotPredicate;
|
||||
import com.google.gerrit.server.query.OrPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** Rewriter that pushes boolean logic into the secondary index. */
|
||||
public class IndexRewriteImpl implements IndexRewrite {
|
||||
/** Set of all open change statuses. */
|
||||
public static final Set<Change.Status> OPEN_STATUSES;
|
||||
|
||||
/** Set of all closed change statuses. */
|
||||
public static final Set<Change.Status> CLOSED_STATUSES;
|
||||
|
||||
static {
|
||||
EnumSet<Change.Status> open = EnumSet.noneOf(Change.Status.class);
|
||||
EnumSet<Change.Status> closed = EnumSet.noneOf(Change.Status.class);
|
||||
for (Change.Status s : Change.Status.values()) {
|
||||
if (s.isOpen()) {
|
||||
open.add(s);
|
||||
} else {
|
||||
closed.add(s);
|
||||
}
|
||||
}
|
||||
OPEN_STATUSES = Sets.immutableEnumSet(open);
|
||||
CLOSED_STATUSES = Sets.immutableEnumSet(closed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of statuses that changes matching the given predicate may have.
|
||||
*
|
||||
* @param in predicate
|
||||
* @return the maximal set of statuses that any changes matching the input
|
||||
* predicates may have, based on examining boolean and
|
||||
* {@link ChangeStatusPredicate}s.
|
||||
*/
|
||||
public static EnumSet<Change.Status> getPossibleStatus(Predicate<ChangeData> in) {
|
||||
if (in instanceof ChangeStatusPredicate) {
|
||||
return EnumSet.of(((ChangeStatusPredicate) in).getStatus());
|
||||
} else if (in instanceof NotPredicate) {
|
||||
return EnumSet.complementOf(getPossibleStatus(in.getChild(0)));
|
||||
} else if (in instanceof OrPredicate) {
|
||||
EnumSet<Change.Status> s = EnumSet.noneOf(Change.Status.class);
|
||||
for (int i = 0; i < in.getChildCount(); i++) {
|
||||
s.addAll(getPossibleStatus(in.getChild(i)));
|
||||
}
|
||||
return s;
|
||||
} else if (in instanceof AndPredicate) {
|
||||
EnumSet<Change.Status> s = EnumSet.allOf(Change.Status.class);
|
||||
for (int i = 0; i < in.getChildCount(); i++) {
|
||||
s.retainAll(getPossibleStatus(in.getChild(i)));
|
||||
}
|
||||
return s;
|
||||
} else if (in.getChildCount() == 0) {
|
||||
return EnumSet.allOf(Change.Status.class);
|
||||
} else {
|
||||
throw new IllegalStateException(
|
||||
"Invalid predicate type in change index query: " + in.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private final ChangeIndex index;
|
||||
|
||||
@Inject
|
||||
IndexRewriteImpl(ChangeIndex index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> rewrite(Predicate<ChangeData> in) {
|
||||
Predicate<ChangeData> out = rewriteImpl(in);
|
||||
if (out == null) {
|
||||
return in;
|
||||
} else if (out == in) {
|
||||
return wrap(out);
|
||||
} else {
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrite a single predicate subtree.
|
||||
*
|
||||
* @param in predicate to rewrite.
|
||||
* @return {@code null} if no part of this subtree can be queried in the
|
||||
* index directly. {@code in} if this subtree and all its children can be
|
||||
* queried directly in the index. Otherwise, a predicate that is
|
||||
* semantically equivalent, with some of its subtrees wrapped to query the
|
||||
* index directly.
|
||||
*/
|
||||
private Predicate<ChangeData> rewriteImpl(Predicate<ChangeData> in) {
|
||||
if (in instanceof IndexPredicate) {
|
||||
return in;
|
||||
}
|
||||
if (!isBoolean(in)) {
|
||||
return null;
|
||||
}
|
||||
int n = in.getChildCount();
|
||||
BitSet toKeep = new BitSet(n);
|
||||
BitSet toWrap = new BitSet(n);
|
||||
BitSet rewritten = new BitSet(n);
|
||||
List<Predicate<ChangeData>> newChildren = Lists.newArrayListWithCapacity(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
Predicate<ChangeData> c = in.getChild(i);
|
||||
Predicate<ChangeData> nc = rewriteImpl(c);
|
||||
if (nc == null) {
|
||||
toKeep.set(i);
|
||||
newChildren.add(c);
|
||||
} else if (nc == c) {
|
||||
toWrap.set(i);
|
||||
newChildren.add(nc);
|
||||
} else {
|
||||
rewritten.set(i);
|
||||
newChildren.add(nc);
|
||||
}
|
||||
}
|
||||
if (toKeep.cardinality() == n) {
|
||||
return null; // Can't rewrite any children.
|
||||
}
|
||||
if (rewritten.cardinality() == n) {
|
||||
// All children were partially, but not fully, rewritten.
|
||||
return in.copy(newChildren);
|
||||
}
|
||||
if (toWrap.cardinality() == n) {
|
||||
// All children can be fully rewritten, push work to parent.
|
||||
return in;
|
||||
}
|
||||
return partitionChildren(in, newChildren, toWrap);
|
||||
}
|
||||
|
||||
|
||||
private Predicate<ChangeData> partitionChildren(Predicate<ChangeData> in,
|
||||
List<Predicate<ChangeData>> newChildren, BitSet toWrap) {
|
||||
if (toWrap.cardinality() == 1) {
|
||||
int i = toWrap.nextSetBit(0);
|
||||
newChildren.set(i, wrap(newChildren.get(i)));
|
||||
return in.copy(newChildren);
|
||||
}
|
||||
|
||||
// Group all toWrap predicates into a wrapped subtree and place it as a
|
||||
// sibling of the non-/partially-wrapped predicates. Assumes partitioning
|
||||
// the children into arbitrary subtrees of the same type is logically
|
||||
// equivalent to having them as siblings.
|
||||
List<Predicate<ChangeData>> wrapped = Lists.newArrayListWithCapacity(
|
||||
toWrap.cardinality());
|
||||
List<Predicate<ChangeData>> all = Lists.newArrayListWithCapacity(
|
||||
newChildren.size() - toWrap.cardinality() + 1);
|
||||
for (int i = 0; i < newChildren.size(); i++) {
|
||||
Predicate<ChangeData> child = newChildren.get(i);
|
||||
if (toWrap.get(i)) {
|
||||
wrapped.add(child);
|
||||
if (allNonIndexOnly(child)) {
|
||||
// Duplicate non-index-only predicate subtrees alongside the wrapped
|
||||
// subtrees so they can provide index hints to the DB-based rewriter.
|
||||
all.add(child);
|
||||
}
|
||||
} else {
|
||||
all.add(child);
|
||||
}
|
||||
}
|
||||
all.add(wrap(in.copy(wrapped)));
|
||||
return in.copy(all);
|
||||
}
|
||||
|
||||
private static boolean allNonIndexOnly(Predicate<ChangeData> p) {
|
||||
if (p instanceof IndexPredicate) {
|
||||
return !((IndexPredicate<ChangeData>) p).isIndexOnly();
|
||||
}
|
||||
if (isBoolean(p)) {
|
||||
for (int i = 0; i < p.getChildCount(); i++) {
|
||||
if (!allNonIndexOnly(p.getChild(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private PredicateWrapper wrap(Predicate<ChangeData> p) {
|
||||
try {
|
||||
return new PredicateWrapper(index, p);
|
||||
} catch (QueryParseException e) {
|
||||
throw new IllegalStateException(
|
||||
"Failed to convert " + p + " to index predicate", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isBoolean(Predicate<ChangeData> p) {
|
||||
return p instanceof AndPredicate || p instanceof OrPredicate
|
||||
|| p instanceof NotPredicate;
|
||||
}
|
||||
}
|
||||
@@ -59,9 +59,4 @@ class MessagePredicate extends IndexPredicate<ChangeData> {
|
||||
public int getCost() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndexOnly() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
class OrSource extends OrPredicate<ChangeData> implements ChangeDataSource {
|
||||
public class OrSource extends OrPredicate<ChangeData> implements ChangeDataSource {
|
||||
private int cardinality = -1;
|
||||
|
||||
OrSource(final Collection<? extends Predicate<ChangeData>> that) {
|
||||
public OrSource(Collection<? extends Predicate<ChangeData>> that) {
|
||||
super(that);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
// Copyright (C) 2009 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.reviewdb.client.Branch;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.reviewdb.server.ChangeAccess;
|
||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||
import com.google.gerrit.server.query.IntPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryRewriter;
|
||||
import com.google.gerrit.server.query.RewritePredicate;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.name.Named;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class SqlRewriterImpl extends BasicChangeRewrites
|
||||
implements ChangeQueryRewriter {
|
||||
private static final QueryRewriter.Definition<ChangeData, SqlRewriterImpl> mydef =
|
||||
new QueryRewriter.Definition<ChangeData, SqlRewriterImpl>(
|
||||
SqlRewriterImpl.class, BUILDER);
|
||||
|
||||
@Inject
|
||||
SqlRewriterImpl(Provider<ReviewDb> dbProvider) {
|
||||
super(mydef, dbProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> and(Collection<? extends Predicate<ChangeData>> l) {
|
||||
return hasSource(l) ? new AndSource(dbProvider, l) : super.and(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<ChangeData> or(Collection<? extends Predicate<ChangeData>> l) {
|
||||
return hasSource(l) ? new OrSource(l) : super.or(l);
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) B=(ref:*)")
|
||||
public Predicate<ChangeData> r05_byBranchOpen(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b) {
|
||||
return new ChangeSource(500) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a)
|
||||
throws OrmException {
|
||||
return a.byBranchOpenAll(
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen()
|
||||
&& p.match(cd)
|
||||
&& b.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) B=(ref:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r05_byBranchMergedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byBranchClosedPrev(Change.Status.MERGED.getCode(), //
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& b.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) B=(ref:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r05_byBranchMergedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("B") final RefPredicate b,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byBranchClosedNext(Change.Status.MERGED.getCode(), //
|
||||
new Branch.NameKey(p.getValueKey(), b.getValue()), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& b.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectOpenPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(500, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectOpenPrev(p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() //
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectOpenNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(500, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectOpenNext(p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() //
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectMergedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedPrev(Change.Status.MERGED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:merged P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectMergedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedNext(Change.Status.MERGED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:abandoned P=(project:*) S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectAbandonedPrev(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedPrev(Change.Status.ABANDONED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:abandoned P=(project:*) S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r10_byProjectAbandonedNext(
|
||||
@Named("P") final ProjectPredicate p,
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(40000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.byProjectClosedNext(Change.Status.ABANDONED.getCode(), //
|
||||
p.getValueKey(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& p.match(cd) //
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byOpenPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(2000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allOpenPrev(key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("status:open S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byOpenNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(2000, s.getValue(), l.intValue()) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allOpenNext(key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:merged S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byMergedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byMergedPrev", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedPrev(Change.Status.MERGED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:merged S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byMergedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byMergedNext", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedNext(Change.Status.MERGED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.MERGED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:abandoned S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byAbandonedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byAbandonedPrev", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedPrev(Change.Status.ABANDONED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:abandoned S=(sortkey_before:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byAbandonedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return new PaginatedSource(50000, s.getValue(), l.intValue()) {
|
||||
{
|
||||
init("r20_byAbandonedNext", s, l);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException {
|
||||
return a.allClosedNext(Change.Status.ABANDONED.getCode(), key, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.ABANDONED
|
||||
&& s.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byClosedPrev(
|
||||
@Named("S") final SortKeyPredicate.After s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return or(r20_byMergedPrev(s, l), r20_byAbandonedPrev(s, l));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed S=(sortkey_after:*) L=(limit:*)")
|
||||
public Predicate<ChangeData> r20_byClosedNext(
|
||||
@Named("S") final SortKeyPredicate.Before s,
|
||||
@Named("L") final IntPredicate<ChangeData> l) {
|
||||
return or(r20_byMergedNext(s, l), r20_byAbandonedNext(s, l));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:open O=(owner:*)")
|
||||
public Predicate<ChangeData> r25_byOwnerOpen(
|
||||
@Named("O") final OwnerPredicate o) {
|
||||
return new ChangeSource(50) {
|
||||
{
|
||||
init("r25_byOwnerOpen", o);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byOwnerOpen(o.getAccountId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isOpen() && o.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed O=(owner:*)")
|
||||
public Predicate<ChangeData> r25_byOwnerClosed(
|
||||
@Named("O") final OwnerPredicate o) {
|
||||
return new ChangeSource(5000) {
|
||||
{
|
||||
init("r25_byOwnerClosed", o);
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byOwnerClosedAll(o.getAccountId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus().isClosed() && o.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("O=(owner:*)")
|
||||
public Predicate<ChangeData> r26_byOwner(@Named("O") OwnerPredicate o) {
|
||||
return or(r25_byOwnerOpen(o), r25_byOwnerClosed(o));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:open R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r30_byReviewerOpen(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return new Source() {
|
||||
{
|
||||
init("r30_byReviewerOpen", r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.patchSetApproval(dbProvider.get()
|
||||
.patchSetApprovals().openByUser(r.getAccountId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
Change change = cd.change(dbProvider);
|
||||
return change != null && change.getStatus().isOpen() && r.match(cd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return 50;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.APPROVALS_SCAN, getCardinality());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("status:closed R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r30_byReviewerClosed(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return new Source() {
|
||||
{
|
||||
init("r30_byReviewerClosed", r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.patchSetApproval(dbProvider.get()
|
||||
.patchSetApprovals().closedByUserAll(r.getAccountId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
Change change = cd.change(dbProvider);
|
||||
return change != null && change.getStatus().isClosed() && r.match(cd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return 5000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.APPROVALS_SCAN, getCardinality());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Rewrite("R=(reviewer:*)")
|
||||
public Predicate<ChangeData> r31_byReviewer(
|
||||
@Named("R") final ReviewerPredicate r) {
|
||||
return or(r30_byReviewerOpen(r), r30_byReviewerClosed(r));
|
||||
}
|
||||
|
||||
@Rewrite("status:submitted")
|
||||
public Predicate<ChangeData> r99_allSubmitted() {
|
||||
return new ChangeSource(50) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.allSubmitted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return cd.change(dbProvider).getStatus() == Change.Status.SUBMITTED;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Rewrite("P=(project:*)")
|
||||
public Predicate<ChangeData> r99_byProject(
|
||||
@Named("P") final ProjectPredicate p) {
|
||||
return new ChangeSource(1000000) {
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return a.byProject(p.getValueKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(ChangeData cd) throws OrmException {
|
||||
return p.match(cd);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean hasSource(Collection<? extends Predicate<ChangeData>> l) {
|
||||
for (Predicate<ChangeData> p : l) {
|
||||
if (p instanceof ChangeDataSource) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private abstract static class Source extends RewritePredicate<ChangeData>
|
||||
implements ChangeDataSource {
|
||||
@Override
|
||||
public boolean hasChange() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class ChangeSource extends Source {
|
||||
private final int cardinality;
|
||||
|
||||
ChangeSource(int card) {
|
||||
this.cardinality = card;
|
||||
}
|
||||
|
||||
abstract ResultSet<Change> scan(ChangeAccess a) throws OrmException;
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
return ChangeDataResultSet.change(scan(dbProvider.get().changes()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChange() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
return cardinality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCost() {
|
||||
return ChangeCosts.cost(ChangeCosts.CHANGES_SCAN, getCardinality());
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class PaginatedSource extends ChangeSource implements
|
||||
Paginated {
|
||||
private final String startKey;
|
||||
private final int limit;
|
||||
|
||||
PaginatedSource(int card, String start, int lim) {
|
||||
super(card);
|
||||
this.startKey = start;
|
||||
this.limit = lim;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
ResultSet<Change> scan(ChangeAccess a) throws OrmException {
|
||||
return scan(a, startKey, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> restart(ChangeData last) throws OrmException {
|
||||
return ChangeDataResultSet.change(scan(dbProvider.get().changes(), //
|
||||
last.change(dbProvider).getSortKey(), //
|
||||
limit));
|
||||
}
|
||||
|
||||
abstract ResultSet<Change> scan(ChangeAccess a, String key, int limit)
|
||||
throws OrmException;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.query.change;
|
||||
package com.google.gerrit.server.index;
|
||||
|
||||
import static com.google.gerrit.reviewdb.client.Change.Status.ABANDONED;
|
||||
import static com.google.gerrit.reviewdb.client.Change.Status.DRAFT;
|
||||
@@ -23,13 +23,14 @@ import static com.google.gerrit.reviewdb.client.Change.Status.SUBMITTED;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.gerrit.reviewdb.client.Change;
|
||||
import com.google.gerrit.server.index.ChangeIndex;
|
||||
import com.google.gerrit.server.index.PredicateWrapper;
|
||||
import com.google.gerrit.server.query.AndPredicate;
|
||||
import com.google.gerrit.server.query.OperatorPredicate;
|
||||
import com.google.gerrit.server.query.OrPredicate;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gerrit.server.query.change.AndSource;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeDataSource;
|
||||
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
|
||||
import com.google.gerrit.server.query.change.OrSource;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class IndexRewriteTest extends TestCase {
|
||||
@Override
|
||||
public ChangeDataSource getSource(Predicate<ChangeData> p)
|
||||
throws QueryParseException {
|
||||
return new Source();
|
||||
return new Source(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,6 +75,12 @@ public class IndexRewriteTest extends TestCase {
|
||||
}
|
||||
|
||||
private static class Source implements ChangeDataSource {
|
||||
private final Predicate<ChangeData> p;
|
||||
|
||||
Source(Predicate<ChangeData> p) {
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCardinality() {
|
||||
throw new UnsupportedOperationException();
|
||||
@@ -88,6 +95,11 @@ public class IndexRewriteTest extends TestCase {
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return p.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class QueryBuilder extends ChangeQueryBuilder {
|
||||
@@ -127,19 +139,22 @@ public class IndexRewriteTest extends TestCase {
|
||||
|
||||
private DummyIndex index;
|
||||
private ChangeQueryBuilder queryBuilder;
|
||||
private IndexRewrite rewrite;
|
||||
private IndexRewriteImpl rewrite;
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
index = new DummyIndex();
|
||||
queryBuilder = new QueryBuilder();
|
||||
rewrite = new IndexRewriteImpl(index);
|
||||
rewrite = new IndexRewriteImpl(
|
||||
index,
|
||||
null,
|
||||
new IndexRewriteImpl.BasicRewritesImpl(null));
|
||||
}
|
||||
|
||||
public void testIndexPredicate() throws Exception {
|
||||
Predicate<ChangeData> in = parse("file:a");
|
||||
assertEquals(wrap(in), rewrite(in));
|
||||
assertEquals(query(in), rewrite(in));
|
||||
}
|
||||
|
||||
public void testNonIndexPredicate() throws Exception {
|
||||
@@ -149,33 +164,37 @@ public class IndexRewriteTest extends TestCase {
|
||||
|
||||
public void testIndexPredicates() throws Exception {
|
||||
Predicate<ChangeData> in = parse("file:a file:b");
|
||||
assertEquals(wrap(in), rewrite(in));
|
||||
assertEquals(query(in), rewrite(in));
|
||||
}
|
||||
|
||||
public void testNonIndexPredicates() throws Exception {
|
||||
Predicate<ChangeData> in = parse("foo:a OR foo:b");
|
||||
assertSame(in, rewrite(in));
|
||||
assertEquals(in, rewrite(in));
|
||||
}
|
||||
|
||||
public void testOneIndexPredicate() throws Exception {
|
||||
Predicate<ChangeData> in = parse("foo:a file:b");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertSame(AndPredicate.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(in.getChild(0), wrap(in.getChild(1))),
|
||||
assertSame(AndSource.class, out.getClass());
|
||||
assertEquals(
|
||||
ImmutableList.of(query(in.getChild(1)), in.getChild(0)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
public void testThreeLevelTreeWithAllIndexPredicates() throws Exception {
|
||||
Predicate<ChangeData> in =
|
||||
parse("-status:abandoned (status:open OR status:merged)");
|
||||
assertEquals(wrap(in), rewrite.rewrite(in));
|
||||
assertEquals(
|
||||
query(parse("status:new OR status:submitted OR status:draft OR status:merged")),
|
||||
rewrite.rewrite(in));
|
||||
}
|
||||
|
||||
public void testThreeLevelTreeWithSomeIndexPredicates() throws Exception {
|
||||
Predicate<ChangeData> in = parse("-foo:a (file:b OR file:c)");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertEquals(AndPredicate.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(in.getChild(0), wrap(in.getChild(1))),
|
||||
assertEquals(AndSource.class, out.getClass());
|
||||
assertEquals(
|
||||
ImmutableList.of(query(in.getChild(1)), in.getChild(0)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
@@ -183,20 +202,20 @@ public class IndexRewriteTest extends TestCase {
|
||||
Predicate<ChangeData> in =
|
||||
parse("file:a OR foo:b OR file:c OR foo:d");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertSame(OrPredicate.class, out.getClass());
|
||||
assertSame(OrSource.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(
|
||||
in.getChild(1), in.getChild(3),
|
||||
wrap(Predicate.or(in.getChild(0), in.getChild(2)))),
|
||||
query(Predicate.or(in.getChild(0), in.getChild(2))),
|
||||
in.getChild(1), in.getChild(3)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
public void testDuplicateSimpleNonIndexOnlyPredicates() throws Exception {
|
||||
public void testIndexAndNonIndexPredicates() throws Exception {
|
||||
Predicate<ChangeData> in = parse("status:new bar:p file:a");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertSame(AndPredicate.class, out.getClass());
|
||||
assertSame(AndSource.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(
|
||||
in.getChild(0), in.getChild(1),
|
||||
wrap(Predicate.and(in.getChild(0), in.getChild(2)))),
|
||||
query(Predicate.and(in.getChild(0), in.getChild(2))),
|
||||
in.getChild(1)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
@@ -204,10 +223,10 @@ public class IndexRewriteTest extends TestCase {
|
||||
Predicate<ChangeData> in =
|
||||
parse("(status:new OR status:draft) bar:p file:a");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertSame(AndPredicate.class, out.getClass());
|
||||
assertSame(AndSource.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(
|
||||
in.getChild(0), in.getChild(1),
|
||||
wrap(Predicate.and(in.getChild(0), in.getChild(2)))),
|
||||
query(Predicate.and(in.getChild(0), in.getChild(2))),
|
||||
in.getChild(1)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
@@ -215,10 +234,10 @@ public class IndexRewriteTest extends TestCase {
|
||||
Predicate<ChangeData> in =
|
||||
parse("(status:new OR file:a) bar:p file:b");
|
||||
Predicate<ChangeData> out = rewrite(in);
|
||||
assertSame(AndPredicate.class, out.getClass());
|
||||
assertSame(AndSource.class, out.getClass());
|
||||
assertEquals(ImmutableList.of(
|
||||
in.getChild(1),
|
||||
wrap(Predicate.and(in.getChild(0), in.getChild(2)))),
|
||||
query(Predicate.and(in.getChild(0), in.getChild(2))),
|
||||
in.getChild(1)),
|
||||
out.getChildren());
|
||||
}
|
||||
|
||||
@@ -246,9 +265,9 @@ public class IndexRewriteTest extends TestCase {
|
||||
return rewrite.rewrite(in);
|
||||
}
|
||||
|
||||
private PredicateWrapper wrap(Predicate<ChangeData> p)
|
||||
private IndexedChangeQuery query(Predicate<ChangeData> p)
|
||||
throws QueryParseException {
|
||||
return new PredicateWrapper(index, p);
|
||||
return new IndexedChangeQuery(index, p);
|
||||
}
|
||||
|
||||
private Set<Change.Status> status(String query) throws QueryParseException {
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
package com.google.gerrit.solr;
|
||||
|
||||
import static com.google.gerrit.server.query.change.IndexRewriteImpl.CLOSED_STATUSES;
|
||||
import static com.google.gerrit.server.query.change.IndexRewriteImpl.OPEN_STATUSES;
|
||||
import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES;
|
||||
import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES;
|
||||
import static com.google.gerrit.solr.IndexVersionCheck.SCHEMA_VERSIONS;
|
||||
import static com.google.gerrit.solr.IndexVersionCheck.solrIndexConfig;
|
||||
|
||||
@@ -34,11 +34,11 @@ import com.google.gerrit.server.index.ChangeIndex;
|
||||
import com.google.gerrit.server.index.FieldDef;
|
||||
import com.google.gerrit.server.index.FieldDef.FillArgs;
|
||||
import com.google.gerrit.server.index.FieldType;
|
||||
import com.google.gerrit.server.index.IndexRewriteImpl;
|
||||
import com.google.gerrit.server.query.Predicate;
|
||||
import com.google.gerrit.server.query.QueryParseException;
|
||||
import com.google.gerrit.server.query.change.ChangeData;
|
||||
import com.google.gerrit.server.query.change.ChangeDataSource;
|
||||
import com.google.gerrit.server.query.change.IndexRewriteImpl;
|
||||
import com.google.gwtorm.server.OrmException;
|
||||
import com.google.gwtorm.server.ResultSet;
|
||||
import com.google.inject.Inject;
|
||||
@@ -224,6 +224,11 @@ class SolrChangeIndex implements ChangeIndex, LifecycleListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return query.getQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSet<ChangeData> read() throws OrmException {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user