SQL: Fix issue regarding INTERVAL * number (#42014)
Interval * integer number is a valid operation which previously was only supported for foldables (literals) and not when a field was involved. That was because: 1. There was no common type returned for that combination 2. The `BinaryArithmeticOperation` was permitting the multiplication (called by fold()) but the BinaryArithmeticProcessor didn't allow it Moreover the error message for invalid arithmetic operations was wrong because of the issue with the overloading methods of `LoggerMessageFormat.format`. Fixes: #41239 Fixes: #41200 (cherry picked from commit 91039bab12d3ef27d6eac9cdc891a3b3ad0c694d)
This commit is contained in:
parent
9191b02213
commit
7473742e6e
|
@ -182,6 +182,26 @@ SELECT -2 * INTERVAL '1 23:45' DAY TO MINUTES AS result;
|
||||||
-3 23:30:00.0
|
-3 23:30:00.0
|
||||||
;
|
;
|
||||||
|
|
||||||
|
intervalHoursMultiply
|
||||||
|
SELECT 4 * -INTERVAL '2' HOURS AS result1, -5 * -INTERVAL '3' HOURS AS result2;
|
||||||
|
result1 | result2
|
||||||
|
---------------+--------------
|
||||||
|
-0 08:00:00.0 | +0 15:00:00.0
|
||||||
|
;
|
||||||
|
|
||||||
|
intervalAndFieldMultiply
|
||||||
|
schema::languages:byte|result:string
|
||||||
|
SELECT languages, CAST (languages * INTERVAL '1 10:30' DAY TO MINUTES AS string) AS result FROM test_emp ORDER BY emp_no LIMIT 5;
|
||||||
|
|
||||||
|
languages | result
|
||||||
|
---------------+---------------------------------------------
|
||||||
|
2 | +2 21:00:00.0
|
||||||
|
5 | +7 04:30:00.0
|
||||||
|
4 | +5 18:00:00.0
|
||||||
|
5 | +7 04:30:00.0
|
||||||
|
1 | +1 10:30:00.0
|
||||||
|
;
|
||||||
|
|
||||||
dateMinusInterval
|
dateMinusInterval
|
||||||
SELECT CAST('2018-05-13T12:34:56' AS DATETIME) - INTERVAL '2-8' YEAR TO MONTH AS result;
|
SELECT CAST('2018-05-13T12:34:56' AS DATETIME) - INTERVAL '2-8' YEAR TO MONTH AS result;
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor<Object,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f == BinaryArithmeticOperation.MUL || f == BinaryArithmeticOperation.DIV || f == BinaryArithmeticOperation.MOD) {
|
if (f == BinaryArithmeticOperation.DIV || f == BinaryArithmeticOperation.MOD) {
|
||||||
if (!(left instanceof Number)) {
|
if (!(left instanceof Number)) {
|
||||||
throw new SqlIllegalArgumentException("A number is required; received {}", left);
|
throw new SqlIllegalArgumentException("A number is required; received {}", left);
|
||||||
}
|
}
|
||||||
|
@ -176,8 +176,8 @@ public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor<Object,
|
||||||
return f.apply(left, right);
|
return f.apply(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f == BinaryArithmeticOperation.ADD || f == BinaryArithmeticOperation.SUB) {
|
if (f == BinaryArithmeticOperation.ADD || f == BinaryArithmeticOperation.SUB || f == BinaryArithmeticOperation.MUL) {
|
||||||
return f.apply(left, right);
|
return f.apply(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should not occur
|
// this should not occur
|
||||||
|
|
|
@ -43,7 +43,7 @@ abstract class DateTimeArithmeticOperation extends ArithmeticOperation {
|
||||||
// 2. 3. 4. intervals
|
// 2. 3. 4. intervals
|
||||||
if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) {
|
if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) {
|
||||||
if (DataTypeConversion.commonType(l, r) == null) {
|
if (DataTypeConversion.commonType(l, r) == null) {
|
||||||
return new TypeResolution(format("[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r));
|
return new TypeResolution(format(null, "[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r));
|
||||||
} else {
|
} else {
|
||||||
return resolveWithIntervals();
|
return resolveWithIntervals();
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,12 @@ abstract class DateTimeArithmeticOperation extends ArithmeticOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TypeResolution resolveWithIntervals() {
|
protected TypeResolution resolveWithIntervals() {
|
||||||
|
DataType l = left().dataType();
|
||||||
|
DataType r = right().dataType();
|
||||||
|
|
||||||
|
if (!(r.isDateOrTimeBased() || DataTypes.isInterval(r))|| !(l.isDateOrTimeBased() || DataTypes.isInterval(l))) {
|
||||||
|
return new TypeResolution(format(null, "[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r));
|
||||||
|
}
|
||||||
return TypeResolution.TYPE_RESOLVED;
|
return TypeResolution.TYPE_RESOLVED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class Mul extends ArithmeticOperation {
|
||||||
return TypeResolution.TYPE_RESOLVED;
|
return TypeResolution.TYPE_RESOLVED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TypeResolution(format("[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r));
|
return new TypeResolution(format(null, "[{}] has arguments with incompatible types [{}] and [{}]", symbol(), l, r));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -34,6 +34,10 @@ public class Sub extends DateTimeArithmeticOperation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TypeResolution resolveWithIntervals() {
|
protected TypeResolution resolveWithIntervals() {
|
||||||
|
TypeResolution resolution = super.resolveWithIntervals();
|
||||||
|
if (resolution.unresolved()) {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
if ((right().dataType().isDateOrTimeBased()) && DataTypes.isInterval(left().dataType())) {
|
if ((right().dataType().isDateOrTimeBased()) && DataTypes.isInterval(left().dataType())) {
|
||||||
return new TypeResolution(format(null, "Cannot subtract a {}[{}] from an interval[{}]; do you mean the reverse?",
|
return new TypeResolution(format(null, "Cannot subtract a {}[{}] from an interval[{}]; do you mean the reverse?",
|
||||||
right().dataType().typeName, right().source().text(), left().source().text()));
|
right().dataType().typeName, right().source().text(), left().source().text()));
|
||||||
|
|
|
@ -121,6 +121,17 @@ public abstract class DataTypeConversion {
|
||||||
return right;
|
return right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Interval * integer is a valid operation
|
||||||
|
if (DataTypes.isInterval(left)) {
|
||||||
|
if (right.isInteger()) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (DataTypes.isInterval(right)) {
|
||||||
|
if (left.isInteger()) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (DataTypes.isInterval(left)) {
|
if (DataTypes.isInterval(left)) {
|
||||||
// intervals widening
|
// intervals widening
|
||||||
if (DataTypes.isInterval(right)) {
|
if (DataTypes.isInterval(right)) {
|
||||||
|
|
|
@ -241,6 +241,27 @@ public class VerifierErrorMessagesTests extends ESTestCase {
|
||||||
error("SELECT INTERVAL 1 MONTH - CAST('12:23:56.789' AS TIME)"));
|
error("SELECT INTERVAL 1 MONTH - CAST('12:23:56.789' AS TIME)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAddIntervalAndNumberNotAllowed() {
|
||||||
|
assertEquals("1:8: [+] has arguments with incompatible types [INTERVAL_DAY] and [INTEGER]",
|
||||||
|
error("SELECT INTERVAL 1 DAY + 100"));
|
||||||
|
assertEquals("1:8: [+] has arguments with incompatible types [INTEGER] and [INTERVAL_DAY]",
|
||||||
|
error("SELECT 100 + INTERVAL 1 DAY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSubtractIntervalAndNumberNotAllowed() {
|
||||||
|
assertEquals("1:8: [-] has arguments with incompatible types [INTERVAL_MINUTE] and [DOUBLE]",
|
||||||
|
error("SELECT INTERVAL 10 MINUTE - 100.0"));
|
||||||
|
assertEquals("1:8: [-] has arguments with incompatible types [DOUBLE] and [INTERVAL_MINUTE]",
|
||||||
|
error("SELECT 100.0 - INTERVAL 10 MINUTE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testMultiplyIntervalWithDecimalNotAllowed() {
|
||||||
|
assertEquals("1:8: [*] has arguments with incompatible types [INTERVAL_MONTH] and [DOUBLE]",
|
||||||
|
error("SELECT INTERVAL 1 MONTH * 1.234"));
|
||||||
|
assertEquals("1:8: [*] has arguments with incompatible types [DOUBLE] and [INTERVAL_MONTH]",
|
||||||
|
error("SELECT 1.234 * INTERVAL 1 MONTH"));
|
||||||
|
}
|
||||||
|
|
||||||
public void testMultipleColumns() {
|
public void testMultipleColumns() {
|
||||||
assertEquals("1:43: Unknown column [xxx]\nline 1:8: Unknown column [xxx]",
|
assertEquals("1:43: Unknown column [xxx]\nline 1:8: Unknown column [xxx]",
|
||||||
error("SELECT xxx FROM test GROUP BY DAY_oF_YEAR(xxx)"));
|
error("SELECT xxx FROM test GROUP BY DAY_oF_YEAR(xxx)"));
|
||||||
|
|
|
@ -627,6 +627,10 @@ public class DataTypeConversionTests extends ESTestCase {
|
||||||
assertEquals(FLOAT, commonType(FLOAT, INTEGER));
|
assertEquals(FLOAT, commonType(FLOAT, INTEGER));
|
||||||
assertEquals(DOUBLE, commonType(DOUBLE, FLOAT));
|
assertEquals(DOUBLE, commonType(DOUBLE, FLOAT));
|
||||||
|
|
||||||
|
// numeric and intervals
|
||||||
|
assertEquals(INTERVAL_YEAR_TO_MONTH, commonType(INTERVAL_YEAR_TO_MONTH, LONG));
|
||||||
|
assertEquals(INTERVAL_HOUR_TO_MINUTE, commonType(INTEGER, INTERVAL_HOUR_TO_MINUTE));
|
||||||
|
|
||||||
// dates/datetimes and intervals
|
// dates/datetimes and intervals
|
||||||
assertEquals(DATETIME, commonType(DATE, DATETIME));
|
assertEquals(DATETIME, commonType(DATE, DATETIME));
|
||||||
assertEquals(DATETIME, commonType(DATETIME, DATE));
|
assertEquals(DATETIME, commonType(DATETIME, DATE));
|
||||||
|
|
Loading…
Reference in New Issue