mihaibudiu commented on code in PR #5073:
URL: https://github.com/apache/calcite/pull/5073#discussion_r3554969445


##########
core/src/test/java/org/apache/calcite/test/SqlFunctionsTest.java:
##########
@@ -2068,6 +2068,34 @@ private long sqlTimestamp(String str) {
         SqlFunctions.leftShift(new byte[]{(byte) 0x40, (byte) 0x00}, 1));
   }
 
+  @Test void testRightShift() {
+    // Test 1-byte array
+    byte[] data1 = {(byte) 0xF0}; // 11110000
+    for (int shift = -10; shift <= 10; shift++) {
+      byte[] result = SqlFunctions.rightShift(data1.clone(), shift);
+      // Just verify it doesn't crash and returns correct length
+      assertEquals(1, result.length);
+    }
+
+    // Test 2-byte array
+    byte[] data2 = {(byte) 0x12, (byte) 0x34};
+    for (int shift = -18; shift <= 18; shift++) {
+      byte[] result = SqlFunctions.rightShift(data2.clone(), shift);
+      assertEquals(2, result.length);

Review Comment:
   you don't really look at the result, maybe you can compute the same using 
integers and check?



##########
testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java:
##########
@@ -17158,6 +17168,204 @@ 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-7639";>[CALCITE-7639]
+   * Support bitwise right shift (&gt;&gt;) operator and RIGHTSHIFT 
function</a>.
+   */
+  @Test void testRightShiftScalarFunc() {
+    final SqlOperatorFixture f = fixture();
+    f.setFor(SqlStdOperatorTable.BIT_RIGHT_SHIFT, VmName.EXPAND);
+
+    // === Basic functionality ===
+    f.checkScalar("8 >> 2", "2", "INTEGER NOT NULL");
+    f.checkScalar("1024 >> 10", "1", "INTEGER NOT NULL");
+    f.checkScalar("0 >> 5", "0", "INTEGER NOT NULL");
+
+    // === Type coercion and signed (arithmetic) behavior ===
+    f.checkScalar("CAST(16 AS INTEGER) >> CAST(3 AS BIGINT)", "2", "INTEGER 
NOT NULL");
+    f.checkScalar("-20 >> 2", "-5", "INTEGER NOT NULL");
+    f.checkScalar("-40 >> 3", "-5", "INTEGER NOT NULL");
+    f.checkScalar("CAST(-20 AS TINYINT) >> CAST(2 AS TINYINT)", "-5", "TINYINT 
NOT NULL");
+
+    // === Verify return type matches first argument type ===
+    f.checkType("CAST(8 AS TINYINT) >> CAST(2 AS TINYINT)", "TINYINT NOT 
NULL");
+    f.checkType("CAST(8 AS SMALLINT) >> CAST(2 AS SMALLINT)", "SMALLINT NOT 
NULL");
+    f.checkType("CAST(8 AS INTEGER) >> CAST(2 AS INTEGER)", "INTEGER NOT 
NULL");
+    f.checkType("CAST(8 AS BIGINT) >> CAST(2 AS BIGINT)", "BIGINT NOT NULL");
+    f.checkScalar("CAST(8 AS BIGINT) >> CAST(2 AS BIGINT)", "2", "BIGINT NOT 
NULL");
+
+    // === BigInt shifts with explicit BIGINT inputs 
(arithmetic/sign-preserving) ===
+    f.checkScalar("CAST(4611686018427387904 AS BIGINT) >> 62", "1", "BIGINT 
NOT NULL"); // 2^62
+    f.checkScalar("CAST(9223372036854775807 AS BIGINT) >> 1",
+        BigInteger.valueOf(Long.MAX_VALUE).shiftRight(1).toString(), "BIGINT 
NOT NULL");
+    f.checkScalar("CAST(-1 AS BIGINT) >> 63", "-1", "BIGINT NOT NULL"); // 
sign bit preserved
+    f.checkScalar("CAST(-1 AS BIGINT) >> 1", "-1", "BIGINT NOT NULL");
+    f.checkScalar("CAST(1000000000 AS BIGINT) >> 5", "31250000", "BIGINT NOT 
NULL");
+
+    // === Shift amount normalized using modulo of the bit width ===
+    f.checkScalar("CAST(1024 AS BIGINT) >> 64", "1024", "BIGINT NOT NULL"); // 
64 % 64 = 0
+    f.checkScalar("CAST(1024 AS BIGINT) >> 74", "1", "BIGINT NOT NULL"); // 74 
% 64 = 10
+    f.checkScalar("1 >> 32", "1", "INTEGER NOT NULL"); // 32 % 32 = 0
+    f.checkScalar("123 >> 60", "0", "INTEGER NOT NULL"); // 60 % 32 = 28
+
+    // === Unsigned types ===
+    f.checkScalar("CAST(252 AS TINYINT UNSIGNED) >> 2", "63", "TINYINT 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(65280 AS SMALLINT UNSIGNED) >> 8", "255", "SMALLINT 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(4294901760 AS INTEGER UNSIGNED) >> 16", "65535",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("CAST(2147483648 AS INTEGER UNSIGNED) >> 31", "1", "INTEGER 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(1 AS INTEGER UNSIGNED) >> -1", "2147483648", "INTEGER 
UNSIGNED NOT NULL");
+    // BIGINT UNSIGNED with the high bit set (2^63, built via 1 << 63): the
+    // right shift must be logical, not arithmetic (the raw long is negative).
+    f.checkScalar("CAST(1 AS BIGINT UNSIGNED) << 63 >> 4",
+        "576460752303423488", "BIGINT UNSIGNED NOT NULL");
+
+    // A BIGINT shift amount is accepted (INTEGER family), but an unsigned 
shift
+    // amount is not: the second operand must be a signed integer type.
+    f.checkScalar("CAST(8 AS INTEGER) >> CAST(2 AS BIGINT)", "2", "INTEGER NOT 
NULL");
+    f.checkFails("^8 >> CAST(2 AS INTEGER UNSIGNED)^",
+        "Cannot apply '>>' to arguments of type '<INTEGER> >> <INTEGER 
UNSIGNED>'\\. "
+            + "Supported form\\(s\\): '<INTEGER> >> <INTEGER>'\\n'<BINARY> >> 
<INTEGER>'\\n"
+            + "'<UNSIGNED_NUMERIC> >> <INTEGER>'",
+        false);
+
+    // === Negative shift counts (normalized via modulo, then shifted the 
other way) ===
+    // A negative right shift shifts left by the normalized magnitude (here
+    // 32 - 2 = 30); it is not the same as a left shift by the given amount.
+    f.checkScalar("1 >> -2", "1073741824", "INTEGER NOT NULL"); // 1 << 30
+    f.checkScalar("8 >> -1", "0", "INTEGER NOT NULL");
+    f.checkScalar("16 >> -2", "0", "INTEGER NOT NULL");
+
+    // === Shift by zero and large shifts ===
+    f.checkScalar("0 >> 32", "0", "INTEGER NOT NULL");
+    f.checkScalar("0 >> 100", "0", "INTEGER NOT NULL");
+
+    // === Binary type tests ===
+    f.checkScalar("CAST(X'FF' AS BINARY(1)) >> 1", "7f", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'F0' AS BINARY(1)) >> 4", "0f", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'08' AS BINARY(1)) >> 3", "01", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'00' AS BINARY(1)) >> 5", "00", "BINARY(1) NOT NULL");
+
+    f.checkScalar("CAST(X'FFFF' AS BINARY(2)) >> 1", "ff7f", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'1234' AS BINARY(2)) >> 4", "4103", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'1234' AS BINARY(2)) >> 8", "3400", "BINARY(2) NOT 
NULL");
+
+    f.checkScalar("CAST(X'FF' AS BINARY(1)) >> 8", "ff", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'FFFF' AS BINARY(2)) >> 16", "ffff", "BINARY(2) NOT 
NULL");
+
+    f.checkScalar("CAST(X'ABCD' AS BINARY(2)) >> 0", "abcd", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'123456' AS BINARY(3)) >> 4", "416305", "BINARY(3) 
NOT NULL");
+    f.checkScalar("CAST(X'0001' AS BINARY(2)) >> 1", "8000", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'8000' AS BINARY(2)) >> 1", "4000", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'F0' AS BINARY(1)) >> -4", "0f", "BINARY(1) NOT 
NULL");
+
+    // === 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> >> 
<INTEGER>'\\n'<UNSIGNED_NUMERIC> "
+            + ">> <INTEGER>'",
+        false);
+
+    // === Null propagation ===
+    f.checkNull("CAST(NULL AS INTEGER) >> 5");
+    f.checkNull("10 >> CAST(NULL AS INTEGER)");
+    f.checkNull("CAST(NULL AS INTEGER) >> CAST(NULL AS INTEGER)");
+    f.checkNull("CAST(NULL AS INTEGER UNSIGNED) >> 2");
+  }
+
+  @Test void testRightShiftFunctionCall() {
+    final SqlOperatorFixture f = fixture();
+    f.setFor(SqlStdOperatorTable.BIT_RIGHT_SHIFT, VmName.EXPAND);
+
+    // === Basic functionality ===
+    f.checkScalar("RIGHTSHIFT(8, 2)", "2", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(1024, 10)", "1", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(0, 5)", "0", "INTEGER NOT NULL");
+
+    // === Type coercion and signed (arithmetic) behavior ===
+    f.checkScalar("RIGHTSHIFT(CAST(16 AS INTEGER), CAST(3 AS BIGINT))", "2", 
"INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(-20, 2)", "-5", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(-40, 3)", "-5", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-20 AS TINYINT), CAST(2 AS TINYINT))", 
"-5", "TINYINT NOT NULL");
+
+    // === Verify return type matches first argument type ===
+    f.checkType("RIGHTSHIFT(CAST(8 AS TINYINT), CAST(2 AS TINYINT))", "TINYINT 
NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS SMALLINT), CAST(2 AS SMALLINT))", 
"SMALLINT NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS INTEGER), CAST(2 AS INTEGER))", "INTEGER 
NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS BIGINT), CAST(2 AS BIGINT))", "BIGINT 
NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(8 AS BIGINT), CAST(2 AS BIGINT))", "2", 
"BIGINT NOT NULL");
+
+    // === BigInt shifts with explicit BIGINT inputs ===
+    f.checkScalar("RIGHTSHIFT(CAST(4611686018427387904 AS BIGINT), 62)", "1", 
"BIGINT NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(9223372036854775807 AS BIGINT), 1)",
+        BigInteger.valueOf(Long.MAX_VALUE).shiftRight(1).toString(), "BIGINT 
NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-1 AS BIGINT), 63)", "-1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-1 AS BIGINT), 1)", "-1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1000000000 AS BIGINT), 5)", "31250000", 
"BIGINT NOT NULL");
+
+    // === Shift amount normalized using modulo of the bit width ===
+    f.checkScalar("RIGHTSHIFT(CAST(1024 AS BIGINT), 64)", "1024", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1024 AS BIGINT), 74)", "1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(1, 32)", "1", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(123, 60)", "0", "INTEGER NOT NULL");
+
+    // === Unsigned types ===
+    f.checkScalar("RIGHTSHIFT(CAST(252 AS TINYINT UNSIGNED), 2)", "63",
+        "TINYINT UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(65280 AS SMALLINT UNSIGNED), 8)", "255",
+        "SMALLINT UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(4294901760 AS INTEGER UNSIGNED), 16)", 
"65535",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(2147483648 AS INTEGER UNSIGNED), 31)", "1",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1 AS INTEGER UNSIGNED), -1)", "2147483648",
+        "INTEGER UNSIGNED NOT NULL");
+
+    // === Negative shifts ===
+    f.checkScalar("RIGHTSHIFT(8, -1)", "0", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(16, -2)", "0", "INTEGER NOT NULL");
+
+    // === Large shifts ===
+    f.checkScalar("RIGHTSHIFT(0, 32)", "0", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(0, 100)", "0", "INTEGER NOT NULL");
+
+    // === Binary types ===
+    f.checkScalar("RIGHTSHIFT(CAST(X'FF' AS BINARY(1)), 1)", "7f", "BINARY(1) 
NOT NULL");

Review Comment:
   Is the cast necessary in these tests?
   I would think x'FF' is a BINARY(1).
   Is shift defined for VARBINARY?



##########
testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java:
##########
@@ -17158,6 +17168,204 @@ 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-7639";>[CALCITE-7639]
+   * Support bitwise right shift (&gt;&gt;) operator and RIGHTSHIFT 
function</a>.
+   */
+  @Test void testRightShiftScalarFunc() {
+    final SqlOperatorFixture f = fixture();
+    f.setFor(SqlStdOperatorTable.BIT_RIGHT_SHIFT, VmName.EXPAND);
+
+    // === Basic functionality ===
+    f.checkScalar("8 >> 2", "2", "INTEGER NOT NULL");
+    f.checkScalar("1024 >> 10", "1", "INTEGER NOT NULL");
+    f.checkScalar("0 >> 5", "0", "INTEGER NOT NULL");
+
+    // === Type coercion and signed (arithmetic) behavior ===
+    f.checkScalar("CAST(16 AS INTEGER) >> CAST(3 AS BIGINT)", "2", "INTEGER 
NOT NULL");
+    f.checkScalar("-20 >> 2", "-5", "INTEGER NOT NULL");
+    f.checkScalar("-40 >> 3", "-5", "INTEGER NOT NULL");
+    f.checkScalar("CAST(-20 AS TINYINT) >> CAST(2 AS TINYINT)", "-5", "TINYINT 
NOT NULL");
+
+    // === Verify return type matches first argument type ===
+    f.checkType("CAST(8 AS TINYINT) >> CAST(2 AS TINYINT)", "TINYINT NOT 
NULL");
+    f.checkType("CAST(8 AS SMALLINT) >> CAST(2 AS SMALLINT)", "SMALLINT NOT 
NULL");
+    f.checkType("CAST(8 AS INTEGER) >> CAST(2 AS INTEGER)", "INTEGER NOT 
NULL");
+    f.checkType("CAST(8 AS BIGINT) >> CAST(2 AS BIGINT)", "BIGINT NOT NULL");
+    f.checkScalar("CAST(8 AS BIGINT) >> CAST(2 AS BIGINT)", "2", "BIGINT NOT 
NULL");
+
+    // === BigInt shifts with explicit BIGINT inputs 
(arithmetic/sign-preserving) ===
+    f.checkScalar("CAST(4611686018427387904 AS BIGINT) >> 62", "1", "BIGINT 
NOT NULL"); // 2^62
+    f.checkScalar("CAST(9223372036854775807 AS BIGINT) >> 1",
+        BigInteger.valueOf(Long.MAX_VALUE).shiftRight(1).toString(), "BIGINT 
NOT NULL");
+    f.checkScalar("CAST(-1 AS BIGINT) >> 63", "-1", "BIGINT NOT NULL"); // 
sign bit preserved
+    f.checkScalar("CAST(-1 AS BIGINT) >> 1", "-1", "BIGINT NOT NULL");
+    f.checkScalar("CAST(1000000000 AS BIGINT) >> 5", "31250000", "BIGINT NOT 
NULL");
+
+    // === Shift amount normalized using modulo of the bit width ===
+    f.checkScalar("CAST(1024 AS BIGINT) >> 64", "1024", "BIGINT NOT NULL"); // 
64 % 64 = 0
+    f.checkScalar("CAST(1024 AS BIGINT) >> 74", "1", "BIGINT NOT NULL"); // 74 
% 64 = 10
+    f.checkScalar("1 >> 32", "1", "INTEGER NOT NULL"); // 32 % 32 = 0
+    f.checkScalar("123 >> 60", "0", "INTEGER NOT NULL"); // 60 % 32 = 28
+
+    // === Unsigned types ===
+    f.checkScalar("CAST(252 AS TINYINT UNSIGNED) >> 2", "63", "TINYINT 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(65280 AS SMALLINT UNSIGNED) >> 8", "255", "SMALLINT 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(4294901760 AS INTEGER UNSIGNED) >> 16", "65535",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("CAST(2147483648 AS INTEGER UNSIGNED) >> 31", "1", "INTEGER 
UNSIGNED NOT NULL");
+    f.checkScalar("CAST(1 AS INTEGER UNSIGNED) >> -1", "2147483648", "INTEGER 
UNSIGNED NOT NULL");
+    // BIGINT UNSIGNED with the high bit set (2^63, built via 1 << 63): the
+    // right shift must be logical, not arithmetic (the raw long is negative).
+    f.checkScalar("CAST(1 AS BIGINT UNSIGNED) << 63 >> 4",
+        "576460752303423488", "BIGINT UNSIGNED NOT NULL");
+
+    // A BIGINT shift amount is accepted (INTEGER family), but an unsigned 
shift
+    // amount is not: the second operand must be a signed integer type.
+    f.checkScalar("CAST(8 AS INTEGER) >> CAST(2 AS BIGINT)", "2", "INTEGER NOT 
NULL");
+    f.checkFails("^8 >> CAST(2 AS INTEGER UNSIGNED)^",
+        "Cannot apply '>>' to arguments of type '<INTEGER> >> <INTEGER 
UNSIGNED>'\\. "
+            + "Supported form\\(s\\): '<INTEGER> >> <INTEGER>'\\n'<BINARY> >> 
<INTEGER>'\\n"
+            + "'<UNSIGNED_NUMERIC> >> <INTEGER>'",
+        false);
+
+    // === Negative shift counts (normalized via modulo, then shifted the 
other way) ===
+    // A negative right shift shifts left by the normalized magnitude (here
+    // 32 - 2 = 30); it is not the same as a left shift by the given amount.
+    f.checkScalar("1 >> -2", "1073741824", "INTEGER NOT NULL"); // 1 << 30
+    f.checkScalar("8 >> -1", "0", "INTEGER NOT NULL");
+    f.checkScalar("16 >> -2", "0", "INTEGER NOT NULL");
+
+    // === Shift by zero and large shifts ===
+    f.checkScalar("0 >> 32", "0", "INTEGER NOT NULL");
+    f.checkScalar("0 >> 100", "0", "INTEGER NOT NULL");
+
+    // === Binary type tests ===
+    f.checkScalar("CAST(X'FF' AS BINARY(1)) >> 1", "7f", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'F0' AS BINARY(1)) >> 4", "0f", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'08' AS BINARY(1)) >> 3", "01", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'00' AS BINARY(1)) >> 5", "00", "BINARY(1) NOT NULL");
+
+    f.checkScalar("CAST(X'FFFF' AS BINARY(2)) >> 1", "ff7f", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'1234' AS BINARY(2)) >> 4", "4103", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'1234' AS BINARY(2)) >> 8", "3400", "BINARY(2) NOT 
NULL");
+
+    f.checkScalar("CAST(X'FF' AS BINARY(1)) >> 8", "ff", "BINARY(1) NOT NULL");
+    f.checkScalar("CAST(X'FFFF' AS BINARY(2)) >> 16", "ffff", "BINARY(2) NOT 
NULL");
+
+    f.checkScalar("CAST(X'ABCD' AS BINARY(2)) >> 0", "abcd", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'123456' AS BINARY(3)) >> 4", "416305", "BINARY(3) 
NOT NULL");
+    f.checkScalar("CAST(X'0001' AS BINARY(2)) >> 1", "8000", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'8000' AS BINARY(2)) >> 1", "4000", "BINARY(2) NOT 
NULL");
+    f.checkScalar("CAST(X'F0' AS BINARY(1)) >> -4", "0f", "BINARY(1) NOT 
NULL");
+
+    // === 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> >> 
<INTEGER>'\\n'<UNSIGNED_NUMERIC> "
+            + ">> <INTEGER>'",
+        false);
+
+    // === Null propagation ===
+    f.checkNull("CAST(NULL AS INTEGER) >> 5");
+    f.checkNull("10 >> CAST(NULL AS INTEGER)");
+    f.checkNull("CAST(NULL AS INTEGER) >> CAST(NULL AS INTEGER)");
+    f.checkNull("CAST(NULL AS INTEGER UNSIGNED) >> 2");
+  }
+
+  @Test void testRightShiftFunctionCall() {
+    final SqlOperatorFixture f = fixture();
+    f.setFor(SqlStdOperatorTable.BIT_RIGHT_SHIFT, VmName.EXPAND);
+
+    // === Basic functionality ===
+    f.checkScalar("RIGHTSHIFT(8, 2)", "2", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(1024, 10)", "1", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(0, 5)", "0", "INTEGER NOT NULL");
+
+    // === Type coercion and signed (arithmetic) behavior ===
+    f.checkScalar("RIGHTSHIFT(CAST(16 AS INTEGER), CAST(3 AS BIGINT))", "2", 
"INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(-20, 2)", "-5", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(-40, 3)", "-5", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-20 AS TINYINT), CAST(2 AS TINYINT))", 
"-5", "TINYINT NOT NULL");
+
+    // === Verify return type matches first argument type ===
+    f.checkType("RIGHTSHIFT(CAST(8 AS TINYINT), CAST(2 AS TINYINT))", "TINYINT 
NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS SMALLINT), CAST(2 AS SMALLINT))", 
"SMALLINT NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS INTEGER), CAST(2 AS INTEGER))", "INTEGER 
NOT NULL");
+    f.checkType("RIGHTSHIFT(CAST(8 AS BIGINT), CAST(2 AS BIGINT))", "BIGINT 
NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(8 AS BIGINT), CAST(2 AS BIGINT))", "2", 
"BIGINT NOT NULL");
+
+    // === BigInt shifts with explicit BIGINT inputs ===
+    f.checkScalar("RIGHTSHIFT(CAST(4611686018427387904 AS BIGINT), 62)", "1", 
"BIGINT NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(9223372036854775807 AS BIGINT), 1)",
+        BigInteger.valueOf(Long.MAX_VALUE).shiftRight(1).toString(), "BIGINT 
NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-1 AS BIGINT), 63)", "-1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(-1 AS BIGINT), 1)", "-1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1000000000 AS BIGINT), 5)", "31250000", 
"BIGINT NOT NULL");
+
+    // === Shift amount normalized using modulo of the bit width ===
+    f.checkScalar("RIGHTSHIFT(CAST(1024 AS BIGINT), 64)", "1024", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1024 AS BIGINT), 74)", "1", "BIGINT NOT 
NULL");
+    f.checkScalar("RIGHTSHIFT(1, 32)", "1", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(123, 60)", "0", "INTEGER NOT NULL");
+
+    // === Unsigned types ===
+    f.checkScalar("RIGHTSHIFT(CAST(252 AS TINYINT UNSIGNED), 2)", "63",
+        "TINYINT UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(65280 AS SMALLINT UNSIGNED), 8)", "255",
+        "SMALLINT UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(4294901760 AS INTEGER UNSIGNED), 16)", 
"65535",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(2147483648 AS INTEGER UNSIGNED), 31)", "1",
+        "INTEGER UNSIGNED NOT NULL");
+    f.checkScalar("RIGHTSHIFT(CAST(1 AS INTEGER UNSIGNED), -1)", "2147483648",
+        "INTEGER UNSIGNED NOT NULL");
+
+    // === Negative shifts ===
+    f.checkScalar("RIGHTSHIFT(8, -1)", "0", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(16, -2)", "0", "INTEGER NOT NULL");
+
+    // === Large shifts ===
+    f.checkScalar("RIGHTSHIFT(0, 32)", "0", "INTEGER NOT NULL");
+    f.checkScalar("RIGHTSHIFT(0, 100)", "0", "INTEGER NOT NULL");
+
+    // === Binary types ===
+    f.checkScalar("RIGHTSHIFT(CAST(X'FF' AS BINARY(1)), 1)", "7f", "BINARY(1) 
NOT NULL");

Review Comment:
   you will need some tests with VARBINARY too



##########
site/_docs/reference.md:
##########
@@ -2973,7 +2973,8 @@ In the following:
 | * | BITAND(value1, value2)                         | Returns the bitwise AND 
of *value1* and *value2*. *value1* and *value2* must both be integer or binary 
values. Binary values must be of the same length.
 | * | BITOR(value1, value2)                          | Returns the bitwise OR 
of *value1* and *value2*. *value1* and *value2* must both be integer or binary 
values. Binary values must be of the same length.
 | * | BITXOR(value1, value2)                         | Returns the bitwise XOR 
of *value1* and *value2*. *value1* and *value2* must both be integer or binary 
values. Binary values must be of the same length.
-| * | LEFTSHIFT(value1, value2) | Returns the result of left-shifting *value1* 
by *value2* bits. *value1* can be integer, unsigned integer, or binary. For 
binary, the result has the same length as *value1*. The shift amount *value2* 
is normalized using modulo arithmetic based on the bit width of *value1*. For 
integers, this uses modulo 32; for binary types, it uses modulo (8 × 
byte_length). Negative shift amounts are converted to equivalent positive 
shifts through this modulo operation. For example, `LEFTSHIFT(1, -2)` returns 
`1073741824` (equivalent to `1 << 30`), and `LEFTSHIFT(8, -1)` returns `0` due 
to overflow.
+| * | LEFTSHIFT(value1, value2) | Returns the result of left-shifting *value1* 
by *value2* bits. *value1* can be integer, unsigned integer, or binary. For 
binary, the result has the same length as *value1*. The shift amount *value2* 
is normalized using modulo arithmetic based on the bit width of *value1*. For 
integers, this uses modulo 32; for binary types, it uses modulo (8 × 
byte_length). The sign of *value2* selects the direction: a non-negative amount 
shifts left, and a negative amount shifts right by the normalized magnitude. 
For example, `LEFTSHIFT(1, -2)` returns `0` (a right shift by 30), and 
`LEFTSHIFT(8, -1)` returns `0`.

Review Comment:
   this definition is not entirely clear to me for a type like VARBINARY or 
VARBINARY(10). Is it the dynamic length of the binary string?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to