diff --git a/fe/src/main/cup/sql_parser.cup b/fe/src/main/cup/sql_parser.cup index a8ad6ccbacc245..24d5e4c39857d7 100644 --- a/fe/src/main/cup/sql_parser.cup +++ b/fe/src/main/cup/sql_parser.cup @@ -205,9 +205,9 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALL, KW_ALTER, KW_A KW_INNER, KW_INSERT, KW_INT, KW_INTERVAL, KW_INTO, KW_IS, KW_ISNULL, KW_ISOLATION, KW_JOIN, KW_KEY, KW_KILL, - KW_LABEL, KW_LARGEINT, KW_LEFT, KW_LESS, KW_LEVEL, KW_LIKE, KW_LIMIT, KW_LINK, KW_LOAD, KW_LOCAL, KW_LOCATION, + KW_LABEL, KW_LARGEINT, KW_LAST, KW_LEFT, KW_LESS, KW_LEVEL, KW_LIKE, KW_LIMIT, KW_LINK, KW_LOAD, KW_LOCAL, KW_LOCATION, KW_MAX, KW_MAX_VALUE, KW_MERGE, KW_MIN, KW_MIGRATE, KW_MIGRATIONS, KW_MODIFY, - KW_NAME, KW_NAMES, KW_NEGATIVE, KW_NO, KW_NOT, KW_NULL, + KW_NAME, KW_NAMES, KW_NEGATIVE, KW_NO, KW_NOT, KW_NULL, KW_NULLS, KW_OBSERVER, KW_OFFSET, KW_ON, KW_ONLY, KW_OPEN, KW_OR, KW_ORDER, KW_OUTER, KW_OVER, KW_PARTITION, KW_PARTITIONS, KW_PRECEDING, KW_PASSWORD, KW_PLUGIN, KW_PLUGINS, @@ -299,6 +299,8 @@ nonterminal ArrayList group_by_clause, opt_partition_by_clause; nonterminal Expr having_clause; nonterminal ArrayList order_by_elements, order_by_clause; nonterminal OrderByElement order_by_element; +nonterminal Boolean opt_order_param; +nonterminal Boolean opt_nulls_order_param; nonterminal LimitElement limit_clause; nonterminal Expr cast_expr, case_else_clause, analytic_expr; nonterminal LiteralExpr literal; @@ -2981,12 +2983,26 @@ order_by_elements ::= ; order_by_element ::= - expr:e - {: RESULT = new OrderByElement(e, true); :} - | expr:e KW_ASC - {: RESULT = new OrderByElement(e, true); :} - | expr:e KW_DESC - {: RESULT = new OrderByElement(e, false); :} + expr:e opt_order_param:o opt_nulls_order_param:n + {: RESULT = new OrderByElement(e, o, n); :} + ; + +opt_order_param ::= + KW_ASC + {: RESULT = true; :} + | KW_DESC + {: RESULT = false; :} + | /* empty */ + {: RESULT = true; :} + ; + +opt_nulls_order_param ::= + KW_NULLS KW_FIRST + {: RESULT = true; :} + | KW_NULLS KW_LAST + {: RESULT = false; :} + | /* empty */ + {: RESULT = null; :} ; limit_clause ::= @@ -3673,6 +3689,8 @@ keyword ::= {: RESULT = id; :} | KW_LABEL:id {: RESULT = id; :} + | KW_LAST:id + {: RESULT = id; :} | KW_LESS:id {: RESULT = id; :} | KW_LEVEL:id @@ -3693,6 +3711,8 @@ keyword ::= {: RESULT = id; :} | KW_NO:id {: RESULT = id; :} + | KW_NULLS:id + {: RESULT = id; :} | KW_OFFSET:id {: RESULT = id; :} | KW_ONLY:id diff --git a/fe/src/main/java/org/apache/doris/analysis/OrderByElement.java b/fe/src/main/java/org/apache/doris/analysis/OrderByElement.java index da008994c4c832..8d041bcbf2b6c6 100644 --- a/fe/src/main/java/org/apache/doris/analysis/OrderByElement.java +++ b/fe/src/main/java/org/apache/doris/analysis/OrderByElement.java @@ -25,7 +25,7 @@ /** - * Combination of expr and ASC/DESC. + * Combination of expr and ASC/DESC, and nulls ordering. */ public class OrderByElement { private Expr expr; @@ -33,12 +33,13 @@ public class OrderByElement { // Represents the NULLs ordering specified: true when "NULLS FIRST", false when // "NULLS LAST", and null if not specified. - private Boolean nullsFirstParam; + private final Boolean nullsFirstParam; - public OrderByElement(Expr expr, boolean isAsc) { + public OrderByElement(Expr expr, boolean isAsc, Boolean nullsFirstParam) { super(); this.expr = expr; this.isAsc = isAsc; + this.nullsFirstParam = nullsFirstParam; } public void setExpr(Expr e) { @@ -58,7 +59,7 @@ public Boolean getNullsFirstParam() { } public OrderByElement clone() { OrderByElement clone = new OrderByElement( - expr.clone(), isAsc); + expr.clone(), isAsc, nullsFirstParam); return clone; } /** @@ -71,7 +72,7 @@ public static List reverse(List src) { for (int i = 0; i < src.size(); ++i) { OrderByElement element = src.get(i); OrderByElement reverseElement = - new OrderByElement(element.getExpr().clone(), !element.isAsc); + new OrderByElement(element.getExpr().clone(), !element.isAsc, !element.nullsFirstParam); result.add(reverseElement); } @@ -101,7 +102,7 @@ public static ArrayList substitute(List src, for (OrderByElement element: src) { result.add(new OrderByElement(element.getExpr().substitute(smap, analyzer, false), - element.isAsc)); + element.isAsc, element.nullsFirstParam)); } return result; @@ -138,7 +139,7 @@ public boolean equals(Object obj) { } OrderByElement o = (OrderByElement)obj; - return expr.equals(o.expr) && isAsc == o.isAsc; + return expr.equals(o.expr) && isAsc == o.isAsc && nullsFirstParam == o.nullsFirstParam; } /** * Compute nullsFirst. diff --git a/fe/src/main/jflex/sql_scanner.flex b/fe/src/main/jflex/sql_scanner.flex index 8bf045ce4f1209..bdc878b38ccd3e 100644 --- a/fe/src/main/jflex/sql_scanner.flex +++ b/fe/src/main/jflex/sql_scanner.flex @@ -184,6 +184,7 @@ import org.apache.doris.common.util.SqlUtils; keywordMap.put("kill", new Integer(SqlParserSymbols.KW_KILL)); keywordMap.put("label", new Integer(SqlParserSymbols.KW_LABEL)); keywordMap.put("largeint", new Integer(SqlParserSymbols.KW_LARGEINT)); + keywordMap.put("last", new Integer(SqlParserSymbols.KW_LAST)); keywordMap.put("left", new Integer(SqlParserSymbols.KW_LEFT)); keywordMap.put("less", new Integer(SqlParserSymbols.KW_LESS)); keywordMap.put("level", new Integer(SqlParserSymbols.KW_LEVEL)); @@ -203,6 +204,7 @@ import org.apache.doris.common.util.SqlUtils; keywordMap.put("no", new Integer(SqlParserSymbols.KW_NO)); keywordMap.put("not", new Integer(SqlParserSymbols.KW_NOT)); keywordMap.put("null", new Integer(SqlParserSymbols.KW_NULL)); + keywordMap.put("nulls", new Integer(SqlParserSymbols.KW_NULLS)); keywordMap.put("observer", new Integer(SqlParserSymbols.KW_OBSERVER)); keywordMap.put("offset", new Integer(SqlParserSymbols.KW_OFFSET)); keywordMap.put("on", new Integer(SqlParserSymbols.KW_ON));