Skip to content

Commit

Permalink
[CALCITE-3592] Implement BITNOT scalar function
Browse files Browse the repository at this point in the history
* Implemented the BITNOT scalar function as a standard function
* Supports integer and binary arguments
* Returns NULL if the argument is NULL
  • Loading branch information
normanj-bitquill committed Sep 26, 2024
1 parent ed18e58 commit 1467695
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@
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.BITCOUNT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITNOT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITOR;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BITXOR;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.BIT_AND;
Expand Down Expand Up @@ -702,6 +703,8 @@ void populate1() {
NullPolicy.STRICT);
defineMethod(BITXOR, BuiltInMethod.BIT_XOR.method,
NullPolicy.STRICT);
defineMethod(BITNOT, BuiltInMethod.BIT_NOT.method,
NullPolicy.STRICT);
define(CONCAT, new ConcatImplementor());
defineMethod(CONCAT_FUNCTION, BuiltInMethod.MULTI_STRING_CONCAT.method,
NullPolicy.STRICT);
Expand Down
15 changes: 15 additions & 0 deletions core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
Original file line number Diff line number Diff line change
Expand Up @@ -2940,6 +2940,21 @@ public static ByteString bitXor(ByteString b0, ByteString b1) {
return binaryOperator(b0, b1, (x, y) -> (byte) (x ^ y));
}

/** Bitwise function <code>BIT_NOT</code> applied to integer values. */
public static long bitNot(long b) {
return ~b;
}

/** Bitwise function <code>BIT_NOT</code> applied to a binary value. */
public static ByteString bitNot(ByteString b) {
final byte[] result = new byte[b.length()];
for (int i = 0; i < b.length(); i++) {
result[i] = (byte) ~b.byteAt(i);
}

return new ByteString(result);
}

/**
* Utility for bitwise function applied to two byteString values.
*
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/SqlKind.java
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,9 @@ public enum SqlKind {
/** The {@code BITXOR} scalar function. */
BITXOR,

/** The {@code BITNOT} scalar function. */
BITNOT,

/** The {@code BIT_AND} aggregate function. */
BIT_AND,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,14 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
ReturnTypes.LARGEST_INT_OR_FIRST_NON_NULL,
OperandTypes.INTEGER_INTEGER.or(OperandTypes.BINARY_BINARY));

/**
* <code>BITNOT</code> scalar function.
*/
public static final SqlFunction BITNOT =
SqlBasicFunction.create("BITNOT", SqlKind.BITNOT,
ReturnTypes.ARG0_OR_INTEGER,
OperandTypes.INTEGER.or(OperandTypes.BINARY));

/**
* <code>BIT_AND</code> aggregate function.
*/
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,16 @@ public static SqlCall stripSeparator(SqlCall call) {
public static final SqlReturnTypeInference ARG0_EXCEPT_INTEGER_NULLABLE =
ARG0_EXCEPT_INTEGER.andThen(SqlTypeTransforms.TO_NULLABLE);

public static final SqlReturnTypeInference ARG0_OR_INTEGER =
opBinding -> {
final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
if (SqlTypeName.NULL == opBinding.getOperandType(0).getSqlTypeName()) {
return typeFactory.createTypeWithNullability(
typeFactory.createSqlType(SqlTypeName.INTEGER), true);
}
return opBinding.getOperandType(0);
};

/**
* Chooses a type to return.
* If all arguments are null, return nullable integer type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ public enum BuiltInMethod {
BITCOUNT(SqlFunctions.class, "bitCount", BigDecimal.class),
BIT_OR(SqlFunctions.class, "bitOr", long.class, long.class),
BIT_XOR(SqlFunctions.class, "bitXor", long.class, long.class),
BIT_NOT(SqlFunctions.class, "bitNot", long.class),
MODIFIABLE_TABLE_GET_MODIFIABLE_COLLECTION(ModifiableTable.class,
"getModifiableCollection"),
SCANNABLE_TABLE_SCAN(ScannableTable.class, "scan", DataContext.class),
Expand Down
1 change: 1 addition & 0 deletions site/_docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2753,6 +2753,7 @@ 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.
| * | BITNOT(value) | Returns the bitwise NOT of *value*. *value* must be either an integer type or a binary value.
| f | BITAND_AGG(value) | Equivalent to `BIT_AND(value)`
| f | BITOR_AGG(value) | Equivalent to `BIT_OR(value)`
| * | BITCOUNT(value) | Returns the bitwise COUNT of *value* or NULL if *value* is NULL. *value* must be and integer or binary value.
Expand Down
23 changes: 23 additions & 0 deletions testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15738,6 +15738,29 @@ private static void checkLogicalOrFunc(SqlOperatorFixture f) {
true);
}

@Test void testBitNotScalarFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlStdOperatorTable.BITXOR, VmName.EXPAND);
f.checkFails("bitnot(^*^)", "Unknown identifier '\\*'", false);
f.checkScalar("bitnot(2)", Integer.toString(~2), "INTEGER NOT NULL");
f.checkScalar("bitnot(-5)", Integer.toString(~-5), "INTEGER NOT NULL");
f.checkScalar("bitnot(CAST(-5 AS TINYINT))", Byte.toString((byte) ~-5), "TINYINT NOT NULL");
f.checkScalar("bitnot(CAST(2 AS SMALLINT))", Short.toString((short) ~2), "SMALLINT NOT NULL");
f.checkScalar("bitnot(CAST(2 AS INTEGER))", Integer.toString(~2), "INTEGER NOT NULL");
f.checkScalar("bitnot(CAST(2 AS BIGINT))", Long.toString(~2), "BIGINT NOT NULL");
f.checkScalar("bitnot(CAST(x'0201' AS BINARY(2)))", "fdfe",
"BINARY(2) NOT NULL");
f.checkScalar("bitnot(CAST(x'0201' AS VARBINARY(2)))", "fdfe",
"VARBINARY(2) NOT NULL");
f.checkFails("^bitnot()^",
"Invalid number of arguments to function 'BITNOT'. Was expecting 1 arguments",
false);
f.checkFails("^bitnot(1, 2)^",
"Invalid number of arguments to function 'BITNOT'. Was expecting 1 arguments",
false);
f.checkNull("bitnot(NULL)");
}

@Test void testBitAndAggFunc() {
final SqlOperatorFixture f = fixture();
f.setFor(SqlLibraryOperators.BITAND_AGG, VmName.EXPAND);
Expand Down

0 comments on commit 1467695

Please sign in to comment.