Skip to content

Commit 5d687ce

Browse files
committed
[CALCITE-7270] Add support for a DIVIDE_0_NULL operation
Signed-off-by: Mihai Budiu <mbudiu@feldera.com>
1 parent df3de96 commit 5d687ce

22 files changed

Lines changed: 511 additions & 5 deletions

File tree

core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@
116116
import static org.apache.calcite.linq4j.tree.ExpressionType.Add;
117117
import static org.apache.calcite.linq4j.tree.ExpressionType.AddChecked;
118118
import static org.apache.calcite.linq4j.tree.ExpressionType.Divide;
119+
import static org.apache.calcite.linq4j.tree.ExpressionType.Divide0Null;
120+
import static org.apache.calcite.linq4j.tree.ExpressionType.Divide0NullChecked;
119121
import static org.apache.calcite.linq4j.tree.ExpressionType.DivideChecked;
120122
import static org.apache.calcite.linq4j.tree.ExpressionType.Equal;
121123
import static org.apache.calcite.linq4j.tree.ExpressionType.GreaterThan;
@@ -384,6 +386,7 @@
384386
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHARACTER_LENGTH;
385387
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHAR_LENGTH;
386388
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHECKED_DIVIDE;
389+
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHECKED_DIVIDE_0_NULL;
387390
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHECKED_DIVIDE_INTEGER;
388391
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHECKED_MINUS;
389392
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.CHECKED_MULTIPLY;
@@ -410,6 +413,7 @@
410413
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DEGREES;
411414
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DENSE_RANK;
412415
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE;
416+
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE_0_NULL;
413417
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.DIVIDE_INTEGER;
414418
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ELEMENT;
415419
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.EQUALS;
@@ -478,6 +482,7 @@
478482
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MINUS_DATE;
479483
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MOD;
480484
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MODE;
485+
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MOD_0_NULL;
481486
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTIPLY;
482487
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_EXCEPT;
483488
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_EXCEPT_DISTINCT;
@@ -828,6 +833,7 @@ void populate1() {
828833
defineBinary(MINUS, Subtract, NullPolicy.STRICT, "minus");
829834
defineBinary(MULTIPLY, Multiply, NullPolicy.STRICT, "multiply");
830835
defineBinary(DIVIDE, Divide, NullPolicy.STRICT, "divide");
836+
defineBinary(DIVIDE_0_NULL, Divide, NullPolicy.SEMI_STRICT, "divide0Null");
831837
defineBinary(DIVIDE_INTEGER, Divide, NullPolicy.STRICT, "divide");
832838
defineUnary(UNARY_MINUS, Negate, NullPolicy.STRICT,
833839
BuiltInMethod.BIG_DECIMAL_NEGATE.getMethodName());
@@ -839,8 +845,13 @@ void populate1() {
839845
defineBinary(CHECKED_DIVIDE, DivideChecked, NullPolicy.STRICT, "checkedDivide");
840846
defineBinary(CHECKED_DIVIDE_INTEGER, DivideChecked, NullPolicy.STRICT, "checkedDivide");
841847
defineUnary(CHECKED_UNARY_MINUS, NegateChecked, NullPolicy.STRICT, "checkedUnaryMinus");
848+
// nullable division
849+
defineBinary(DIVIDE_0_NULL, Divide0Null, NullPolicy.SEMI_STRICT, "nullableDivide");
850+
defineBinary(CHECKED_DIVIDE_0_NULL, Divide0NullChecked,
851+
NullPolicy.SEMI_STRICT, "checkedNullableDivide");
842852

843853
defineMethod(MOD, BuiltInMethod.MOD.method, NullPolicy.STRICT);
854+
defineMethod(MOD_0_NULL, BuiltInMethod.MOD_0_NULL.method, NullPolicy.SEMI_STRICT);
844855
defineMethod(EXP, BuiltInMethod.EXP.method, NullPolicy.STRICT);
845856
defineMethod(POWER, BuiltInMethod.POWER.method, NullPolicy.STRICT);
846857
defineMethod(POWER_PG, BuiltInMethod.POWER_PG.method, NullPolicy.STRICT);
@@ -3347,7 +3358,10 @@ private static class BinaryImplementor extends AbstractRexCallImplementor {
33473358
}
33483359

33493360
// For checked arithmetic call the method.
3350-
if (CHECKED_OPERATORS.contains(op)) {
3361+
if (CHECKED_OPERATORS.contains(op)
3362+
|| op.kind == SqlKind.DIVIDE_0_NULL
3363+
|| op.kind == SqlKind.MOD_0_NULL
3364+
|| op.kind == SqlKind.CHECKED_DIVIDE_0_NULL) {
33513365
return Expressions.call(SqlFunctions.class, backupMethodName, argValueList);
33523366
}
33533367

core/src/main/java/org/apache/calcite/plan/Strong.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ private static Map<SqlKind, Policy> createPolicyMap() {
355355
map.put(SqlKind.CHECKED_MINUS_PREFIX, Policy.ANY);
356356
map.put(SqlKind.CHECKED_TIMES, Policy.ANY);
357357
map.put(SqlKind.CHECKED_DIVIDE, Policy.ANY);
358+
map.put(SqlKind.DIVIDE_0_NULL, Policy.AS_IS);
359+
map.put(SqlKind.MOD_0_NULL, Policy.AS_IS);
358360

359361
map.put(SqlKind.DIVIDE, Policy.ANY);
360362
map.put(SqlKind.CAST, Policy.ANY);

core/src/main/java/org/apache/calcite/rex/RexSimplify.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,8 @@ RexNode simplify(RexNode e, RexUnknownAs unknownAs) {
327327
case MINUS:
328328
case TIMES:
329329
case DIVIDE:
330+
case DIVIDE_0_NULL:
331+
case CHECKED_DIVIDE_0_NULL:
330332
case CHECKED_PLUS:
331333
case CHECKED_MINUS:
332334
case CHECKED_TIMES:
@@ -457,6 +459,8 @@ private RexNode simplifyArithmetic(RexCall e) {
457459
return simplifyMultiply(e);
458460
case DIVIDE:
459461
case CHECKED_DIVIDE:
462+
case DIVIDE_0_NULL:
463+
case CHECKED_DIVIDE_0_NULL:
460464
return simplifyDivide(e);
461465
default:
462466
throw new IllegalArgumentException("Unsupported arithmetic operation " + e.getKind());

core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,6 +2823,147 @@ public static long divide(long b0, BigDecimal b1) {
28232823
: ULong.valueOf(UnsignedType.toBigInteger(b0).divide(UnsignedType.toBigInteger(b1)));
28242824
}
28252825

2826+
// nullable divide
2827+
2828+
public static @Nullable Integer divide0Null(int b0, BigDecimal b1) {
2829+
if (b1.equals(BigDecimal.ZERO)) {
2830+
return null;
2831+
}
2832+
return BigDecimal.valueOf(b0)
2833+
.divide(b1, RoundingMode.HALF_DOWN).intValue();
2834+
}
2835+
2836+
public static @Nullable Long divide0Null(long b0, BigDecimal b1) {
2837+
if (b1.equals(BigDecimal.ZERO)) {
2838+
return null;
2839+
}
2840+
return BigDecimal.valueOf(b0)
2841+
.divide(b1, RoundingMode.HALF_DOWN).longValue();
2842+
}
2843+
2844+
public static @Nullable UByte divide0Null(@PolyNull UByte b0,
2845+
@PolyNull UByte b1) {
2846+
if (b0 == null || b1 == null) {
2847+
return castNonNull(null);
2848+
} else if (b1.intValue() == 0) {
2849+
return null;
2850+
} else {
2851+
return UByte.valueOf(b0.intValue() / b1.intValue());
2852+
}
2853+
}
2854+
2855+
public static @Nullable UShort divide0Null(@PolyNull UShort b0,
2856+
@PolyNull UShort b1) {
2857+
if (b0 == null || b1 == null) {
2858+
return null;
2859+
} else if (b1.intValue() == 0) {
2860+
return null;
2861+
} else {
2862+
return UShort.valueOf(b0.intValue() / b1.intValue());
2863+
}
2864+
}
2865+
2866+
public static @Nullable UInteger divide0Null(@PolyNull UInteger b0,
2867+
@PolyNull UInteger b1) {
2868+
if (b0 == null || b1 == null) {
2869+
return null;
2870+
} else if (b1.longValue() == 0L) {
2871+
return null;
2872+
} else {
2873+
return UInteger.valueOf(b0.longValue() / b1.longValue());
2874+
}
2875+
}
2876+
2877+
public static @Nullable ULong divide0Null(@PolyNull ULong b0,
2878+
@PolyNull ULong b1) {
2879+
if (b0 == null || b1 == null) {
2880+
return null;
2881+
} else if (b1.equals(ULong.valueOf(0))) {
2882+
return null;
2883+
} else {
2884+
return ULong.valueOf(UnsignedType.toBigInteger(b0).divide(UnsignedType.toBigInteger(b1)));
2885+
}
2886+
}
2887+
2888+
/** SQL <code>/</code> operator applied to int values. */
2889+
public static @Nullable Integer divide0Null(int b0, int b1) {
2890+
if (b1 == 0) {
2891+
return null;
2892+
} else {
2893+
return b0 / b1;
2894+
}
2895+
}
2896+
2897+
/** SQL <code>/</code> operator applied to int values; left side may be
2898+
* null. */
2899+
public static @Nullable Integer divide0Null(@PolyNull Integer b0, int b1) {
2900+
if (b0 == null) {
2901+
return null;
2902+
} else if (b1 == 0) {
2903+
return null;
2904+
} else {
2905+
return b0 / b1;
2906+
}
2907+
}
2908+
2909+
/** SQL <code>/</code> operator applied to int values; right side may be
2910+
* null. */
2911+
public static @Nullable Integer divide0Null(int b0, @PolyNull Integer b1) {
2912+
if (b1 == null) {
2913+
return null;
2914+
} else if (b1 == 0) {
2915+
return null;
2916+
} else {
2917+
return b0 / b1;
2918+
}
2919+
}
2920+
2921+
/** SQL <code>/</code> operator applied to nullable int values. */
2922+
public static @Nullable Integer divide0Null(@PolyNull Integer b0,
2923+
@PolyNull Integer b1) {
2924+
if (b0 == null || b1 == null) {
2925+
return null;
2926+
} else if (b1 == 0) {
2927+
return null;
2928+
} else {
2929+
return b0 / b1;
2930+
}
2931+
}
2932+
2933+
/** SQL <code>/</code> operator applied to nullable long and int values. */
2934+
public static @Nullable Long divide0Null(Long b0, @PolyNull Integer b1) {
2935+
if (b0 == null || b1 == null) {
2936+
return null;
2937+
} else if (b1 == 0) {
2938+
return null;
2939+
} else {
2940+
return b0.longValue() / b1.longValue();
2941+
}
2942+
}
2943+
2944+
/** SQL <code>/</code> operator applied to nullable int and long values. */
2945+
public static @Nullable Long divide0Null(@PolyNull Integer b0, @PolyNull Long b1) {
2946+
if (b0 == null || b1 == null) {
2947+
return null;
2948+
} else if (b1 == 0) {
2949+
return null;
2950+
} else {
2951+
return b0.longValue() / b1.longValue();
2952+
}
2953+
}
2954+
2955+
/** SQL <code>/</code> operator applied to BigDecimal values. */
2956+
public static @Nullable BigDecimal divide0Null(@PolyNull BigDecimal b0,
2957+
@PolyNull BigDecimal b1) {
2958+
if (b0 == null || b1 == null) {
2959+
return null;
2960+
} else if (b1.equals(BigDecimal.ZERO)) {
2961+
return null;
2962+
} else {
2963+
return b0.divide(b1, MathContext.DECIMAL64);
2964+
}
2965+
}
2966+
28262967
public static byte checkedDivide(byte b0, byte b1) {
28272968
return intToByte(b0 / b1);
28282969
}
@@ -3906,6 +4047,54 @@ public static BigDecimal mod(BigDecimal b0, BigDecimal b1) {
39064047
return bigDecimals[1];
39074048
}
39084049

4050+
/** SQL nullable <code>MOD</code> operator applied to byte values. */
4051+
public static @Nullable Byte mod0Null(byte b0, byte b1) {
4052+
if (b1 == 0) {
4053+
return null;
4054+
}
4055+
return (byte) (b0 % b1);
4056+
}
4057+
4058+
/** SQL nullable <code>MOD</code> operator applied to short values. */
4059+
public static @Nullable Short mod0Null(short b0, short b1) {
4060+
if (b1 == 0) {
4061+
return null;
4062+
}
4063+
return (short) (b0 % b1);
4064+
}
4065+
4066+
/** SQL nullable <code>MOD</code> operator applied to int values. */
4067+
public static @Nullable Integer mod0Null(int b0, int b1) {
4068+
if (b1 == 0) {
4069+
return null;
4070+
}
4071+
return b0 % b1;
4072+
}
4073+
4074+
/** SQL nullable <code>MOD</code> operator applied to long values. */
4075+
public static @Nullable Long mod0Null(long b0, long b1) {
4076+
if (b1 == 0L) {
4077+
return null;
4078+
}
4079+
return b0 % b1;
4080+
}
4081+
4082+
public static @Nullable BigDecimal mod0Null(BigDecimal b0, int b1) {
4083+
return mod0Null(b0, BigDecimal.valueOf(b1));
4084+
}
4085+
4086+
public static @Nullable BigDecimal mod0Null(int b0, BigDecimal b1) {
4087+
return mod(BigDecimal.valueOf(b0), b1);
4088+
}
4089+
4090+
public static @Nullable BigDecimal mod0Null(BigDecimal b0, BigDecimal b1) {
4091+
if (b1.equals(BigDecimal.ZERO)) {
4092+
return null;
4093+
}
4094+
final BigDecimal[] bigDecimals = b0.divideAndRemainder(b1);
4095+
return bigDecimals[1];
4096+
}
4097+
39094098
// FLOOR
39104099

39114100
public static double floor(double b0) {

core/src/main/java/org/apache/calcite/sql/SqlKind.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ public enum SqlKind {
296296
/** Arithmetic remainder operator, "MOD" (and "%" in some dialects). */
297297
MOD,
298298

299+
/** Nullable arithmetic remainder operator which returns NULL when remainder is zero,
300+
* "MOD" (and "%" in some dialects). */
301+
MOD_0_NULL,
302+
299303
/**
300304
* Arithmetic plus operator, "+".
301305
*
@@ -335,6 +339,16 @@ public enum SqlKind {
335339
*/
336340
CHECKED_DIVIDE,
337341

342+
/**
343+
* Unchecked nullable version of DIVIDE, which produces NULL when dividing by zero.
344+
*/
345+
DIVIDE_0_NULL,
346+
347+
/**
348+
* Checked nullable version of DIVIDE, which produces NULL when dividing by zero.
349+
*/
350+
CHECKED_DIVIDE_0_NULL,
351+
338352
/**
339353
* Alternation operator in a pattern expression within a
340354
* {@code MATCH_RECOGNIZE} clause.
@@ -1566,7 +1580,8 @@ public enum SqlKind {
15661580
CHECKED_PLUS, CHECKED_MINUS, CHECKED_TIMES, CHECKED_DIVIDE);
15671581

15681582
public static final Set<SqlKind> CHECKED_ARITHMETIC =
1569-
EnumSet.of(CHECKED_PLUS, CHECKED_MINUS, CHECKED_TIMES, CHECKED_DIVIDE, CHECKED_MINUS_PREFIX);
1583+
EnumSet.of(CHECKED_PLUS, CHECKED_MINUS, CHECKED_TIMES, CHECKED_DIVIDE,
1584+
CHECKED_DIVIDE_0_NULL, CHECKED_MINUS_PREFIX);
15701585

15711586

15721587
/**

0 commit comments

Comments
 (0)