blob: 904bc3cb705bffc6f26118d529843f761ef6d31a [file] [log] [blame]
// 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.Change;
import com.google.gerrit.reviewdb.ChangeAccess;
import com.google.gerrit.reviewdb.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.client.OrmException;
import com.google.gwtorm.client.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));
private final Provider<ReviewDb> dbProvider;
@Inject
ChangeQueryRewriter(Provider<ReviewDb> dbProvider) {
super(mydef);
this.dbProvider = dbProvider;
}
@Override
public Predicate<ChangeData> and(Collection<? extends Predicate<ChangeData>> l) {
return hasSource(l) ? new AndSource(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")
@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);
}
@SuppressWarnings("unchecked")
@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;
}
@SuppressWarnings("unchecked")
@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;
}
@SuppressWarnings("unchecked")
@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:*) 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));
}
@SuppressWarnings("unchecked")
@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;
}
};
}
@SuppressWarnings("unchecked")
@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");
}
}
}