Преглед на файлове

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
Marios Trivyzas преди 6 години
родител
ревизия
91039bab12

+ 20 - 0
x-pack/plugin/sql/qa/src/main/resources/datetime-interval.csv-spec

@@ -182,6 +182,26 @@ SELECT -2 * INTERVAL '1 23:45' DAY TO MINUTES AS result;
 -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
 SELECT CAST('2018-05-13T12:34:56' AS DATETIME) - INTERVAL '2-8' YEAR TO MONTH AS result;
 

+ 3 - 3
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/BinaryArithmeticProcessor.java

@@ -164,7 +164,7 @@ public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor<Object,
             return null;
         }
 
-        if (f == BinaryArithmeticOperation.MUL || f == BinaryArithmeticOperation.DIV || f == BinaryArithmeticOperation.MOD) {
+        if (f == BinaryArithmeticOperation.DIV || f == BinaryArithmeticOperation.MOD) {
             if (!(left instanceof Number)) {
                 throw new SqlIllegalArgumentException("A number is required; received {}", left);
             }
@@ -176,8 +176,8 @@ public class BinaryArithmeticProcessor extends FunctionalBinaryProcessor<Object,
             return f.apply(left, right);
         }
 
-        if (f == BinaryArithmeticOperation.ADD || f == BinaryArithmeticOperation.SUB) {
-                return f.apply(left, right);
+        if (f == BinaryArithmeticOperation.ADD || f == BinaryArithmeticOperation.SUB || f == BinaryArithmeticOperation.MUL) {
+            return f.apply(left, right);
         }
 
         // this should not occur

+ 7 - 1
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/DateTimeArithmeticOperation.java

@@ -43,7 +43,7 @@ abstract class DateTimeArithmeticOperation extends ArithmeticOperation {
         // 2. 3. 4. intervals
         if ((DataTypes.isInterval(l) || DataTypes.isInterval(r))) {
             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 {
                 return resolveWithIntervals();
             }
@@ -54,6 +54,12 @@ abstract class DateTimeArithmeticOperation extends ArithmeticOperation {
     }
 
     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;
     }
 }

+ 1 - 1
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Mul.java

@@ -47,7 +47,7 @@ public class Mul extends ArithmeticOperation {
             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

+ 4 - 0
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/expression/predicate/operator/arithmetic/Sub.java

@@ -34,6 +34,10 @@ public class Sub extends DateTimeArithmeticOperation {
 
     @Override
     protected TypeResolution resolveWithIntervals() {
+        TypeResolution resolution = super.resolveWithIntervals();
+        if (resolution.unresolved()) {
+            return resolution;
+        }
         if ((right().dataType().isDateOrTimeBased()) && DataTypes.isInterval(left().dataType())) {
             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()));

+ 11 - 0
x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/type/DataTypeConversion.java

@@ -121,6 +121,17 @@ public abstract class DataTypeConversion {
                 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)) {
             // intervals widening
             if (DataTypes.isInterval(right)) {

+ 21 - 0
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

@@ -241,6 +241,27 @@ public class VerifierErrorMessagesTests extends ESTestCase {
             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() {
         assertEquals("1:43: Unknown column [xxx]\nline 1:8: Unknown column [xxx]",
                 error("SELECT xxx FROM test GROUP BY DAY_oF_YEAR(xxx)"));

+ 4 - 0
x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/type/DataTypeConversionTests.java

@@ -628,6 +628,10 @@ public class DataTypeConversionTests extends ESTestCase {
         assertEquals(FLOAT, commonType(FLOAT, INTEGER));
         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
         assertEquals(DATETIME, commonType(DATE, DATETIME));
         assertEquals(DATETIME, commonType(DATETIME, DATE));