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 (>>) 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 (>>) 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]