[email protected] has uploaded a new change for review.
https://asterix-gerrit.ics.uci.edu/3084
Change subject: [NO ISSUE][API] added parse-only request parameter
......................................................................
[NO ISSUE][API] added parse-only request parameter
- user model changes: parse-only request parameter has been added
- storage format changes: no
- interface changes: no
Details:
- Support for new request parameter parse-only;
Returns parsevals as result, which has statement-parameters
as one of the key. The value of statement-parameters
is all the named parameters.
- Augmented the test framework to support parse request
- Added test cases
Change-Id: I6a6c6f64301c5826c6d67de650b63a8d29cbef10
---
M
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
M
asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
M
asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
M
asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
M
asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
M
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseOnlyResult.java
M
asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
7 files changed, 103 insertions(+), 95 deletions(-)
git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb
refs/changes/84/3084/1
diff --git
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
index 16a2105..e0af3bd 100644
---
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
+++
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceRequestParameters.java
@@ -44,6 +44,7 @@
private String planFormat;
private Map<String, JsonNode> statementParams;
private boolean expressionTree;
+ private boolean parseOnly; //don't execute; simply check for syntax
correctness and named parameters.
private boolean rewrittenExpressionTree;
private boolean logicalPlan;
private boolean optimizedLogicalPlan;
@@ -171,6 +172,14 @@
this.optimizedLogicalPlan = optimizedLogicalPlan;
}
+ public void setParseOnly(boolean parseOnly) {
+ this.parseOnly = parseOnly;
+ }
+
+ public boolean isParseOnly() {
+ return parseOnly;
+ }
+
public boolean isJob() {
return job;
}
diff --git
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
index 914c527..3f9e968 100644
---
a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
+++
b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/http/server/QueryServiceServlet.java
@@ -121,19 +121,7 @@
@Override
protected void post(IServletRequest request, IServletResponse response) {
- try {
- handleRequest(request, response);
- } catch (IOException e) {
- // Servlet methods should not throw exceptions
- // http://cwe.mitre.org/data/definitions/600.html
- GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR, e.getMessage(), e);
- } catch (Throwable th) {// NOSONAR: Logging and re-throwing
- try {
- GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR, th.getMessage(),
th);
- } catch (Throwable ignored) { // NOSONAR: Logging failure
- }
- throw th;
- }
+ handleRequest(request, response);
}
@Override
@@ -216,7 +204,7 @@
private ResultStatus resultStatus = ResultStatus.SUCCESS;
private HttpResponseStatus httpResponseStatus = HttpResponseStatus.OK;
- void setStatus(ResultStatus resultStatus, HttpResponseStatus
httpResponseStatus) {
+ public void setStatus(ResultStatus resultStatus, HttpResponseStatus
httpResponseStatus) {
this.resultStatus = resultStatus;
this.httpResponseStatus = httpResponseStatus;
}
@@ -288,29 +276,12 @@
return SessionConfig.OutputFormat.CLEAN_JSON;
}
- private static SessionOutput
createSessionOutput(QueryServiceRequestParameters param, String handleUrl,
- PrintWriter resultWriter) {
+ private static SessionOutput createSessionOutput(PrintWriter resultWriter)
{
SessionOutput.ResultDecorator resultPrefix =
ResultUtil.createPreResultDecorator();
SessionOutput.ResultDecorator resultPostfix =
ResultUtil.createPostResultDecorator();
- SessionOutput.ResultAppender appendHandle =
ResultUtil.createResultHandleAppender(handleUrl);
SessionOutput.ResultAppender appendStatus =
ResultUtil.createResultStatusAppender();
-
- SessionConfig.OutputFormat format = getFormat(param.getFormat());
- final SessionConfig.PlanFormat planFormat =
SessionConfig.PlanFormat.get(param.getPlanFormat(),
- param.getPlanFormat(), SessionConfig.PlanFormat.JSON, LOGGER);
- SessionConfig sessionConfig = new SessionConfig(format, planFormat);
- sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true);
- sessionConfig.set(SessionConfig.OOB_EXPR_TREE,
param.isExpressionTree());
- sessionConfig.set(SessionConfig.OOB_REWRITTEN_EXPR_TREE,
param.isRewrittenExpressionTree());
- sessionConfig.set(SessionConfig.OOB_LOGICAL_PLAN,
param.isLogicalPlan());
- sessionConfig.set(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN,
param.isOptimizedLogicalPlan());
- sessionConfig.set(SessionConfig.OOB_HYRACKS_JOB, param.isJob());
- sessionConfig.set(SessionConfig.FORMAT_INDENT_JSON, param.isPretty());
- sessionConfig.set(SessionConfig.FORMAT_QUOTE_RECORD,
- format != SessionConfig.OutputFormat.CLEAN_JSON && format !=
SessionConfig.OutputFormat.LOSSLESS_JSON);
- sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, format ==
SessionConfig.OutputFormat.CSV
- && "present".equals(getParameterValue(param.getFormat(),
Attribute.HEADER.str())));
- return new SessionOutput(sessionConfig, resultWriter, resultPrefix,
resultPostfix, appendHandle, appendStatus);
+ SessionConfig sessionConfig = new
SessionConfig(SessionConfig.OutputFormat.CLEAN_JSON);
+ return new SessionOutput(sessionConfig, resultWriter, resultPrefix,
resultPostfix, null, appendStatus);
}
private static void printClientContextID(PrintWriter pw,
QueryServiceRequestParameters params) {
@@ -418,64 +389,69 @@
return result;
}
- private QueryServiceRequestParameters getRequestParameters(IServletRequest
request) throws IOException {
- final String contentType = HttpUtil.getContentTypeOnly(request);
- QueryServiceRequestParameters param = new
QueryServiceRequestParameters();
+ protected void setRequestParam(IServletRequest request,
QueryServiceRequestParameters param)
+ throws IOException, AlgebricksException {
param.setHost(host(request));
param.setPath(servletPath(request));
+ String contentType = HttpUtil.getContentTypeOnly(request);
if (HttpUtil.ContentType.APPLICATION_JSON.equals(contentType)) {
try {
- JsonNode jsonRequest =
OBJECT_MAPPER.readTree(HttpUtil.getRequestBody(request));
- final String statementParam = Parameter.STATEMENT.str();
- if (jsonRequest.has(statementParam)) {
-
param.setStatement(jsonRequest.get(statementParam).asText());
- }
- param.setFormat(toLower(getOptText(jsonRequest,
Parameter.FORMAT.str())));
- param.setPretty(getOptBoolean(jsonRequest,
Parameter.PRETTY.str(), false));
- param.setMode(toLower(getOptText(jsonRequest,
Parameter.MODE.str())));
- param.setClientContextID(getOptText(jsonRequest,
Parameter.CLIENT_ID.str()));
- param.setTimeout(getOptText(jsonRequest,
Parameter.TIMEOUT.str()));
- param.setMaxResultReads(getOptText(jsonRequest,
Parameter.MAX_RESULT_READS.str()));
- param.setPlanFormat(getOptText(jsonRequest,
Parameter.PLAN_FORMAT.str()));
- param.setExpressionTree(getOptBoolean(jsonRequest,
Parameter.EXPRESSION_TREE.str(), false));
- param.setRewrittenExpressionTree(
- getOptBoolean(jsonRequest,
Parameter.REWRITTEN_EXPRESSION_TREE.str(), false));
- param.setLogicalPlan(getOptBoolean(jsonRequest,
Parameter.LOGICAL_PLAN.str(), false));
- param.setOptimizedLogicalPlan(
- getOptBoolean(jsonRequest,
Parameter.OPTIMIZED_LOGICAL_PLAN.str(), false));
- param.setParseOnly(getOptBoolean(jsonRequest,
Parameter.PARSE_ONLY.str(), false));
- param.setJob(getOptBoolean(jsonRequest, Parameter.JOB.str(),
false));
- param.setSignature(getOptBoolean(jsonRequest,
Parameter.SIGNATURE.str(), true));
- param.setStatementParams(
- getOptStatementParameters(jsonRequest,
jsonRequest.fieldNames(), JsonNode::get, v -> v));
- param.setMultiStatement(getOptBoolean(jsonRequest,
Parameter.MULTI_STATEMENT.str(), true));
+ setParamFromJSON(request, param);
} catch (JsonParseException | JsonMappingException e) {
// if the JSON parsing fails, the statement is empty and we
get an empty statement error
GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR, e.getMessage(),
e);
}
} else {
-
param.setStatement(request.getParameter(Parameter.STATEMENT.str()));
- if (param.getStatement() == null) {
- param.setStatement(HttpUtil.getRequestBody(request));
- }
-
param.setFormat(toLower(request.getParameter(Parameter.FORMAT.str())));
-
param.setPretty(Boolean.parseBoolean(request.getParameter(Parameter.PRETTY.str())));
- param.setMode(toLower(request.getParameter(Parameter.MODE.str())));
-
param.setClientContextID(request.getParameter(Parameter.CLIENT_ID.str()));
- param.setTimeout(request.getParameter(Parameter.TIMEOUT.str()));
-
param.setMaxResultReads(request.getParameter(Parameter.MAX_RESULT_READS.str()));
-
param.setPlanFormat(request.getParameter(Parameter.PLAN_FORMAT.str()));
-
param.setParseOnly(Boolean.parseBoolean(request.getParameter(Parameter.PARSE_ONLY.str())));
- final String multiStatementParam =
request.getParameter(Parameter.MULTI_STATEMENT.str());
- param.setMultiStatement(multiStatementParam == null ||
Boolean.parseBoolean(multiStatementParam));
- try {
- param.setStatementParams(getOptStatementParameters(request,
request.getParameterNames().iterator(),
- IServletRequest::getParameter,
OBJECT_MAPPER::readTree));
- } catch (JsonParseException | JsonMappingException e) {
- GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR, e.getMessage(),
e);
- }
+ setParamFromRequest(request, param);
}
- return param;
+ }
+
+ private void setParamFromJSON(IServletRequest request,
QueryServiceRequestParameters param) throws IOException {
+ JsonNode jsonRequest =
OBJECT_MAPPER.readTree(HttpUtil.getRequestBody(request));
+ param.setFormat(toLower(getOptText(jsonRequest,
Parameter.FORMAT.str())));
+ param.setPretty(getOptBoolean(jsonRequest, Parameter.PRETTY.str(),
false));
+ param.setMode(toLower(getOptText(jsonRequest, Parameter.MODE.str())));
+ param.setClientContextID(getOptText(jsonRequest,
Parameter.CLIENT_ID.str()));
+ param.setTimeout(getOptText(jsonRequest, Parameter.TIMEOUT.str()));
+ param.setMaxResultReads(getOptText(jsonRequest,
Parameter.MAX_RESULT_READS.str()));
+ param.setPlanFormat(getOptText(jsonRequest,
Parameter.PLAN_FORMAT.str()));
+ param.setExpressionTree(getOptBoolean(jsonRequest,
Parameter.EXPRESSION_TREE.str(), false));
+ param.setRewrittenExpressionTree(getOptBoolean(jsonRequest,
Parameter.REWRITTEN_EXPRESSION_TREE.str(), false));
+ param.setLogicalPlan(getOptBoolean(jsonRequest,
Parameter.LOGICAL_PLAN.str(), false));
+ param.setParseOnly(getOptBoolean(jsonRequest,
Parameter.PARSE_ONLY.str(), false));
+ param.setOptimizedLogicalPlan(getOptBoolean(jsonRequest,
Parameter.OPTIMIZED_LOGICAL_PLAN.str(), false));
+ param.setJob(getOptBoolean(jsonRequest, Parameter.JOB.str(), false));
+ param.setSignature(getOptBoolean(jsonRequest,
Parameter.SIGNATURE.str(), true));
+ param.setStatementParams(
+ getOptStatementParameters(jsonRequest,
jsonRequest.fieldNames(), JsonNode::get, v -> v));
+ param.setMultiStatement(getOptBoolean(jsonRequest,
Parameter.MULTI_STATEMENT.str(), true));
+ String statementParam = Parameter.STATEMENT.str();
+ if (jsonRequest.has(statementParam)) {
+ param.setStatement(jsonRequest.get(statementParam).asText());
+ }
+ }
+
+ private void setParamFromRequest(IServletRequest request,
QueryServiceRequestParameters param) throws IOException {
+ param.setStatement(request.getParameter(Parameter.STATEMENT.str()));
+ if (param.getStatement() == null) {
+ param.setStatement(HttpUtil.getRequestBody(request));
+ }
+ param.setFormat(toLower(request.getParameter(Parameter.FORMAT.str())));
+
param.setPretty(Boolean.parseBoolean(request.getParameter(Parameter.PRETTY.str())));
+ param.setMode(toLower(request.getParameter(Parameter.MODE.str())));
+
param.setClientContextID(request.getParameter(Parameter.CLIENT_ID.str()));
+ param.setTimeout(request.getParameter(Parameter.TIMEOUT.str()));
+
param.setMaxResultReads(request.getParameter(Parameter.MAX_RESULT_READS.str()));
+ param.setPlanFormat(request.getParameter(Parameter.PLAN_FORMAT.str()));
+
param.setParseOnly(Boolean.parseBoolean(request.getParameter(Parameter.PARSE_ONLY.str())));
+ final String multiStatementParam =
request.getParameter(Parameter.MULTI_STATEMENT.str());
+ param.setMultiStatement(multiStatementParam == null ||
Boolean.parseBoolean(multiStatementParam));
+ try {
+ param.setStatementParams(getOptStatementParameters(request,
request.getParameterNames().iterator(),
+ IServletRequest::getParameter, OBJECT_MAPPER::readTree));
+ } catch (JsonParseException | JsonMappingException e) {
+ GlobalConfig.ASTERIX_LOGGER.log(Level.ERROR, e.getMessage(), e);
+ }
}
private static ResultDelivery parseResultDelivery(String mode) {
@@ -517,9 +493,7 @@
return "http://" + host + path + handlePath(delivery);
}
-
-
- private void handleRequest(IServletRequest request, IServletResponse
response) throws IOException {
+ private void handleRequest(IServletRequest request, IServletResponse
response) {
long elapsedStart = System.nanoTime();
long errorCount = 1;
Stats stats = new Stats();
@@ -573,8 +547,8 @@
}
response.setHeader("Access-Control-Allow-Headers", "Origin,
X-Requested-With, Content-Type, Accept");
response.setStatus(execution.getHttpStatus());
- executeStatement(statementsText, sessionOutput,
resultProperties, stats, param, execution, optionalParams,
- statementParams);
+ executeStatement(statementsText, sessionOutput,
resultProperties, stats, param, execution,
+ optionalParams, statementParams);
if (ResultDelivery.IMMEDIATE == delivery ||
ResultDelivery.DEFERRED == delivery) {
ResultUtil.printStatus(sessionOutput,
execution.getResultStatus());
}
@@ -594,7 +568,7 @@
execution.finish();
}
printMetrics(sessionOutput.out(), System.nanoTime() - elapsedStart,
execution.duration(), stats.getCount(),
- stats.getSize(), stats.getProcessedObjects(), errorCount,
warnings.size());
+ stats.getSize(), stats.getProcessedObjects(), errorCount,
warnings.size());
sessionOutput.out().print("}\n");
sessionOutput.out().flush();
if (sessionOutput.out().checkError()) {
@@ -679,10 +653,37 @@
}
}
+ private void setSessionConfig(SessionOutput sessionOutput,
QueryServiceRequestParameters param,
+ ResultDelivery delivery) {
+ String handleUrl = getHandleUrl(param.getHost(), param.getPath(),
delivery);
+
sessionOutput.setHandleAppender(ResultUtil.createResultHandleAppender(handleUrl));
+ SessionConfig sessionConfig = sessionOutput.config();
+ SessionConfig.OutputFormat format = getFormat(param.getFormat());
+ SessionConfig.PlanFormat planFormat =
SessionConfig.PlanFormat.get(param.getPlanFormat(), param.getPlanFormat(),
+ SessionConfig.PlanFormat.JSON, LOGGER);
+ sessionConfig.setFmt(format);
+ sessionConfig.setPlanFormat(planFormat);
+ sessionConfig.set(SessionConfig.FORMAT_WRAPPER_ARRAY, true);
+ sessionConfig.set(SessionConfig.OOB_EXPR_TREE,
param.isExpressionTree());
+ sessionConfig.set(SessionConfig.OOB_REWRITTEN_EXPR_TREE,
param.isRewrittenExpressionTree());
+ sessionConfig.set(SessionConfig.OOB_LOGICAL_PLAN,
param.isLogicalPlan());
+ sessionConfig.set(SessionConfig.OOB_OPTIMIZED_LOGICAL_PLAN,
param.isOptimizedLogicalPlan());
+ sessionConfig.set(SessionConfig.OOB_HYRACKS_JOB, param.isJob());
+ sessionConfig.set(SessionConfig.FORMAT_INDENT_JSON, param.isPretty());
+ sessionConfig.set(SessionConfig.FORMAT_QUOTE_RECORD,
+ format != SessionConfig.OutputFormat.CLEAN_JSON && format !=
SessionConfig.OutputFormat.LOSSLESS_JSON);
+ sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, format ==
SessionConfig.OutputFormat.CSV
+ && "present".equals(getParameterValue(param.getFormat(),
Attribute.HEADER.str())));
+ }
+
protected void printError(PrintWriter sessionOut, Throwable throwable) {
ResultUtil.printError(sessionOut, throwable);
}
+ protected void printAdditionalResultFields(PrintWriter sessionOut) {
+ // do nothing
+ }
+
protected void printWarnings(PrintWriter pw, List<ExecutionWarning>
warnings) {
ResultUtil.printWarnings(pw, warnings);
}
diff --git
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
index 1ecaa1a..66eb265 100644
---
a/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
+++
b/asterixdb/asterix-app/src/test/java/org/apache/asterix/test/common/TestExecutor.java
@@ -1504,7 +1504,7 @@
String name = m.group("name");
param.setName(name);
-/* if (name.equals("parse")) {
+ /* if (name.equals("parse")) {
String newname = "parse-only";
param.setName(newname);
} else {
diff --git
a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
index 08c8d60..e98f991 100644
---
a/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
+++
b/asterixdb/asterix-lang-aql/src/main/java/org/apache/asterix/lang/aql/rewrites/AqlQueryRewriter.java
@@ -150,7 +150,6 @@
throw new UnsupportedOperationException("getExternalVariables not
implemented for AQL");
}
-
private static class GatherFunctionCalls extends
GatherFunctionCallsVisitor implements IAQLVisitor<Void, Void> {
public GatherFunctionCalls() {
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
index 852c17a..304814c 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/base/IQueryRewriter.java
@@ -55,6 +55,7 @@
Set<CallExpr> getFunctionCalls(Expression expression) throws
CompilationException;
Set<VariableExpr> getFreeVariables(Expression expr) throws
CompilationException;
+
Set<VariableExpr> getExternalVariables(Expression expr) throws
CompilationException;
}
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseOnlyResult.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseOnlyResult.java
index 9c0060e..3c41269 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseOnlyResult.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/parser/ParseOnlyResult.java
@@ -42,7 +42,7 @@
for (VariableExpr extVarRef : externalVariables) {
extVarsSerialized +=
SqlppVariableUtil.toUserDefinedName(extVarRef.getVar().getValue()) + ",";
}
- extVarsSerialized = extVarsSerialized.replaceAll(",$","]");
+ extVarsSerialized = extVarsSerialized.replaceAll(",$", "]");
return extVarsSerialized;
}
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
index b2d839e..171ba3f 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/SqlppQueryRewriter.java
@@ -268,10 +268,8 @@
public Set<VariableExpr> getExternalVariables(Expression expr) throws
CompilationException {
Set<VariableExpr> freeVars = getFreeVariables(expr);
Set<VariableExpr> extVars = new HashSet<>();
- for(VariableExpr ve : freeVars)
- {
- if(SqlppVariableUtil.isExternalVariableReference(ve))
- {
+ for (VariableExpr ve : freeVars) {
+ if (SqlppVariableUtil.isExternalVariableReference(ve)) {
extVars.add(ve);
}
}
--
To view, visit https://asterix-gerrit.ics.uci.edu/3084
To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I6a6c6f64301c5826c6d67de650b63a8d29cbef10
Gerrit-PatchSet: 1
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Owner: [email protected]