Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
80,
ReturnTypes.ARG0,
InferTypes.RETURN_TYPE,
OperandTypes.NUMERIC_OR_INTERVAL);
OperandTypes.SIGNED_OR_INTERVAL);

/**
* Checked version of prefix arithmetic minus operator, '<code>-</code>'.
Expand Down
65 changes: 64 additions & 1 deletion core/src/main/java/org/apache/calcite/sql/type/OperandTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,70 @@ public static SqlOperandTypeChecker variadic(
public static final SqlSingleOperandTypeChecker INTEGER =
family(SqlTypeFamily.INTEGER);

/** Operand type checker that only allows signed types.
* This is almost like an OR of 4 type families (INTEGER, APPROXIMATE_NUMERIC, DECIMAL)
* but OR allows implicit casts to any of the types, and this checker doesn't. */
public static final SqlSingleOperandTypeChecker SIGNED = new SqlSingleOperandTypeChecker() {
@Override public boolean checkSingleOperandType(SqlCallBinding callBinding, SqlNode operand,
int iFormalOperand, boolean throwOnFailure) {
RelDataType type = SqlTypeUtil.deriveType(callBinding, operand);
SqlTypeName typeName = type.getSqlTypeName();
boolean isLegal = SqlTypeName.INT_TYPES.contains(typeName)
|| SqlTypeName.APPROX_TYPES.contains(typeName)
|| typeName == SqlTypeName.DECIMAL;

if (!isLegal) {
if (throwOnFailure) {
throw callBinding.newValidationSignatureError();
}
return false;
}
return true;
}

@Override public boolean checkOperandTypes(
SqlCallBinding callBinding,
boolean throwOnFailure) {
// This is a specialized implementation of FamilyOperandTypeChecker.checkOperandTypes.
SqlNode op = callBinding.operands().get(0);
if (!checkSingleOperandType(callBinding, op, 0, false)) {
// try to coerce type if it is allowed.
boolean coerced = false;
if (callBinding.isTypeCoercionEnabled()) {
// Also allow expressions that can be coerced to NUMERIC (e.g. type CHAR)
TypeCoercion typeCoercion = callBinding.getValidator().getTypeCoercion();
ImmutableList.Builder<RelDataType> builder = ImmutableList.builder();
builder.add(callBinding.getOperandType(0));
ImmutableList<RelDataType> dataTypes = builder.build();
coerced =
typeCoercion.builtinFunctionCoercion(
callBinding, dataTypes, ImmutableList.of(SqlTypeFamily.NUMERIC));
}
// re-validate the new nodes type.
SqlNode op1 = callBinding.operands().get(0);
if (!checkSingleOperandType(
callBinding,
op1,
0,
throwOnFailure)) {
return false;
}
return coerced;
}
return true;
}

@Override public String getAllowedSignatures(SqlOperator op, String opName) {
return SqlUtil.getAliasedSignature(op, opName, ImmutableList.of(SqlTypeFamily.INTEGER)) + "\n"
+ SqlUtil.getAliasedSignature(
op, opName, ImmutableList.of(SqlTypeFamily.APPROXIMATE_NUMERIC)) + "\n"
+ SqlUtil.getAliasedSignature(op, opName, ImmutableList.of(SqlTypeFamily.DECIMAL));
}
};

public static final SqlSingleOperandTypeChecker UNSIGNED_NUMERIC_UNSIGNED_NUMERIC =
family(SqlTypeFamily.UNSIGNED_NUMERIC, SqlTypeFamily.UNSIGNED_NUMERIC);


public static final SqlSingleOperandTypeChecker INTEGER_INTEGER =
family(SqlTypeFamily.INTEGER, SqlTypeFamily.INTEGER);

Expand Down Expand Up @@ -1297,6 +1357,9 @@ public static SqlSingleOperandTypeChecker same(int operandCount,
public static final SqlSingleOperandTypeChecker NUMERIC_OR_INTERVAL =
NUMERIC.or(INTERVAL);

public static final SqlSingleOperandTypeChecker SIGNED_OR_INTERVAL =
SIGNED.or(INTERVAL);

public static final SqlSingleOperandTypeChecker NUMERIC_OR_STRING =
NUMERIC.or(STRING);

Expand Down
5 changes: 5 additions & 0 deletions core/src/test/resources/sql/unsigned.iq
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ EXPR$0
6
!ok

SELECT -CAST(200 AS INT UNSIGNED);
java.sql.SQLException: Error while executing SQL "SELECT -CAST(200 AS INT UNSIGNED)": From line 1, column 8 to line 1, column 33: Cannot apply '-' to arguments of type '-<INTEGER UNSIGNED>'. Supported form(s): '-<INTEGER>'

!error

SELECT CAST(200 AS INT UNSIGNED) - 100;
EXPR$0
100
Expand Down
22 changes: 22 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 @@ -16747,6 +16747,28 @@ private static void checkLogicalOrFunc(SqlOperatorFixture f) {
f.checkNull("CAST(NULL AS INTEGER UNSIGNED) ^^ CAST(NULL AS INTEGER UNSIGNED)");
}

@Test void testUnsignedArithmetic() {
final SqlOperatorFixture f = fixture();
// Test case for [CALCITE-7360] The meaning of negation for unsigned numbers is not defined
f.checkFails("^-CAST (100 AS INT UNSIGNED)^",
"Cannot apply '-' to arguments of type '-<INTEGER UNSIGNED>'\\. "
+ "Supported form\\(s\\): '-<INTEGER>'\\n"
+ "'-<APPROXIMATE_NUMERIC>'\\n"
+ "'-<DECIMAL>'\\n"
+ "'-<DATETIME_INTERVAL>'", false);
f.checkScalar("CAST(2 AS INT UNSIGNED)", "2", "INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) + CAST(2 AS INT UNSIGNED)", "4",
"INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) + CAST(2 AS TINYINT UNSIGNED)", "4",
"INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) + 2", "4", "INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) - 2", "0", "INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) - CAST(2 AS TINYINT UNSIGNED)", "0",
"INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) * 2", "4", "INTEGER UNSIGNED NOT NULL");
f.checkScalar("CAST(2 AS INT UNSIGNED) / 2", "1", "INTEGER UNSIGNED NOT NULL");
}

/**
* Test cases for
* <a href="https://issues.apache.org/jira/browse/CALCITE-7109">[CALCITE-7109]
Expand Down
Loading