This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new a0e861e36f Support 'EXTRACT' as a scalar function (#13463)
a0e861e36f is described below

commit a0e861e36f198f64b93cfcae81b95a2f7e8a9703
Author: Yash Mayya <[email protected]>
AuthorDate: Sat Jun 29 05:14:16 2024 +0530

    Support 'EXTRACT' as a scalar function (#13463)
---
 .../pinot/common/function/DateTimeUtils.java       | 40 ++++++++++++-
 .../common/function/scalar/DateTimeFunctions.java  |  5 ++
 .../pinot/sql/parsers/CalciteSqlCompilerTest.java  | 37 +++++-------
 .../function/ExtractTransformFunction.java         | 66 ++--------------------
 .../function/ExtractTransformFunctionTest.java     |  3 +
 .../rel/rules/PinotEvaluateLiteralRule.java        |  3 +
 6 files changed, 70 insertions(+), 84 deletions(-)

diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/function/DateTimeUtils.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/function/DateTimeUtils.java
index b2a8ce933c..f211bc71f3 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/function/DateTimeUtils.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/function/DateTimeUtils.java
@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
 
 
 /**
- * Helper methods and constructs for datetrunc function
+ * Helper methods and constructs for date/time functions
  */
 public class DateTimeUtils {
   private DateTimeUtils() {
@@ -41,6 +41,7 @@ public class DateTimeUtils {
 
   private static final Logger LOGGER = 
LoggerFactory.getLogger(DateTimeUtils.class);
   private static final DateTimeFieldType QUARTER_OF_YEAR = new 
QuarterOfYearDateTimeField();
+  private static final Chronology CHRONOLOGY_UTC = 
ISOChronology.getInstanceUTC();
 
   public static DateTimeField getTimestampField(ISOChronology chronology, 
String unitString) {
     switch (unitString.toLowerCase()) {
@@ -152,4 +153,41 @@ public class DateTimeUtils {
       }
     }
   }
+
+  /**
+   * The supported field types for the EXTRACT operator
+   */
+  public enum ExtractFieldType {
+    YEAR, QUARTER, MONTH, WEEK, DAY, DOY, DOW, HOUR, MINUTE, SECOND
+  }
+
+  /**
+   * Helper method to implement the SQL <code>EXTRACT</code> operator.
+   */
+  public static int extract(ExtractFieldType extractFieldType, long timestamp) 
{
+    switch (extractFieldType) {
+      case YEAR:
+        return CHRONOLOGY_UTC.year().get(timestamp);
+      case QUARTER:
+        return (CHRONOLOGY_UTC.monthOfYear().get(timestamp) - 1) / 3 + 1;
+      case MONTH:
+        return CHRONOLOGY_UTC.monthOfYear().get(timestamp);
+      case WEEK:
+        return CHRONOLOGY_UTC.weekOfWeekyear().get(timestamp);
+      case DAY:
+        return CHRONOLOGY_UTC.dayOfMonth().get(timestamp);
+      case DOY:
+        return CHRONOLOGY_UTC.dayOfYear().get(timestamp);
+      case DOW:
+        return CHRONOLOGY_UTC.dayOfWeek().get(timestamp);
+      case HOUR:
+        return CHRONOLOGY_UTC.hourOfDay().get(timestamp);
+      case MINUTE:
+        return CHRONOLOGY_UTC.minuteOfHour().get(timestamp);
+      case SECOND:
+        return CHRONOLOGY_UTC.secondOfMinute().get(timestamp);
+      default:
+        throw new IllegalArgumentException("Unsupported FIELD type");
+    }
+  }
 }
diff --git 
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/DateTimeFunctions.java
 
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/DateTimeFunctions.java
index adb6c9bda6..ac734dd4da 100644
--- 
a/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/DateTimeFunctions.java
+++ 
b/pinot-common/src/main/java/org/apache/pinot/common/function/scalar/DateTimeFunctions.java
@@ -1259,4 +1259,9 @@ public class DateTimeFunctions {
     }
     return results;
   }
+
+  @ScalarFunction
+  public static int extract(String interval, long timestamp) {
+    return 
DateTimeUtils.extract(DateTimeUtils.ExtractFieldType.valueOf(interval), 
timestamp);
+  }
 }
diff --git 
a/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
 
b/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
index 3576c19660..7fb1f7ae10 100644
--- 
a/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
+++ 
b/pinot-common/src/test/java/org/apache/pinot/sql/parsers/CalciteSqlCompilerTest.java
@@ -263,32 +263,25 @@ public class CalciteSqlCompilerTest {
   @Test
   public void testExtract() {
     {
-      // Case 1 -- Year and date format ('2017-06-15')
-      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(YEAR FROM 
'2017-06-15')");
-      Function function = pinotQuery.getSelectList().get(0).getFunctionCall();
-      
Assert.assertEquals(function.getOperands().get(0).getLiteral().getStringValue(),
 "YEAR");
-      
Assert.assertEquals(function.getOperands().get(1).getLiteral().getStringValue(),
 "2017-06-15");
+      // Case 1 -- Year
+      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(YEAR FROM 
1719573611000)");
+      // The CompileTimeFunctionsInvoker will rewrite the query to replace the 
function call with the resultant literal
+      // value
+      
Assert.assertEquals(pinotQuery.getSelectList().get(0).getLiteral().getIntValue(),
 2024);
     }
     {
-      // Case 2 -- date format ('2017-06-15 09:34:21')
-      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(YEAR FROM 
'2017-06-15 09:34:21')");
-      Function function = pinotQuery.getSelectList().get(0).getFunctionCall();
-      
Assert.assertEquals(function.getOperands().get(0).getLiteral().getStringValue(),
 "YEAR");
-      
Assert.assertEquals(function.getOperands().get(1).getLiteral().getStringValue(),
 "2017-06-15 09:34:21");
+      // Case 2 -- Month
+      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(MONTH FROM 
'1719573611000')");
+      // The CompileTimeFunctionsInvoker will rewrite the query to replace the 
function call with the resultant literal
+      // value
+      
Assert.assertEquals(pinotQuery.getSelectList().get(0).getLiteral().getIntValue(),
 6);
     }
     {
-      // Case 3 -- Month
-      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(MONTH FROM 
'2017-06-15')");
-      Function function = pinotQuery.getSelectList().get(0).getFunctionCall();
-      
Assert.assertEquals(function.getOperands().get(0).getLiteral().getStringValue(),
 "MONTH");
-      
Assert.assertEquals(function.getOperands().get(1).getLiteral().getStringValue(),
 "2017-06-15");
-    }
-    {
-      // Case 4 -- Day
-      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(DAY FROM 
'2017-06-15')");
-      Function function = pinotQuery.getSelectList().get(0).getFunctionCall();
-      
Assert.assertEquals(function.getOperands().get(0).getLiteral().getStringValue(),
 "DAY");
-      
Assert.assertEquals(function.getOperands().get(1).getLiteral().getStringValue(),
 "2017-06-15");
+      // Case 3 -- Day
+      PinotQuery pinotQuery = compileToPinotQuery("SELECT EXTRACT(DAY FROM 
1719573611000)");
+      // The CompileTimeFunctionsInvoker will rewrite the query to replace the 
function call with the resultant literal
+      // value
+      
Assert.assertEquals(pinotQuery.getSelectList().get(0).getLiteral().getIntValue(),
 28);
     }
   }
 
diff --git 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunction.java
 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunction.java
index 7bbc19acde..906ce38781 100644
--- 
a/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunction.java
+++ 
b/pinot-core/src/main/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunction.java
@@ -20,24 +20,18 @@ package org.apache.pinot.core.operator.transform.function;
 
 import java.util.List;
 import java.util.Map;
+import java.util.stream.IntStream;
+import org.apache.pinot.common.function.DateTimeUtils;
 import org.apache.pinot.core.operator.ColumnContext;
 import org.apache.pinot.core.operator.blocks.ValueBlock;
 import org.apache.pinot.core.operator.transform.TransformResultMetadata;
-import org.joda.time.Chronology;
-import org.joda.time.DateTimeField;
-import org.joda.time.chrono.ISOChronology;
 import org.roaringbitmap.RoaringBitmap;
 
 
 public class ExtractTransformFunction extends BaseTransformFunction {
   public static final String FUNCTION_NAME = "extract";
   private TransformFunction _mainTransformFunction;
-  protected Field _field;
-  protected Chronology _chronology = ISOChronology.getInstanceUTC();
-
-  private enum Field {
-    YEAR, QUARTER, MONTH, WEEK, DAY, DOY, DOW, HOUR, MINUTE, SECOND
-  }
+  private DateTimeUtils.ExtractFieldType _field;
 
   @Override
   public String getName() {
@@ -51,7 +45,7 @@ public class ExtractTransformFunction extends 
BaseTransformFunction {
       throw new IllegalArgumentException("Exactly 2 arguments are required for 
EXTRACT transform function");
     }
 
-    _field = Field.valueOf(((LiteralTransformFunction) 
arguments.get(0)).getStringLiteral());
+    _field = 
DateTimeUtils.ExtractFieldType.valueOf(((LiteralTransformFunction) 
arguments.get(0)).getStringLiteral());
     _mainTransformFunction = arguments.get(1);
   }
 
@@ -65,60 +59,10 @@ public class ExtractTransformFunction extends 
BaseTransformFunction {
     int numDocs = valueBlock.getNumDocs();
     initIntValuesSV(numDocs);
     long[] timestamps = 
_mainTransformFunction.transformToLongValuesSV(valueBlock);
-    convert(timestamps, numDocs, _intValuesSV);
+    IntStream.range(0, numDocs).forEach(i -> _intValuesSV[i] = 
DateTimeUtils.extract(_field, timestamps[i]));
     return _intValuesSV;
   }
 
-  private void convert(long[] timestamps, int numDocs, int[] output) {
-    for (int i = 0; i < numDocs; i++) {
-      DateTimeField accessor;
-      switch (_field) {
-        case YEAR:
-          accessor = _chronology.year();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case QUARTER:
-          accessor = _chronology.monthOfYear();
-          output[i] = (accessor.get(timestamps[i]) - 1) / 3 + 1;
-          break;
-        case MONTH:
-          accessor = _chronology.monthOfYear();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case WEEK:
-          accessor = _chronology.weekOfWeekyear();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case DAY:
-          accessor = _chronology.dayOfMonth();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case DOY:
-          accessor = _chronology.dayOfYear();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case DOW:
-          accessor = _chronology.dayOfWeek();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case HOUR:
-          accessor = _chronology.hourOfDay();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case MINUTE:
-          accessor = _chronology.minuteOfHour();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        case SECOND:
-          accessor = _chronology.secondOfMinute();
-          output[i] = accessor.get(timestamps[i]);
-          break;
-        default:
-          throw new IllegalArgumentException("Unsupported FIELD type");
-      }
-    }
-  }
-
   @Override
   public RoaringBitmap getNullBitmap(ValueBlock valueBlock) {
     return _mainTransformFunction.getNullBitmap(valueBlock);
diff --git 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunctionTest.java
 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunctionTest.java
index ea74f266c5..96eda1c96c 100644
--- 
a/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunctionTest.java
+++ 
b/pinot-core/src/test/java/org/apache/pinot/core/operator/transform/function/ExtractTransformFunctionTest.java
@@ -38,9 +38,12 @@ public class ExtractTransformFunctionTest extends 
BaseTransformFunctionTest {
     return new Object[][]{
         //@formatter:off
         {"year", (LongToIntFunction) DateTimeFunctions::year},
+        {"quarter", (LongToIntFunction) timestamp -> 
(DateTimeFunctions.monthOfYear(timestamp) - 1) / 3 + 1},
         {"month", (LongToIntFunction) DateTimeFunctions::monthOfYear},
         {"week", (LongToIntFunction) DateTimeFunctions::weekOfYear},
         {"day", (LongToIntFunction) DateTimeFunctions::dayOfMonth},
+        {"doy", (LongToIntFunction) DateTimeFunctions::dayOfYear},
+        {"dow", (LongToIntFunction) DateTimeFunctions::dayOfWeek},
         {"hour", (LongToIntFunction) DateTimeFunctions::hour},
         {"minute", (LongToIntFunction) DateTimeFunctions::minute},
         {"second", (LongToIntFunction) DateTimeFunctions::second},
diff --git 
a/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
 
b/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
index 5ea790b408..c0b7b4b21c 100644
--- 
a/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
+++ 
b/pinot-query-planner/src/main/java/org/apache/pinot/calcite/rel/rules/PinotEvaluateLiteralRule.java
@@ -27,6 +27,7 @@ import java.util.List;
 import java.util.Objects;
 import javax.annotation.Nullable;
 import org.apache.calcite.avatica.util.ByteString;
+import org.apache.calcite.avatica.util.TimeUnitRange;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.rel.logical.LogicalFilter;
@@ -233,6 +234,8 @@ public class PinotEvaluateLiteralRule {
     } else if (value instanceof ByteString) {
       // BYTES
       return ((ByteString) value).getBytes();
+    } else if (value instanceof TimeUnitRange) {
+      return ((TimeUnitRange) value).name();
     } else {
       return value;
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to