This is an automated email from the ASF dual-hosted git repository.
htowaileb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push:
new 761c9a6 [NO ISSUE][COMP] Support OFFSET without LIMIT
761c9a6 is described below
commit 761c9a6b38b7f176648ffa0e348d5cd92c30037b
Author: Dmitry Lychagin <[email protected]>
AuthorDate: Thu Nov 19 10:22:11 2020 -0800
[NO ISSUE][COMP] Support OFFSET without LIMIT
- user model changes: yes
- storage format changes: no
- interface changes: no
Details:
- Add support for a standalone OFFSET clause
(without LIMIT clause)
- Add testcases and update documentation
Change-Id: I1c8b968fcc8beaa1028b8370610ff490e391d6f9
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/8963
Integration-Tests: Jenkins <[email protected]>
Tested-by: Jenkins <[email protected]>
Reviewed-by: Dmitry Lychagin <[email protected]>
Reviewed-by: Ali Alsuliman <[email protected]>
---
.../optimizer/rules/PushLimitIntoOrderByRule.java | 8 +-
.../translator/LangExpressionToPlanTranslator.java | 38 ++---
.../limit_negative_value.1.ddl.sqlpp | 2 +-
.../offset_without_limit.1.ddl.sqlpp} | 5 +-
.../offset_without_limit.2.update.sqlpp} | 20 +--
.../offset_without_limit.3.query.sqlpp} | 22 +--
.../offset_without_limit.4.query.sqlpp} | 22 +--
.../offset_without_limit.5.query.sqlpp} | 21 +--
.../offset_without_limit.6.query.sqlpp} | 21 +--
.../offset_without_limit.3.adm | 2 +
.../offset_without_limit.4.adm | 1 +
.../offset_without_limit.5.adm | 2 +
.../offset_without_limit.6.adm | 18 ++
.../push-limit-to-primary-lookup-select.3.adm | 2 +-
.../push-limit-to-primary-lookup.3.adm | 2 +-
.../push-limit-to-primary-lookup.5.adm | 2 +-
.../push-limit-to-primary-scan-select.3.adm | 2 +-
.../push-limit-to-primary-scan.3.adm | 2 +-
.../push-limit-to-primary-scan.5.adm | 2 +-
.../push-limit-to-primary-scan.8.adm | 2 +-
.../test/resources/runtimets/testsuite_sqlpp.xml | 5 +
asterixdb/asterix-doc/src/main/grammar/sqlpp.ebnf | 6 +-
.../asterix-doc/src/main/markdown/sqlpp/0_toc.md | 2 +-
.../asterix-doc/src/main/markdown/sqlpp/3_query.md | 186 +++++++++++----------
.../asterix/lang/common/clause/LimitClause.java | 19 ++-
.../common/visitor/AbstractInlineUdfsVisitor.java | 13 +-
.../CloneAndSubstituteVariablesVisitor.java | 18 +-
.../lang/common/visitor/FormatPrintVisitor.java | 15 +-
.../common/visitor/GatherFunctionCallsVisitor.java | 6 +-
.../lang/common/visitor/QueryPrintVisitor.java | 17 +-
.../lang/sqlpp/visitor/DeepCopyVisitor.java | 3 +-
.../AbstractSqlppExpressionScopingVisitor.java | 4 +-
.../base/AbstractSqlppSimpleExpressionVisitor.java | 4 +-
.../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj | 21 ++-
.../algebra/operators/logical/LimitOperator.java | 42 +++--
.../visitors/IsomorphismOperatorVisitor.java | 6 +-
.../visitors/SubstituteVariableVisitor.java | 9 +-
.../logical/visitors/UsedVariableVisitor.java | 9 +-
.../operators/physical/StreamLimitPOperator.java | 10 +-
.../LogicalOperatorPrettyPrintVisitor.java | 10 +-
.../LogicalOperatorPrettyPrintVisitorJson.java | 19 ++-
.../core/utils/LogicalOperatorDotVisitor.java | 10 +-
.../rewriter/rules/CopyLimitDownRule.java | 4 +-
.../operators/std/StreamLimitRuntimeFactory.java | 49 +++---
44 files changed, 361 insertions(+), 322 deletions(-)
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoOrderByRule.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoOrderByRule.java
index 51e536a..62ea303 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoOrderByRule.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoOrderByRule.java
@@ -116,6 +116,10 @@ public class PushLimitIntoOrderByRule implements
IAlgebraicRewriteRule {
}
static Integer getOutputLimit(LimitOperator limitOp) {
+ if (!limitOp.hasMaxObjects()) {
+ // No limit
+ return null;
+ }
// Currently, we support LIMIT with a constant value.
ILogicalExpression maxObjectsExpr = limitOp.getMaxObjects().getValue();
IAObject maxObjectsValue =
ConstantExpressionUtil.getConstantIaObject(maxObjectsExpr, ATypeTag.INTEGER);
@@ -130,8 +134,8 @@ public class PushLimitIntoOrderByRule implements
IAlgebraicRewriteRule {
// Get the offset constant if there is one. If one presents, then topK
= topK + offset.
// This is because we can't apply offset to the external sort.
// Final topK will be applied through LIMIT.
- ILogicalExpression offsetExpr = limitOp.getOffset().getValue();
- if (offsetExpr != null) {
+ if (limitOp.hasOffset()) {
+ ILogicalExpression offsetExpr = limitOp.getOffset().getValue();
IAObject offsetValue =
ConstantExpressionUtil.getConstantIaObject(offsetExpr, ATypeTag.INTEGER);
if (offsetValue == null) {
return null;
diff --git
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
index 93cb403..6759f1c 100644
---
a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
+++
b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/translator/LangExpressionToPlanTranslator.java
@@ -1469,26 +1469,26 @@ abstract class LangExpressionToPlanTranslator
@Override
public Pair<ILogicalOperator, LogicalVariable> visit(LimitClause lc,
Mutable<ILogicalOperator> tupSource)
throws CompilationException {
- SourceLocation sourceLoc = lc.getSourceLocation();
- LimitOperator opLim;
-
- Pair<ILogicalExpression, Mutable<ILogicalOperator>> p1 =
langExprToAlgExpression(lc.getLimitExpr(), tupSource);
- ILogicalExpression maxObjectsExpr =
- createLimitOffsetValueExpression(p1.first,
lc.getLimitExpr().getSourceLocation());
- Expression offset = lc.getOffset();
- if (offset != null) {
- Pair<ILogicalExpression, Mutable<ILogicalOperator>> p2 =
langExprToAlgExpression(offset, p1.second);
- ILogicalExpression offsetExpr =
- createLimitOffsetValueExpression(p2.first,
lc.getOffset().getSourceLocation());
- opLim = new LimitOperator(maxObjectsExpr, offsetExpr);
- opLim.getInputs().add(p2.second);
- opLim.setSourceLocation(sourceLoc);
- } else {
- opLim = new LimitOperator(maxObjectsExpr);
- opLim.getInputs().add(p1.second);
- opLim.setSourceLocation(sourceLoc);
+ Mutable<ILogicalOperator> topOp = tupSource;
+ ILogicalExpression maxObjectsExpr = null;
+ if (lc.hasLimitExpr()) {
+ Pair<ILogicalExpression, Mutable<ILogicalOperator>> p1 =
langExprToAlgExpression(lc.getLimitExpr(), topOp);
+ // if user did provide the limit expression and it is NULL or
MISSING then it'll be coerced to 0
+ maxObjectsExpr = createLimitOffsetValueExpression(p1.first,
lc.getLimitExpr().getSourceLocation());
+ topOp = p1.second;
}
- return new Pair<>(opLim, null);
+ ILogicalExpression offsetExpr = null;
+ if (lc.hasOffset()) {
+ Pair<ILogicalExpression, Mutable<ILogicalOperator>> p2 =
langExprToAlgExpression(lc.getOffset(), topOp);
+ offsetExpr = createLimitOffsetValueExpression(p2.first,
lc.getOffset().getSourceLocation());
+ topOp = p2.second;
+ }
+
+ LimitOperator limitOp = new LimitOperator(maxObjectsExpr, offsetExpr);
+ limitOp.getInputs().add(topOp);
+ limitOp.setSourceLocation(lc.getSourceLocation());
+
+ return new Pair<>(limitOp, null);
}
private ILogicalExpression
createLimitOffsetValueExpression(ILogicalExpression inputExpr, SourceLocation
sourceLoc)
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
index 33a9c58..527aadd 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
@@ -17,7 +17,7 @@
* under the License.
*/
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test negative limit and offset values
* Expected Result : Success
*/
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.1.ddl.sqlpp
similarity index 89%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.1.ddl.sqlpp
index 33a9c58..2478dde 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.1.ddl.sqlpp
@@ -17,7 +17,7 @@
* under the License.
*/
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test offset clause without limit clause
* Expected Result : Success
*/
@@ -35,5 +35,4 @@ create type test.DBLPType as
misc : string
};
-create dataset DBLP1(DBLPType) primary key id;
-
+create dataset DBLP1(DBLPType) primary key id;
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.2.update.sqlpp
similarity index 70%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.2.update.sqlpp
index 33a9c58..06ceb9a 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.2.update.sqlpp
@@ -16,24 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-/*
- * Description : Test push down limit into the primary index scan operator
- * Expected Result : Success
- */
-
-drop dataverse test if exists;
-create dataverse test;
-
use test;
-create type test.DBLPType as
-{
- id : bigint,
- dblpid : string,
- title : string,
- authors : string,
- misc : string
-};
-
-create dataset DBLP1(DBLPType) primary key id;
-
+load dataset DBLP1 using localfs
((`path`=`asterix_nc1://data/dblp-small/dblp-small-id.txt`),(`format`=`delimited-text`),(`delimiter`=`:`));
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.3.query.sqlpp
similarity index 72%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.3.query.sqlpp
index 33a9c58..ab2af72 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.3.query.sqlpp
@@ -17,23 +17,11 @@
* under the License.
*/
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test offset clause without limit clause (with order by)
* Expected Result : Success
*/
-drop dataverse test if exists;
-create dataverse test;
-
-use test;
-
-create type test.DBLPType as
-{
- id : bigint,
- dblpid : string,
- title : string,
- authors : string,
- misc : string
-};
-
-create dataset DBLP1(DBLPType) primary key id;
-
+select value t
+from [6,5,4,3,2,1] t
+order by t
+offset 4
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.4.query.sqlpp
similarity index 73%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.4.query.sqlpp
index 33a9c58..2088016 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.4.query.sqlpp
@@ -16,24 +16,16 @@
* specific language governing permissions and limitations
* under the License.
*/
+
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test offset clause without limit clause (without order by)
* Expected Result : Success
*/
-drop dataverse test if exists;
-create dataverse test;
-
use test;
-create type test.DBLPType as
-{
- id : bigint,
- dblpid : string,
- title : string,
- authors : string,
- misc : string
-};
-
-create dataset DBLP1(DBLPType) primary key id;
-
+array_sum((
+ select value t
+ from [2,2,2,2,2,2] t
+ offset 4
+))
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.5.query.sqlpp
similarity index 73%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.5.query.sqlpp
index 33a9c58..d32190a 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.5.query.sqlpp
@@ -16,24 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
+
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test offset clause without limit clause (with order by)
* Expected Result : Success
*/
-drop dataverse test if exists;
-create dataverse test;
-
use test;
-create type test.DBLPType as
-{
- id : bigint,
- dblpid : string,
- title : string,
- authors : string,
- misc : string
-};
-
-create dataset DBLP1(DBLPType) primary key id;
-
+select id, dblpid
+from DBLP1 as paper
+order by id
+offset 98
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.6.query.sqlpp
similarity index 73%
copy from
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
copy to
asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.6.query.sqlpp
index 33a9c58..8501d99 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_negative_value/limit_negative_value.1.ddl.sqlpp
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/offset_without_limit/offset_without_limit.6.query.sqlpp
@@ -16,24 +16,17 @@
* specific language governing permissions and limitations
* under the License.
*/
+
/*
- * Description : Test push down limit into the primary index scan operator
+ * Description : Test that offset without limit is NOT pushed into a
primary scan
* Expected Result : Success
*/
-drop dataverse test if exists;
-create dataverse test;
-
use test;
-create type test.DBLPType as
-{
- id : bigint,
- dblpid : string,
- title : string,
- authors : string,
- misc : string
-};
-
-create dataset DBLP1(DBLPType) primary key id;
+explain
+select id, dblpid
+from DBLP1 as paper
+order by id
+offset 98
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.3.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.3.adm
new file mode 100644
index 0000000..1f7a723
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.3.adm
@@ -0,0 +1,2 @@
+5
+6
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.4.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.4.adm
new file mode 100644
index 0000000..bf0d87a
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.4.adm
@@ -0,0 +1 @@
+4
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.5.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.5.adm
new file mode 100644
index 0000000..6687400
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.5.adm
@@ -0,0 +1,2 @@
+{ "id": 99, "dblpid": "series/synthesis/2009Weintraub" }
+{ "id": 100, "dblpid": "series/synthesis/2009Brozos" }
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.6.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.6.adm
new file mode 100644
index 0000000..726ee49
--- /dev/null
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/offset_without_limit/offset_without_limit.6.adm
@@ -0,0 +1,18 @@
+distribute result [$$15]
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ limit offset 98
+ -- STREAM_LIMIT |UNPARTITIONED|
+ project ([$$15])
+ -- STREAM_PROJECT |PARTITIONED|
+ assign [$$15] <- [{"id": $$17, "dblpid": $$paper.getField(1)}]
+ -- ASSIGN |PARTITIONED|
+ exchange
+ -- SORT_MERGE_EXCHANGE [$$17(ASC) ] |PARTITIONED|
+ data-scan []<-[$$17, $$paper] <- test.DBLP1
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |PARTITIONED|
\ No newline at end of file
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
index 21618d0..3543f5d 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup-select/push-limit-to-primary-lookup-select.3.adm
@@ -2,7 +2,7 @@ distribute result [$$c]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$c])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
index d070b2b..a0a3c84 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.3.adm
@@ -2,7 +2,7 @@ distribute result [$$c]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$c])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
index 1e25eea..44507f4 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
@@ -2,7 +2,7 @@ distribute result [$$c]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$c])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.3.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.3.adm
index a1f79bb..db1c3d8 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.3.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan-select/push-limit-to-primary-scan-select.3.adm
@@ -2,7 +2,7 @@ distribute result [$$paper]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$paper])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.3.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.3.adm
index ee3e565..aaf0c53 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.3.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.3.adm
@@ -2,7 +2,7 @@ distribute result [$$paper]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$paper])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.adm
index 939637d..2176e36 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.adm
@@ -2,7 +2,7 @@ distribute result [$$paper]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$paper])
-- STREAM_PROJECT |PARTITIONED|
diff --git
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.8.adm
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.8.adm
index e11c19d..06a28e4 100644
---
a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.8.adm
+++
b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.8.adm
@@ -2,7 +2,7 @@ distribute result [$$75]
-- DISTRIBUTE_RESULT |UNPARTITIONED|
exchange
-- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
- limit 5, 5
+ limit 5 offset 5
-- STREAM_LIMIT |UNPARTITIONED|
project ([$$75])
-- STREAM_PROJECT |PARTITIONED|
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 7f002ee..be711d3 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -13949,6 +13949,11 @@
</compilation-unit>
</test-case>
<test-case FilePath="limit">
+ <compilation-unit name="offset_without_limit">
+ <output-dir compare="Text">offset_without_limit</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="limit">
<compilation-unit name="push-limit-to-external-scan">
<output-dir compare="Text">push-limit-to-external-scan</output-dir>
</compilation-unit>
diff --git a/asterixdb/asterix-doc/src/main/grammar/sqlpp.ebnf
b/asterixdb/asterix-doc/src/main/grammar/sqlpp.ebnf
index aaf6761..9904d92 100644
--- a/asterixdb/asterix-doc/src/main/grammar/sqlpp.ebnf
+++ b/asterixdb/asterix-doc/src/main/grammar/sqlpp.ebnf
@@ -82,7 +82,7 @@ HavingClause ::= "HAVING" Expr
GroupAsClause ::= "GROUP AS" Identifier
-Selection ::= WithClause? QueryBlock UnionOption* OrderByClause? LimitClause?
+Selection ::= WithClause? QueryBlock UnionOption* OrderByClause? ( LimitClause
| | OffsetClause )?
UnionOption ::= "UNION ALL" (QueryBlock | Subquery)
@@ -92,7 +92,9 @@ WithClause ::= "WITH" Identifier "AS" Expr
OrderbyClause ::= "ORDER BY" Expr ( "ASC" | "DESC" )?
( "," Expr ( "ASC" | "DESC" )? )*
-LimitClause ::= "LIMIT" Expr ("OFFSET" Expr)?
+LimitClause ::= "LIMIT" Expr OffsetClause?
+
+OffsetClause ::= "OFFSET" Expr
Subquery ::= "(" Selection ")"
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/0_toc.md
b/asterixdb/asterix-doc/src/main/markdown/sqlpp/0_toc.md
index 1ac6ab0..5d084c5 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/0_toc.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/0_toc.md
@@ -55,7 +55,7 @@
* [GROUP AS Clause](#GROUP_AS_Clause)
* [Selection and UNION ALL](#Union_all)
* [WITH Clauses](#With_clauses)
- * [ORDER By and LIMIT Clauses](#Order_By_clauses)
+ * [ORDER BY, LIMIT, and OFFSET Clauses](#Order_By_clauses)
* [Subqueries](#Subqueries)
* [4. Window Functions](#Over_clauses)
* [Window Function Call](#Window_function_call)
diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
index 5e4358f..17e6339 100644
--- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
+++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/3_query.md
@@ -47,7 +47,7 @@ In SQL++, the `SELECT` clause may appear either at the
beginning or at the end o
### <a id="Select_element">SELECT VALUE</a>
-
+
The `SELECT VALUE` clause returns an array or multiset that contains the
results of evaluating the `VALUE` expression, with one evaluation being
performed per "binding tuple" (i.e., per `FROM` clause item) satisfying the
statement's selection criteria.
If there is no `FROM` clause, the expression after `VALUE` is evaluated once
with no binding tuples
(except those inherited from an outer environment).
@@ -103,7 +103,7 @@ Returns:
"customer_name": "T. Henry"
}
]
-
+
### <a id="Select_star">SELECT *</a>
As in SQL, the phrase `SELECT *` suggests, "select everything."
@@ -136,7 +136,7 @@ The following example applies `SELECT *` to a single
collection.
FROM ages AS a
SELECT * ;
-
+
Result:
[
@@ -190,12 +190,12 @@ Result:
{ "name": "Bill", "age": 21 },
{ "name": "Sue", "age": 32 }
]
-
+
Note that, for queries over a single collection, `SELECT` *variable* `.*`
returns a simpler result and therefore may be preferable to `SELECT *`. In
fact, `SELECT` *variable* `.*`, like `SELECT *` in SQL, is equivalent to a
`SELECT` clause that enumerates all the fields of the collection, as in (Q3.4d):
##### Example
-(Q3.4d) Return all the information in the `ages` collection.
+(Q3.4d) Return all the information in the `ages` collection.
FROM ages AS a
SELECT a.name, a.age
@@ -224,7 +224,7 @@ Result:
### <a id="Select_distinct">SELECT DISTINCT</a>
-The `DISTINCT` keyword is used to eliminate duplicate items from the results
of a query block.
+The `DISTINCT` keyword is used to eliminate duplicate items from the results
of a query block.
##### Example
@@ -234,7 +234,7 @@ The `DISTINCT` keyword is used to eliminate duplicate items
from the results of
SELECT DISTINCT c.address.city;
Result:
-
+
[
{
"city": "Boston, MA"
@@ -248,7 +248,7 @@ Result:
{
"city": "Rome, Italy"
}
- ]
+ ]
### <a id="Unnamed_projections">Unnamed Projections</a>
Similar to standard SQL, the query language supports unnamed projections
(a.k.a, unnamed `SELECT` clause items), for which names are generated rather
than user-provided.
@@ -286,10 +286,10 @@ As in standard SQL, field access expressions can be
abbreviated when there is no
##### Example
-(Q3.7) Same as Q3.6, omitting the variable reference for the order number and
date and providing custom names for `SELECT` clause items.
+(Q3.7) Same as Q3.6, omitting the variable reference for the order number and
date and providing custom names for `SELECT` clause items.
FROM orders AS o
- WHERE o.custid = "C41"
+ WHERE o.custid = "C41"
SELECT orderno % 1000 AS last_digit, order_date;
Result:
@@ -326,24 +326,24 @@ Result:
##### Synonyms for `UNNEST`: `CORRELATE`, `FLATTEN`
---
-The purpose of a `FROM` clause is to iterate over a collection, binding a
variable to each item in turn. Here's a query that iterates over the
`customers` dataset, choosing certain customers and returning some of their
attributes.
+The purpose of a `FROM` clause is to iterate over a collection, binding a
variable to each item in turn. Here's a query that iterates over the
`customers` dataset, choosing certain customers and returning some of their
attributes.
##### Example
-
+
(Q3.8) List the customer ids and names of the customers in zipcode 63101, in
order by their customer IDs.
-
+
FROM customers
WHERE address.zipcode = "63101"
SELECT custid AS customer_id, name
ORDER BY customer_id;
-
+
Result:
-
+
[
{
@@ -359,43 +359,43 @@ Result:
"name": "R. Dodge"
}
]
-
+
Let's take a closer look at what this `FROM` clause is doing. A `FROM` clause
always produces a stream of bindings, in which an iteration variable is bound
in turn to each item in a collection. In Q3.8, since no explicit iteration
variable is provided, the `FROM` clause defines an implicit variable named
`customers`, the same name as the dataset that is being iterated over. The
implicit iteration variable serves as the object-name for all field-names in
the query block that do not have e [...]
You may also provide an explicit iteration variable, as in this version of the
same query:
-##### Example
+##### Example
(Q3.9) Alternative version of Q3.8 (same result).
-
+
FROM customers AS c
WHERE c.address.zipcode = "63101"
SELECT c.custid AS customer_id, c.name
ORDER BY customer_id;
-
+
In Q3.9, the variable `c` is bound to each `customer` object in turn as the
query iterates over the `customers` dataset. An explicit iteration variable can
be used to identify the fields of the referenced object, as in `c.name` in the
`SELECT` clause of Q3.9. When referencing a field of an object, the iteration
variable can be omitted when there is no ambiguity. For example, `c.name` could
be replaced by `name` in the `SELECT` clause of Q3.9. That's why field-names
like `name` and `custi [...]
-
+
In the examples above, the `FROM` clause iterates over the objects in a
dataset. But in general, a `FROM` clause can iterate over any collection. For
example, the objects in the `orders` dataset each contain a field called
`items`, which is an array of nested objects. In some cases, you will write a
`FROM` clause that iterates over a nested array like `items`.
-
+
The stream of objects (more accurately, variable bindings) that is produced by
the `FROM` clause does not have any particular order. The system will choose
the most efficient order for the iteration. If you want your query result to
have a specific order, you must use an `ORDER BY` clause.
-
+
It's good practice to specify an explicit iteration variable for each
collection in the `FROM` clause, and to use these variables to qualify the
field-names in other clauses. Here are some reasons for this convention:
-
+
- It's nice to have different names for the collection as a whole and an
object in the collection. For example, in the clause `FROM customers AS c`, the
name `customers` represents the dataset and the name `c` represents one object
in the dataset.
-
+
- In some cases, iteration variables are required. For example, when joining
a dataset to itself, distinct iteration variables are required to distinguish
the left side of the join from the right side.
-
+
- In a subquery it's sometimes necessary to refer to an object in an outer
query block (this is called a *correlated subquery*). To avoid confusion in
correlated subqueries, it's best to use explicit variables.
-
+
### <a id="Left_outer_unnests">Joins</a>
@@ -405,7 +405,7 @@ A `FROM` clause gets more interesting when there is more
than one collection inv
(Q3.10) Create a packing list for order number 1001, showing the customer name
and address and all the items in the order.
-
+
FROM customers AS c, orders AS o
WHERE c.custid = o.custid
AND o.orderno = 1001
@@ -414,7 +414,7 @@ A `FROM` clause gets more interesting when there is more
than one collection inv
c.address,
o.items AS items_ordered;
-
+
Result:
[
@@ -441,11 +441,11 @@ Result:
}
]
-
+
Q3.10 is called a *join query* because it joins the `customers` collection and
the `orders` collection, using the join condition `c.custid = o.custid`. In
SQL++, as in SQL, you can express this query more explicitly by a `JOIN` clause
that includes the join condition, as follows:
-
+
##### Example
(Q3.11) Alternative statement of Q3.10 (same result).
@@ -459,10 +459,10 @@ Q3.10 is called a *join query* because it joins the
`customers` collection and t
c.address,
o.items AS items_ordered;
-
+
Whether you express the join condition in a `JOIN` clause or in a `WHERE`
clause is a matter of taste; the result is the same. This manual will generally
use a comma-separated list of collection-names in the `FROM` clause, leaving
the join condition to be expressed elsewhere. As we'll soon see, in some query
blocks the join condition can be omitted entirely.
-
+
There is, however, one case in which an explicit `JOIN` clause is necessary.
That is when you need to join collection A to collection B, and you want to
make sure that every item in collection A is present in the query result, even
if it doesn't match any item in collection B. This kind of query is called a
*left outer join*, and it is illustrated by the following example.
##### Example
@@ -475,7 +475,7 @@ There is, however, one case in which an explicit `JOIN`
clause is necessary. Tha
SELECT c.custid, c.name, o.orderno, o.order_date
ORDER BY c.custid, o.order_date;
-
+
Result:
@@ -509,17 +509,17 @@ Result:
"name": "M. Sinclair"
}
]
-
+
As you can see from the result of this left outer join, our data includes four
orders from customer T. Cody, but no orders from customer M. Sinclair. The
behavior of left outer join in SQL++ is different from that of SQL. SQL would
have provided M. Sinclair with an order in which all the fields were `null`.
SQL++, on the other hand, deals with schemaless data, which permits it to
simply omit the order fields from the outer join.
Now we're ready to look at a new kind of join that was not provided (or
needed) in original SQL. Consider this query:
-##### Example
+##### Example
(Q3.13) For every case in which an item is ordered in a quantity greater than
100, show the order number, date, item number, and quantity.
-
+
FROM orders AS o, o.items AS i
WHERE i.qty > 100
@@ -549,13 +549,13 @@ Result:
"quantity": 120
}
]
-
+
Q3.13 illustrates a feature called *left-correlation* in the `FROM` clause.
Notice that we are joining `orders`, which is a dataset, to `items`, which is
an array nested inside each order. In effect, for each order, we are unnesting
the `items` array and joining it to the `order` as though it were a separate
collection. For this reason, this kind of query is sometimes called an
*unnesting query*. The keyword `UNNEST` may be used whenever left-correlation
is used in a `FROM` clause, as sh [...]
-
-
-##### Example
+
+
+##### Example
(Q3.14) Alternative statement of Q3.13 (same result).
@@ -565,16 +565,16 @@ Q3.13 illustrates a feature called *left-correlation* in
the `FROM` clause. Noti
i.qty AS quantity
ORDER BY o.orderno, item_number;
-
+
The results of Q3.13 and Q3.14 are exactly the same. `UNNEST` serves as a
reminder that left-correlation is being used to join an object with its nested
items. The join condition in Q3.14 is expressed by the left-correlation: each
order `o` is joined to its own items, referenced as `o.items`. The result of
the `FROM` clause is a stream of binding tuples, each containing two variables,
`o` and `i`. The variable `o` is bound to an order and the variable `i` is
bound to one item inside that order.
Like `JOIN`, `UNNEST` has a `LEFT OUTER` option. Q3.14 could have specified:
-
+
FROM orders AS o LEFT OUTER UNNEST o.items AS i
-
+
In this case, orders that have no nested items would appear in the query
result.
@@ -592,10 +592,10 @@ In this case, orders that have no nested items would
appear in the query result.
`LET` clauses can be useful when a (complex) expression is used several times
within a query, allowing it to be written once to make the query more concise.
The word `LETTING` can also be used, although this is not as common. The next
query shows an example.
##### Example
-
+
(Q3.15) For each item in an order, the revenue is defined as the quantity
times the price of that item. Find individual items for which the revenue is
greater than 5000. For each of these, list the order number, item number, and
revenue, in descending order by revenue.
-
+
FROM orders AS o, o.items AS i
LET revenue = i.qty * i.price
@@ -622,7 +622,7 @@ Result:
"revenue": 5525
}
]
-
+
The expression for computing revenue is defined once in the `LET` clause and
then used three times in the remainder of the query. Avoiding repetition of the
revenue expression makes the query shorter and less prone to errors.
@@ -698,13 +698,13 @@ In the `GROUP BY`clause, you may optionally define an
alias for the grouping exp
Q3.16 had a single grouping expression, `o.custid`. If a query has multiple
grouping expressions, the combination of grouping expressions is evaluated for
every binding tuple, and the stream of binding tuples is partitioned into
groups that have values in common for all of the grouping expressions. We'll
see an example of such a query in Q3.18.
-
+
After grouping, the number of binding tuples is reduced: instead of a binding
tuple for each of the input objects, there is a binding tuple for each group.
The grouping expressions (identified by their aliases, if any) are bound to the
results of their evaluations. However, all the non-grouping fields (that is,
fields that were not named in the grouping expressions), are accessible only in
a special way: as an argument of one of the special aggregation
pseudo-functions such as: `SUM`, `A [...]
You may notice that the results of Q3.16 do not include customers who have no
`orders`. If we want to include these `customers`, we need to use an outer join
between the `customers` and `orders` collections. This is illustrated by the
following example, which also includes the name of each customer.
##### Example
-
+
(Q3.17) List the number of orders placed by each customer including those
customers who have placed no orders.
SELECT c.custid, c.name, COUNT(o.orderno) AS `order count`
@@ -752,7 +752,7 @@ You may notice that the results of Q3.16 do not include
customers who have no `o
}
]
-
+
Notice in Q3.17 what happens when the special aggregation function `COUNT` is
applied to a collection that does not exist, such as the orders of M. Sinclair:
it returns zero. This behavior is unlike that of the other special aggregation
functions `SUM`, `AVG`, `MAX`, and `MIN`, which return `null` if their operand
does not exist. This should make you cautious about the `COUNT` function: If it
returns zero, that may mean that the collection you are counting has zero
members, or that it do [...]
Q3.17 also shows how a query block can have more than one grouping expression.
In general, the `GROUP BY`clause produces a binding tuple for each different
combination of values for the grouping expressions. In Q3.17, the `c.custid`
field uniquely identifies a customer, so adding `c.name` as a grouping
expression does not result in any more groups. Nevertheless, `c.name` must be
included as a grouping expression if it is to be referenced outside (after) the
`GROUP BY` clause. If `c.name` [...]
@@ -802,9 +802,9 @@ Q3.19 also shows how a `LET` clause can be used after a
`GROUP BY` clause to def
LET total_revenue = sum(i.qty * i.price)
SELECT o.orderno, total_revenue
ORDER BY total_revenue desc;
-
+
Result:
-
+
[
{
"orderno": 1002,
@@ -836,7 +836,7 @@ By adding a `HAVING` clause to Q3.19, we can filter the
results to include only
##### Example
-
+
(Q3.20) Modify Q3.19 to include only orders whose total revenue is greater
than 5000.
FROM orders AS o, o.items as i
@@ -861,12 +861,12 @@ Result:
SQL provides several special functions for performing aggregations on groups
including: `SUM`, `AVG`, `MAX`, `MIN`, and `COUNT` (some implementations
provide more). These same functions are supported in SQL++. However, it's worth
spending some time on these special functions because they don't behave like
ordinary functions. They are called "pseudo-functions" here because they don't
evaluate their operands in the same way as ordinary functions. To see the
difference, consider these two e [...]
##### Example 1:
-
+
SELECT LENGTH(name) FROM customers
In Example 1, `LENGTH` is an ordinary function. It simply evaluates its
operand (name) and then returns a result computed from the operand.
-##### Example 2:
+##### Example 2:
SELECT AVG(rating) FROM customers
The effect of `AVG` in Example 2 is quite different. Rather than performing a
computation on an individual rating value, `AVG` has a global effect: it
effectively restructures the query. As a pseudo-function, `AVG` requires its
operand to be a group; therefore, it automatically collects all the rating
values from the query block and forms them into a group.
@@ -906,7 +906,7 @@ When an aggregation pseudo-function is used without an
explicit `GROUP BY` claus
##### Example
(Q3.22) Find the average credit rating among all customers.
-
+
FROM customers AS c
SELECT AVG(c.rating) AS `avg credit rating`;
@@ -919,15 +919,15 @@ Result:
}
]
-
+
The aggregation pseudo-function `COUNT` has a special form in which its
operand is `*` instead of an expression. For example, `SELECT COUNT(*) FROM
customers` simply returns the total number of customers, whereas `SELECT
COUNT(rating) FROM customers` returns the number of customers who have known
ratings (that is, their ratings are not `null` or `missing`).
-
+
Because the aggregation pseudo-functions sometimes restructure their
operands, they can be used only in query blocks where (explicit or implicit)
grouping is being done. Therefore the pseudo-functions cannot operate directly
on arrays or multisets. For operating directly on JSON collections, SQL++
provides a set of ordinary functions for computing aggregations. Each ordinary
aggregation function (except the ones corresponding to `COUNT` and `ARRAY_AGG`)
has two versions: one that ignore [...]
-
-| Aggregation pseudo-function; operates on groups only | ordinary functions:
Ignores NULL or MISSING values | ordinary functions: Returns NULL if NULL or
MISSING are encountered|
+
+| Aggregation pseudo-function; operates on groups only | ordinary functions:
Ignores NULL or MISSING values | ordinary functions: Returns NULL if NULL or
MISSING are encountered|
|----------|----------|--------|
|SUM| ARRAY_SUM| STRICT_SUM |
| AVG |ARRAY_MAX| STRICT_MAX |
@@ -947,15 +947,15 @@ The aggregation pseudo-function `COUNT` has a special
form in which its operand
Note that the ordinary aggregation functions that ignore `null` have names
beginning with "ARRAY." This naming convention has historical roots. Despite
their names, the functions operate on both arrays and multisets.
-
+
Because of the special properties of the aggregation pseudo-functions, SQL
(and therefore SQL++) is not a pure functional language. But every query that
uses a pseudo-function can be expressed as an equivalent query that uses an
ordinary function. Q3.23 is an example of how queries can be expressed without
pseudo-functions. A more detailed explanation of all of the functions is also
available [here](builtins.html#AggregateFunctions) .
-##### Example
+##### Example
(Q3.23) Alternative form of Q3.22, using the ordinary function `ARRAY_AVG`
rather than the aggregating pseudo-function `AVG`.
-
+
SELECT ARRAY_AVG(
(SELECT VALUE c.rating
@@ -963,7 +963,7 @@ Because of the special properties of the aggregation
pseudo-functions, SQL (and
Result (same as Q3.22):
-
+
[
{
"avg credit rating": 670
@@ -986,17 +986,17 @@ If the function `STRICT_AVG` had been used in Q3.23 in
place of `ARRAY_AVG`, the
JSON is a hierarchical format, and a fully featured JSON query language needs
to be able to produce hierarchies of its own, with computed data at every level
of the hierarchy. The key feature of SQL++ that makes this possible is the
`GROUP AS` clause.
-
+
A query may have a `GROUP AS` clause only if it has a `GROUP BY` clause. The
`GROUP BY` clause "hides" the original objects in each group, exposing only the
grouping expressions and special aggregation functions on the non-grouping
fields. The purpose of the `GROUP AS` clause is to make the original objects in
the group visible to subsequent clauses. Thus the query can generate output
data both for the group as a whole and for the individual objects inside the
group.
-
+
For each group, the `GROUP AS` clause preserves all the objects in the group,
just as they were before grouping, and gives a name to this preserved group.
The group name can then be used in the `FROM` clause of a subquery to process
and return the individual objects in the group.
-
-To see how this works, we'll write some queries that investigate the customers
in each zipcode and their credit ratings. This would be a good time to review
the sample database in Appendix 4. A part of the data is summarized below.
+
+To see how this works, we'll write some queries that investigate the customers
in each zipcode and their credit ratings. This would be a good time to review
the sample database in Appendix 4. A part of the data is summarized below.
Customers in zipcode 02115:
C35, J. Roberts, rating 565
@@ -1009,11 +1009,11 @@ To see how this works, we'll write some queries that
investigate the customers i
C13, T. Cody, rating 750
C31, B. Pruitt, (no rating)
C41, R. Dodge, rating 640
-
+
Customers with no zipcode:
C47, S. Logan, rating 625
-
+
Now let's consider the effect of the following clauses:
@@ -1022,12 +1022,12 @@ Now let's consider the effect of the following clauses:
GROUP AS g
This query fragment iterates over the `customers` objects, using the iteration
variable `c`. The `GROUP BY` clause forms the objects into groups, each with a
common zipcode (including one group for customers with no zipcode). After the
`GROUP BY` clause, we can see the grouping expression, `c.address.zipcode`, but
other fields such as `c.custid` and `c.name` are visible only to special
aggregation functions.
-
+
The clause `GROUP AS g` now makes the original objects visible again. For each
group in turn, the variable `g` is bound to a multiset of objects, each of
which has a field named `c`, which in turn contains one of the original
objects. Thus after `GROUP AS g`, for the group with zipcode 02115, `g` is
bound to the following multiset:
-
- [
- { "c":
+
+ [
+ { "c":
{ "custid": "C35",
"name": "J. Roberts",
"address":
@@ -1051,7 +1051,7 @@ The clause `GROUP AS g` now makes the original objects
visible again. For each g
}
]
-
+
Thus, the clauses following `GROUP AS` can see the original objects by writing
subqueries that iterate over the multiset `g`.
@@ -1064,7 +1064,7 @@ The extra level named `c` was introduced into this
multiset because the groups m
In this case, following `GROUP AS g`, the variable `g` would be bound to the
following collection:
- [
+ [
{ "c": { an original customers object },
"o": { an original orders object }
},
@@ -1078,7 +1078,7 @@ After using `GROUP AS` to make the content of a group
accessible, you will proba
Now we are ready to take a look at how `GROUP AS` might be used in a query.
Suppose that we want to group customers by zipcode, and for each group we want
to see the average credit rating and a list of the individual customers in the
group. Here's a query that does that:
-##### Example
+##### Example
(Q3.24) For each zipcode, list the average credit rating in that zipcode,
followed by the customer numbers and names in numeric order.
FROM customers AS c
@@ -1166,13 +1166,13 @@ When two or more query blocks are connected by `UNION
ALL`, they can be followed
In this example, a customer might be selected because he has ordered more than
two different items (first query block) or because he has a high credit rating
(second query block). By adding an explanatory string to each query block, the
query writer can cause the output objects to be labeled to distinguish these
two cases.
-
+
##### Example
(Q3.25a) Find customer ids for customers who have placed orders for more than
two different items or who have a credit rating greater than 700, with labels
to distinguish these cases.
-
+
FROM orders AS o, o.items AS i
GROUP BY o.orderno, o.custid
@@ -1188,7 +1188,7 @@ In this example, a customer might be selected because he
has ordered more than t
Result:
-
+
[
{
"reason": "High rating",
@@ -1208,15 +1208,15 @@ Result:
}
]
-
+
If, on the other hand, you simply want a list of the customer ids and you
don't care to preserve the reasons, you can simplify your output by using
`SELECT VALUE`, as follows:
-
+
(Q3.25b) Simplify Q3.25a to return a simple list of unlabeled customer ids.
-
+
FROM orders AS o, o.items AS i
GROUP BY o.orderno, o.custid
@@ -1252,7 +1252,7 @@ As in standard SQL, a `WITH` clause can be used to
improve the modularity of a q
##### Example
-(Q3.26) Find the minimum, maximum, and average revenue among all orders in
2020, rounded to the nearest integer.
+(Q3.26) Find the minimum, maximum, and average revenue among all orders in
2020, rounded to the nearest integer.
WITH order_revenue AS
(FROM orders AS o, o.items AS i
@@ -1264,7 +1264,7 @@ As in standard SQL, a `WITH` clause can be used to
improve the modularity of a q
SELECT AVG(revenue) AS average,
MIN(revenue) AS minimum,
MAX(revenue) AS maximum;
-
+
Result:
@@ -1278,7 +1278,7 @@ Result:
`WITH` can be particularly useful when a value needs to be used several times
in a query.
-## <a id="Order_By_clauses">ORDER BY and LIMIT Clauses</a>
+## <a id="Order_By_clauses">ORDER BY, LIMIT, and OFFSET Clauses</a>
---
### OrderbyClause
@@ -1287,16 +1287,20 @@ Result:
### LimitClause
****
+### OffsetClause
+****
---
-
-The last two (optional) clauses to be processed in a query are `ORDER BY` and
`LIMIT`.
+
+The last three (optional) clauses to be processed in a query are `ORDER BY`,
`LIMIT`, and `OFFSET`.
The `ORDER BY` clause is used to globally sort data in either ascending order
(i.e., `ASC`) or descending order (i.e., `DESC`).
During ordering, `MISSING` and `NULL` are treated as being smaller than any
other value if they are encountered
in the ordering key(s). `MISSING` is treated as smaller than `NULL` if both
occur in the data being sorted.
-The ordering of values of a given type is consistent with its type's `<=`
ordering; the ordering of values across types is implementation-defined but
stable.
+The ordering of values of a given type is consistent with its type's `<=`
ordering; the ordering of values across types is implementation-defined but
stable.
-The `LIMIT` clause is used to limit the result set to a specified maximum
size. The optional `OFFSET` clause is used to specify a number of items in the
output stream to be discarded before the query result begins.
+The `LIMIT` clause is used to limit the result set to a specified maximum size.
+The optional `OFFSET` clause is used to specify a number of items in the
output stream to be discarded before the query result begins.
+The `OFFSET` can also be used as a standalone clause, without the `LIMIT`.
The following example illustrates use of the `ORDER BY` and `LIMIT` clauses.
@@ -1365,7 +1369,7 @@ A subquery is simply a query surrounded by parentheses.
In SQL++, a subquery can
##### Example
(Q3.29)(Subquery in SELECT clause)
-For every order that includes item no. 120, find the order number, customer
id, and customer name.
+For every order that includes item no. 120, find the order number, customer
id, and customer name.
Here, the subquery is used to find a customer name, given a customer id. Since
the outer query expects a scalar result, the subquery uses SELECT VALUE and is
followed by the indexing operator [0].
@@ -1398,7 +1402,7 @@ Find the customer number, name, and rating of all
customers whose rating is grea
Here, the subquery is used to find the average rating among all customers.
Once again, SELECT VALUE and indexing [0] have been used to get a single scalar
value.
-
+
FROM customers AS c1
WHERE c1.rating >
(FROM customers AS c2
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/LimitClause.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/LimitClause.java
index d597bc2..25ddce4 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/LimitClause.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/clause/LimitClause.java
@@ -26,15 +26,16 @@ import org.apache.asterix.lang.common.base.Expression;
import org.apache.asterix.lang.common.visitor.base.ILangVisitor;
public class LimitClause extends AbstractClause {
+
private Expression limitExpr;
- private Expression offset;
- public LimitClause() {
- // Default constructor.
- }
+ private Expression offset;
- public LimitClause(Expression limitexpr, Expression offset) {
- this.limitExpr = limitexpr;
+ public LimitClause(Expression limitExpr, Expression offset) {
+ if (limitExpr == null && offset == null) {
+ throw new IllegalArgumentException();
+ }
+ this.limitExpr = limitExpr;
this.offset = offset;
}
@@ -46,6 +47,10 @@ public class LimitClause extends AbstractClause {
this.limitExpr = limitexpr;
}
+ public boolean hasLimitExpr() {
+ return limitExpr != null;
+ }
+
public Expression getOffset() {
return offset;
}
@@ -82,6 +87,6 @@ public class LimitClause extends AbstractClause {
return false;
}
LimitClause target = (LimitClause) object;
- return limitExpr.equals(target.getLimitExpr()) &&
Objects.equals(offset, target.getOffset());
+ return Objects.equals(limitExpr, target.getLimitExpr()) &&
Objects.equals(offset, target.getOffset());
}
}
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
index c866eff..f1bcb93 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/AbstractInlineUdfsVisitor.java
@@ -244,13 +244,16 @@ public abstract class AbstractInlineUdfsVisitor extends
AbstractQueryExpressionV
@Override
public Boolean visit(LimitClause lc, List<FunctionDecl> arg) throws
CompilationException {
- Pair<Boolean, Expression> p1 = inlineUdfsInExpr(lc.getLimitExpr(),
arg);
- lc.setLimitExpr(p1.second);
- boolean changed = p1.first;
- if (lc.getOffset() != null) {
+ boolean changed = false;
+ if (lc.hasLimitExpr()) {
+ Pair<Boolean, Expression> p1 = inlineUdfsInExpr(lc.getLimitExpr(),
arg);
+ lc.setLimitExpr(p1.second);
+ changed = p1.first;
+ }
+ if (lc.hasOffset()) {
Pair<Boolean, Expression> p2 = inlineUdfsInExpr(lc.getOffset(),
arg);
lc.setOffset(p2.second);
- changed = changed || p2.first;
+ changed |= p2.first;
}
return changed;
}
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
index 97701e4..1fcf822 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/CloneAndSubstituteVariablesVisitor.java
@@ -198,15 +198,17 @@ public class CloneAndSubstituteVariablesVisitor extends
@Override
public Pair<ILangExpression, VariableSubstitutionEnvironment>
visit(LimitClause lc,
VariableSubstitutionEnvironment env) throws CompilationException {
- Pair<ILangExpression, VariableSubstitutionEnvironment> p1 =
lc.getLimitExpr().accept(this, env);
- Pair<ILangExpression, VariableSubstitutionEnvironment> p2;
- Expression lcOffsetExpr = lc.getOffset();
- if (lcOffsetExpr != null) {
- p2 = lcOffsetExpr.accept(this, env);
- } else {
- p2 = new Pair<>(null, null);
+ Expression newLimitExpr = null;
+ if (lc.hasLimitExpr()) {
+ Pair<ILangExpression, VariableSubstitutionEnvironment> p1 =
lc.getLimitExpr().accept(this, env);
+ newLimitExpr = (Expression) p1.first;
+ }
+ Expression newOffsetExpr = null;
+ if (lc.hasOffset()) {
+ Pair<ILangExpression, VariableSubstitutionEnvironment> p2 =
lc.getOffset().accept(this, env);
+ newOffsetExpr = (Expression) p2.first;
}
- LimitClause c = new LimitClause((Expression) p1.first, (Expression)
p2.first);
+ LimitClause c = new LimitClause(newLimitExpr, newOffsetExpr);
c.setSourceLocation(lc.getSourceLocation());
return new Pair<>(c, env);
}
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
index 290f9ea..0474c0e 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/FormatPrintVisitor.java
@@ -317,11 +317,18 @@ public abstract class FormatPrintVisitor implements
ILangVisitor<Void, Integer>
@Override
public Void visit(LimitClause lc, Integer step) throws
CompilationException {
- out.print(skip(step) + "limit ");
- lc.getLimitExpr().accept(this, step + 1);
- if (lc.getOffset() != null) {
- out.print(" offset ");
+ if (lc.hasLimitExpr()) {
+ out.print(skip(step) + "limit ");
+ lc.getLimitExpr().accept(this, step + 1);
+ if (lc.hasOffset()) {
+ out.print(" offset ");
+ lc.getOffset().accept(this, step + 1);
+ }
+ } else if (lc.hasOffset()) {
+ out.print(skip(step) + "offset ");
lc.getOffset().accept(this, step + 1);
+ } else {
+ throw new
CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
lc.getSourceLocation(), "");
}
out.println();
return null;
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
index 858729c..44b2092 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/GatherFunctionCallsVisitor.java
@@ -138,8 +138,10 @@ public class GatherFunctionCallsVisitor extends
AbstractQueryExpressionVisitor<V
@Override
public Void visit(LimitClause lc, Void arg) throws CompilationException {
- lc.getLimitExpr().accept(this, arg);
- if (lc.getOffset() != null) {
+ if (lc.hasLimitExpr()) {
+ lc.getLimitExpr().accept(this, arg);
+ }
+ if (lc.hasOffset()) {
lc.getOffset().accept(this, arg);
}
return null;
diff --git
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
index 64d97f3..e756eee 100644
---
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
+++
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/visitor/QueryPrintVisitor.java
@@ -252,11 +252,18 @@ public abstract class QueryPrintVisitor extends
AbstractQueryExpressionVisitor<V
@Override
public Void visit(LimitClause lc, Integer step) throws
CompilationException {
- out.println(skip(step) + "Limit");
- lc.getLimitExpr().accept(this, step + 1);
- if (lc.getOffset() != null) {
- out.println(skip(step + 1) + "Offset");
- lc.getOffset().accept(this, step + 2);
+ if (lc.hasLimitExpr()) {
+ out.println(skip(step) + "Limit");
+ lc.getLimitExpr().accept(this, step + 1);
+ if (lc.hasOffset()) {
+ out.println(skip(step + 1) + "Offset");
+ lc.getOffset().accept(this, step + 2);
+ }
+ } else if (lc.hasOffset()) {
+ out.println(skip(step) + "Offset");
+ lc.getOffset().accept(this, step + 1);
+ } else {
+ throw new
CompilationException(ErrorCode.COMPILATION_ILLEGAL_STATE,
lc.getSourceLocation(), "");
}
return null;
}
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
index 51bbe98..b5375d2 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/DeepCopyVisitor.java
@@ -324,7 +324,8 @@ public class DeepCopyVisitor extends
AbstractSqlppQueryExpressionVisitor<ILangEx
@Override
public LimitClause visit(LimitClause limitClause, Void arg) throws
CompilationException {
- Expression limitExpr = (Expression)
limitClause.getLimitExpr().accept(this, arg);
+ Expression limitExpr =
+ limitClause.hasLimitExpr() ? (Expression)
limitClause.getLimitExpr().accept(this, arg) : null;
Expression offsetExpr = limitClause.hasOffset() ? (Expression)
limitClause.getOffset().accept(this, arg) : null;
LimitClause copy = new LimitClause(limitExpr, offsetExpr);
copy.setSourceLocation(limitClause.getSourceLocation());
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
index f0a9d9a..7539046 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppExpressionScopingVisitor.java
@@ -286,7 +286,9 @@ public class AbstractSqlppExpressionScopingVisitor extends
AbstractSqlppSimpleEx
@Override
public Expression visit(LimitClause limitClause, ILangExpression arg)
throws CompilationException {
scopeChecker.pushForbiddenScope(scopeChecker.getCurrentScope());
- limitClause.setLimitExpr(visit(limitClause.getLimitExpr(),
limitClause));
+ if (limitClause.hasLimitExpr()) {
+ limitClause.setLimitExpr(visit(limitClause.getLimitExpr(),
limitClause));
+ }
if (limitClause.hasOffset()) {
limitClause.setOffset(visit(limitClause.getOffset(), limitClause));
}
diff --git
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
index 6dacea6..e331173 100644
---
a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
+++
b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/visitor/base/AbstractSqlppSimpleExpressionVisitor.java
@@ -223,7 +223,9 @@ public class AbstractSqlppSimpleExpressionVisitor
@Override
public Expression visit(LimitClause limitClause, ILangExpression arg)
throws CompilationException {
- limitClause.setLimitExpr(visit(limitClause.getLimitExpr(),
limitClause));
+ if (limitClause.hasLimitExpr()) {
+ limitClause.setLimitExpr(visit(limitClause.getLimitExpr(),
limitClause));
+ }
if (limitClause.hasOffset()) {
limitClause.setOffset(visit(limitClause.getOffset(), limitClause));
}
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 5ecc35f..a4995da 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -4417,16 +4417,23 @@ HavingClause HavingClause() throws ParseException:
LimitClause LimitClause() throws ParseException:
{
Token startToken = null;
- LimitClause lc = new LimitClause();
- Expression expr;
- pushForbiddenScope(getCurrentScope());
+ Expression limitExpr = null, offsetExpr = null;
}
{
- <LIMIT> { startToken = token; } expr = Expression() {
lc.setLimitExpr(expr); }
- (<OFFSET> expr = Expression() { lc.setOffset(expr); })?
-
+ (
+ (
+ <LIMIT> { startToken = token; pushForbiddenScope(getCurrentScope()); }
limitExpr = Expression()
+ ( <OFFSET> offsetExpr = Expression() )?
+ { popForbiddenScope(); }
+ )
+ |
+ (
+ <OFFSET> { startToken = token; pushForbiddenScope(getCurrentScope()); }
offsetExpr = Expression()
+ { popForbiddenScope(); }
+ )
+ )
{
- popForbiddenScope();
+ LimitClause lc = new LimitClause(limitExpr, offsetExpr);
return addSourceLocation(lc, startToken);
}
}
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LimitOperator.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LimitOperator.java
index 313b77e..7f13f61 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LimitOperator.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/LimitOperator.java
@@ -25,7 +25,6 @@ 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.LogicalOperatorTag;
-import org.apache.hyracks.algebricks.core.algebra.base.LogicalVariable;
import
org.apache.hyracks.algebricks.core.algebra.expressions.IVariableTypeEnvironment;
import
org.apache.hyracks.algebricks.core.algebra.properties.VariablePropagationPolicy;
import org.apache.hyracks.algebricks.core.algebra.typing.ITypingContext;
@@ -34,32 +33,43 @@ import
org.apache.hyracks.algebricks.core.algebra.visitors.ILogicalOperatorVisit
public class LimitOperator extends AbstractLogicalOperator {
- private final Mutable<ILogicalExpression> maxObjects; // mandatory
- private final Mutable<ILogicalExpression> offset; // optional
- private boolean topmost;
+ private final Mutable<ILogicalExpression> maxObjects; // optional, if not
specified then offset is required
+ private final Mutable<ILogicalExpression> offset; // optional if
maxObjects is specified, required otherwise
+ private final boolean topmost;
public LimitOperator(ILogicalExpression maxObjectsExpr, ILogicalExpression
offsetExpr, boolean topmost) {
- this.maxObjects = new
MutableObject<ILogicalExpression>(maxObjectsExpr);
- this.offset = new MutableObject<ILogicalExpression>(offsetExpr);
+ if (maxObjectsExpr == null && offsetExpr == null) {
+ throw new IllegalArgumentException();
+ }
+ this.maxObjects = new MutableObject<>(maxObjectsExpr);
+ this.offset = new MutableObject<>(offsetExpr);
this.topmost = topmost;
}
+ public LimitOperator(ILogicalExpression maxObjects, ILogicalExpression
offset) {
+ this(maxObjects, offset, true);
+ }
+
public LimitOperator(ILogicalExpression maxObjectsExpr, boolean topmost) {
this(maxObjectsExpr, null, topmost);
}
- public LimitOperator(ILogicalExpression maxObjects, ILogicalExpression
offset) {
- this(maxObjects, offset, true);
+ public LimitOperator(ILogicalExpression maxObjects) {
+ this(maxObjects, true);
}
- public LimitOperator(ILogicalExpression maxObjects) {
- this(maxObjects, null, true);
+ public boolean hasMaxObjects() {
+ return maxObjects.getValue() != null;
}
public Mutable<ILogicalExpression> getMaxObjects() {
return maxObjects;
}
+ public boolean hasOffset() {
+ return offset.getValue() != null;
+ }
+
public Mutable<ILogicalExpression> getOffset() {
return offset;
}
@@ -70,7 +80,7 @@ public class LimitOperator extends AbstractLogicalOperator {
@Override
public void recomputeSchema() {
- schema = new ArrayList<LogicalVariable>();
+ schema = new ArrayList<>();
schema.addAll(inputs.get(0).getValue().getSchema());
}
@@ -82,13 +92,11 @@ public class LimitOperator extends AbstractLogicalOperator {
@Override
public boolean
acceptExpressionTransform(ILogicalExpressionReferenceTransform visitor) throws
AlgebricksException {
boolean b = false;
- if (visitor.transform(maxObjects)) {
- b = true;
+ if (hasMaxObjects()) {
+ b = visitor.transform(maxObjects);
}
- if (offset.getValue() != null) {
- if (visitor.transform(offset)) {
- b = true;
- }
+ if (hasOffset()) {
+ b |= visitor.transform(offset);
}
return b;
}
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
index 5dfdbbd..09d0c14 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/IsomorphismOperatorVisitor.java
@@ -197,11 +197,13 @@ public class IsomorphismOperatorVisitor implements
ILogicalOperatorVisitor<Boole
return Boolean.FALSE;
}
LimitOperator limitOpArg = (LimitOperator) copyAndSubstituteVar(op,
arg);
+ if (!Objects.equals(op.getMaxObjects().getValue(),
limitOpArg.getMaxObjects().getValue())) {
+ return Boolean.FALSE;
+ }
if (!Objects.equals(op.getOffset().getValue(),
limitOpArg.getOffset().getValue())) {
return Boolean.FALSE;
}
- boolean isomorphic =
op.getMaxObjects().getValue().equals(limitOpArg.getMaxObjects().getValue());
- return isomorphic;
+ return Boolean.TRUE;
}
@Override
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
index f7c7287..61da1b5 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/SubstituteVariableVisitor.java
@@ -179,10 +179,11 @@ public class SubstituteVariableVisitor
@Override
public Void visitLimitOperator(LimitOperator op, Pair<LogicalVariable,
LogicalVariable> pair)
throws AlgebricksException {
- op.getMaxObjects().getValue().substituteVar(pair.first, pair.second);
- ILogicalExpression offset = op.getOffset().getValue();
- if (offset != null) {
- offset.substituteVar(pair.first, pair.second);
+ if (op.hasMaxObjects()) {
+ op.getMaxObjects().getValue().substituteVar(pair.first,
pair.second);
+ }
+ if (op.hasOffset()) {
+ op.getOffset().getValue().substituteVar(pair.first, pair.second);
}
substVarTypes(op, pair);
return null;
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
index 182f61d..65e9023 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/logical/visitors/UsedVariableVisitor.java
@@ -219,10 +219,11 @@ public class UsedVariableVisitor implements
ILogicalOperatorVisitor<Void, Void>
@Override
public Void visitLimitOperator(LimitOperator op, Void arg) {
- op.getMaxObjects().getValue().getUsedVariables(usedVariables);
- ILogicalExpression offsetExpr = op.getOffset().getValue();
- if (offsetExpr != null) {
- offsetExpr.getUsedVariables(usedVariables);
+ if (op.hasMaxObjects()) {
+ op.getMaxObjects().getValue().getUsedVariables(usedVariables);
+ }
+ if (op.hasOffset()) {
+ op.getOffset().getValue().getUsedVariables(usedVariables);
}
return null;
}
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/StreamLimitPOperator.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/StreamLimitPOperator.java
index 90732ce..5939ec2 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/StreamLimitPOperator.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/operators/physical/StreamLimitPOperator.java
@@ -20,7 +20,6 @@ package
org.apache.hyracks.algebricks.core.algebra.operators.physical;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.IHyracksJobBuilder;
-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.PhysicalOperatorTag;
@@ -89,11 +88,10 @@ public class StreamLimitPOperator extends
AbstractPhysicalOperator {
LimitOperator limit = (LimitOperator) op;
IExpressionRuntimeProvider expressionRuntimeProvider =
context.getExpressionRuntimeProvider();
IVariableTypeEnvironment env = context.getTypeEnvironment(op);
- IScalarEvaluatorFactory maxObjectsFact = expressionRuntimeProvider
- .createEvaluatorFactory(limit.getMaxObjects().getValue(), env,
inputSchemas, context);
- ILogicalExpression offsetExpr = limit.getOffset().getValue();
- IScalarEvaluatorFactory offsetFact = (offsetExpr == null) ? null
- : expressionRuntimeProvider.createEvaluatorFactory(offsetExpr,
env, inputSchemas, context);
+ IScalarEvaluatorFactory maxObjectsFact = limit.hasMaxObjects() ?
expressionRuntimeProvider
+ .createEvaluatorFactory(limit.getMaxObjects().getValue(), env,
inputSchemas, context) : null;
+ IScalarEvaluatorFactory offsetFact = limit.hasOffset() ?
expressionRuntimeProvider
+ .createEvaluatorFactory(limit.getOffset().getValue(), env,
inputSchemas, context) : null;
RecordDescriptor recDesc =
JobGenHelper.mkRecordDescriptor(context.getTypeEnvironment(op),
propagatedSchema, context);
StreamLimitRuntimeFactory runtime = new
StreamLimitRuntimeFactory(maxObjectsFact, offsetFact, null,
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
index bc8e024..cccd4ae 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitor.java
@@ -386,10 +386,12 @@ public class LogicalOperatorPrettyPrintVisitor extends
AbstractLogicalOperatorPr
@Override
public Void visitLimitOperator(LimitOperator op, Integer indent) throws
AlgebricksException {
- addIndent(indent).append("limit " +
op.getMaxObjects().getValue().accept(exprVisitor, indent));
- ILogicalExpression offset = op.getOffset().getValue();
- if (offset != null) {
- buffer.append(", " + offset.accept(exprVisitor, indent));
+ addIndent(indent).append("limit");
+ if (op.hasMaxObjects()) {
+ buffer.append('
').append(op.getMaxObjects().getValue().accept(exprVisitor, indent));
+ }
+ if (op.hasOffset()) {
+ buffer.append(" offset
").append(op.getOffset().getValue().accept(exprVisitor, indent));
}
return null;
}
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
index ac559d2..4cffaeb 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/prettyprint/LogicalOperatorPrettyPrintVisitorJson.java
@@ -511,10 +511,11 @@ public class LogicalOperatorPrettyPrintVisitorJson
extends AbstractLogicalOperat
public Void visitLimitOperator(LimitOperator op, Void indent) throws
AlgebricksException {
try {
jsonGenerator.writeStringField(OPERATOR_FIELD, "limit");
- writeStringFieldExpression("value", op.getMaxObjects(), indent);
- Mutable<ILogicalExpression> offsetRef = op.getOffset();
- if (offsetRef != null && offsetRef.getValue() != null) {
- writeStringFieldExpression("offset", offsetRef, indent);
+ if (op.hasMaxObjects()) {
+ writeStringFieldExpression("value", op.getMaxObjects(),
indent);
+ }
+ if (op.hasOffset()) {
+ writeStringFieldExpression("offset",
op.getOffset().getValue(), indent);
}
return null;
} catch (IOException e) {
@@ -858,9 +859,15 @@ public class LogicalOperatorPrettyPrintVisitorJson extends
AbstractLogicalOperat
/////////////// string fields ///////////////
/** Writes "fieldName": "expr" */
- private void writeStringFieldExpression(String fieldName,
Mutable<ILogicalExpression> expression, Void indent)
+ private void writeStringFieldExpression(String fieldName,
Mutable<ILogicalExpression> expressionRef, Void indent)
+ throws AlgebricksException, IOException {
+ writeStringFieldExpression(fieldName, expressionRef.getValue(),
indent);
+ }
+
+ /** Writes "fieldName": "expr" */
+ private void writeStringFieldExpression(String fieldName,
ILogicalExpression expression, Void indent)
throws AlgebricksException, IOException {
- jsonGenerator.writeStringField(fieldName,
expression.getValue().accept(exprVisitor, indent));
+ jsonGenerator.writeStringField(fieldName,
expression.accept(exprVisitor, indent));
}
/////////////// array fields ///////////////
diff --git
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
index 5a8c128..67963ce 100644
---
a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
+++
b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/utils/LogicalOperatorDotVisitor.java
@@ -425,10 +425,12 @@ public class LogicalOperatorDotVisitor implements
ILogicalOperatorVisitor<String
@Override
public String visitLimitOperator(LimitOperator op, Boolean showDetails)
throws AlgebricksException {
stringBuilder.setLength(0);
- stringBuilder.append("limit
").append(op.getMaxObjects().getValue().toString());
- ILogicalExpression offset = op.getOffset().getValue();
- if (offset != null) {
- stringBuilder.append(", ").append(offset.toString());
+ stringBuilder.append("limit");
+ if (op.hasMaxObjects()) {
+ stringBuilder.append(' ').append(op.getMaxObjects().getValue());
+ }
+ if (op.hasOffset()) {
+ stringBuilder.append(" offset ").append(op.getOffset().getValue());
}
appendSchema(op, showDetails);
appendAnnotations(op, showDetails);
diff --git
a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/CopyLimitDownRule.java
b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/CopyLimitDownRule.java
index 7dd86b1..672234c 100644
---
a/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/CopyLimitDownRule.java
+++
b/hyracks-fullstack/algebricks/algebricks-rewriter/src/main/java/org/apache/hyracks/algebricks/rewriter/rules/CopyLimitDownRule.java
@@ -56,7 +56,7 @@ public class CopyLimitDownRule implements
IAlgebraicRewriteRule {
return false;
}
LimitOperator limitOp = (LimitOperator) op;
- if (!limitOp.isTopmostLimitOp()) {
+ if (!limitOp.isTopmostLimitOp() || !limitOp.hasMaxObjects()) {
return false;
}
@@ -79,7 +79,7 @@ public class CopyLimitDownRule implements
IAlgebraicRewriteRule {
ILogicalOperator unsafeOp = unsafeOpRef.getValue();
ILogicalExpression maxObjectsExpr =
limitOp.getMaxObjects().getValue();
ILogicalExpression newMaxObjectsExpr;
- if (limitOp.getOffset().getValue() == null) {
+ if (!limitOp.hasOffset()) {
newMaxObjectsExpr = maxObjectsExpr.cloneExpression();
} else {
// Need to add an offset to the given limit value
diff --git
a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/std/StreamLimitRuntimeFactory.java
b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/std/StreamLimitRuntimeFactory.java
index 2ae91ef..793f095 100644
---
a/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/std/StreamLimitRuntimeFactory.java
+++
b/hyracks-fullstack/algebricks/algebricks-runtime/src/main/java/org/apache/hyracks/algebricks/runtime/operators/std/StreamLimitRuntimeFactory.java
@@ -45,6 +45,9 @@ public class StreamLimitRuntimeFactory extends
AbstractOneInputOneOutputRuntimeF
IScalarEvaluatorFactory offsetEvalFactory, int[] projectionList,
IBinaryIntegerInspectorFactory binaryIntegerInspectorFactory) {
super(projectionList);
+ if (maxObjectsEvalFactory == null && offsetEvalFactory == null) {
+ throw new IllegalArgumentException();
+ }
this.maxObjectsEvalFactory = maxObjectsEvalFactory;
this.offsetEvalFactory = offsetEvalFactory;
this.binaryIntegerInspectorFactory = binaryIntegerInspectorFactory;
@@ -61,29 +64,32 @@ public class StreamLimitRuntimeFactory extends
AbstractOneInputOneOutputRuntimeF
}
@Override
- public AbstractOneInputOneOutputOneFramePushRuntime
createOneOutputPushRuntime(final IHyracksTaskContext ctx) {
+ public AbstractOneInputOneOutputOneFramePushRuntime
createOneOutputPushRuntime(final IHyracksTaskContext ctx)
+ throws HyracksDataException {
IEvaluatorContext evalCtx = new EvaluatorContext(ctx);
final IBinaryIntegerInspector bii =
binaryIntegerInspectorFactory.createBinaryIntegerInspector(ctx);
return new AbstractOneInputOneOutputOneFramePushRuntime() {
private final IPointable p =
VoidPointable.FACTORY.createPointable();
- private IScalarEvaluator evalMaxObjects;
- private IScalarEvaluator evalOffset = null;
- private int toWrite = 0; // how many tuples still to write
- private int toSkip = 0; // how many tuples still to skip
- private boolean firstTuple = true;
- private boolean afterLastTuple = false;
+ private final IScalarEvaluator evalMaxObjects =
+ maxObjectsEvalFactory != null ?
maxObjectsEvalFactory.createScalarEvaluator(evalCtx) : null;
+ private final IScalarEvaluator evalOffset =
+ offsetEvalFactory != null ?
offsetEvalFactory.createScalarEvaluator(evalCtx) : null;
+ private final boolean toWriteUnlimited = maxObjectsEvalFactory ==
null;
+ private int toWrite; // how many tuples still to write
+ private int toSkip; // how many tuples still to skip
+ private boolean firstTuple;
+ private boolean afterLastTuple;
@Override
public void open() throws HyracksDataException {
super.open();
- if (evalMaxObjects == null) {
+ if (tRef == null) {
initAccessAppendRef(ctx);
- evalMaxObjects =
maxObjectsEvalFactory.createScalarEvaluator(evalCtx);
- if (offsetEvalFactory != null) {
- evalOffset =
offsetEvalFactory.createScalarEvaluator(evalCtx);
- }
}
+ firstTuple = true;
afterLastTuple = false;
+ toWrite = 0;
+ toSkip = 0;
}
@Override
@@ -104,14 +110,16 @@ public class StreamLimitRuntimeFactory extends
AbstractOneInputOneOutputRuntimeF
for (int t = start; t < nTuple; t++) {
if (firstTuple) {
firstTuple = false;
- toWrite = evaluateInteger(evalMaxObjects, t);
+ if (evalMaxObjects != null) {
+ toWrite = evaluateInteger(evalMaxObjects, t);
+ }
if (evalOffset != null) {
toSkip = evaluateInteger(evalOffset, t);
}
}
if (toSkip > 0) {
toSkip--;
- } else if (toWrite > 0) {
+ } else if (toWriteUnlimited || toWrite > 0) {
toWrite--;
if (projectionList != null) {
appendProjectionToFrame(t, projectionList);
@@ -125,27 +133,16 @@ public class StreamLimitRuntimeFactory extends
AbstractOneInputOneOutputRuntimeF
}
}
- @Override
- public void close() throws HyracksDataException {
- toWrite = 0; // how many tuples still to write
- toSkip = 0; // how many tuples still to skip
- firstTuple = true;
- afterLastTuple = false;
- super.close();
- }
-
private int evaluateInteger(IScalarEvaluator eval, int tIdx)
throws HyracksDataException {
tRef.reset(tAccess, tIdx);
eval.evaluate(tRef, p);
- int lim = bii.getIntegerValue(p.getByteArray(),
p.getStartOffset(), p.getLength());
- return lim;
+ return bii.getIntegerValue(p.getByteArray(),
p.getStartOffset(), p.getLength());
}
@Override
public void flush() throws HyracksDataException {
appender.flush(writer);
}
-
};
}
}