diff options
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java')
-rw-r--r-- | gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java | 248 |
1 files changed, 176 insertions, 72 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java index a975f1b85b..a2fa7fe34c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java @@ -14,17 +14,20 @@ package com.google.gerrit.server.query.change; -import com.google.gerrit.reviewdb.Change; -import com.google.gerrit.reviewdb.PatchSet; -import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.common.data.GlobalCapability; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gerrit.reviewdb.server.ReviewDb; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.events.ChangeAttribute; import com.google.gerrit.server.events.EventFactory; +import com.google.gerrit.server.events.PatchSetAttribute; import com.google.gerrit.server.events.QueryStats; +import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.query.Predicate; import com.google.gerrit.server.query.QueryParseException; import com.google.gson.Gson; -import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.server.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; @@ -64,12 +67,17 @@ public class QueryProcessor { private final ChangeQueryBuilder queryBuilder; private final ChangeQueryRewriter queryRewriter; private final Provider<ReviewDb> db; + private final GitRepositoryManager repoManager; + private final int maxLimit; - private int defaultLimit = 500; private OutputFormat outputFormat = OutputFormat.TEXT; private boolean includePatchSets; private boolean includeCurrentPatchSet; private boolean includeApprovals; + private boolean includeComments; + private boolean includeFiles; + private boolean includeCommitMessage; + private boolean includeDependencies; private OutputStream outputStream = DisabledOutputStream.INSTANCE; private PrintWriter out; @@ -77,87 +85,147 @@ public class QueryProcessor { @Inject QueryProcessor(EventFactory eventFactory, ChangeQueryBuilder.Factory queryBuilder, CurrentUser currentUser, - ChangeQueryRewriter queryRewriter, Provider<ReviewDb> db) { + ChangeQueryRewriter queryRewriter, Provider<ReviewDb> db, + GitRepositoryManager repoManager) { this.eventFactory = eventFactory; this.queryBuilder = queryBuilder.create(currentUser); this.queryRewriter = queryRewriter; this.db = db; + this.repoManager = repoManager; + this.maxLimit = currentUser.getCapabilities() + .getRange(GlobalCapability.QUERY_LIMIT) + .getMax(); } public void setIncludePatchSets(boolean on) { includePatchSets = on; } + public boolean getIncludePatchSets() { + return includePatchSets; + } + public void setIncludeCurrentPatchSet(boolean on) { includeCurrentPatchSet = on; } + public boolean getIncludeCurrentPatchSet() { + return includeCurrentPatchSet; + } + public void setIncludeApprovals(boolean on) { includeApprovals = on; } + public void setIncludeComments(boolean on) { + includeComments = on; + } + + public void setIncludeFiles(boolean on) { + includeFiles = on; + } + + public boolean getIncludeFiles() { + return includeFiles; + } + + public void setIncludeDependencies(boolean on) { + includeDependencies = on; + } + + public boolean getIncludeDependencies() { + return includeDependencies; + } + + public void setIncludeCommitMessage(boolean on) { + includeCommitMessage = on; + } + public void setOutput(OutputStream out, OutputFormat fmt) { this.outputStream = out; this.outputFormat = fmt; } + public List<ChangeData> queryChanges(final String queryString) + throws OrmException, QueryParseException { + final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible(); + Predicate<ChangeData> s = compileQuery(queryString, visibleToMe); + List<ChangeData> results = new ArrayList<ChangeData>(); + HashSet<Change.Id> want = new HashSet<Change.Id>(); + for (ChangeData d : ((ChangeDataSource) s).read()) { + if (d.hasChange()) { + // Checking visibleToMe here should be unnecessary, the + // query should have already performed it. But we don't + // want to trust the query rewriter that much yet. + // + if (visibleToMe.match(d)) { + results.add(d); + } + } else { + want.add(d.getId()); + } + } + + if (!want.isEmpty()) { + for (Change c : db.get().changes().get(want)) { + ChangeData d = new ChangeData(c); + if (visibleToMe.match(d)) { + results.add(d); + } + } + } + + Collections.sort(results, new Comparator<ChangeData>() { + @Override + public int compare(ChangeData a, ChangeData b) { + return b.getChange().getSortKey().compareTo( + a.getChange().getSortKey()); + } + }); + + int limit = limit(s); + if (limit < results.size()) { + results = results.subList(0, limit); + } + + return results; + } + public void query(String queryString) throws IOException { out = new PrintWriter( // new BufferedWriter( // new OutputStreamWriter(outputStream, "UTF-8"))); try { + if (maxLimit <= 0) { + ErrorMessage m = new ErrorMessage(); + m.message = "query disabled"; + show(m); + return; + } + try { final QueryStats stats = new QueryStats(); stats.runTimeMilliseconds = System.currentTimeMillis(); - final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible(); - Predicate<ChangeData> s = compileQuery(queryString, visibleToMe); - List<ChangeData> results = new ArrayList<ChangeData>(); - HashSet<Change.Id> want = new HashSet<Change.Id>(); - for (ChangeData d : ((ChangeDataSource) s).read()) { - if (d.hasChange()) { - // Checking visibleToMe here should be unnecessary, the - // query should have already performed it. But we don't - // want to trust the query rewriter that much yet. - // - if (visibleToMe.match(d)) { - results.add(d); - } - } else { - want.add(d.getId()); - } - } - - if (!want.isEmpty()) { - for (Change c : db.get().changes().get(want)) { - ChangeData d = new ChangeData(c); - if (visibleToMe.match(d)) { - results.add(d); - } - } - } - - Collections.sort(results, new Comparator<ChangeData>() { - @Override - public int compare(ChangeData a, ChangeData b) { - return b.getChange().getSortKey().compareTo( - a.getChange().getSortKey()); - } - }); - - int limit = limit(s); - if (limit < results.size()) { - results = results.subList(0, limit); - } - + List<ChangeData> results = queryChanges(queryString); for (ChangeData d : results) { ChangeAttribute c = eventFactory.asChangeAttribute(d.getChange()); eventFactory.extend(c, d.getChange()); eventFactory.addTrackingIds(c, d.trackingIds(db)); + if (includeCommitMessage) { + eventFactory.addCommitMessage(c, d.commitMessage(repoManager, db)); + } + if (includePatchSets) { - eventFactory.addPatchSets(c, d.patches(db), - includeApprovals ? d.approvalsMap(db) : null); + if (includeFiles) { + eventFactory.addPatchSets(c, d.patches(db), + includeApprovals ? d.approvalsMap(db) : null, + includeFiles, d.change(db)); + } else { + eventFactory.addPatchSets(c, d.patches(db), + includeApprovals ? d.approvalsMap(db) : null); + } } if (includeCurrentPatchSet) { @@ -166,9 +234,27 @@ public class QueryProcessor { c.currentPatchSet = eventFactory.asPatchSetAttribute(current); eventFactory.addApprovals(c.currentPatchSet, // d.approvalsFor(db, current.getId())); + + if (includeFiles) { + eventFactory.addPatchSetFileNames(c.currentPatchSet, + d.change(db), d.currentPatchSet(db)); + } } } + if (includeComments) { + eventFactory.addComments(c, d.messages(db)); + if (includePatchSets) { + for (PatchSetAttribute attribute : c.patchSets) { + eventFactory.addPatchSetComments(attribute, d.comments(db)); + } + } + } + + if (includeDependencies) { + eventFactory.addDependencies(c, d.getChange()); + } + show(c); } @@ -198,7 +284,7 @@ public class QueryProcessor { } private int limit(Predicate<ChangeData> s) { - return queryBuilder.hasLimit(s) ? queryBuilder.getLimit(s) : defaultLimit; + return queryBuilder.hasLimit(s) ? queryBuilder.getLimit(s) : maxLimit; } @SuppressWarnings("unchecked") @@ -206,13 +292,10 @@ public class QueryProcessor { final Predicate<ChangeData> visibleToMe) throws QueryParseException { Predicate<ChangeData> q = queryBuilder.parse(queryString); - if (!queryBuilder.hasLimit(q)) { - q = Predicate.and(q, queryBuilder.limit(defaultLimit)); - } if (!queryBuilder.hasSortKey(q)) { q = Predicate.and(q, queryBuilder.sortkey_before("z")); } - q = Predicate.and(q, visibleToMe); + q = Predicate.and(q, queryBuilder.limit(maxLimit), visibleToMe); Predicate<ChangeData> s = queryRewriter.rewrite(q); if (!(s instanceof ChangeDataSource)) { @@ -262,42 +345,61 @@ public class QueryProcessor { continue; } - indent(depth); - out.print(f.getName()); - out.print(":"); - - if (val instanceof Long && isDateField(f.getName())) { - out.print(' '); - out.print(sdf.format(new Date(((Long) val) * 1000L))); - out.print('\n'); - } else { - showTextValue(val, depth); - } + showField(f.getName(), val, depth); } } - private void indent(int depth) { - for (int i = 0; i < depth; i++) { - out.print(" "); + private String indent(int spaces) { + if (spaces == 0) { + return ""; + } else { + return String.format("%" + spaces + "s", " "); } } - private void showTextValue(Object value, int depth) { - if (isPrimitive(value)) { + private void showField(String field, Object value, int depth) { + final int spacesDepthRatio = 2; + String indent = indent(depth * spacesDepthRatio); + out.print(indent); + out.print(field); + out.print(':'); + if (value instanceof String && ((String) value).contains("\n")) { + out.print(' '); + // Idention for multi-line text is + // current depth indetion + length of field + length of ": " + indent = indent(indent.length() + field.length() + spacesDepthRatio); + out.print(((String) value).replaceAll("\n", "\n" + indent).trim()); + out.print('\n'); + } else if (value instanceof Long && isDateField(field)) { + out.print(' '); + out.print(sdf.format(new Date(((Long) value) * 1000L))); + out.print('\n'); + } else if (isPrimitive(value)) { out.print(' '); out.print(value); out.print('\n'); - } else if (value instanceof Collection) { out.print('\n'); + boolean firstElement = true; for (Object thing : ((Collection<?>) value)) { + // The name of the collection was initially printed at the beginning + // of this routine. Beginning at the second sub-element, reprint + // the collection name so humans can separate individual elements + // with less strain and error. + // + if (firstElement) { + firstElement = false; + } else { + out.print(indent); + out.print(field); + out.print(":\n"); + } if (isPrimitive(thing)) { out.print(' '); out.print(value); out.print('\n'); } else { showText(thing, depth + 1); - out.print('\n'); } } } else { @@ -315,7 +417,9 @@ public class QueryProcessor { private static boolean isDateField(String name) { return "lastUpdated".equals(name) // - || "grantedOn".equals(name); + || "grantedOn".equals(name) // + || "timestamp".equals(name) // + || "createdOn".equals(name); } private List<Field> fieldsOf(Class<?> type) { |