kangkaisen commented on a change in pull request #2725: Add 
timestampadd/timestampdiff function
URL: https://github.com/apache/incubator-doris/pull/2725#discussion_r365064741
 
 

 ##########
 File path: 
fe/src/main/java/org/apache/doris/analysis/TimestampArithmeticExpr.java
 ##########
 @@ -120,65 +120,142 @@ private Type fixType() {
     @Override
     public void analyzeImpl(Analyzer analyzer) throws AnalysisException {
         // Check if name of function call is date_sub or date_add.
+        String funcOpName;
         if (funcName != null) {
-            if (funcName.toUpperCase().equals("DATE_ADD")
-                    || funcName.toUpperCase().equals("DAYS_ADD")
-                    || funcName.toUpperCase().equals("ADDDATE")) {
-                op = ArithmeticExpr.Operator.ADD;
-            } else if (funcName.toUpperCase().equals("DATE_SUB")
-                    || funcName.toUpperCase().equals("DAYS_SUB")
-                    || funcName.toUpperCase().equals("SUBDATE")) {
-                op = ArithmeticExpr.Operator.SUBTRACT;
+            if (funcName.equalsIgnoreCase("TIMESTAMPDIFF")) {
+                timeUnit = TIME_UNITS_MAP.get(timeUnitIdent.toUpperCase());
+                if (timeUnit == null) {
+                    throw new AnalysisException("Invalid time unit '" + 
timeUnitIdent
+                            + "' in timestamp arithmetic expression '" + 
toSql() + "'.");
+                }
+                Type dateType = fixType();
+                if (dateType.isDate() && timeUnit.isDateTime()) {
+                    dateType = Type.DATETIME;
+                }
+                // The first child must return a timestamp or null.
+                if (!getChild(0).getType().isDateType() && 
!getChild(0).getType().isNull()) {
+                    if (!dateType.isValid()) {
+                        throw new AnalysisException("Operand '" + 
getChild(0).toSql()
+                                + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                                + getChild(0).getType() + "'. Expected type 
'TIMESTAMP/DATE/DATETIME'.");
+                    }
+                    castChild(dateType, 0);
+                }
+
+                // The first child must return a timestamp or null.
+                if (!getChild(1).getType().isDateType() && 
!getChild(1).getType().isNull()) {
+                    if (!dateType.isValid()) {
+                        throw new AnalysisException("Operand '" + 
getChild(1).toSql()
+                                + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                                + getChild(1).getType() + "'. Expected type 
'TIMESTAMP/DATE/DATETIME'.");
+                    }
+                    castChild(dateType, 1);
+                }
+
+                type = Type.BIGINT;
+                opcode = getOpCode();
+                funcOpName = String.format("%sS_%s", timeUnit, "DIFF");
             } else {
-                throw new AnalysisException("Encountered function name '" + 
funcName
-                        + "' in timestamp arithmetic expression '" + toSql() + 
"'. "
-                        + "Expected function name 'DATE_ADD/DAYS_ADD/ADDDATE'"
-                        + "or 'DATE_SUB/DAYS_SUB/SUBDATE");
+                if (funcName.toUpperCase().equals("DATE_ADD")
+                        || funcName.toUpperCase().equals("DAYS_ADD")
+                        || funcName.toUpperCase().equals("ADDDATE")
+                        || funcName.toUpperCase().equals("TIMESTAMPADD")) {
+                    op = ArithmeticExpr.Operator.ADD;
+                } else if (funcName.toUpperCase().equals("DATE_SUB")
+                        || funcName.toUpperCase().equals("DAYS_SUB")
+                        || funcName.toUpperCase().equals("SUBDATE")) {
+                    op = ArithmeticExpr.Operator.SUBTRACT;
+                } else {
+                    throw new AnalysisException("Encountered function name '" 
+ funcName
+                            + "' in timestamp arithmetic expression '" + 
toSql() + "'. "
+                            + "Expected function name 
'DATE_ADD/DAYS_ADD/ADDDATE/TIMESTAMPADD'"
+                            + "or 'DATE_SUB/DAYS_SUB/SUBDATE");
+                }
+
+                timeUnit = TIME_UNITS_MAP.get(timeUnitIdent.toUpperCase());
+                if (timeUnit == null) {
+                    throw new AnalysisException("Invalid time unit '" + 
timeUnitIdent
+                            + "' in timestamp arithmetic expression '" + 
toSql() + "'.");
+                }
+
+                Type dateType = fixType();
+                if (dateType.isDate() && timeUnit.isDateTime()) {
+                    dateType = Type.DATETIME;
+                }
+                // The first child must return a timestamp or null.
+                if (!getChild(0).getType().isDateType() && 
!getChild(0).getType().isNull()) {
+                    if (!dateType.isValid()) {
+                        throw new AnalysisException("Operand '" + 
getChild(0).toSql()
+                                + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                                + getChild(0).getType() + "'. Expected type 
'TIMESTAMP/DATE/DATETIME'.");
+                    }
+                    castChild(dateType, 0);
+                }
+
+                if (!getChild(1).getType().isScalarType()) {
+                    throw new AnalysisException("must be a scalar type.");
+                }
+
+                // The second child must be of type 'INT' or castable to it.
+                if (!getChild(1).getType().isScalarType(PrimitiveType.INT)) {
+                    if (!ScalarType.canCastTo((ScalarType) 
getChild(1).getType(), Type.INT)) {
+                        throw new AnalysisException("Operand '" + 
getChild(1).toSql()
+                                + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                                + getChild(1).getType() + "' which is 
incompatible with expected type 'INT'.");
+                    }
+                    castChild(Type.INT, 1);
+                }
+
+                type = dateType;
+                opcode = getOpCode();
+                funcOpName = String.format("%sS_%s", timeUnit,
+                        (op == ArithmeticExpr.Operator.ADD) ? "ADD" : "SUB");
+                // For the month interval, use the invisible special-case 
implementation.
+                // "ADD_MONTHS(t, m)" by definition is different from "t + 
INTERVAL m MONTHS".
+                // if (timeUnit == TimeUnit.MONTH) {
+                //     funcOpName += "_INTERVAL";
+                // }
+            }
+        } else {
+            timeUnit = TIME_UNITS_MAP.get(timeUnitIdent.toUpperCase());
+            if (timeUnit == null) {
+                throw new AnalysisException("Invalid time unit '" + 
timeUnitIdent
+                        + "' in timestamp arithmetic expression '" + toSql() + 
"'.");
             }
-        }
-        timeUnit = TIME_UNITS_MAP.get(timeUnitIdent.toUpperCase());
-        if (timeUnit == null) {
-            throw new AnalysisException("Invalid time unit '" + timeUnitIdent
-                    + "' in timestamp arithmetic expression '" + toSql() + 
"'.");
-        }
 
-        Type dateType = fixType();
-        if (dateType.isDate() && timeUnit.isDateTime()) {
-            dateType = Type.DATETIME;
-        }
-        // The first child must return a timestamp or null.
-        if (!getChild(0).getType().isDateType() && 
!getChild(0).getType().isNull()) {
-            if (!dateType.isValid()) {
-                throw new AnalysisException("Operand '" + getChild(0).toSql()
-                        + "' of timestamp arithmetic expression '" + toSql() + 
"' returns type '"
-                        + getChild(0).getType() + "'. Expected type 
'TIMESTAMP/DATE/DATETIME'.");
+            Type dateType = fixType();
+            if (dateType.isDate() && timeUnit.isDateTime()) {
+                dateType = Type.DATETIME;
+            }
+            // The first child must return a timestamp or null.
+            if (!getChild(0).getType().isDateType() && 
!getChild(0).getType().isNull()) {
+                if (!dateType.isValid()) {
+                    throw new AnalysisException("Operand '" + 
getChild(0).toSql()
+                            + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                            + getChild(0).getType() + "'. Expected type 
'TIMESTAMP/DATE/DATETIME'.");
+                }
+                castChild(dateType, 0);
             }
-            castChild(dateType, 0);
-        }
 
-        if (!getChild(1).getType().isScalarType()) {
-            throw new AnalysisException("must be a scalar type.");
-        }
+            if (!getChild(1).getType().isScalarType()) {
+                throw new AnalysisException("must be a scalar type.");
+            }
 
-        // The second child must be of type 'INT' or castable to it.
-        if (!getChild(1).getType().isScalarType(PrimitiveType.INT)) {
-            if (!ScalarType.canCastTo((ScalarType) getChild(1).getType(), 
Type.INT)) {
-                throw new AnalysisException("Operand '" + getChild(1).toSql()
-                        + "' of timestamp arithmetic expression '" + toSql() + 
"' returns type '"
-                        + getChild(1).getType() + "' which is incompatible 
with expected type 'INT'.");
+            // The second child must be of type 'INT' or castable to it.
+            if (!getChild(1).getType().isScalarType(PrimitiveType.INT)) {
+                if (!ScalarType.canCastTo((ScalarType) getChild(1).getType(), 
Type.INT)) {
+                    throw new AnalysisException("Operand '" + 
getChild(1).toSql()
+                            + "' of timestamp arithmetic expression '" + 
toSql() + "' returns type '"
+                            + getChild(1).getType() + "' which is incompatible 
with expected type 'INT'.");
+                }
+                castChild(Type.INT, 1);
             }
-            castChild(Type.INT, 1);
-        }
 
-        type = dateType;
-        opcode = getOpCode();
-        String funcOpName = String.format("%sS_%s",  timeUnit,
-                (op == ArithmeticExpr.Operator.ADD) ? "ADD" : "SUB");
-        // For the month interval, use the invisible special-case 
implementation.
-        // "ADD_MONTHS(t, m)" by definition is different from "t + INTERVAL m 
MONTHS".
-        // if (timeUnit == TimeUnit.MONTH) {
-        //     funcOpName += "_INTERVAL";
-        // }
+            type = dateType;
+            opcode = getOpCode();
+            funcOpName = String.format("%sS_%s", timeUnit,
+                    (op == ArithmeticExpr.Operator.ADD) ? "ADD" : "SUB");
+        }
 
         fn = getBuiltinFunction(analyzer, funcOpName.toLowerCase(),
                 collectChildReturnTypes(), 
Function.CompareMode.IS_NONSTRICT_SUPERTYPE_OF);
 
 Review comment:
   `fn is {} name is {}` thie log should be remove or change to debug level.  
There will be a lot of this log from doris 0.11

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

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

Reply via email to