Conditional expressions like | (ns metabase.lib.schema.expression.conditional (:require [clojure.set :as set] [metabase.lib.schema.expression :as expression] [metabase.lib.schema.mbql-clause :as mbql-clause] [metabase.types :as types])) |
For expressions like the logic for calculating the return type of a | (defn- best-return-type [x y] (cond (nil? x) y ;; if the type of either x or y is unknown, then the overall type of this has to be unknown as well. (or (= x ::expression/type.unknown) (= y ::expression/type.unknown)) ::expression/type.unknown ;; if both types are keywords return their most-specific ancestor. (and (keyword? x) (keyword? y)) (types/most-specific-common-ancestor x y) ;; if one type is a specific type but the other is an ambiguous union of possible types, return the specific ;; type. A case can't possibly have multiple different return types, so if one expression has an unambiguous ;; type then the whole thing has to have a compatible type. (keyword? x) x (keyword? y) y ;; if both types are ambiguous unions of possible types then return the intersection of the two. But if the ;; intersection is empty, return the union of everything instead. I don't really want to go down a rabbit ;; hole of trying to find the intersection between the most-specific common ancestors :else (or (when-let [intersection (not-empty (set/intersection x y))] (if (= (count intersection) 1) (first intersection) intersection)) (set/union x y)))) |
believe it or not, a | (doseq [tag [:case :if]] (mbql-clause/define-catn-mbql-clause tag ;; TODO -- we should further constrain this so all of the exprs are of the same type [:pred-expr-pairs [:sequential {:min 1} [:tuple {:error/message "Valid [pred expr] pair"} #_pred [:ref ::expression/boolean] #_expr [:ref ::expression/expression]]]] [:default [:? [:schema [:ref ::expression/expression]]]]) (defmethod expression/type-of-method tag [[_tag _opts pred-expr-pairs _default]] ;; Following logic for picking a type is taken from ;; the [[metabase.query-processor.middleware.annotate/infer-expression-type]]. (some (fn [[_pred expr]] (if-some [t (expression/type-of expr)] t ::expression/type.unknown)) pred-expr-pairs))) |
TODO -- add constraint that these types have to be compatible | (mbql-clause/define-catn-mbql-clause :coalesce [:exprs [:repeat {:min 2} [:schema [:ref ::expression/expression]]]]) |
(defmethod expression/type-of-method :coalesce [[_tag _opts & exprs]] #_{:clj-kondo/ignore [:reduce-without-init]} (reduce best-return-type (map expression/type-of exprs))) | |