diff --git a/core/src/main/java/org/opensearch/sql/calcite/utils/PPLOperandTypes.java b/core/src/main/java/org/opensearch/sql/calcite/utils/PPLOperandTypes.java
index dfdebb233c..abf37e6839 100644
--- a/core/src/main/java/org/opensearch/sql/calcite/utils/PPLOperandTypes.java
+++ b/core/src/main/java/org/opensearch/sql/calcite/utils/PPLOperandTypes.java
@@ -104,6 +104,13 @@ private PPLOperandTypes() {}
UDFOperandMetadata.wrap(
OperandTypes.family(
SqlTypeFamily.CHARACTER, SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER));
+
+ public static final UDFOperandMetadata STRING_OR_STRING_INTEGER =
+ UDFOperandMetadata.wrap(
+ (CompositeOperandTypeChecker)
+ OperandTypes.family(SqlTypeFamily.CHARACTER)
+ .or(OperandTypes.family(SqlTypeFamily.CHARACTER, SqlTypeFamily.INTEGER)));
+
public static final UDFOperandMetadata STRING_STRING_INTEGER_INTEGER =
UDFOperandMetadata.wrap(
OperandTypes.family(
diff --git a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java
index edc9ebb0df..0fe042a515 100644
--- a/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java
+++ b/core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java
@@ -156,6 +156,7 @@ public enum BuiltinFunctionName {
/** Text Functions. */
TOSTRING(FunctionName.of("tostring")),
+ TONUMBER(FunctionName.of("tonumber")),
/** IP Functions. */
CIDRMATCH(FunctionName.of("cidrmatch")),
diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java
index 36d617af01..26960192f0 100644
--- a/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java
+++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLBuiltinOperators.java
@@ -67,6 +67,7 @@
import org.opensearch.sql.expression.function.udf.RexExtractMultiFunction;
import org.opensearch.sql.expression.function.udf.RexOffsetFunction;
import org.opensearch.sql.expression.function.udf.SpanFunction;
+import org.opensearch.sql.expression.function.udf.ToNumberFunction;
import org.opensearch.sql.expression.function.udf.ToStringFunction;
import org.opensearch.sql.expression.function.udf.condition.EarliestFunction;
import org.opensearch.sql.expression.function.udf.condition.EnhancedCoalesceFunction;
@@ -412,6 +413,7 @@ public class PPLBuiltinOperators extends ReflectiveSqlOperatorTable {
RELEVANCE_QUERY_FUNCTION_INSTANCE.toUDF("multi_match", false);
public static final SqlOperator NUMBER_TO_STRING =
new NumberToStringFunction().toUDF("NUMBER_TO_STRING");
+ public static final SqlOperator TONUMBER = new ToNumberFunction().toUDF("TONUMBER");
public static final SqlOperator TOSTRING = new ToStringFunction().toUDF("TOSTRING");
public static final SqlOperator WIDTH_BUCKET =
new org.opensearch.sql.expression.function.udf.binning.WidthBucketFunction()
diff --git a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java
index d72b6a7a25..76a6755ad5 100644
--- a/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java
+++ b/core/src/main/java/org/opensearch/sql/expression/function/PPLFuncImpTable.java
@@ -213,6 +213,7 @@
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TIMESTAMPDIFF;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TIME_FORMAT;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TIME_TO_SEC;
+import static org.opensearch.sql.expression.function.BuiltinFunctionName.TONUMBER;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TOSTRING;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TO_DAYS;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.TO_SECONDS;
@@ -956,6 +957,7 @@ void populate() {
registerOperator(WEEKOFYEAR, PPLBuiltinOperators.WEEK);
registerOperator(INTERNAL_PATTERN_PARSER, PPLBuiltinOperators.PATTERN_PARSER);
+ registerOperator(TONUMBER, PPLBuiltinOperators.TONUMBER);
registerOperator(TOSTRING, PPLBuiltinOperators.TOSTRING);
register(
TOSTRING,
@@ -1131,7 +1133,6 @@ void populate() {
SqlTypeFamily.INTEGER,
SqlTypeFamily.INTEGER)),
false));
-
register(
LOG,
(FunctionImp2)
diff --git a/core/src/main/java/org/opensearch/sql/expression/function/udf/ToNumberFunction.java b/core/src/main/java/org/opensearch/sql/expression/function/udf/ToNumberFunction.java
new file mode 100644
index 0000000000..2557121b0c
--- /dev/null
+++ b/core/src/main/java/org/opensearch/sql/expression/function/udf/ToNumberFunction.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.sql.expression.function.udf;
+
+import java.math.BigInteger;
+import java.util.List;
+import org.apache.calcite.adapter.enumerable.NotNullImplementor;
+import org.apache.calcite.adapter.enumerable.NullPolicy;
+import org.apache.calcite.adapter.enumerable.RexToLixTranslator;
+import org.apache.calcite.linq4j.function.Strict;
+import org.apache.calcite.linq4j.tree.Expression;
+import org.apache.calcite.linq4j.tree.Expressions;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.opensearch.sql.calcite.utils.PPLOperandTypes;
+import org.opensearch.sql.expression.function.ImplementorUDF;
+import org.opensearch.sql.expression.function.UDFOperandMetadata;
+
+/**
+ * A custom implementation of number/boolean to string .
+ *
+ *
This operator is necessary because tostring has following requirements "binary" Converts a
+ * number to a binary value. "hex" Converts the number to a hexadecimal value. "commas" Formats the
+ * number with commas. If the number includes a decimal, the function rounds the number to nearest
+ * two decimal places. "duration" Converts the value in seconds to the readable time format
+ * HH:MM:SS. if not format parameter provided, then consider value as boolean
+ */
+public class ToNumberFunction extends ImplementorUDF {
+ public ToNumberFunction() {
+ super(
+ new org.opensearch.sql.expression.function.udf.ToNumberFunction.ToNumberImplementor(),
+ NullPolicy.ANY);
+ }
+
+ @Override
+ public SqlReturnTypeInference getReturnTypeInference() {
+ return ReturnTypes.DOUBLE_FORCE_NULLABLE;
+ }
+
+ @Override
+ public UDFOperandMetadata getOperandMetadata() {
+ return PPLOperandTypes.STRING_OR_STRING_INTEGER;
+ }
+
+ public static class ToNumberImplementor implements NotNullImplementor {
+
+ @Override
+ public Expression implement(
+ RexToLixTranslator translator, RexCall call, List translatedOperands) {
+ Expression fieldValue = translatedOperands.get(0);
+ int base = 10;
+ if (translatedOperands.size() > 1) {
+ Expression baseExpr = translatedOperands.get(1);
+ return Expressions.call(ToNumberFunction.class, "toNumber", fieldValue, baseExpr);
+ } else {
+ return Expressions.call(ToNumberFunction.class, "toNumber", fieldValue);
+ }
+ }
+ }
+
+ @Strict
+ public static Number toNumber(String numStr) {
+ return toNumber(numStr, 10);
+ }
+
+ @Strict
+ public static Number toNumber(String numStr, int base) {
+ if (base < 2 || base > 36) {
+ throw new IllegalArgumentException("Base has to be between 2 and 36.");
+ }
+ Number result = null;
+ try {
+ if (base == 10) {
+ if (numStr.contains(".")) {
+ result = Double.parseDouble(numStr);
+ } else {
+ result = Long.parseLong(numStr);
+ }
+ } else {
+ BigInteger bigInteger = new BigInteger(numStr, base);
+ result = bigInteger.longValue();
+ }
+ } catch (Exception e) {
+
+ }
+ return result;
+ }
+}
diff --git a/core/src/test/java/org/opensearch/sql/expression/function/udf/ToNumberFunctionTest.java b/core/src/test/java/org/opensearch/sql/expression/function/udf/ToNumberFunctionTest.java
new file mode 100644
index 0000000000..34ed102ec5
--- /dev/null
+++ b/core/src/test/java/org/opensearch/sql/expression/function/udf/ToNumberFunctionTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.sql.expression.function.udf;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.junit.jupiter.api.Test;
+import org.opensearch.sql.calcite.utils.PPLOperandTypes;
+
+public class ToNumberFunctionTest {
+
+ private final ToNumberFunction function = new ToNumberFunction();
+
+ @Test
+ void testGetReturnTypeInference() {
+ assertEquals(ReturnTypes.DOUBLE_FORCE_NULLABLE, function.getReturnTypeInference());
+ }
+
+ @Test
+ void testGetOperandMetadata() {
+ assertEquals(PPLOperandTypes.STRING_OR_STRING_INTEGER, function.getOperandMetadata());
+ }
+
+ @Test
+ void testToNumberWithDefaultBase() {
+ assertEquals(123L, ToNumberFunction.toNumber("123"));
+ assertEquals(0L, ToNumberFunction.toNumber("0"));
+ assertEquals(-456L, ToNumberFunction.toNumber("-456"));
+ assertEquals(123.45, ToNumberFunction.toNumber("123.45"));
+ assertEquals(-123.45, ToNumberFunction.toNumber("-123.45"));
+ assertEquals(0.5, ToNumberFunction.toNumber("0.5"));
+ assertEquals(-0.5, ToNumberFunction.toNumber("-0.5"));
+ }
+
+ @Test
+ void testToNumberWithBase10() {
+ assertEquals(123L, ToNumberFunction.toNumber("123", 10));
+ assertEquals(0L, ToNumberFunction.toNumber("0", 10));
+ assertEquals(-456L, ToNumberFunction.toNumber("-456", 10));
+ assertEquals(123.45, ToNumberFunction.toNumber("123.45", 10));
+ assertEquals(-123.45, ToNumberFunction.toNumber("-123.45", 10));
+ }
+
+ @Test
+ void testToNumberWithBase2() {
+ assertEquals(5L, ToNumberFunction.toNumber("101", 2));
+ assertEquals(0L, ToNumberFunction.toNumber("0", 2));
+ assertEquals(1L, ToNumberFunction.toNumber("1", 2));
+ assertEquals(7L, ToNumberFunction.toNumber("111", 2));
+ assertEquals(10L, ToNumberFunction.toNumber("1010", 2));
+ }
+
+ @Test
+ void testToNumberWithBase8() {
+ assertEquals(64L, ToNumberFunction.toNumber("100", 8));
+ assertEquals(8L, ToNumberFunction.toNumber("10", 8));
+ assertEquals(83L, ToNumberFunction.toNumber("123", 8));
+ assertEquals(511L, ToNumberFunction.toNumber("777", 8));
+ }
+
+ @Test
+ void testToNumberWithBase16() {
+ assertEquals(255L, ToNumberFunction.toNumber("FF", 16));
+ assertEquals(16L, ToNumberFunction.toNumber("10", 16));
+ assertEquals(171L, ToNumberFunction.toNumber("AB", 16));
+ assertEquals(291L, ToNumberFunction.toNumber("123", 16));
+ assertEquals(4095L, ToNumberFunction.toNumber("FFF", 16));
+ }
+
+ @Test
+ void testToNumberWithBase36() {
+ assertEquals(35L, ToNumberFunction.toNumber("Z", 36));
+ assertEquals(1295L, ToNumberFunction.toNumber("ZZ", 36));
+ assertEquals(46655L, ToNumberFunction.toNumber("ZZZ", 36));
+ }
+
+ @Test
+ void testToNumberWithDecimalBase2() {
+ assertEquals(2L, ToNumberFunction.toNumber("10", 2));
+ assertEquals(1L, ToNumberFunction.toNumber("1", 2));
+ assertEquals(3L, ToNumberFunction.toNumber("11", 2));
+ }
+
+ @Test
+ void testToNumberWithDecimalBase16() {
+ assertEquals(255L, ToNumberFunction.toNumber("FF", 16));
+ assertEquals(16L, ToNumberFunction.toNumber("10", 16));
+ assertEquals(171L, ToNumberFunction.toNumber("AB", 16));
+ }
+
+ @Test
+ void testToNumberWithNegativeDecimal() {
+ assertEquals(-2L, ToNumberFunction.toNumber("-10", 2));
+ assertEquals(-255L, ToNumberFunction.toNumber("-FF", 16));
+ assertEquals(-123.45, ToNumberFunction.toNumber("-123.45", 10));
+ }
+
+ @Test
+ void testToNumberWithEmptyFractionalPart() {
+ assertEquals(123.0, ToNumberFunction.toNumber("123.", 10));
+ assertEquals(255L, ToNumberFunction.toNumber("FF", 16));
+ assertEquals(5L, ToNumberFunction.toNumber("101", 2));
+ }
+
+ @Test
+ void testToNumberWithZeroIntegerPart() {
+ assertEquals(0.5, ToNumberFunction.toNumber("0.5", 10));
+ assertEquals(0L, ToNumberFunction.toNumber("0", 2));
+ }
+
+ @Test
+ void testToNumberInvalidBase() {
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ToNumberFunction.toNumber("123", 1);
+ });
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ToNumberFunction.toNumber("123", 37);
+ });
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ToNumberFunction.toNumber("123", 0);
+ });
+
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> {
+ ToNumberFunction.toNumber("123", -1);
+ });
+ }
+
+ @Test
+ void testToNumberInvalidDigits() {
+ assertEquals(null, ToNumberFunction.toNumber("12A", 10));
+ assertEquals(null, ToNumberFunction.toNumber("102", 2));
+ assertEquals(null, ToNumberFunction.toNumber("101.101", 2));
+ assertEquals(null, ToNumberFunction.toNumber("189", 8));
+ assertEquals(null, ToNumberFunction.toNumber("GHI", 16));
+ assertEquals(null, ToNumberFunction.toNumber("FF.8", 16));
+ }
+
+ @Test
+ void testToNumberEdgeCases() {
+ assertEquals(0L, ToNumberFunction.toNumber("0", 2));
+ assertEquals(0L, ToNumberFunction.toNumber("0", 36));
+ assertEquals(0.0, ToNumberFunction.toNumber("0.0", 10));
+ assertEquals(0.0, ToNumberFunction.toNumber("0.000", 10));
+ }
+
+ @Test
+ void testToNumberLargeNumbers() {
+ assertEquals(
+ (long) Integer.MAX_VALUE, ToNumberFunction.toNumber(String.valueOf(Integer.MAX_VALUE), 10));
+ assertEquals(
+ (long) Integer.MIN_VALUE, ToNumberFunction.toNumber(String.valueOf(Integer.MIN_VALUE), 10));
+ }
+
+ @Test
+ void testToNumberCaseInsensitivity() {
+ assertEquals(255L, ToNumberFunction.toNumber("ff", 16));
+ assertEquals(255L, ToNumberFunction.toNumber("FF", 16));
+ assertEquals(255L, ToNumberFunction.toNumber("fF", 16));
+ assertEquals(171L, ToNumberFunction.toNumber("ab", 16));
+ assertEquals(171L, ToNumberFunction.toNumber("AB", 16));
+ }
+}
diff --git a/docs/user/ppl/functions/conversion.rst b/docs/user/ppl/functions/conversion.rst
index 82d760cc3c..c55e868f2c 100644
--- a/docs/user/ppl/functions/conversion.rst
+++ b/docs/user/ppl/functions/conversion.rst
@@ -118,6 +118,58 @@ Use string in comparison operator example ::
+------+-------+------+-------+------+------+------+
+
+TONUMBER
+--------
+
+Description
+>>>>>>>>>>>
+
+The following usage options are available, depending on the parameter types and the number of parameters.
+
+Usage: tonumber(string, [base]) converts the value in first argument to provided base type string in second argument. If second argument is not provided, then it converts to base 10 number representation.
+
+Return type: Number
+
+
+You can use this function with the eval commands and as part of eval expressions.
+Base values can be between 2 and 36. The maximum value supported for base 10 is +(2-2^-52)·2^1023 and minimum is -(2-2^-52)·2^1023.
+The maximum for other supported bases is 2^63-1 (or 7FFFFFFFFFFFFFFF) and minimum is -2^63 (or -7FFFFFFFFFFFFFFF).
+
+You can use this function to convert a string representation of a binary number to return the corresponding number in base 10.
+
+Following example converts a string in binary to the number representation::
+
+ os> source=people | eval int_value = tonumber('010101',2) | fields int_value | head 1
+ fetched rows / total rows = 1/1
+ +-----------+
+ | int_value |
+ |-----------|
+ | 21.0 |
+ +-----------+
+
+
+Following example converts a string in hex to the number representation::
+
+ os> source=people | eval int_value = tonumber('FA34',16) | fields int_value | head 1
+ fetched rows / total rows = 1/1
+ +-----------+
+ | int_value |
+ |-----------|
+ | 64052.0 |
+ +-----------+
+
+Following example converts a string in decimal to the number representation::
+
+ os> source=people | eval int_value = tonumber('4598') | fields int_value | head 1
+ fetched rows / total rows = 1/1
+ +-----------+
+ | int_value |
+ |-----------|
+ | 4598.0 |
+ +-----------+
+
+
TOSTRING
-----------
@@ -200,4 +252,3 @@ Example::
|-------------|
| TRUE |
+-------------+
-
diff --git a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 b/ppl/src/main/antlr/OpenSearchPPLLexer.g4
index b3587c319c..8d64bfabb1 100644
--- a/ppl/src/main/antlr/OpenSearchPPLLexer.g4
+++ b/ppl/src/main/antlr/OpenSearchPPLLexer.g4
@@ -408,6 +408,7 @@ STRFTIME: 'STRFTIME';
// TEXT FUNCTIONS
SUBSTR: 'SUBSTR';
SUBSTRING: 'SUBSTRING';
+TONUMBER: 'TONUMBER';
TOSTRING: 'TOSTRING';
LTRIM: 'LTRIM';
RTRIM: 'RTRIM';
diff --git a/ppl/src/main/antlr/OpenSearchPPLParser.g4 b/ppl/src/main/antlr/OpenSearchPPLParser.g4
index d115f3dec3..cc5d3d4aa8 100644
--- a/ppl/src/main/antlr/OpenSearchPPLParser.g4
+++ b/ppl/src/main/antlr/OpenSearchPPLParser.g4
@@ -1304,6 +1304,7 @@ textFunctionName
| LOCATE
| REPLACE
| REVERSE
+ | TONUMBER
| REGEXP_REPLACE
;
@@ -1509,6 +1510,7 @@ searchableKeyWord
| USING
| VALUE
| CAST
+ | TONUMBER
| TOSTRING
| GET_FORMAT
| EXTRACT
diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLToNumberFunctionTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLToNumberFunctionTest.java
new file mode 100644
index 0000000000..bd4f2c293b
--- /dev/null
+++ b/ppl/src/test/java/org/opensearch/sql/ppl/calcite/CalcitePPLToNumberFunctionTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.opensearch.sql.ppl.calcite;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.test.CalciteAssert;
+import org.junit.Test;
+
+public class CalcitePPLToNumberFunctionTest extends CalcitePPLAbstractTest {
+
+ public CalcitePPLToNumberFunctionTest() {
+ super(CalciteAssert.SchemaSpec.SCOTT_WITH_TEMPORAL);
+ }
+
+ @Test
+ public void testNumberBinary() {
+ String ppl = "source=EMP | eval int_value = tonumber('010101',2) | fields int_value|head 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(int_value=[TONUMBER('010101':VARCHAR, 2)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "int_value=21.0\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('010101', 2) `int_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberHex() {
+ String ppl = "source=EMP | eval int_value = tonumber('FA34',16) | fields int_value|head 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(int_value=[TONUMBER('FA34':VARCHAR, 16)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "int_value=64052.0\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('FA34', 16) `int_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberHexMinLimit() {
+ String ppl =
+ "source=EMP | eval long_value = tonumber('-7FFFFFFFFFFFFFFF',16) | fields long_value|head"
+ + " 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(long_value=[TONUMBER('-7FFFFFFFFFFFFFFF':VARCHAR, 16)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "long_value=-9.223372036854776E18\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('-7FFFFFFFFFFFFFFF', 16) `long_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberHexMaxLimit() {
+ String ppl =
+ "source=EMP | eval long_value = tonumber('7FFFFFFFFFFFFFFF',16) | fields long_value|head"
+ + " 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(long_value=[TONUMBER('7FFFFFFFFFFFFFFF':VARCHAR, 16)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "long_value=9.223372036854776E18\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('7FFFFFFFFFFFFFFF', 16) `long_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberHexOverNegativeMaxLimit() {
+ String ppl =
+ "source=EMP | eval long_value = tonumber('-FFFFFFFFFFFFFFFF',16) | fields long_value|head"
+ + " 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(long_value=[TONUMBER('-FFFFFFFFFFFFFFFF':VARCHAR, 16)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "long_value=1.0\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('-FFFFFFFFFFFFFFFF', 16) `long_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberHexOverPositiveMaxLimit() {
+ String ppl =
+ "source=EMP | eval long_value = tonumber('FFFFFFFFFFFFFFFF',16) | fields long_value|head 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(long_value=[TONUMBER('FFFFFFFFFFFFFFFF':VARCHAR, 16)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "long_value=-1.0\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('FFFFFFFFFFFFFFFF', 16) `long_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumber() {
+ String ppl = "source=EMP | eval int_value = tonumber('4598') | fields int_value|head 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(int_value=[TONUMBER('4598':VARCHAR)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "int_value=4598.0\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql = "SELECT `TONUMBER`('4598') `int_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+
+ @Test
+ public void testNumberDecimal() {
+ String ppl = "source=EMP | eval int_value = tonumber('4598.54922') | fields int_value|head 1";
+ RelNode root = getRelNode(ppl);
+ String expectedLogical =
+ "LogicalSort(fetch=[1])\n"
+ + " LogicalProject(int_value=[TONUMBER('4598.54922':VARCHAR)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ verifyLogical(root, expectedLogical);
+ String expectedResult = "int_value=4598.54922\n";
+ verifyResult(root, expectedResult);
+
+ String expectedSparkSql =
+ "SELECT `TONUMBER`('4598.54922') `int_value`\nFROM `scott`.`EMP`\nLIMIT 1";
+ verifyPPLToSparkSQL(root, expectedSparkSql);
+ }
+}