You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by zo...@apache.org on 2023/08/22 02:17:45 UTC
[calcite] branch main updated: [CALCITE-5848] Add BIT_GET and GETBIT functions (enabled in Spark library)
This is an automated email from the ASF dual-hosted git repository.
zoudan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 95a59d66c0 [CALCITE-5848] Add BIT_GET and GETBIT functions (enabled in Spark library)
95a59d66c0 is described below
commit 95a59d66c0448f2742f229c0d7e7ed140254c880
Author: Runkang He <hr...@gmail.com>
AuthorDate: Sat Jul 15 10:53:07 2023 +0800
[CALCITE-5848] Add BIT_GET and GETBIT functions (enabled in Spark library)
---
.../calcite/adapter/enumerable/RexImpTable.java | 6 ++
.../apache/calcite/runtime/CalciteResource.java | 6 ++
.../org/apache/calcite/runtime/SqlFunctions.java | 33 ++++++++++
.../calcite/sql/fun/SqlLibraryOperators.java | 13 ++++
.../org/apache/calcite/sql/type/ReturnTypes.java | 13 ++++
.../org/apache/calcite/util/BuiltInMethod.java | 1 +
.../calcite/runtime/CalciteResource.properties | 2 +
site/_docs/reference.md | 2 +
.../org/apache/calcite/test/SqlOperatorTest.java | 77 ++++++++++++++++++++++
9 files changed, 153 insertions(+)
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 588b49a15a..1f8994072a 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -143,6 +143,7 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_TO_STRING;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ARRAY_UNION;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ASINH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ATANH;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.BIT_GET;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.BIT_LENGTH;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.BOOL_AND;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.BOOL_OR;
@@ -180,6 +181,7 @@ import static org.apache.calcite.sql.fun.SqlLibraryOperators.FORMAT_TIMESTAMP;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_BASE32;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_BASE64;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.FROM_HEX;
+import static org.apache.calcite.sql.fun.SqlLibraryOperators.GETBIT;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.ILIKE;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_INF;
import static org.apache.calcite.sql.fun.SqlLibraryOperators.IS_NAN;
@@ -528,6 +530,10 @@ public class RexImpTable {
NullPolicy.STRICT);
defineMethod(BIT_LENGTH, BuiltInMethod.BIT_LENGTH.method,
NullPolicy.STRICT);
+ defineMethod(BIT_GET, BuiltInMethod.BIT_GET.method,
+ NullPolicy.STRICT);
+ defineMethod(GETBIT, BuiltInMethod.BIT_GET.method,
+ NullPolicy.STRICT);
map.put(CONCAT, new ConcatImplementor());
defineMethod(CONCAT_FUNCTION, BuiltInMethod.MULTI_STRING_CONCAT.method,
NullPolicy.STRICT);
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index bb33ae9cf0..83a7ec5af7 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -1055,4 +1055,10 @@ public interface CalciteResource {
@BaseMessage("A table function at most has one input table with row semantics. Table function ''{0}'' has multiple input tables with row semantics")
ExInst<SqlValidatorException> multipleRowSemanticsTables(String funcName);
+
+ @BaseMessage("BIT_GET/GETBIT error: negative position {0,number} not allowed")
+ ExInst<CalciteException> illegalNegativeBitGetPosition(int position);
+
+ @BaseMessage("BIT_GET/GETBIT error: position {0,number} exceeds the bit upper limit {1,number}")
+ ExInst<CalciteException> illegalBitGetPositionExceedsLimit(int position, int size);
}
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 3faab94e95..8caa2803dd 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -949,6 +949,39 @@ public class SqlFunctions {
return s.length() * 8;
}
+ /** SQL BIT_GET(value, position) function. */
+ public static byte bitGet(long value, int position) {
+ checkPosition(position, java.lang.Long.SIZE - 1);
+ return (byte) ((value >> position) & 1);
+ }
+
+ /** SQL BIT_GET(value, position) function. */
+ public static byte bitGet(int value, int position) {
+ checkPosition(position, java.lang.Integer.SIZE - 1);
+ return (byte) ((value >> position) & 1);
+ }
+
+ /** SQL BIT_GET(value, position) function. */
+ public static byte bitGet(short value, int position) {
+ checkPosition(position, java.lang.Short.SIZE - 1);
+ return (byte) ((value >> position) & 1);
+ }
+
+ /** SQL BIT_GET(value, position) function. */
+ public static byte bitGet(byte value, int position) {
+ checkPosition(position, java.lang.Byte.SIZE - 1);
+ return (byte) ((value >> position) & 1);
+ }
+
+ private static void checkPosition(int position, int limit) {
+ if (position < 0) {
+ throw RESOURCE.illegalNegativeBitGetPosition(position).ex();
+ }
+ if (limit < position) {
+ throw RESOURCE.illegalBitGetPositionExceedsLimit(position, limit).ex();
+ }
+ }
+
/** SQL {@code string || string} operator. */
public static String concat(String s0, String s1) {
return s0 + s1;
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 7aaa505cfb..6bceb25d3e 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -1941,4 +1941,17 @@ public abstract class SqlLibraryOperators {
ReturnTypes.INTEGER_NULLABLE,
OperandTypes.or(OperandTypes.CHARACTER, OperandTypes.BINARY),
SqlFunctionCategory.NUMERIC);
+
+ /** The "BIT_GET(value, position)" function. */
+ @LibraryOperator(libraries = {SPARK})
+ public static final SqlBasicFunction BIT_GET =
+ SqlBasicFunction.create("BIT_GET",
+ ReturnTypes.TINYINT_NULLABLE,
+ OperandTypes.NUMERIC_INTEGER,
+ SqlFunctionCategory.NUMERIC);
+
+ /** Alias for {@link #BIT_GET}. */
+ @LibraryOperator(libraries = {SPARK})
+ public static final SqlFunction GETBIT =
+ BIT_GET.withName("GETBIT");
}
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index acdd678a9d..3b13ba24cc 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -409,6 +409,19 @@ public abstract class ReturnTypes {
public static final SqlReturnTypeInference CHAR_FORCE_NULLABLE =
CHAR.andThen(SqlTypeTransforms.FORCE_NULLABLE);
+ /**
+ * Type-inference strategy whereby the result type of a call is a TINYINT.
+ */
+ public static final SqlReturnTypeInference TINYINT =
+ explicit(SqlTypeName.TINYINT);
+
+ /**
+ * Type-inference strategy whereby the result type of a call is a TINYINT
+ * with nulls allowed if any of the operands allow nulls.
+ */
+ public static final SqlReturnTypeInference TINYINT_NULLABLE =
+ TINYINT.andThen(SqlTypeTransforms.TO_NULLABLE);
+
/**
* Type-inference strategy whereby the result type of a call is an Integer.
*/
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 19aadc93d7..d4390f1bed 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -428,6 +428,7 @@ public enum BuiltInMethod {
OCTET_LENGTH(SqlFunctions.class, "octetLength", ByteString.class),
CHAR_LENGTH(SqlFunctions.class, "charLength", String.class),
BIT_LENGTH(SqlFunctions.class, "bitLength", String.class),
+ BIT_GET(SqlFunctions.class, "bitGet", long.class, int.class),
STRING_CONCAT(SqlFunctions.class, "concat", String.class, String.class),
STRING_CONCAT_WITH_NULL(SqlFunctions.class, "concatWithNull", String.class,
String.class),
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index 8c67a7538b..809d168a8e 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -345,4 +345,6 @@ InvalidPartitionKeys=Only tables with set semantics may be partitioned. Invalid
InvalidOrderBy=Only tables with set semantics may be ordered. Invalid ORDER BY clause in the {0,number,#}-th operand of table function ''{1}''
MultipleRowSemanticsTables=A table function at most has one input table with row semantics. Table function ''{0}'' has multiple input tables with row semantics
NoOperator=No operator for ''{0}'' with kind: ''{1}'', syntax: ''{2}'' during JSON deserialization
+IllegalNegativeBitGetPosition=BIT_GET/GETBIT error: negative position {0,number} not allowed
+IllegalBitGetPositionExceedsLimit=BIT_GET/GETBIT error: position {0,number} exceeds the bit upper limit {1,number}
# End CalciteResource.properties
diff --git a/site/_docs/reference.md b/site/_docs/reference.md
index 018a96cf41..c7555adcbf 100644
--- a/site/_docs/reference.md
+++ b/site/_docs/reference.md
@@ -2681,6 +2681,7 @@ BigQuery's type system uses confusingly different names for types and functions:
| * | ATANH(numeric) | Returns the inverse hyperbolic tangent of *numeric*
| s | BIT_LENGTH(binary) | Returns the bit length of *binary*
| s | BIT_LENGTH(string) | Returns the bit length of *string*
+| s | BIT_GET(value, position) | Returns the bit (0 or 1) value at the specified *position* of numeric *value*. The positions are numbered from right to left, starting at zero. The *position* argument cannot be negative
| b | CEIL(value) | Similar to standard `CEIL(value)` except if *value* is an integer type, the return type is a double
| m s | CHAR(integer) | Returns the character whose ASCII code is *integer* % 256, or null if *integer* < 0
| b o p | CHR(integer) | Returns the character whose UTF-8 code is *integer*
@@ -2732,6 +2733,7 @@ BigQuery's type system uses confusingly different names for types and functions:
| b | FORMAT_DATETIME(string, timestamp) | Formats *timestamp* according to the specified format *string*
| b | FORMAT_TIME(string, time) | Formats *time* according to the specified format *string*
| b | FORMAT_TIMESTAMP(string timestamp) | Formats *timestamp* according to the specified format *string*
+| s | GETBIT(value, position) | Equivalent to `BIT_GET(value, position)`
| b o | GREATEST(expr [, expr ]*) | Returns the greatest of the expressions
| b h s | IF(condition, value1, value2) | Returns *value1* if *condition* is TRUE, *value2* otherwise
| b | IFNULL(value1, value2) | Equivalent to `NVL(value1, value2)`
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index d8a0264c0a..ec30702df8 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -3958,6 +3958,83 @@ public class SqlOperatorTest {
f1.checkNull("BIT_LENGTH(cast(null as binary))");
}
+ /** Generates parameters to test both BIT_GET and GETBIT functions. */
+ static Stream<Arguments> bitGetParameters() {
+ SqlOperatorFixture f0 = SqlOperatorFixtureImpl.DEFAULT.withTester(t -> TESTER);
+ f0.setFor(SqlLibraryOperators.BIT_GET, VmName.EXPAND);
+ SqlOperatorFixture f1 = SqlOperatorFixtureImpl.DEFAULT.withTester(t -> TESTER);
+ f1.setFor(SqlLibraryOperators.GETBIT, VmName.EXPAND);
+ return Stream.of(
+ () -> new Object[] {f0, "BIT_GET"},
+ () -> new Object[] {f1, "GETBIT"});
+ }
+
+ @ParameterizedTest
+ @MethodSource("bitGetParameters")
+ void testBitGetFunc(SqlOperatorFixture f, String functionName) {
+ f.checkFails("^" + functionName + "(cast(11 as bigint), 1)^",
+ "No match found for function signature " + functionName + "\\(<NUMERIC>, <NUMERIC>\\)",
+ false);
+
+ final SqlOperatorFixture f1 = f.withLibrary(SqlLibrary.SPARK);
+ // test for bigint value
+ f1.checkScalarExact(functionName + "(cast(11 as bigint), 3)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(-1 as bigint), 0)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(11 as bigint), 63)",
+ "TINYINT NOT NULL", "0");
+ f1.checkScalarExact(functionName + "(cast(-1 as bigint), 63)",
+ "TINYINT NOT NULL", "1");
+ f1.checkFails(functionName + "(cast(11 as bigint), 64)",
+ "BIT_GET/GETBIT error: position 64 exceeds the bit upper limit 63",
+ true);
+ f1.checkFails(functionName + "(cast(11 as bigint), -1)",
+ "BIT_GET/GETBIT error: negative position -1 not allowed",
+ true);
+ f1.checkNull(functionName + "(cast(null as bigint), 1)");
+ f1.checkNull(functionName + "(cast(11 as bigint), cast(null as integer))");
+
+ // test for integer value
+ f1.checkScalarExact(functionName + "(cast(11 as integer), 3)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(-1 as integer), 0)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(11 as integer), 31)",
+ "TINYINT NOT NULL", "0");
+ f1.checkScalarExact(functionName + "(cast(-1 as integer), 31)",
+ "TINYINT NOT NULL", "1");
+ f1.checkFails(functionName + "(cast(11 as integer), 32)",
+ "BIT_GET/GETBIT error: position 32 exceeds the bit upper limit 31",
+ true);
+
+ // test for smallint value
+ f1.checkScalarExact(functionName + "(cast(11 as smallint), 3)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(-1 as smallint), 0)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(11 as smallint), 15)",
+ "TINYINT NOT NULL", "0");
+ f1.checkScalarExact(functionName + "(cast(-1 as smallint), 15)",
+ "TINYINT NOT NULL", "1");
+ f1.checkFails(functionName + "(cast(11 as smallint), 16)",
+ "BIT_GET/GETBIT error: position 16 exceeds the bit upper limit 15",
+ true);
+
+ // test for tinyint value
+ f1.checkScalarExact(functionName + "(cast(11 as tinyint), 3)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(-1 as tinyint), 0)",
+ "TINYINT NOT NULL", "1");
+ f1.checkScalarExact(functionName + "(cast(11 as tinyint), 7)",
+ "TINYINT NOT NULL", "0");
+ f1.checkScalarExact(functionName + "(cast(-1 as tinyint), 7)",
+ "TINYINT NOT NULL", "1");
+ f1.checkFails(functionName + "(cast(11 as tinyint), 8)",
+ "BIT_GET/GETBIT error: position 8 exceeds the bit upper limit 7",
+ true);
+ }
+
@Test void testAsciiFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.ASCII, VmName.EXPAND);