This is an automated email from the ASF dual-hosted git repository.
cancai 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 09cc28c062 [CALCITE-7184] Support for bitwise AND (&) operator in SQL
09cc28c062 is described below
commit 09cc28c06222dd9ef631131051bd792f8aa04662
Author: wuxiaojun <[email protected]>
AuthorDate: Sun Apr 27 17:05:51 2025 +0800
[CALCITE-7184] Support for bitwise AND (&) operator in SQL
---
core/src/main/codegen/templates/Parser.jj | 2 +
.../calcite/adapter/enumerable/RexImpTable.java | 3 +
.../org/apache/calcite/runtime/SqlFunctions.java | 119 +++++++++++++++
.../calcite/sql/fun/SqlStdOperatorTable.java | 17 +++
.../apache/calcite/sql/test/SqlAdvisorTest.java | 1 +
.../org/apache/calcite/test/SqlValidatorTest.java | 2 +
.../org/apache/calcite/test/SqlOperatorTest.java | 167 ++++++++++++++++++++-
7 files changed, 310 insertions(+), 1 deletion(-)
diff --git a/core/src/main/codegen/templates/Parser.jj
b/core/src/main/codegen/templates/Parser.jj
index 319a67c90e..95d34d4eb6 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -8002,6 +8002,7 @@ SqlBinaryOperator BinaryRowOperator() :
| <OVERLAPS> { return SqlStdOperatorTable.OVERLAPS; }
| <CARET> { return SqlStdOperatorTable.BITXOR_OPERATOR; }
| <EQUALS> { return SqlStdOperatorTable.PERIOD_EQUALS; }
+| <AMPERSAND> { return SqlStdOperatorTable.BITAND_OPERATOR; }
| <PRECEDES> { return SqlStdOperatorTable.PRECEDES; }
| <SUCCEEDS> { return SqlStdOperatorTable.SUCCEEDS; }
| LOOKAHEAD(2) <IMMEDIATELY> <PRECEDES> { return
SqlStdOperatorTable.IMMEDIATELY_PRECEDES; }
@@ -8992,6 +8993,7 @@ void NonReservedKeyWord2of3() :
| < DOUBLE_QUOTE: "\"" >
| < VERTICAL_BAR: "|" >
| < CARET: "^" >
+| < AMPERSAND: "&" >
| < LEFTSHIFT: "<<" >
| < DOLLAR: "$" >
<#list (parser.binaryOperatorsTokens!default.parser.binaryOperatorsTokens) as
operator>
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 2fdc90e5fb..521cc9f7e4 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
@@ -367,6 +367,7 @@
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ATAN;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ATAN2;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITAND;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITAND_OPERATOR;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITCOUNT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITNOT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITOR;
@@ -744,6 +745,8 @@ void populate1() {
NullPolicy.STRICT);
defineMethod(BITXOR_OPERATOR, BuiltInMethod.BIT_XOR.method,
NullPolicy.STRICT);
+ defineMethod(BITAND_OPERATOR, BuiltInMethod.BIT_AND.method,
+ NullPolicy.STRICT);
defineMethod(BITNOT, BuiltInMethod.BIT_NOT.method,
NullPolicy.STRICT);
define(CONCAT, new ConcatImplementor());
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 6d94477de8..45c2f7be97 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -3277,6 +3277,125 @@ public static ByteString bitAnd(ByteString b0,
ByteString b1) {
return binaryOperator(b0, b1, (x, y) -> (byte) (x & y));
}
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UByte}
values.
+ * Returns {@code null} if any operand is null.
+ */
+ public static UByte bitAnd(UByte b0, UByte b1) {
+ return UByte.valueOf((short) (b0.shortValue() & b1.shortValue()));
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UShort}
values.
+ * Returns {@code null} if any operand is null.
+ */
+ public static UShort bitAnd(UShort b0, UShort b1) {
+ return UShort.valueOf(b0.intValue() & b1.intValue());
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UInteger}
values.
+ * Returns {@code null} if any operand is null.
+ */
+ public static UInteger bitAnd(UInteger b0, UInteger b1) {
+ return UInteger.valueOf(b0.longValue() & b1.longValue());
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.ULong}
values. Returns
+ * {@code null} if any operand is null.
+ */
+ public static ULong bitAnd(ULong b0, ULong b1) {
+ return ULong.valueOf(b0.longValue() & b1.longValue());
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UInteger}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(UInteger b0, long b1) {
+ return b0.intValue() & b1;
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.ULong}
and {@link long}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(ULong b0, long b1) {
+ return b0.longValue() & b1;
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link long} and {@link
org.joou.UInteger}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(long b1, ULong b2) {
+ return b1 & b2.longValue();
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link long} and {@link
org.joou.UInteger}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(long b1, UInteger b2) {
+ return b1 & b2.longValue();
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UShort}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(UShort b0, long b1) {
+ return b0.intValue() & b1;
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UShort}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static long bitAnd(long b0, UShort b1) {
+ return b0 & b1.intValue();
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UInteger}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static UInteger bitAnd(UInteger b0, int b1) {
+ return UInteger.valueOf(b0.intValue() & b1);
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UInteger}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static ULong bitAnd(ULong b0, int b1) {
+ return ULong.valueOf(b0.longValue() & b1);
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link Integer} and
{@link org.joou.UInteger}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static ULong bitAnd(int b1, ULong b2) {
+ return ULong.valueOf(b1 & b2.longValue());
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UShort}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static Integer bitAnd(int b0, UShort b1) {
+ return b0 & b1.intValue();
+ }
+
+ /**
+ * Bitwise function <code>BITAND</code> applied to {@link org.joou.UShort}
and {@link Integer}
+ * values. Returns {@code null} if any operand is null.
+ */
+ public static Integer bitAnd(UShort b0, int b1) {
+ return b0.intValue() & b1;
+ }
/** Helper function for implementing <code>BITCOUNT</code>. Counts the number
* of bits set in an integer value. */
public static long bitCount(long b) {
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 1f42de7c1f..074c3103f2 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -1319,6 +1319,23 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
InferTypes.FIRST_KNOWN,
OperandTypes.INTEGER_INTEGER.or(OperandTypes.BINARY_BINARY)
.or(OperandTypes.UNSIGNED_NUMERIC_UNSIGNED_NUMERIC));
+ // Both operands should support bitwise operations
+
+ /**
+ * <code>{@code &}</code> operator.
+ */
+ public static final SqlBinaryOperator BITAND_OPERATOR =
+ new SqlBinaryOperator(
+ "&",
+ SqlKind.BITAND,
+ 50, // Higher precedence than XOR but lower than
multiplication
+ true,
+ ReturnTypes.LEAST_RESTRICTIVE,
+ InferTypes.FIRST_KNOWN,
+ OperandTypes.INTEGER_INTEGER.or(OperandTypes.BINARY_BINARY)
+ .or(OperandTypes.UNSIGNED_NUMERIC_UNSIGNED_NUMERIC)
+ .or(OperandTypes.family(SqlTypeFamily.UNSIGNED_NUMERIC,
SqlTypeFamily.INTEGER)
+ .or(OperandTypes.family(SqlTypeFamily.INTEGER,
SqlTypeFamily.UNSIGNED_NUMERIC))));
/**
* <code>BITNOT</code> scalar function.
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index b1385d01cb..e7a57630d1 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -300,6 +300,7 @@ class SqlAdvisorTest extends SqlValidatorTestCase {
protected static final List<String> PREDICATE_KEYWORDS =
Arrays.asList(
+ "KEYWORD(&)",
"KEYWORD(()",
"KEYWORD(*)",
"KEYWORD(+)",
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index e353447d3c..9232c0ad93 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -10123,6 +10123,8 @@ private static int prec(SqlOperator op) {
+ "/INT left\n" // checked
+ "|| left\n"
+ "\n"
+ + "& left\n"
+ + "\n"
+ "+ left\n"
+ "+ left\n" // checked
+ "+ -\n"
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 f10bc70879..a35ae72694 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -16666,6 +16666,11 @@ private static void
checkLogicalOrFunc(SqlOperatorFixture f) {
f.checkNull("CAST(NULL AS INTEGER UNSIGNED) << 2");
}
+ /**
+ * Test cases for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7109">[CALCITE-7109]
+ * Implement SHIFT_LEFT operator </a>.
+ */
@Test void testLeftShiftFunctionCall() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.BIT_LEFT_SHIFT, VmName.EXPAND);
@@ -16759,7 +16764,6 @@ private static void
checkLogicalOrFunc(SqlOperatorFixture f) {
"Cannot apply 'LEFTSHIFT' to arguments of type
'LEFTSHIFT\\(<DECIMAL\\(2, 1\\)>, <INTEGER>\\)'\\. Supported form\\(s\\):
'LEFTSHIFT\\(<INTEGER>, <INTEGER>\\)'\\n'LEFTSHIFT\\(<BINARY>,
<INTEGER>\\)'\\n'LEFTSHIFT\\(<UNSIGNED_NUMERIC>, <INTEGER>\\)'",
false);
-
// === Nulls ===
f.checkNull("LEFTSHIFT(CAST(NULL AS INTEGER), 5)");
f.checkNull("LEFTSHIFT(10, CAST(NULL AS INTEGER))");
@@ -16767,6 +16771,167 @@ private static void
checkLogicalOrFunc(SqlOperatorFixture f) {
f.checkNull("LEFTSHIFT(CAST(NULL AS INTEGER UNSIGNED), 2)");
}
+ /**
+ * Test cases for
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-7184">[CALCITE-7184]
+ * Implement BIT_AND operator </a>.
+ */
+ @Test void testBitAndOperatorScalarFunc() {
+ final SqlOperatorFixture f = fixture();
+ // Set the test fixture for the BITAND_OPERATOR
+ f.setFor(SqlStdOperatorTable.BITAND_OPERATOR, VmName.EXPAND);
+
+ // Basic test cases
+ f.checkScalar("2 & 3", "2", "INTEGER NOT NULL");
+ f.checkScalar("5 & 3", "1", "INTEGER NOT NULL");
+ f.checkScalar("-5 & -3", "-7", "INTEGER NOT NULL");
+ f.checkScalar("8 & 7", "0", "INTEGER NOT NULL");
+ f.checkScalar("-1 & 255", "255", "INTEGER NOT NULL");
+
+ // Tests with different integer types and type coercion
+ f.checkScalar("CAST(2 AS INTEGER) & CAST(3 AS BIGINT)", "2", "BIGINT NOT
NULL");
+ f.checkScalar("-5 & 7", "3", "INTEGER NOT NULL");
+ f.checkScalar("-5 & -31", "-31", "INTEGER NOT NULL");
+ f.checkScalar("CAST(-5 AS TINYINT) & CAST(7 AS TINYINT)", "3", "TINYINT
NOT NULL");
+
+ // Verify return types
+ f.checkType("CAST(2 AS TINYINT) & CAST(3 AS TINYINT)", "TINYINT NOT NULL");
+ f.checkType("CAST(2 AS SMALLINT) & CAST(6 AS SMALLINT)", "SMALLINT NOT
NULL");
+ f.checkType("CAST(2 AS BIGINT) & CAST(6 AS BIGINT)", "BIGINT NOT NULL");
+
+ // Mixed cases: negative signed integer with unsigned integer.
+ // Ensure consistency with MySQL semantics where negative values behave as
all bits set.
+ f.checkScalar("CAST(1 AS INTEGER UNSIGNED) & CAST(255 AS INTEGER
UNSIGNED)",
+ "1", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(5 AS INTEGER UNSIGNED) & CAST(3 AS INTEGER UNSIGNED)",
+ "1", "INTEGER UNSIGNED NOT NULL");
+
+ // Pure unsigned cases: bitwise AND across unsigned integer families.
+ // Verify result type and range are preserved after the operation.
+ f.checkScalar("CAST(255 AS INTEGER UNSIGNED) & CAST(65535 AS INTEGER
UNSIGNED)",
+ "255", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(4294967295 AS BIGINT UNSIGNED) & CAST(255 AS BIGINT
UNSIGNED)",
+ "255", "BIGINT UNSIGNED NOT NULL");
+ f.checkScalar("CAST(15 AS INTEGER UNSIGNED) & CAST(7 AS INTEGER)",
+ "7", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(255 AS INTEGER UNSIGNED) & CAST(-1 AS INTEGER)",
+ "255", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(128 AS INTEGER UNSIGNED) & CAST(127 AS INTEGER)",
+ "0", "INTEGER UNSIGNED NOT NULL");
+
+ // Test cases for ULong & Integer -> ULong
+ f.checkScalar("CAST(4294967295 AS BIGINT UNSIGNED) & CAST(255 AS INTEGER)",
+ "255", "BIGINT UNSIGNED NOT NULL");
+ f.checkScalar("CAST(1099511627775 AS BIGINT UNSIGNED) & CAST(-1 AS
INTEGER)",
+ "1099511627775", "BIGINT UNSIGNED NOT NULL");
+ f.checkScalar("CAST(0 AS BIGINT UNSIGNED) & CAST(255 AS INTEGER)",
+ "0", "BIGINT UNSIGNED NOT NULL");
+
+ // Test cases for Integer & ULong -> ULong
+ f.checkScalar("CAST(-1 AS INTEGER) & CAST(4294967295 AS BIGINT UNSIGNED)",
+ "4294967295", "BIGINT UNSIGNED NOT NULL");
+ f.checkScalar("CAST(127 AS INTEGER) & CAST(255 AS BIGINT UNSIGNED)",
+ "127", "BIGINT UNSIGNED NOT NULL");
+ f.checkScalar("CAST(0 AS INTEGER) & CAST(1099511627775 AS BIGINT
UNSIGNED)",
+ "0", "BIGINT UNSIGNED NOT NULL");
+
+ // Test cases for UShort & Integer -> Integer
+ f.checkScalar("CAST(255 AS SMALLINT UNSIGNED) & CAST(15 AS INTEGER)",
+ "15", "INTEGER NOT NULL");
+ f.checkScalar("CAST(65535 AS SMALLINT UNSIGNED) & CAST(-1 AS INTEGER)",
+ "65535", "INTEGER NOT NULL");
+ f.checkScalar("CAST(0 AS SMALLINT UNSIGNED) & CAST(255 AS INTEGER)",
+ "0", "INTEGER NOT NULL");
+
+ // Test cases for Integer & UShort -> Integer
+ f.checkScalar("CAST(-1 AS INTEGER) & CAST(255 AS SMALLINT UNSIGNED)",
+ "255", "INTEGER NOT NULL");
+ f.checkScalar("CAST(127 AS INTEGER) & CAST(128 AS SMALLINT UNSIGNED)",
+ "0", "INTEGER NOT NULL");
+ f.checkScalar("CAST(65535 AS INTEGER) & CAST(255 AS SMALLINT UNSIGNED)",
+ "255", "INTEGER NOT NULL");
+
+ // Edge cases with powers of 2
+ f.checkScalar("CAST(1024 AS INTEGER UNSIGNED) & CAST(512 AS INTEGER)",
+ "0", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(1023 AS INTEGER UNSIGNED) & CAST(512 AS INTEGER)",
+ "512", "INTEGER UNSIGNED NOT NULL");
+
+ // Mixed operations with zero
+ f.checkScalar("CAST(0 AS INTEGER UNSIGNED) & CAST(-1 AS INTEGER)",
+ "0", "INTEGER UNSIGNED NOT NULL");
+ f.checkScalar("CAST(4294967295 AS BIGINT UNSIGNED) & CAST(0 AS INTEGER)",
+ "0", "BIGINT UNSIGNED NOT NULL");
+
+ // UInteger & Long -> ULong
+ f.checkScalar("CAST(255 AS INTEGER UNSIGNED) & CAST(127 AS BIGINT)",
+ "127", "BIGINT NOT NULL");
+ f.checkScalar("CAST(4294967295 AS INTEGER UNSIGNED) & CAST(-1 AS BIGINT)",
+ "-1", "BIGINT NOT NULL");
+ f.checkScalar("CAST(1024 AS INTEGER UNSIGNED) & CAST(512 AS BIGINT)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("CAST(0 AS INTEGER UNSIGNED) & CAST(9223372036854775807 AS
BIGINT)",
+ "0", "BIGINT NOT NULL");
+
+ // Long & UInteger -> ULong
+ f.checkScalar("CAST(-1 AS BIGINT) & CAST(255 AS INTEGER UNSIGNED)",
+ "255", "BIGINT NOT NULL");
+ f.checkScalar("CAST(127 AS BIGINT) & CAST(128 AS INTEGER UNSIGNED)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("CAST(9223372036854775807 AS BIGINT) & CAST(1 AS INTEGER
UNSIGNED)",
+ "1", "BIGINT NOT NULL");
+ f.checkScalar("CAST(0 AS BIGINT) & CAST(4294967295 AS INTEGER UNSIGNED)",
+ "0", "BIGINT NOT NULL");
+
+ // Edge cases with negative Long values
+ f.checkScalar("CAST(-128 AS BIGINT) & CAST(255 AS INTEGER UNSIGNED)",
+ "128", "BIGINT NOT NULL");
+ f.checkScalar("CAST(-256 AS BIGINT) & CAST(255 AS INTEGER UNSIGNED)",
+ "0", "BIGINT NOT NULL");
+
+ // Powers of 2 tests
+ f.checkScalar("CAST(65536 AS INTEGER UNSIGNED) & CAST(32768 AS BIGINT)",
+ "0", "BIGINT NOT NULL");
+ f.checkScalar("CAST(65535 AS INTEGER UNSIGNED) & CAST(32768 AS BIGINT)",
+ "32768", "BIGINT NOT NULL");
+
+ // Large number tests
+ f.checkScalar("CAST(4294967295 AS INTEGER UNSIGNED) & CAST(4294967296 AS
BIGINT)",
+ "4294967296", "BIGINT NOT NULL");
+ f.checkScalar("CAST(4294967295 AS INTEGER UNSIGNED) & CAST(4294967295 AS
BIGINT)",
+ "4294967295", "BIGINT NOT NULL");
+
+ // Mixed sign tests
+ f.checkScalar("CAST(4294967295 AS INTEGER UNSIGNED) & CAST(-4294967296 AS
BIGINT)",
+ "-4294967296", "BIGINT NOT NULL");
+ // Binary type tests
+ f.checkScalar("CAST(x'0201' AS BINARY(2)) & CAST(x'07f9' AS BINARY(2))",
"0201",
+ "BINARY(2) NOT NULL");
+ f.checkScalar("CAST(x'0201' AS VARBINARY(2)) & CAST(x'07f9' AS
VARBINARY(2))", "0201",
+ "VARBINARY(2) NOT NULL");
+
+ // Test invalid argument types
+ f.checkFails("^1.2 & 2^",
+ "Cannot apply '&' to arguments of type '<DECIMAL\\(2, 1\\)> &
<INTEGER>'\\. Supported form\\(s\\): '<INTEGER> & <INTEGER>'\\n'<BINARY> &
<BINARY>'\\n'<UNSIGNED_NUMERIC> & <UNSIGNED_NUMERIC>'\\n'<UNSIGNED_NUMERIC> &
<INTEGER>'\\n'<INTEGER> & <UNSIGNED_NUMERIC>'",
+ false);
+
+ // NULL value tests
+ f.checkNull("CAST(NULL AS INTEGER) & 5");
+ f.checkNull("10 & CAST(NULL AS INTEGER)");
+ f.checkNull("CAST(NULL AS INTEGER) & CAST(NULL AS INTEGER)");
+
+ // Test binary operands with different lengths
+ f.checkFails("CAST(x'0201' AS VARBINARY) & CAST(x'02' AS VARBINARY)",
+ "Different length for bitwise operands: the first: 2, the second: 1",
+ true);
+
+ // Boundary cases for bitwise operations
+ f.checkScalar("0 & 0", "0", "INTEGER NOT NULL");
+ f.checkScalar("-1 & -1", "-1", "INTEGER NOT NULL");
+ f.checkScalar("2147483647 & 0", "0", "INTEGER NOT NULL"); // MAX_INT & 0
+ f.checkScalar("-2147483648 & -1", "-2147483648", "INTEGER NOT NULL"); //
MIN_INT & -1
+ }
+
@Test void testBitAndScalarFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.BITAND, VmName.EXPAND);