Dmitry Lychagin has submitted this change and it was merged. ( https://asterix-gerrit.ics.uci.edu/3428 )
Change subject: [NO ISSUE][SQLPP] Support SELECT LET ...................................................................... [NO ISSUE][SQLPP] Support SELECT LET - user model changes: yes - storage format changes: no - interface changes: no Details: - Support SELECT ... LET ... (no FROM clause) - Add CancelUnnestSingletonListRule to the optimizer - Add negative testcase for unnamed field value expressions in object constructor Change-Id: I1226dca83e756075608232c642851f646a9bee3b Reviewed-on: https://asterix-gerrit.ics.uci.edu/3428 Sonar-Qube: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Reviewed-by: Dmitry Lychagin <dmitry.lycha...@couchbase.com> --- M asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java A asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/CancelUnnestSingletonListRule.java A asterixdb/asterix-app/src/test/resources/optimizerts/queries/flwr/select-let-1.sqlpp A asterixdb/asterix-app/src/test/resources/optimizerts/results/flwr/select-let-1.plan M asterixdb/asterix-app/src/test/resources/optimizerts/results/subquery/query-ASTERIXDB-1572.plan A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/flwor/select-let/select-let.1.query.sqlpp M asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml A asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/no_fieldname_constr_negative/no_fieldname_constr_negative.2.query.sqlpp A asterixdb/asterix-app/src/test/resources/runtimets/results/flwor/select-let/select-let.1.adm M asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml M asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj 11 files changed, 256 insertions(+), 70 deletions(-) Approvals: Jenkins: Verified; No violations found; Verified Anon. E. Moose (1000171): Dmitry Lychagin: Looks good to me, approved diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java index f9aecab..d5f2e00 100644 --- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java +++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/base/RuleCollections.java @@ -32,6 +32,7 @@ import org.apache.asterix.optimizer.rules.AsterixIntroduceGroupByCombinerRule; import org.apache.asterix.optimizer.rules.AsterixPushAssignBelowUnionAllRule; import org.apache.asterix.optimizer.rules.ByNameToByIndexFieldAccessRule; +import org.apache.asterix.optimizer.rules.CancelUnnestSingletonListRule; import org.apache.asterix.optimizer.rules.CancelUnnestWithNestedListifyRule; import org.apache.asterix.optimizer.rules.CheckFilterExpressionTypeRule; import org.apache.asterix.optimizer.rules.CheckFullParallelSortRule; @@ -203,6 +204,7 @@ normalization.add(new PushAggFuncIntoStandaloneAggregateRule()); normalization.add(new ListifyUnnestingFunctionRule()); normalization.add(new RemoveRedundantSelectRule()); + normalization.add(new CancelUnnestSingletonListRule()); normalization.add(new UnnestToDataScanRule()); normalization.add(new MetaFunctionToMetaVariableRule()); normalization.add(new FuzzyEqRule()); diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/CancelUnnestSingletonListRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/CancelUnnestSingletonListRule.java new file mode 100644 index 0000000..ab7abdf --- /dev/null +++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/CancelUnnestSingletonListRule.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.asterix.optimizer.rules; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.asterix.om.base.IACollection; +import org.apache.asterix.om.base.IACursor; +import org.apache.asterix.om.base.IAObject; +import org.apache.asterix.om.constants.AsterixConstantValue; +import org.apache.asterix.om.functions.BuiltinFunctions; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; +import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression; +import org.apache.hyracks.algebricks.core.algebra.base.ILogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.base.IOptimizationContext; +import org.apache.hyracks.algebricks.core.algebra.base.LogicalExpressionTag; +import org.apache.hyracks.algebricks.core.algebra.base.LogicalOperatorTag; +import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable; +import org.apache.hyracks.algebricks.core.algebra.expressions.AbstractFunctionCallExpression; +import org.apache.hyracks.algebricks.core.algebra.expressions.ConstantExpression; +import org.apache.hyracks.algebricks.core.algebra.expressions.IAlgebricksConstantValue; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.AssignOperator; +import org.apache.hyracks.algebricks.core.algebra.operators.logical.UnnestOperator; +import org.apache.hyracks.algebricks.core.rewriter.base.IAlgebraicRewriteRule; + +/** + * This rule removes UNNEST of a constant singleton list and replaces it with ASSIGN as follows: + * Before plan: + * <ul> + * <li>unnest $x <- scan-collection( [ constant ] ) (or {{ constant }}) + * </ul> + * <p> + * After plan: + * <ul> + * <li>assign $x <- constant + * </ul> + * + * This rule must run after {@link ConstantFoldingRule} + */ +public final class CancelUnnestSingletonListRule implements IAlgebraicRewriteRule { + @Override + public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) + throws AlgebricksException { + AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue(); + if (op.getOperatorTag() != LogicalOperatorTag.UNNEST) { + return false; + } + UnnestOperator unnest = (UnnestOperator) op; + if (unnest.getPositionalVariable() != null) { + return false; + } + ILogicalExpression expr = unnest.getExpressionRef().getValue(); + if (expr.getExpressionTag() != LogicalExpressionTag.FUNCTION_CALL) { + return false; + } + if (((AbstractFunctionCallExpression) expr).getFunctionIdentifier() != BuiltinFunctions.SCAN_COLLECTION) { + return false; + } + AbstractFunctionCallExpression callExpr = (AbstractFunctionCallExpression) expr; + ILogicalExpression argExpr = callExpr.getArguments().get(0).getValue(); + if (argExpr.getExpressionTag() != LogicalExpressionTag.CONSTANT) { + return false; + } + ConstantExpression cExpr = (ConstantExpression) argExpr; + IAlgebricksConstantValue cValue = cExpr.getValue(); + if (!(cValue instanceof AsterixConstantValue)) { + return false; + } + AsterixConstantValue aValue = (AsterixConstantValue) cValue; + IAObject value = aValue.getObject(); + if (!value.getType().getTypeTag().isListType()) { + return false; + } + IACollection list = (IACollection) value; + if (list.size() != 1) { + return false; + } + IACursor cur = list.getCursor(); + if (!cur.next()) { + return false; + } + IAObject item = cur.get(); + + List<LogicalVariable> assignVars = new ArrayList<>(1); + assignVars.add(unnest.getVariable()); + List<Mutable<ILogicalExpression>> assignExprs = new ArrayList<>(1); + ConstantExpression itemExpr = new ConstantExpression(new AsterixConstantValue(item)); + itemExpr.setSourceLocation(cExpr.getSourceLocation()); + assignExprs.add(new MutableObject<>(itemExpr)); + AssignOperator assignOp = new AssignOperator(assignVars, assignExprs); + assignOp.setSourceLocation(op.getSourceLocation()); + assignOp.getInputs().addAll(op.getInputs()); + + context.computeAndSetTypeEnvironmentForOperator(assignOp); + opRef.setValue(assignOp); + return true; + } + + @Override + public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) { + return false; + } +} diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/queries/flwr/select-let-1.sqlpp b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/flwr/select-let-1.sqlpp new file mode 100644 index 0000000..0d45eee --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/optimizerts/queries/flwr/select-let-1.sqlpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * Description: Test SELECT ... LET ... (no FROM clause) + */ + +select value x +let x = 2; \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/flwr/select-let-1.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/flwr/select-let-1.plan new file mode 100644 index 0000000..de331ad --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/flwr/select-let-1.plan @@ -0,0 +1,4 @@ +-- DISTRIBUTE_RESULT |UNPARTITIONED| + -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED| + -- ASSIGN |UNPARTITIONED| + -- EMPTY_TUPLE_SOURCE |UNPARTITIONED| \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/optimizerts/results/subquery/query-ASTERIXDB-1572.plan b/asterixdb/asterix-app/src/test/resources/optimizerts/results/subquery/query-ASTERIXDB-1572.plan index 1b0be9d..c6ed7f9 100644 --- a/asterixdb/asterix-app/src/test/resources/optimizerts/results/subquery/query-ASTERIXDB-1572.plan +++ b/asterixdb/asterix-app/src/test/resources/optimizerts/results/subquery/query-ASTERIXDB-1572.plan @@ -4,73 +4,26 @@ -- ASSIGN |PARTITIONED| -- STREAM_PROJECT |PARTITIONED| -- STREAM_SELECT |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- PRE_CLUSTERED_GROUP_BY[$$64] |PARTITIONED| + -- SUBPLAN |PARTITIONED| + { + -- AGGREGATE |LOCAL| + -- STREAM_SELECT |LOCAL| + -- NESTED_TUPLE_SOURCE |LOCAL| + } + -- SUBPLAN |PARTITIONED| { -- AGGREGATE |LOCAL| -- STREAM_SELECT |LOCAL| -- NESTED_TUPLE_SOURCE |LOCAL| } - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STABLE_SORT [$$64(ASC)] |PARTITIONED| + -- SUBPLAN |PARTITIONED| + { + -- AGGREGATE |LOCAL| + -- STREAM_SELECT |LOCAL| + -- NESTED_TUPLE_SOURCE |LOCAL| + } + -- STREAM_PROJECT |PARTITIONED| -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STREAM_PROJECT |PARTITIONED| + -- DATASOURCE_SCAN |PARTITIONED| -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- HYBRID_HASH_JOIN [$$64][$#4] |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- PRE_CLUSTERED_GROUP_BY[$$62] |PARTITIONED| - { - -- AGGREGATE |LOCAL| - -- STREAM_SELECT |LOCAL| - -- NESTED_TUPLE_SOURCE |LOCAL| - } - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STREAM_PROJECT |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- HYBRID_HASH_JOIN [$$62][$#3] |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- PRE_CLUSTERED_GROUP_BY[$$60] |PARTITIONED| - { - -- AGGREGATE |LOCAL| - -- STREAM_SELECT |LOCAL| - -- NESTED_TUPLE_SOURCE |LOCAL| - } - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STABLE_SORT [$$60(ASC)] |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STREAM_PROJECT |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- HYBRID_HASH_JOIN [$$60][$#2] |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- PRE_CLUSTERED_GROUP_BY[$$53] |PARTITIONED| - { - -- AGGREGATE |LOCAL| - -- STREAM_SELECT |LOCAL| - -- NESTED_TUPLE_SOURCE |LOCAL| - } - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STREAM_PROJECT |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- HYBRID_HASH_JOIN [$$53][$#1] |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- STREAM_PROJECT |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- DATASOURCE_SCAN |PARTITIONED| - -- ONE_TO_ONE_EXCHANGE |PARTITIONED| - -- EMPTY_TUPLE_SOURCE |PARTITIONED| - -- HASH_PARTITION_EXCHANGE [$#1] |PARTITIONED| - -- ASSIGN |UNPARTITIONED| - -- UNNEST |UNPARTITIONED| - -- EMPTY_TUPLE_SOURCE |UNPARTITIONED| - -- HASH_PARTITION_EXCHANGE [$#2] |PARTITIONED| - -- ASSIGN |UNPARTITIONED| - -- UNNEST |UNPARTITIONED| - -- EMPTY_TUPLE_SOURCE |UNPARTITIONED| - -- HASH_PARTITION_EXCHANGE [$#3] |PARTITIONED| - -- ASSIGN |UNPARTITIONED| - -- UNNEST |UNPARTITIONED| - -- EMPTY_TUPLE_SOURCE |UNPARTITIONED| - -- HASH_PARTITION_EXCHANGE [$#4] |PARTITIONED| - -- ASSIGN |UNPARTITIONED| - -- UNNEST |UNPARTITIONED| - -- EMPTY_TUPLE_SOURCE |UNPARTITIONED| + -- EMPTY_TUPLE_SOURCE |PARTITIONED| \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/flwor/select-let/select-let.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/flwor/select-let/select-let.1.query.sqlpp new file mode 100644 index 0000000..9462429 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/flwor/select-let/select-let.1.query.sqlpp @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * Description: Test SELECT ... LET ... (no FROM clause) + */ + +select value x +let x = 2 \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml index e0114fc..150a37f 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/ObjectsQueries.xml @@ -111,6 +111,7 @@ <compilation-unit name="no_fieldname_constr_negative"> <output-dir compare="Text">no_fieldname_constr</output-dir> <expected-error>ASX1001: Syntax error: Cannot infer field name</expected-error> + <expected-error>ASX0013: Duplicate field name "a"</expected-error> </compilation-unit> </test-case> <test-case FilePath="objects"> diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/no_fieldname_constr_negative/no_fieldname_constr_negative.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/no_fieldname_constr_negative/no_fieldname_constr_negative.2.query.sqlpp new file mode 100644 index 0000000..c20cc84 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/objects/no_fieldname_constr_negative/no_fieldname_constr_negative.2.query.sqlpp @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +/* + * Description : Testing object constructor without field names + * Expected Res : Failure: Duplicate field name + */ + +from ( + from range(1, 2) x + select value { "a": x, "b": { "a": x + 1 } } +) x +select value { x.a, x.b.a } diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/flwor/select-let/select-let.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/flwor/select-let/select-let.1.adm new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/flwor/select-let/select-let.1.adm @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml index 2a1dbd3..661e54e 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -119,6 +119,11 @@ <expected-error>ASX0013: Duplicate field name "g" (in line 27, at column 11)</expected-error> </compilation-unit> </test-case> + <test-case FilePath="flwor"> + <compilation-unit name="select-let"> + <output-dir compare="Text">select-let</output-dir> + </compilation-unit> + </test-case> </test-group> <test-group name="sorting"> <test-case FilePath="sorting"> diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj index 73612ab..73c4137 100644 --- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj +++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj @@ -3114,18 +3114,36 @@ ( selectClause = SelectClause() { startSrcLoc = selectClause.getSourceLocation(); } ( - LOOKAHEAD(1) - fromClause = FromClause() ( - LOOKAHEAD(1) - fromLetClauses = LetClause() - )? + fromClause = FromClause() + ( + fromLetClauses = LetClause() + )? + ) + | + ( + fromLetClauses = LetClause() + { + // LET without FROM -> create dummy FROM clause: FROM {{missing}} AS #0 + SourceLocation sourceLoc = getSourceLocation(token); + LiteralExpr missingExpr = new LiteralExpr(MissingLiteral.INSTANCE); + missingExpr.setSourceLocation(sourceLoc); + List<Expression> list = new ArrayList<Expression>(1); + list.add(missingExpr); + ListConstructor listExpr = new ListConstructor(ListConstructor.Type.ORDERED_LIST_CONSTRUCTOR, list); + listExpr.setSourceLocation(sourceLoc); + List<FromTerm> fromTerms = new ArrayList<FromTerm>(1); + VariableExpr fromVar = new VariableExpr(new VarIdentifier("#0")); + fromVar.setSourceLocation(sourceLoc); + fromTerms.add(new FromTerm(listExpr, fromVar, null, new ArrayList<AbstractBinaryCorrelateClause>())); + fromClause = new FromClause(fromTerms); + } + ) )? (whereClause = WhereClause())? ( groupbyClause = GroupbyClause() ( - LOOKAHEAD(1) gbyLetClauses = LetClause() )? (havingClause = HavingClause())? @@ -3133,7 +3151,6 @@ | fromClause = FromClause() { startSrcLoc = fromClause.getSourceLocation(); } ( - LOOKAHEAD(1) fromLetClauses = LetClause() )? (whereClause = WhereClause())? -- To view, visit https://asterix-gerrit.ics.uci.edu/3428 To unsubscribe, visit https://asterix-gerrit.ics.uci.edu/settings Gerrit-Project: asterixdb Gerrit-Branch: master Gerrit-MessageType: merged Gerrit-Change-Id: I1226dca83e756075608232c642851f646a9bee3b Gerrit-Change-Number: 3428 Gerrit-PatchSet: 4 Gerrit-Owner: Dmitry Lychagin <dmitry.lycha...@couchbase.com> Gerrit-Reviewer: Ali Alsuliman <ali.al.solai...@gmail.com> Gerrit-Reviewer: Anon. E. Moose (1000171) Gerrit-Reviewer: Dmitry Lychagin <dmitry.lycha...@couchbase.com> Gerrit-Reviewer: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Gerrit-Reviewer: Till Westmann <ti...@apache.org>