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);

Reply via email to