[ASTERIXDB-2417][COMP] Fix issues with LIMIT clause
- user model changes: yes
- storage format changes: no
- interface changes: no
Details:
- Fixed NPE in optimizer when limit/offset is non-integer
- Allow float/double values that are integers in limit/offset
- Make consistent limit/offset value types (integer) between
runtime and optimizer rules
- Add internal function treat-as-integer() that returns integer
if input number conforms to the integer type or fails otherwise
Change-Id: I986fab3e79b072aa2441af293f3e16c3f37bf508
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2788
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Till Westmann <tillw@apache.org>
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 134c96f..85d1c29 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
@@ -182,13 +182,14 @@
normalization.add(new IntroduceDynamicTypeCastRule());
normalization.add(new IntroduceDynamicTypeCastForExternalFunctionRule());
normalization.add(new IntroduceEnforcedListTypeRule());
+ // Perform constant folding before common expression extraction
+ normalization.add(new ConstantFoldingRule(appCtx));
normalization.add(new ExtractCommonExpressionsRule());
// Let PushAggFuncIntoStandaloneAggregateRule run after ExtractCommonExpressionsRule
// so that PushAggFunc can happen in fewer places.
normalization.add(new PushAggFuncIntoStandaloneAggregateRule());
normalization.add(new ListifyUnnestingFunctionRule());
- normalization.add(new ConstantFoldingRule(appCtx));
normalization.add(new RemoveRedundantSelectRule());
normalization.add(new UnnestToDataScanRule());
normalization.add(new MetaFunctionToMetaVariableRule());
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 56dc675..e64889d 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
@@ -18,12 +18,15 @@
*/
package org.apache.asterix.optimizer.rules;
-import org.apache.asterix.optimizer.rules.am.AccessMethodUtils;
+import org.apache.asterix.om.base.AInt32;
+import org.apache.asterix.om.base.IAObject;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.utils.ConstantExpressionUtil;
import org.apache.commons.lang3.mutable.Mutable;
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.PhysicalOperatorTag;
import org.apache.hyracks.algebricks.core.algebra.operators.logical.AbstractLogicalOperator;
@@ -89,50 +92,19 @@
PhysicalOptimizationConfig physicalOptimizationConfig = context.getPhysicalOptimizationConfig();
LimitOperator limitOp = (LimitOperator) opRef.getValue();
OrderOperator orderOp = (OrderOperator) opRef2.getValue();
- long topK = -1;
// We don't push-down LIMIT into in-memory sort.
if (orderOp.getPhysicalOperator().getOperatorTag() != PhysicalOperatorTag.STABLE_SORT) {
return false;
}
- // Get the LIMIT constant
- if (limitOp.getMaxObjects().getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
- // Currently, we support LIMIT with a constant value.
- topK = AccessMethodUtils.getInt64Constant(limitOp.getMaxObjects());
- // If topK is huge, there is no reason to use topK sort module
- // since the original external sort's performance might be better.
- if (topK > Integer.MAX_VALUE) {
- return false;
- }
- if (topK < 0) {
- topK = 0;
- }
- } else {
+ Integer topK = getOutputLimit(limitOp);
+ if (topK == null) {
return false;
}
- // 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.
- if (limitOp.getOffset().getValue() != null) {
- if (limitOp.getOffset().getValue().getExpressionTag() == LogicalExpressionTag.CONSTANT) {
- long offset = AccessMethodUtils.getInt64Constant(limitOp.getOffset());
- if (offset < 0) {
- offset = 0;
- }
- // Check the overflow case.
- if (offset >= Integer.MAX_VALUE - topK) {
- return false;
- }
- topK += offset;
- } else {
- return false;
- }
- }
-
// Create the new ORDER operator, set the topK value, and replace the current one.
- OrderOperator newOrderOp = new OrderOperator(orderOp.getOrderExpressions(), (int) topK);
+ OrderOperator newOrderOp = new OrderOperator(orderOp.getOrderExpressions(), topK);
newOrderOp.setSourceLocation(orderOp.getSourceLocation());
newOrderOp.setPhysicalOperator(
new StableSortPOperator(physicalOptimizationConfig.getMaxFramesExternalSort(), newOrderOp.getTopK()));
@@ -146,4 +118,38 @@
return true;
}
+ static Integer getOutputLimit(LimitOperator limitOp) {
+ // Currently, we support LIMIT with a constant value.
+ ILogicalExpression maxObjectsExpr = limitOp.getMaxObjects().getValue();
+ IAObject maxObjectsValue = ConstantExpressionUtil.getConstantIaObject(maxObjectsExpr, ATypeTag.INTEGER);
+ if (maxObjectsValue == null) {
+ return null;
+ }
+ int topK = ((AInt32) maxObjectsValue).getIntegerValue();
+ if (topK < 0) {
+ topK = 0;
+ }
+
+ // 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) {
+ IAObject offsetValue = ConstantExpressionUtil.getConstantIaObject(offsetExpr, ATypeTag.INTEGER);
+ if (offsetValue == null) {
+ return null;
+ }
+ int offset = ((AInt32) offsetValue).getIntegerValue();
+ if (offset < 0) {
+ offset = 0;
+ }
+ // Check the overflow case.
+ if (offset >= Integer.MAX_VALUE - topK) {
+ return null;
+ }
+ topK += offset;
+ }
+
+ return topK;
+ }
}
diff --git a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoPrimarySearchRule.java b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoPrimarySearchRule.java
index 921d231..5427ca6 100644
--- a/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoPrimarySearchRule.java
+++ b/asterixdb/asterix-algebra/src/main/java/org/apache/asterix/optimizer/rules/PushLimitIntoPrimarySearchRule.java
@@ -25,7 +25,6 @@
import org.apache.asterix.metadata.declared.DataSource;
import org.apache.asterix.om.functions.BuiltinFunctions;
import org.apache.asterix.optimizer.rules.am.AccessMethodJobGenParams;
-import org.apache.asterix.optimizer.rules.am.AccessMethodUtils;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
@@ -67,7 +66,7 @@
}
context.addToDontApplySet(this, op);
- Long outputLimit = getOutputLimit((LimitOperator) op);
+ Integer outputLimit = PushLimitIntoOrderByRule.getOutputLimit((LimitOperator) op);
if (outputLimit == null) {
// we cannot push if limit is not constant
return false;
@@ -77,7 +76,7 @@
if (childOp.getValue().getOperatorTag() == LogicalOperatorTag.EXCHANGE) {
childOp = childOp.getValue().getInputs().get(0);
}
- boolean changed = false;
+ boolean changed;
if (childOp.getValue().getOperatorTag() == LogicalOperatorTag.SELECT) {
changed = rewriteSelect(childOp, outputLimit);
} else {
@@ -89,21 +88,7 @@
return changed;
}
- private Long getOutputLimit(LimitOperator limit) {
- if (limit.getMaxObjects().getValue().getExpressionTag() != LogicalExpressionTag.CONSTANT) {
- return null;
- }
- long outputLimit = AccessMethodUtils.getInt64Constant(limit.getMaxObjects());
- if (limit.getOffset() != null && limit.getOffset().getValue() != null) {
- if (limit.getOffset().getValue().getExpressionTag() != LogicalExpressionTag.CONSTANT) {
- return null;
- }
- outputLimit += AccessMethodUtils.getInt64Constant(limit.getOffset());
- }
- return outputLimit;
- }
-
- private boolean rewriteSelect(Mutable<ILogicalOperator> op, long outputLimit) throws AlgebricksException {
+ private boolean rewriteSelect(Mutable<ILogicalOperator> op, int outputLimit) {
SelectOperator select = (SelectOperator) op.getValue();
Set<LogicalVariable> selectedVariables = new HashSet<>();
select.getCondition().getValue().getUsedVariables(selectedVariables);
@@ -131,7 +116,7 @@
return changed;
}
- private boolean setLimitForScanOrUnnestMap(ILogicalOperator op, long outputLimit) throws AlgebricksException {
+ private boolean setLimitForScanOrUnnestMap(ILogicalOperator op, int outputLimit) {
if (op.getOperatorTag() == LogicalOperatorTag.DATASOURCESCAN) {
DataSourceScanOperator scan = (DataSourceScanOperator) op;
if (isScanPushable(scan, Collections.emptySet())) {
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 18a6597..5410a94 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
@@ -1085,38 +1085,27 @@
ILogicalExpression e = p.first;
// now look at the operator
if (i < nOps) {
- if (OperatorExpr.opIsComparison(ops.get(i))) {
- AbstractFunctionCallExpression c = createComparisonExpression(ops.get(i), sourceLoc);
+ OperatorType opType = ops.get(i);
+ boolean isCmpOp = OperatorExpr.opIsComparison(opType);
+ AbstractFunctionCallExpression f = createFunctionCallExpressionForBuiltinOperator(opType, sourceLoc);
- // chain the operators
- if (i == 0) {
- c.getArguments().add(new MutableObject<>(e));
- currExpr = c;
- if (op.isBroadcastOperand(i)) {
- BroadcastExpressionAnnotation bcast = new BroadcastExpressionAnnotation();
- bcast.setObject(BroadcastSide.LEFT);
- c.getAnnotations().put(BroadcastExpressionAnnotation.BROADCAST_ANNOTATION_KEY, bcast);
- }
- } else {
- currExpr.getArguments().add(new MutableObject<>(e));
- c.getArguments().add(new MutableObject<>(currExpr));
- currExpr = c;
- if (i == 1 && op.isBroadcastOperand(i)) {
- BroadcastExpressionAnnotation bcast = new BroadcastExpressionAnnotation();
- bcast.setObject(BroadcastSide.RIGHT);
- c.getAnnotations().put(BroadcastExpressionAnnotation.BROADCAST_ANNOTATION_KEY, bcast);
- }
+ // chain the operators
+ if (i == 0) {
+ f.getArguments().add(new MutableObject<>(e));
+ currExpr = f;
+ if (isCmpOp && op.isBroadcastOperand(i)) {
+ BroadcastExpressionAnnotation bcast = new BroadcastExpressionAnnotation();
+ bcast.setObject(BroadcastSide.LEFT);
+ f.getAnnotations().put(BroadcastExpressionAnnotation.BROADCAST_ANNOTATION_KEY, bcast);
}
} else {
- AbstractFunctionCallExpression f =
- createFunctionCallExpressionForBuiltinOperator(ops.get(i), sourceLoc);
- if (i == 0) {
- f.getArguments().add(new MutableObject<>(e));
- currExpr = f;
- } else {
- currExpr.getArguments().add(new MutableObject<>(e));
- f.getArguments().add(new MutableObject<>(currExpr));
- currExpr = f;
+ currExpr.getArguments().add(new MutableObject<>(e));
+ f.getArguments().add(new MutableObject<>(currExpr));
+ currExpr = f;
+ if (isCmpOp && i == 1 && op.isBroadcastOperand(i)) {
+ BroadcastExpressionAnnotation bcast = new BroadcastExpressionAnnotation();
+ bcast.setObject(BroadcastSide.RIGHT);
+ f.getAnnotations().put(BroadcastExpressionAnnotation.BROADCAST_ANNOTATION_KEY, bcast);
}
}
} else { // don't forget the last expression...
@@ -1359,53 +1348,52 @@
public Pair<ILogicalOperator, LogicalVariable> visit(LimitClause lc, Mutable<ILogicalOperator> tupSource)
throws CompilationException {
SourceLocation sourceLoc = lc.getSourceLocation();
- Pair<ILogicalExpression, Mutable<ILogicalOperator>> p1 = langExprToAlgExpression(lc.getLimitExpr(), tupSource);
LimitOperator opLim;
+
+ Pair<ILogicalExpression, Mutable<ILogicalOperator>> p1 = langExprToAlgExpression(lc.getLimitExpr(), tupSource);
+ AbstractFunctionCallExpression maxObjectsExpr =
+ createFunctionCallExpression(BuiltinFunctions.TREAT_AS_INTEGER, lc.getLimitExpr().getSourceLocation());
+ maxObjectsExpr.getArguments().add(new MutableObject<>(p1.first));
+
Expression offset = lc.getOffset();
if (offset != null) {
Pair<ILogicalExpression, Mutable<ILogicalOperator>> p2 = langExprToAlgExpression(offset, p1.second);
- opLim = new LimitOperator(p1.first, p2.first);
+ AbstractFunctionCallExpression offsetExpr =
+ createFunctionCallExpression(BuiltinFunctions.TREAT_AS_INTEGER, lc.getOffset().getSourceLocation());
+ offsetExpr.getArguments().add(new MutableObject<>(p2.first));
+ opLim = new LimitOperator(maxObjectsExpr, offsetExpr);
opLim.getInputs().add(p2.second);
opLim.setSourceLocation(sourceLoc);
} else {
- opLim = new LimitOperator(p1.first);
+ opLim = new LimitOperator(maxObjectsExpr);
opLim.getInputs().add(p1.second);
opLim.setSourceLocation(sourceLoc);
}
return new Pair<>(opLim, null);
}
- protected AbstractFunctionCallExpression createComparisonExpression(OperatorType t, SourceLocation sourceLoc) {
- FunctionIdentifier fi = operatorTypeToFunctionIdentifier(t);
- IFunctionInfo finfo = FunctionUtil.getFunctionInfo(fi);
- ScalarFunctionCallExpression callExpr = new ScalarFunctionCallExpression(finfo);
- callExpr.setSourceLocation(sourceLoc);
- return callExpr;
- }
-
- private static FunctionIdentifier operatorTypeToFunctionIdentifier(OperatorType t) {
- switch (t) {
- case EQ:
- return AlgebricksBuiltinFunctions.EQ;
- case NEQ:
- return AlgebricksBuiltinFunctions.NEQ;
- case GT:
- return AlgebricksBuiltinFunctions.GT;
- case GE:
- return AlgebricksBuiltinFunctions.GE;
- case LT:
- return AlgebricksBuiltinFunctions.LT;
- case LE:
- return AlgebricksBuiltinFunctions.LE;
- default:
- throw new IllegalStateException();
- }
- }
-
- protected AbstractFunctionCallExpression createFunctionCallExpressionForBuiltinOperator(OperatorType t,
+ private static AbstractFunctionCallExpression createFunctionCallExpressionForBuiltinOperator(OperatorType t,
SourceLocation sourceLoc) throws CompilationException {
FunctionIdentifier fid;
switch (t) {
+ case EQ:
+ fid = AlgebricksBuiltinFunctions.EQ;
+ break;
+ case NEQ:
+ fid = AlgebricksBuiltinFunctions.NEQ;
+ break;
+ case GT:
+ fid = AlgebricksBuiltinFunctions.GT;
+ break;
+ case GE:
+ fid = AlgebricksBuiltinFunctions.GE;
+ break;
+ case LT:
+ fid = AlgebricksBuiltinFunctions.LT;
+ break;
+ case LE:
+ fid = AlgebricksBuiltinFunctions.LE;
+ break;
case PLUS:
fid = AlgebricksBuiltinFunctions.NUMERIC_ADD;
break;
@@ -1440,6 +1428,11 @@
throw new CompilationException(ErrorCode.COMPILATION_ERROR, sourceLoc,
"Operator " + t + " is not yet implemented");
}
+ return createFunctionCallExpression(fid, sourceLoc);
+ }
+
+ private static AbstractFunctionCallExpression createFunctionCallExpression(FunctionIdentifier fid,
+ SourceLocation sourceLoc) {
ScalarFunctionCallExpression callExpr = new ScalarFunctionCallExpression(FunctionUtil.getFunctionInfo(fid));
callExpr.setSourceLocation(sourceLoc);
return callExpr;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_01/limit_type_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_01/limit_type_01.1.query.sqlpp
new file mode 100644
index 0000000..c8b2b7a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_01/limit_type_01.1.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 : Test expressions returning floating point numbers
+ * : in limit/offset
+ * Expected Result : Success
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 7.5/2.5 offset 5/2.5
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.1.query.sqlpp
new file mode 100644
index 0000000..ce55ac9
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.1.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 floating point type in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 5.5/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.2.query.sqlpp
new file mode 100644
index 0000000..e479296
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.2.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 floating point type in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 1 offset 3.5/2
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.3.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.3.query.sqlpp
new file mode 100644
index 0000000..b870b76
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.3.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 floating point type in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit "2"
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.4.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.4.query.sqlpp
new file mode 100644
index 0000000..3e3bfa7
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.4.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 floating point type in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 2 offset true
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.5.query.sqlpp
new file mode 100644
index 0000000..d783bb1
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.5.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 overflow error in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 9999999999
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.6.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.6.query.sqlpp
new file mode 100644
index 0000000..991f189
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/limit_type_02/limit_type_02.6.query.sqlpp
@@ -0,0 +1,28 @@
+/*
+ * 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 overflow error in limit/offset
+ * Expected Result : Failure
+ */
+
+select value t
+from [6,5,4,3,2,1] t
+order by t
+limit 1 offset 8888888888
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.query.sqlpp
new file mode 100644
index 0000000..5673992
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.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.
+ */
+
+use test;
+
+explain select element c
+from LineItem as c
+where (c.l_suppkey < 150)
+limit 7.5/1.5 offset 7.5/1.5;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.query.sqlpp
new file mode 100644
index 0000000..fc9d42f
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+/* scan and print a delimited text file */
+
+use test;
+
+explain
+ select element paper
+ from DBLP1 as paper
+ limit 7.5/1.5 offset 7.5/1.5;
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/limit_type_01/limit_type_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/limit_type_01/limit_type_01.1.adm
new file mode 100644
index 0000000..af64121
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/limit_type_01/limit_type_01.1.adm
@@ -0,0 +1,3 @@
+3
+4
+5
\ No newline at end of file
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
new file mode 100644
index 0000000..d070b2b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-lookup/push-limit-to-primary-lookup.5.adm
@@ -0,0 +1,34 @@
+distribute result [$$c]
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ limit 5, 5
+ -- STREAM_LIMIT |UNPARTITIONED|
+ project ([$$c])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- SORT_MERGE_EXCHANGE [$$14(ASC), $$15(ASC) ] |PARTITIONED|
+ limit 10
+ -- STREAM_LIMIT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$14, $$15, $$c] <- index-search("LineItem", 0, "test", "LineItem", FALSE, FALSE, 2, $$19, $$20, 2, $$19, $$20, TRUE, TRUE, TRUE) condition (lt($$c.getField(2), 150)) limit 10
+ -- BTREE_SEARCH |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ order (ASC, $$19) (ASC, $$20)
+ -- STABLE_SORT [$$19(ASC), $$20(ASC)] |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ project ([$$19, $$20])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ unnest-map [$$18, $$19, $$20] <- index-search("idx_LineItem_suppkey", 0, "test", "LineItem", FALSE, FALSE, 0, 1, $$17, TRUE, FALSE, FALSE)
+ -- BTREE_SEARCH |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ assign [$$17] <- [150]
+ -- ASSIGN |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |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
new file mode 100644
index 0000000..ee3e565
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/limit/push-limit-to-primary-scan/push-limit-to-primary-scan.5.adm
@@ -0,0 +1,20 @@
+distribute result [$$paper]
+-- DISTRIBUTE_RESULT |UNPARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |UNPARTITIONED|
+ limit 5, 5
+ -- STREAM_LIMIT |UNPARTITIONED|
+ project ([$$paper])
+ -- STREAM_PROJECT |PARTITIONED|
+ exchange
+ -- SORT_MERGE_EXCHANGE [$$12(ASC) ] |PARTITIONED|
+ limit 10
+ -- STREAM_LIMIT |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ data-scan []<-[$$12, $$paper] <- test.DBLP1 limit 10
+ -- DATASOURCE_SCAN |PARTITIONED|
+ exchange
+ -- ONE_TO_ONE_EXCHANGE |PARTITIONED|
+ empty-tuple-source
+ -- EMPTY_TUPLE_SOURCE |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 2adba01..c5775d8 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -10156,11 +10156,28 @@
</test-group>
<test-group name="limit">
<test-case FilePath="limit">
+ <compilation-unit name="limit_type_01">
+ <output-dir compare="Text">limit_type_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="limit">
+ <compilation-unit name="limit_type_02">
+ <output-dir compare="Text">limit_type_01</output-dir>
+ <expected-error>ASX0039: Expected integer value, got 2.75 (in line 28, at column 10)</expected-error>
+ <expected-error>ASX0039: Expected integer value, got 1.75 (in line 28, at column 19)</expected-error>
+ <expected-error>ASX1091: Type mismatch: expected value of type integer, but got the value of type string (in line 28, at column 7)</expected-error>
+ <expected-error>ASX1091: Type mismatch: expected value of type integer, but got the value of type boolean (in line 28, at column 16)</expected-error>
+ <expected-error>ASX0021: Source value 9999999999 is out of range that integer can hold - integer.MAX_VALUE: 2147483647, integer.MIN_VALUE: -2147483648</expected-error>
+ <expected-error>ASX0021: Source value 8888888888 is out of range that integer can hold - integer.MAX_VALUE: 2147483647, integer.MIN_VALUE: -2147483648</expected-error>
+ <source-location>false</source-location>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="limit">
<compilation-unit name="push-limit-to-primary-scan">
<output-dir compare="Text">push-limit-to-primary-scan</output-dir>
</compilation-unit>
</test-case>
- <test-case FilePath="limit">
+ <test-case FilePath="limit">
<compilation-unit name="push-limit-to-primary-scan-select">
<output-dir compare="Text">push-limit-to-primary-scan-select</output-dir>
</compilation-unit>
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
index 2fdbe1e..f570aa8 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/exceptions/ErrorCode.java
@@ -79,6 +79,7 @@
public static final int CANNOT_COMPARE_COMPLEX = 36;
public static final int TYPE_MISMATCH_GENERIC = 37;
public static final int DIFFERENT_LIST_TYPE_ARGS = 38;
+ public static final int INTEGER_VALUE_EXPECTED = 39;
public static final int UNSUPPORTED_JRE = 100;
diff --git a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
index ec18324..8c09d75 100644
--- a/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
+++ b/asterixdb/asterix-common/src/main/resources/asx_errormsg/en.properties
@@ -73,6 +73,7 @@
35 = Unsupported multiple statements.
36 = Cannot compare non-primitive values
38 = Input contains different list types
+39 = Expected integer value, got %1$s
100 = Unsupported JRE: %1$s
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index c7441d6..8f24864 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -121,6 +121,7 @@
import org.apache.asterix.om.typecomputer.impl.ToDoubleTypeComputer;
import org.apache.asterix.om.typecomputer.impl.ToNumberTypeComputer;
import org.apache.asterix.om.typecomputer.impl.ToObjectTypeComputer;
+import org.apache.asterix.om.typecomputer.impl.TreatAsTypeComputer;
import org.apache.asterix.om.typecomputer.impl.UnaryBinaryInt64TypeComputer;
import org.apache.asterix.om.typecomputer.impl.UnaryMinusTypeComputer;
import org.apache.asterix.om.typecomputer.impl.UnaryStringInt64TypeComputer;
@@ -1102,6 +1103,9 @@
public static final FunctionIdentifier TO_STRING =
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "to-string", 1);
+ public static final FunctionIdentifier TREAT_AS_INTEGER =
+ new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "treat-as-integer", 1);
+
public static final FunctionIdentifier EXTERNAL_LOOKUP =
new FunctionIdentifier(FunctionConstants.ASTERIX_NS, "external-lookup", FunctionIdentifier.VARARGS);
@@ -1297,6 +1301,8 @@
addFunction(TO_OBJECT, ToObjectTypeComputer.INSTANCE, true);
addFunction(TO_STRING, AStringTypeComputer.INSTANCE, true);
+ addPrivateFunction(TREAT_AS_INTEGER, TreatAsTypeComputer.INSTANCE_INTEGER, true);
+
addFunction(IF_INF, IfNanOrInfTypeComputer.INSTANCE, true);
addFunction(IF_MISSING, IfMissingTypeComputer.INSTANCE, true);
addFunction(IF_MISSING_OR_NULL, IfMissingOrNullTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TreatAsTypeComputer.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TreatAsTypeComputer.java
new file mode 100644
index 0000000..1a5861b
--- /dev/null
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/typecomputer/impl/TreatAsTypeComputer.java
@@ -0,0 +1,48 @@
+/*
+ * 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.om.typecomputer.impl;
+
+import org.apache.asterix.om.exceptions.TypeMismatchException;
+import org.apache.asterix.om.typecomputer.base.AbstractResultTypeComputer;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.IAType;
+import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
+import org.apache.hyracks.algebricks.common.exceptions.AlgebricksException;
+import org.apache.hyracks.algebricks.core.algebra.base.ILogicalExpression;
+
+public class TreatAsTypeComputer extends AbstractResultTypeComputer {
+ public static final TreatAsTypeComputer INSTANCE_INTEGER = new TreatAsTypeComputer(BuiltinType.AINT32);
+
+ private final IAType type;
+
+ private TreatAsTypeComputer(IAType type) {
+ this.type = type;
+ }
+
+ @Override
+ protected IAType getResultType(ILogicalExpression expr, IAType... strippedInputTypes) throws AlgebricksException {
+ IAType inputType = strippedInputTypes[0];
+ if (ATypeHierarchy.isCompatible(inputType.getTypeTag(), type.getTypeTag())) {
+ return type;
+ } else {
+ throw new TypeMismatchException(expr.getSourceLocation(), inputType.getTypeTag(), type.getTypeTag());
+ }
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/TreatAsIntegerDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/TreatAsIntegerDescriptor.java
new file mode 100644
index 0000000..2c3e148
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/TreatAsIntegerDescriptor.java
@@ -0,0 +1,130 @@
+/*
+ * 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.runtime.evaluators.functions;
+
+import java.io.DataOutput;
+
+import org.apache.asterix.common.exceptions.ErrorCode;
+import org.apache.asterix.common.exceptions.RuntimeDataException;
+import org.apache.asterix.formats.nontagged.SerializerDeserializerProvider;
+import org.apache.asterix.om.base.AInt32;
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptor;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.om.types.BuiltinType;
+import org.apache.asterix.om.types.hierachy.ATypeHierarchy;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.asterix.runtime.exceptions.TypeMismatchException;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IHyracksTaskContext;
+import org.apache.hyracks.api.dataflow.value.ISerializerDeserializer;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+
+public class TreatAsIntegerDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+ private static final long serialVersionUID = 1L;
+ public static final IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+ @Override
+ public IFunctionDescriptor createFunctionDescriptor() {
+ return new TreatAsIntegerDescriptor();
+ }
+ };
+
+ @Override
+ public IScalarEvaluatorFactory createEvaluatorFactory(final IScalarEvaluatorFactory[] args) {
+ return new IScalarEvaluatorFactory() {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public IScalarEvaluator createScalarEvaluator(final IHyracksTaskContext ctx) throws HyracksDataException {
+
+ final IScalarEvaluator inputEval = args[0].createScalarEvaluator(ctx);
+ final IPointable inputArg = new VoidPointable();
+ final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage();
+ final DataOutput out = resultStorage.getDataOutput();
+ final AMutableInt32 aInt32 = new AMutableInt32(0);
+
+ @SuppressWarnings("unchecked")
+ final ISerializerDeserializer<AInt32> int32Ser =
+ SerializerDeserializerProvider.INSTANCE.getSerializerDeserializer(BuiltinType.AINT32);
+
+ return new IScalarEvaluator() {
+ @Override
+ public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
+ inputEval.evaluate(tuple, inputArg);
+
+ int intValue;
+ byte[] bytes = inputArg.getByteArray();
+ int startOffset = inputArg.getStartOffset();
+ ATypeTag tt = ATypeTag.VALUE_TYPE_MAPPING[bytes[startOffset]];
+ switch (tt) {
+ case TINYINT:
+ case SMALLINT:
+ case INTEGER:
+ case BIGINT:
+ intValue = ATypeHierarchy.getIntegerValue(getIdentifier().getName(), 0, bytes,
+ startOffset, true);
+ break;
+ case FLOAT:
+ case DOUBLE:
+ double doubleValue =
+ ATypeHierarchy.getDoubleValue(getIdentifier().getName(), 0, bytes, startOffset);
+ intValue = asInt(doubleValue);
+ break;
+ default:
+ throw new TypeMismatchException(sourceLoc, bytes[startOffset],
+ ATypeTag.SERIALIZED_INT8_TYPE_TAG, ATypeTag.SERIALIZED_INT16_TYPE_TAG,
+ ATypeTag.SERIALIZED_INT32_TYPE_TAG, ATypeTag.SERIALIZED_INT64_TYPE_TAG,
+ ATypeTag.SERIALIZED_FLOAT_TYPE_TAG, ATypeTag.SERIALIZED_DOUBLE_TYPE_TAG);
+ }
+
+ resultStorage.reset();
+ aInt32.setValue(intValue);
+ int32Ser.serialize(aInt32, out);
+ result.set(resultStorage);
+ }
+
+ private int asInt(double d) throws HyracksDataException {
+ if (Double.isFinite(d)) {
+ long v = (long) d;
+ if (v == d && Integer.MIN_VALUE <= v && v <= Integer.MAX_VALUE) {
+ return (int) v;
+ }
+ }
+ throw new RuntimeDataException(ErrorCode.INTEGER_VALUE_EXPECTED, sourceLoc, d);
+ }
+ };
+ }
+ };
+
+ }
+
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return BuiltinFunctions.TREAT_AS_INTEGER;
+ }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index 25d90d3..04f4a1e 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -281,6 +281,7 @@
import org.apache.asterix.runtime.evaluators.functions.ToNumberDescriptor;
import org.apache.asterix.runtime.evaluators.functions.ToObjectDescriptor;
import org.apache.asterix.runtime.evaluators.functions.ToStringDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.TreatAsIntegerDescriptor;
import org.apache.asterix.runtime.evaluators.functions.UUIDDescriptor;
import org.apache.asterix.runtime.evaluators.functions.binary.BinaryConcatDescriptor;
import org.apache.asterix.runtime.evaluators.functions.binary.BinaryLengthDescriptor;
@@ -790,6 +791,8 @@
fc.addGenerated(ToObjectDescriptor.FACTORY);
fc.addGenerated(ToStringDescriptor.FACTORY);
+ fc.addGenerated(TreatAsIntegerDescriptor.FACTORY);
+
// Cast function
fc.addGenerated(CastTypeDescriptor.FACTORY);
fc.addGenerated(CastTypeLaxDescriptor.FACTORY);