From 18b54e7a180c8e8716d095246c6efcdbb415fcef Mon Sep 17 00:00:00 2001 From: Cal Herries <39073188+calherries@users.noreply.github.com> Date: Tue, 22 Nov 2022 11:28:57 +0100 Subject: [PATCH] Refactor mbql schema for numeric-expressions and date-time-expressions (#26635) --- shared/src/metabase/mbql/schema.cljc | 105 +++++++----------- .../query_processor/middleware/annotate.clj | 4 +- 2 files changed, 45 insertions(+), 64 deletions(-) diff --git a/shared/src/metabase/mbql/schema.cljc b/shared/src/metabase/mbql/schema.cljc index 549b7f213e7..88ca15da26a 100644 --- a/shared/src/metabase/mbql/schema.cljc +++ b/shared/src/metabase/mbql/schema.cljc @@ -127,8 +127,6 @@ (when (string? s) (not= (.parse js/Date s) ##NaN)))) -;; TODO -- currently these are all the same between date/time/datetime - (def ^{:arglists '([s])} can-parse-date? "Returns whether a string can be parsed to an ISO 8601 date or not." #?(:clj (partial can-parse-iso-8601? DateTimeFormatter/ISO_DATE) @@ -475,8 +473,8 @@ ;; Expressions are "calculated column" definitions, defined once and then used elsewhere in the MBQL query. -(def string-expressions - "String functions" +(def string-functions + "Functions that return string values. Should match [[StringExpression]]." #{:substring :trim :rtrim :ltrim :upper :lower :replace :concat :regex-match-first :coalesce :case}) (declare StringExpression) @@ -486,7 +484,7 @@ string? s/Str - (partial is-clause? string-expressions) + (partial is-clause? string-functions) (s/recursive #'StringExpression) (partial is-clause? :value) @@ -495,34 +493,25 @@ :else Field)) -;; TODO - rename to numeric-expressions -(def arithmetic-expressions - "Set of valid arithmetic expression clause keywords." - #{:+ :- :/ :* :coalesce :length :round :ceil :floor :abs :power :sqrt :log :exp :case :datetime-diff}) +(def numeric-functions + "Functions that return numeric values. Should match [[NumericExpression]]." + #{:+ :- :/ :* :coalesce :length :round :ceil :floor :abs :power :sqrt :log :exp :case :datetime-diff + ;; extraction functions (get some component of a given temporal value/column) + :temporal-extract + ;; SUGAR drivers do not need to implement + :get-year :get-quarter :get-month :get-week :get-day :get-day-of-week :get-hour :get-minute :get-second}) -(def boolean-expressions - "Set of valid boolean expression clause keywords." +(def boolean-functions + "Functions that return boolean values. Should match [[BooleanExpression]]." #{:and :or :not :< :<= :> :>= := :!=}) (def ^:private aggregations #{:sum :avg :stddev :var :median :percentile :min :max :cum-count :cum-sum :count-where :sum-where :share :distinct :metric :aggregation-options :count}) -;; TODO: expressions that return numerics should be in arithmetic-expressions -(def temporal-extract-functions - "Functions to extract components of a date, datetime." - #{;; extraction functions (get some component of a given temporal value/column) - :temporal-extract - ;; SUGAR drivers do not need to implement - :get-year :get-quarter :get-month :get-week :get-day :get-day-of-week :get-hour :get-minute :get-second}) - -(def date-arithmetic-functions - "Functions to do math with date, datetime." +(def datetime-functions + "Functions that return Date or DateTime values. Should match [[DatetimeExpression]]." #{:+ :datetime-add :datetime-subtract}) -(def date+time+timezone-functions - "Date, time, and timezone related functions." - (set/union temporal-extract-functions date-arithmetic-functions)) - -(declare ArithmeticExpression) +(declare NumericExpression) (declare BooleanExpression) (declare DatetimeExpression) (declare Aggregation) @@ -532,11 +521,8 @@ number? s/Num - (partial is-clause? arithmetic-expressions) - (s/recursive #'ArithmeticExpression) - - (partial is-clause? temporal-extract-functions) - (s/recursive #'DatetimeExpression) + (partial is-clause? numeric-functions) + (s/recursive #'NumericExpression) (partial is-clause? aggregations) (s/recursive #'Aggregation) @@ -555,8 +541,7 @@ (partial is-clause? :value) value - ;; Recursively doing date math - (partial is-clause? date-arithmetic-functions) + (partial is-clause? datetime-functions) (s/recursive #'DatetimeExpression) :else @@ -570,21 +555,21 @@ boolean? s/Bool - (partial is-clause? boolean-expressions) + (partial is-clause? boolean-functions) (s/recursive #'BooleanExpression) - (partial is-clause? arithmetic-expressions) - (s/recursive #'ArithmeticExpression) + (partial is-clause? numeric-functions) + (s/recursive #'NumericExpression) + + (partial is-clause? datetime-functions) + (s/recursive #'DatetimeExpression) string? s/Str - (partial is-clause? string-expressions) + (partial is-clause? string-functions) (s/recursive #'StringExpression) - (partial is-clause? temporal-extract-functions) - (s/recursive #'DatetimeExpression) - (partial is-clause? :value) value @@ -663,13 +648,11 @@ (defclause ^{:requires-features #{:advanced-math-expressions}} log x NumericExpressionArg) -;; TODO: rename to NumericExpression* -(declare ArithmeticExpression*) +(declare NumericExpression*) -;; TODO: rename to NumericExpression -(def ^:private ArithmeticExpression - "Schema for the definition of an arithmetic expression. All arithmetic expressions evaluate to numeric values." - (s/recursive #'ArithmeticExpression*)) +(def ^:private NumericExpression + "Schema for the definition of a numeric expression. All numeric expressions evaluate to numeric values." + (s/recursive #'NumericExpression*)) ;; The result is positive if x <= y, and negative otherwise. ;; @@ -736,10 +719,7 @@ unit ArithmeticDateTimeUnit) (def ^:private DatetimeExpression* - (one-of + temporal-extract datetime-add datetime-subtract - ;; SUGAR drivers do not need to implement - get-year get-quarter get-month get-week get-day get-day-of-week - get-hour get-minute get-second)) + (one-of + datetime-add datetime-subtract)) (def DatetimeExpression "Schema for the definition of a date function expression." @@ -891,9 +871,9 @@ (def ^:private Filter* (s/conditional - (partial is-clause? arithmetic-expressions) ArithmeticExpression - (partial is-clause? string-expressions) StringExpression - (partial is-clause? boolean-expressions) BooleanExpression + (partial is-clause? numeric-functions) NumericExpression + (partial is-clause? string-functions) StringExpression + (partial is-clause? boolean-functions) BooleanExpression :else (one-of ;; filters drivers must implement @@ -915,9 +895,10 @@ (defclause ^{:requires-features #{:basic-aggregations}} case clauses CaseClauses, options (optional CaseOptions)) -;; TODO: rename to NumericExpression? -(def ^:private ArithmeticExpression* - (one-of + - / * coalesce length floor ceil round abs power sqrt exp log case datetime-diff)) +(def ^:private NumericExpression* + (one-of + - / * coalesce length floor ceil round abs power sqrt exp log case datetime-diff + temporal-extract get-year get-quarter get-month get-week get-day get-day-of-week + get-hour get-minute get-second)) (def ^:private StringExpression* (one-of substring trim ltrim rtrim replace lower upper concat regex-match-first coalesce case)) @@ -927,10 +908,10 @@ "Schema for anything that is accepted as a top-level expression definition, either an arithmetic expression such as a `:+` clause or a `:field` clause." (s/conditional - (partial is-clause? arithmetic-expressions) ArithmeticExpression - (partial is-clause? string-expressions) StringExpression - (partial is-clause? boolean-expressions) BooleanExpression - (partial is-clause? date+time+timezone-functions) DatetimeExpression + (partial is-clause? numeric-functions) NumericExpression + (partial is-clause? string-functions) StringExpression + (partial is-clause? boolean-functions) BooleanExpression + (partial is-clause? datetime-functions) DatetimeExpression (partial is-clause? :case) case :else Field)) @@ -993,8 +974,8 @@ ;; [:+ [:sum [:field 10 nil]] [:sum [:field 20 nil]]] (def ^:private UnnamedAggregation* - (s/if (partial is-clause? arithmetic-expressions) - ArithmeticExpression + (s/if (partial is-clause? numeric-functions) + NumericExpression (one-of avg cum-sum distinct stddev sum min max metric share count-where sum-where case median percentile ag:var ;; SUGAR clauses diff --git a/src/metabase/query_processor/middleware/annotate.clj b/src/metabase/query_processor/middleware/annotate.clj index e42b98c3cbb..68ed8c8152a 100644 --- a/src/metabase/query_processor/middleware/annotate.clj +++ b/src/metabase/query_processor/middleware/annotate.clj @@ -178,10 +178,10 @@ (datetime-arithmetics? expression) {:base_type :type/DateTime} - (mbql.u/is-clause? mbql.s/string-expressions expression) + (mbql.u/is-clause? mbql.s/string-functions expression) {:base_type :type/Text} - (mbql.u/is-clause? mbql.s/arithmetic-expressions expression) + (mbql.u/is-clause? mbql.s/numeric-functions expression) {:base_type :type/Float} :else -- GitLab