diff --git a/frontend/src/metabase-lib/order_by.unit.spec.ts b/frontend/src/metabase-lib/order_by.unit.spec.ts
index 03c7d545afce3552e0f6e44a1074ac887a4a08d4..04726c23591a75875c64275b59788e9067af7a6a 100644
--- a/frontend/src/metabase-lib/order_by.unit.spec.ts
+++ b/frontend/src/metabase-lib/order_by.unit.spec.ts
@@ -122,7 +122,9 @@ describe("order by", () => {
       const orderBys = ML.orderBys(nextQuery);
 
       expect(orderBys).toHaveLength(1);
-      expect(ML.displayName(nextQuery, orderBys[0])).toBe("Title ascending");
+      expect(ML.displayName(nextQuery, orderBys[0])).toBe(
+        "Products → Title ascending",
+      );
     });
   });
 
@@ -148,7 +150,7 @@ describe("order by", () => {
       );
       const nextOrderBys = ML.orderBys(nextQuery);
       expect(ML.displayName(nextQuery, nextOrderBys[0])).toBe(
-        "Category descending",
+        "Products → Category descending",
       );
       expect(orderBys[0]).not.toEqual(nextOrderBys[0]);
     });
diff --git a/frontend/test/jest-setup.js b/frontend/test/jest-setup.js
index bc4b5ae771a5fb07d6f5618fa474423fff81fd6f..97acb8023cea8e2ceaf044f4347eebcee7af1e42 100644
--- a/frontend/test/jest-setup.js
+++ b/frontend/test/jest-setup.js
@@ -1,3 +1,4 @@
+import { TextEncoder, TextDecoder } from "util";
 import "cross-fetch/polyfill";
 import "raf/polyfill";
 import "jest-localstorage-mock";
@@ -23,3 +24,9 @@ if (process.env["DISABLE_LOGGING"] || process.env["DISABLE_LOGGING_FRONTEND"]) {
     trace: jest.fn(),
   };
 }
+
+// global TextEncoder is not available in jsdom + Jest, see
+// https://stackoverflow.com/questions/70808405/how-to-set-global-textdecoder-in-jest-for-jsdom-if-nodes-util-textdecoder-is-ty
+// (hacky fix)
+global.TextEncoder = TextEncoder;
+global.TextDecoder = TextDecoder;
diff --git a/package.json b/package.json
index d12c4067ae98db9bd11d2e52c4dc7bd9c1613d94..33ff723dfbcdf62d5b9b96b09fb1b8d9dccdb264 100644
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
     "classnames": "^2.1.3",
     "color": "^3.0.0",
     "color-harmony": "^0.3.0",
+    "crc-32": "^1.2.2",
     "cron-expression-validator": "^1.0.20",
     "cronstrue": "^2.11.0",
     "crossfilter": "^1.3.12",
diff --git a/src/metabase/driver/impl.clj b/src/metabase/driver/impl.clj
index 85d7ca6dce205e8c938e4fc6571e9b9249782448..984345c8b18f88ae489f44e188cb97d0af6b897a 100644
--- a/src/metabase/driver/impl.clj
+++ b/src/metabase/driver/impl.clj
@@ -2,6 +2,7 @@
   "Internal implementation functions for [[metabase.driver]]. These functions live in a separate namespace to reduce the
   clutter in [[metabase.driver]] itself."
   (:require
+   [metabase.lib.util :as lib.util]
    [metabase.plugins.classloader :as classloader]
    [metabase.util :as u]
    [metabase.util.i18n :refer [trs tru]]
@@ -216,23 +217,6 @@
 
 ;;; ----------------------------------------------- [[truncate-alias]] -----------------------------------------------
 
-;; To truncate a string to a number of bytes we just iterate thru it character-by-character and keep a cumulative count
-;; of the total number of bytes up to the current character. Once we exceed `max-length-bytes` we return the substring
-;; from the character before we went past the limit.
-(defn- truncate-string-to-byte-count
-  "Truncate string `s` to `max-length-bytes` UTF-8 bytes (as opposed to truncating to some number of *characters*)."
-  ^String [^String s max-length-bytes]
-  {:pre [(not (neg? max-length-bytes))]}
-  (loop [i 0, cumulative-byte-count 0]
-    (cond
-      (= cumulative-byte-count max-length-bytes) (subs s 0 i)
-      (> cumulative-byte-count max-length-bytes) (subs s 0 (dec i))
-      (>= i (count s))                           s
-      :else                                      (recur (inc i)
-                                                        (+
-                                                         cumulative-byte-count
-                                                         (count (.getBytes (str (.charAt s i)) "UTF-8")))))))
-
 (def default-alias-max-length-bytes
   "Default length to truncate column and table identifiers to for the default implementation
   of [[metabase.driver/escape-alias]]."
@@ -241,11 +225,6 @@
   ;; identifiers we generate to 60 bytes so we have room to add `_2` and stuff without drama
   60)
 
-(def ^:private truncated-alias-hash-suffix-length
-  "Length of the hash suffixed to truncated strings by [[truncate-alias]]."
-  ;; 8 bytes for the CRC32 plus one for the underscore
-  9)
-
 (defn truncate-alias
   "Truncate string `s` if it is longer than `max-length-bytes` (default [[default-alias-max-length-bytes]]) and append a
   hex-encoded CRC-32 checksum of the original string. Truncated string is truncated to `max-length-bytes`
@@ -258,13 +237,4 @@
    (truncate-alias s default-alias-max-length-bytes))
 
   (^String [^String s max-length-bytes]
-   ;; we can't truncate to something SHORTER than the suffix length. This precondition is here mostly to make sure
-   ;; driver authors don't try to do something INSANE -- it shouldn't get hit during normal usage if a driver is
-   ;; implemented properly.
-   {:pre [(string? s) (integer? max-length-bytes) (> max-length-bytes truncated-alias-hash-suffix-length)]}
-   (if (<= (count (.getBytes s "UTF-8")) max-length-bytes)
-     s
-     (let [checksum  (Long/toHexString (.getValue (doto (java.util.zip.CRC32.)
-                                                   (.update (.getBytes s "UTF-8")))))
-           truncated (truncate-string-to-byte-count s (- max-length-bytes truncated-alias-hash-suffix-length))]
-       (str truncated \_ checksum)))))
+   (lib.util/truncate-alias s max-length-bytes)))
diff --git a/src/metabase/lib/field.cljc b/src/metabase/lib/field.cljc
index a9497c1f0ce35c12f03d45cea66f019d54895dc0..5f00b3b98f8ce9f87f22a6f15a9feb5437162676 100644
--- a/src/metabase/lib/field.cljc
+++ b/src/metabase/lib/field.cljc
@@ -90,8 +90,8 @@
   (lib.metadata.calculation/type-of query stage-number (resolve-field-metadata query stage-number field-ref)))
 
 (defmethod lib.metadata.calculation/metadata-method :metadata/field
-  [_query _stage-number field-metadata]
-  field-metadata)
+  [_query _stage-number {field-name :name, :as field-metadata}]
+  (assoc field-metadata :name field-name))
 
 ;;; TODO -- base type should be affected by `temporal-unit`, right?
 (defmethod lib.metadata.calculation/metadata-method :field
@@ -122,12 +122,18 @@
                        field-name         :name
                        temporal-unit      :unit
                        join-alias         :source_alias
+                       fk-field-id        :fk_field_id
+                       table-id           :table_id
                        :as                _field-metadata}]
   (let [field-display-name (or field-display-name
                                (u.humanization/name->human-readable-name :simple field-name))
-        join-display-name  (when join-alias
-                             (let [join (lib.join/resolve-join query stage-number join-alias)]
-                               (lib.metadata.calculation/display-name query stage-number join)))
+        join-display-name  (or
+                            (when fk-field-id
+                              (let [table (lib.metadata/table query table-id)]
+                                (lib.metadata.calculation/display-name query stage-number table)))
+                            (when join-alias
+                              (let [join (lib.join/resolve-join query stage-number join-alias)]
+                                (lib.metadata.calculation/display-name query stage-number join))))
         display-name       (if join-display-name
                              (str join-display-name " → " field-display-name)
                              field-display-name)]
@@ -136,14 +142,26 @@
       display-name)))
 
 (defmethod lib.metadata.calculation/display-name-method :field
-  [query stage-number [_field {:keys [join-alias temporal-unit], :as _opts} _id-or-name, :as field-clause]]
+  [query stage-number [_tag {:keys [join-alias temporal-unit source-field], :as _opts} _id-or-name, :as field-clause]]
   (if-let [field-metadata (cond-> (resolve-field-metadata query stage-number field-clause)
                             join-alias    (assoc :source_alias join-alias)
-                            temporal-unit (assoc :unit temporal-unit))]
+                            temporal-unit (assoc :unit temporal-unit)
+                            source-field  (assoc :fk_field_id source-field))]
     (lib.metadata.calculation/display-name query stage-number field-metadata)
     ;; mostly for the benefit of JS, which does not enforce the Malli schemas.
     (i18n/tru "[Unknown Field]")))
 
+(defmethod lib.metadata.calculation/column-name-method :metadata/field
+  [_query _stage-number {field-name :name}]
+  field-name)
+
+(defmethod lib.metadata.calculation/column-name-method :field
+  [query stage-number [_tag _id-or-name, :as field-clause]]
+  (if-let [field-metadata (resolve-field-metadata query stage-number field-clause)]
+    (lib.metadata.calculation/column-name query stage-number field-metadata)
+    ;; mostly for the benefit of JS, which does not enforce the Malli schemas.
+    "unknown_field"))
+
 (defmethod lib.temporal-bucket/current-temporal-bucket-method :field
   [[_tag opts _id-or-name]]
   (:temporal-unit opts))
@@ -206,6 +224,31 @@
                         (:name metadata)
                         (or (:id metadata) (:name metadata)))])))
 
+(mu/defn ^:private joined-field-desired-alias :- ::lib.schema.common/non-blank-string
+  "Desired alias for a Field that comes from a join, e.g.
+
+    MyJoin__my_field
+
+  You should pass the results thru a unique name function."
+  [join-alias :- ::lib.schema.common/non-blank-string
+   field-name :- ::lib.schema.common/non-blank-string]
+  (lib.util/format "%s__%s" join-alias field-name))
+
+(mu/defn desired-alias :- ::lib.schema.common/non-blank-string
+  "Desired alias for a Field e.g.
+
+    my_field
+
+    OR
+
+    MyJoin__my_field
+
+  You should pass the results thru a unique name function."
+  [field-metadata :- lib.metadata/ColumnMetadata]
+  (if-let [join-alias (lib.join/current-join-alias field-metadata)]
+    (joined-field-desired-alias join-alias (:name field-metadata))
+    (:name field-metadata)))
+
 (defn fields
   "Specify the `:fields` for a query."
   ([xs]
diff --git a/src/metabase/lib/join.cljc b/src/metabase/lib/join.cljc
index 5fd5b11c5f56bd18552d251693c71fa7a625b3e8..a06d61e4256e1add2f0ace9b455d1c285f45a5af 100644
--- a/src/metabase/lib/join.cljc
+++ b/src/metabase/lib/join.cljc
@@ -85,7 +85,7 @@
   (let [column-metadata (assoc column-metadata :source_alias join-alias)
         col             (-> (assoc column-metadata
                                    :display_name (lib.metadata.calculation/display-name query stage-number column-metadata)
-                                   :lib/source :source/fields)
+                                   :lib/source   :source/joins)
                             (with-join-alias join-alias))]
     (assert (= (current-join-alias col) join-alias))
     col))
@@ -243,3 +243,13 @@
   ([query        :- ::lib.schema/query
     stage-number :- ::lib.schema.common/int-greater-than-or-equal-to-zero]
    (not-empty (get (lib.util/query-stage query stage-number) :joins))))
+
+(mu/defn implicit-join-name :- ::lib.schema.common/non-blank-string
+  "Name for an implicit join against `table-name` via an FK field, e.g.
+
+    CATEGORIES__via__CATEGORY_ID
+
+  You should make sure this gets ran thru a unique-name fn."
+  [table-name           :- ::lib.schema.common/non-blank-string
+   source-field-id-name :- ::lib.schema.common/non-blank-string]
+  (lib.util/format "%s__via__%s" table-name source-field-id-name))
diff --git a/src/metabase/lib/metadata.cljc b/src/metabase/lib/metadata.cljc
index 4bb03a65dcd4df40b35154574fd68ef7758ef09e..652c4c4715b214aedc3f35dca0724705caab8b67 100644
--- a/src/metabase/lib/metadata.cljc
+++ b/src/metabase/lib/metadata.cljc
@@ -85,7 +85,16 @@
    [:source_alias   {:optional true} [:maybe ::lib.schema.common/non-blank-string]]
    ;; what top-level clause in the query this metadata originated from, if it is calculated (i.e., if this metadata
    ;; was generated by [[metabase.lib.metadata.calculation/metadata]])
-   [:lib/source     {:optional true} [:ref ::column-source]]])
+   [:lib/source     {:optional true} [:ref ::column-source]]
+   ;;
+   ;; this stuff is adapted from [[metabase.query-processor.util.add-alias-info]]. It is included in
+   ;; the [[metabase.lib.metadata.calculation/metadata]]
+   ;;
+   ;; the alias that should be used to this clause on the LHS of a `SELECT <lhs> AS <rhs>` or equivalent, i.e. the
+   ;; name of this clause as exported by the previous stage, source table, or join.
+   [:lib/source-column-alias {:optional true} [:maybe ::lib.schema.common/non-blank-string]]
+   ;; the name we should export this column as, i.e. the RHS of a `SELECT <lhs> AS <rhs>` or equivalent.
+   [:lib/desired-column-alias {:optional true} [:maybe ::lib.schema.common/non-blank-string]]])
 
 (def ^:private CardMetadata
   [:map
diff --git a/src/metabase/lib/metadata/calculation.cljc b/src/metabase/lib/metadata/calculation.cljc
index 803f2c5800c9a1ff7aa79572576da7883c32c1e0..060903ee48ebc1ee070a66bf2f8d182e6d13d613 100644
--- a/src/metabase/lib/metadata/calculation.cljc
+++ b/src/metabase/lib/metadata/calculation.cljc
@@ -92,7 +92,7 @@
 (defn- slugify [s]
   (-> s
       (str/replace #"[\(\)]" "")
-      u/slugify))
+      (u/slugify {:unicode? true})))
 
 ;;; default impl just takes the display name and slugifies it.
 (defmethod column-name-method :default
diff --git a/src/metabase/lib/metadata/jvm.clj b/src/metabase/lib/metadata/jvm.clj
index ee8b75c05c74fb239c97e612b3a77d2b123546ac..f1b2ae30943bc7674c8df5a0e3b26d10329555cb 100644
--- a/src/metabase/lib/metadata/jvm.clj
+++ b/src/metabase/lib/metadata/jvm.clj
@@ -1,4 +1,4 @@
-(ns metabase.lib.metadata.jvm
+ (ns metabase.lib.metadata.jvm
   "Implementation(s) of [[metabase.lib.metadata.protocols/MetadataProvider]] only for the JVM."
   (:require
    [metabase.lib.metadata.cached-provider :as lib.metadata.cached-provider]
@@ -59,7 +59,7 @@
   (fields [_this table-id]
     (log/debugf "Fetching all Fields for Table %d" table-id)
     (mapv #(assoc % :lib/type :metadata/field)
-          (t2/select :table_id table-id)))
+          (t2/select :metabase.models.field/Field :table_id table-id)))
 
   lib.metadata.protocols/BulkMetadataProvider
   (bulk-metadata [_this metadata-type ids]
diff --git a/src/metabase/lib/query.cljc b/src/metabase/lib/query.cljc
index 45a089b503484682e6dc940571b8d65e9d097a2b..e38cf72089fad0082aeeeb0907234288b2d90acf 100644
--- a/src/metabase/lib/query.cljc
+++ b/src/metabase/lib/query.cljc
@@ -146,7 +146,7 @@
   "Convenience for creating a query from a Saved Question (i.e., a Card)."
   [metadata-provider :- lib.metadata/MetadataProvider
    {mbql-query :dataset_query, metadata :result_metadata}]
-  (let [mbql-query (cond-> (assoc (lib.util/pipeline mbql-query)
+  (let [mbql-query (cond-> (assoc (lib.convert/->pMBQL mbql-query)
                                   :lib/metadata metadata-provider)
                      metadata
                      (lib.util/update-query-stage -1 assoc :lib/stage-metadata metadata))]
diff --git a/src/metabase/lib/stage.cljc b/src/metabase/lib/stage.cljc
index 8582bb7f63d0b6cf678b90e768b5030c714c3850..72cc06dc597e75ed26644d75fa4676582523b1af 100644
--- a/src/metabase/lib/stage.cljc
+++ b/src/metabase/lib/stage.cljc
@@ -6,6 +6,8 @@
    [metabase.lib.aggregation :as lib.aggregation]
    [metabase.lib.breakout :as lib.breakout]
    [metabase.lib.expression :as lib.expression]
+   [metabase.lib.field :as lib.field]
+   [metabase.lib.join :as lib.join]
    [metabase.lib.metadata :as lib.metadata]
    [metabase.lib.metadata.calculation :as lib.metadata.calculation]
    [metabase.lib.normalize :as lib.normalize]
@@ -22,6 +24,14 @@
 
 (declare stage-metadata)
 
+(defn- unique-name-generator []
+  (comp (mbql.u/unique-name-generator
+         ;; unique by lower-case name, e.g. `NAME` and `name` => `NAME` and `name_2`
+         :name-key-fn     u/lower-case-en
+         ;; truncate alias to 30 characters (actually 21 characters plus a hash).
+         :unique-alias-fn (fn [original suffix]
+                            (lib.util/truncate-alias (str original \_ suffix))))))
+
 (defmethod lib.normalize/normalize :mbql.stage/mbql
   [stage]
   (lib.normalize/normalize-map
@@ -41,22 +51,24 @@
       (lib.util/update-query-stage previous-stage-number
                                    assoc
                                    ::cached-metadata
-                                   {:lib/type :metadata/results
-                                    :columns  (stage-metadata query previous-stage-number)}))))
+                                   (stage-metadata query previous-stage-number)))))
 
 (def ^:private StageMetadataColumns
-  [:sequential {:min 1} lib.metadata.calculation/ColumnMetadataWithSource])
-
-(def ^:private DistinctStageMetadataColumns
   [:and
-   StageMetadataColumns
+   [:sequential {:min 1}
+    [:merge
+     lib.metadata.calculation/ColumnMetadataWithSource
+     [:map
+      [:lib/source-column-alias  ::lib.schema.common/non-blank-string]
+      [:lib/desired-column-alias ::lib.schema.common/non-blank-string]]]]
    [:fn
     ;; should be dev-facing only, so don't need to i18n
-    {:error/message "Column :names must be distinct!"
+    {:error/message "Column :lib/desired-column-alias values must be distinct, regardless of case, for each stage!"
      :error/fn      (fn [{:keys [value]} _]
-                      (str "Column :names must be distinct, got: " (pr-str (mapv :name value))))}
+                      (str "Column :lib/desired-column-alias values must be distinct, got: "
+                           (pr-str (mapv :lib/desired-column-alias value))))}
     (fn [columns]
-      (apply distinct? (map :name columns)))]])
+      (apply distinct? (map (comp u/lower-case-en :lib/desired-column-alias) columns)))]])
 
 (mu/defn ^:private existing-stage-metadata :- [:maybe StageMetadataColumns]
   "Return existing stage metadata attached to a stage if is already present: return it as-is, but only if this is a
@@ -76,41 +88,71 @@
                 (assoc col :lib/source source-type))))))))
 
 (mu/defn ^:private breakouts-columns :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (not-empty
    (for [breakout (lib.breakout/breakouts query stage-number)]
-     (assoc breakout :lib/source :source/breakouts))))
+     (assoc breakout
+            :lib/source               :source/breakouts
+            :lib/source-column-alias  (:name breakout)
+            :lib/desired-column-alias (unique-name-fn (:name breakout))))))
 
 (mu/defn ^:private aggregations-columns :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (not-empty
    (for [ag (lib.aggregation/aggregations query stage-number)]
-     (assoc ag :lib/source :source/aggregations))))
+     (assoc ag
+            :lib/source               :source/aggregations
+            :lib/source-column-alias  (:name ag)
+            :lib/desired-column-alias (unique-name-fn (:name ag))))))
 
 (mu/defn ^:private fields-columns :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (when-let [{fields :fields} (lib.util/query-stage query stage-number)]
     (not-empty
-     (for [[tag :as ref-clause] fields]
-       (assoc (lib.metadata.calculation/metadata query stage-number ref-clause)
-              :lib/source (case tag
-                            ;; you can't have an `:aggregation` reference in `:fields`; anything in `:aggregations` is
-                            ;; returned automatically anyway by [[aggregations-columns]] above.
-                            :field       :source/fields
-                            :expression  :source/expressions))))))
+     (for [[tag :as ref-clause] fields
+           :let                 [source (case tag
+                                          ;; you can't have an `:aggregation` reference in `:fields`; anything in
+                                          ;; `:aggregations` is returned automatically anyway
+                                          ;; by [[aggregations-columns]] above.
+                                          :field      :source/fields
+                                          :expression :source/expressions)
+                                 metadata (lib.metadata.calculation/metadata query stage-number ref-clause)]]
+       (assoc metadata
+              :lib/source               source
+              :lib/source-column-alias  (lib.metadata.calculation/column-name query stage-number metadata)
+              :lib/desired-column-alias (unique-name-fn (lib.field/desired-alias metadata)))))))
 
 (mu/defn ^:private breakout-ags-fields-columns :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (not-empty
    (into []
-         cat
-         [(breakouts-columns query stage-number)
-          (aggregations-columns query stage-number)
-          (fields-columns query stage-number)])))
+         (mapcat (fn [f]
+                   (f query stage-number unique-name-fn)))
+         [breakouts-columns
+          aggregations-columns
+          fields-columns])))
+
+(mu/defn ^:private previous-stage-metadata :- [:maybe StageMetadataColumns]
+  "Metadata for the previous stage, if there is one."
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
+  (when-let [previous-stage-number (lib.util/previous-stage-number query stage-number)]
+    (for [col  (stage-metadata query previous-stage-number)
+          :let [source-alias (or ((some-fn :lib/desired-column-alias :lib/source-column-alias) col)
+                                 (lib.metadata.calculation/column-name query stage-number col))]]
+      (assoc col
+             :lib/source               :source/previous-stage
+             :lib/source-column-alias  source-alias
+             :lib/desired-column-alias (unique-name-fn source-alias)))))
 
 (defn- remove-hidden-default-fields
   "Remove Fields that shouldn't be visible from the default Fields for a source Table.
@@ -131,14 +173,18 @@
 (mu/defn ^:private source-table-default-fields :- [:maybe StageMetadataColumns]
   "Determine the Fields we'd normally return for a source Table.
   See [[metabase.query-processor.middleware.add-implicit-clauses/add-implicit-fields]]."
-  [query    :- ::lib.schema/query
-   table-id :- ::lib.schema.id/table]
+  [query          :- ::lib.schema/query
+   table-id       :- ::lib.schema.id/table
+   unique-name-fn :- fn?]
   (when-let [field-metadatas (lib.metadata/fields query table-id)]
     (->> field-metadatas
          remove-hidden-default-fields
          sort-default-fields
          (map (fn [col]
-                (assoc col :lib/source :source/table-defaults))))))
+                (assoc col
+                       :lib/source               :source/table-defaults
+                       :lib/source-column-alias  (:name col)
+                       :lib/desired-column-alias (unique-name-fn (:name col))))))))
 
 (mu/defn ^:private default-join-alias :- ::lib.schema.common/non-blank-string
   "Generate an alias for a join that doesn't already have one."
@@ -160,33 +206,39 @@
   [query        :- ::lib.schema/query
    stage-number :- :int
    joins        :- ::lib.schema.join/joins]
-  (let [unique-name-generator (mbql.u/unique-name-generator)]
+  (let [unique-name-fn (unique-name-generator)]
     (mapv (fn [join]
             (cond-> join
-              (not (:alias join)) (assoc :alias (unique-name-generator (default-join-alias query stage-number join)))))
+              (not (:alias join)) (assoc :alias (unique-name-fn (default-join-alias query stage-number join)))))
           joins)))
 
 (mu/defn ^:private default-columns-added-by-join :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int
-   join         :- ::lib.schema.join/join]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?
+   join           :- ::lib.schema.join/join]
+  (assert (:alias join) "Join must have :alias")
   (not-empty
    (for [col (lib.metadata.calculation/metadata query stage-number join)]
-     (assoc col :lib/source :source/joins))))
+     (assoc col
+            :lib/source-column-alias  (lib.metadata.calculation/column-name query stage-number col)
+            :lib/desired-column-alias (unique-name-fn (lib.field/desired-alias col))))))
 
 (mu/defn ^:private default-columns-added-by-joins :- [:maybe StageMetadataColumns]
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (when-let [joins (not-empty (:joins (lib.util/query-stage query stage-number)))]
     (not-empty
      (into []
-           (mapcat (partial default-columns-added-by-join query stage-number))
+           (mapcat (partial default-columns-added-by-join query stage-number unique-name-fn))
            (ensure-all-joins-have-aliases query stage-number joins)))))
 
 (mu/defn ^:private saved-question-metadata :- [:maybe StageMetadataColumns]
   "Metadata associated with a Saved Question, if `:source-table` is a `card__<id>` string."
-  [query :- ::lib.schema/query
-   source-table-id]
+  [query           :- ::lib.schema/query
+   source-table-id :- [:or ::lib.schema.id/table ::lib.schema.id/table-card-id-string]
+   unique-name-fn  :- fn?]
   (when-let [card-id (lib.util/string-table-id->card-id source-table-id)]
     ;; it seems like in some cases the FE is renaming `:result_metadata` to `:fields`, not 100% sure why but
     ;; handle that case anyway. (#29739)
@@ -195,7 +247,21 @@
                                    (map? result-metadata)        (:columns result-metadata)
                                    (sequential? result-metadata) result-metadata))]
         (for [col cols]
-          (assoc col :lib/source :source/card))))))
+          (assoc col
+                 :lib/source               :source/card
+                 :lib/source-column-alias  (:name col)
+                 :lib/desired-column-alias (unique-name-fn (:name col))))))))
+
+(mu/defn ^:private expressions-metadata :- [:maybe StageMetadataColumns]
+  [query           :- ::lib.schema/query
+   stage-number    :- :int
+   unique-name-fn  :- fn?]
+  (not-empty
+   (for [expression (lib.expression/expressions query stage-number)]
+     (assoc expression
+            :lib/source               :source/expressions
+            :lib/source-column-alias  (:name expression)
+            :lib/desired-column-alias (unique-name-fn (:name expression))))))
 
 (mu/defn ^:private default-columns :- StageMetadataColumns
   "Calculate the columns to return if `:aggregations`/`:breakout`/`:fields` are unspecified.
@@ -217,50 +283,43 @@
   PLUS
 
   3. Columns added by joins at this stage"
-  [query        :- ::lib.schema/query
-   stage-number :- :int]
+  [query          :- ::lib.schema/query
+   stage-number   :- :int
+   unique-name-fn :- fn?]
   (concat
    ;; 1: columns from the previous stage, source table or query
-   (if-let [previous-stage-number (lib.util/previous-stage-number query stage-number)]
+   (or
      ;; 1a. columns returned by previous stage
-     (for [col (stage-metadata query previous-stage-number)]
-       (assoc col :lib/source :source/previous-stage))
+    (previous-stage-metadata query stage-number unique-name-fn)
      ;; 1b or 1c
      (let [{:keys [source-table], :as this-stage} (lib.util/query-stage query stage-number)]
        (or
         ;; 1b: default visible Fields for the source Table
         (when (integer? source-table)
-          (source-table-default-fields query source-table))
+          (source-table-default-fields query source-table unique-name-fn))
         ;; 1c. Metadata associated with a saved Question
-        (saved-question-metadata query source-table)
+        (saved-question-metadata query source-table unique-name-fn)
         ;; 1d: `:lib/stage-metadata` for the (presumably native) query
         (for [col (:columns (:lib/stage-metadata this-stage))]
           (assoc col :lib/source :source/native)))))
    ;; 2: expressions (aka calculated columns) added in this stage
-   (lib.expression/expressions query stage-number)
+   (expressions-metadata query stage-number unique-name-fn)
    ;; 3: columns added by joins at this stage
-   (default-columns-added-by-joins query stage-number)))
+   (default-columns-added-by-joins query stage-number unique-name-fn)))
 
-(defn- ensure-distinct-names [metadatas]
-  (when (seq metadatas)
-    (let [f (mbql.u/unique-name-generator)]
-      (for [metadata metadatas]
-        (update metadata :name f)))))
-
-(mu/defn ^:private stage-metadata :- DistinctStageMetadataColumns
+(mu/defn ^:private stage-metadata :- StageMetadataColumns
   "Return results metadata about the expected columns in an MBQL query stage. If the query has
   aggregations/breakouts/fields, then return THOSE. Otherwise return the defaults based on the source Table or
   previous stage + joins."
   [query        :- ::lib.schema/query
    stage-number :- :int]
-  (ensure-distinct-names
-   (or
-    (existing-stage-metadata query stage-number)
-    (let [query (ensure-previous-stages-have-metadata query stage-number)]
-      ;; ... then calculate metadata for this stage
-      (or
-       (breakout-ags-fields-columns query stage-number)
-       (default-columns query stage-number))))))
+  (or
+   (existing-stage-metadata query stage-number)
+   (let [query (ensure-previous-stages-have-metadata query stage-number)]
+     ;; ... then calculate metadata for this stage
+     (or
+      (breakout-ags-fields-columns query stage-number (unique-name-generator))
+      (default-columns query stage-number (unique-name-generator))))))
 
 (doseq [stage-type [:mbql.stage/mbql
                     :mbql.stage/native]]
@@ -298,7 +357,7 @@
   it turns out we do need that stuff.
 
   Does not include columns that would be implicitly joinable via multiple hops."
-  [query column-metadatas]
+  [query column-metadatas unique-name-fn]
   (let [existing-table-ids (into #{} (map :table_id) column-metadatas)]
     (into []
           (comp (filter :fk_target_field_id)
@@ -309,21 +368,31 @@
                 (remove #(contains? existing-table-ids (:table_id %)))
                 (m/distinct-by :table_id)
                 (mapcat (fn [{table-id :table_id, ::keys [source-field-id]}]
-                          (for [field (source-table-default-fields query table-id)]
-                            (assoc field :fk_field_id source-field-id))))
-                (map (fn [metadata]
-                       (assoc metadata :lib/source :source/implicitly-joinable))))
+                          (let [table-name           (:name (lib.metadata/table query table-id))
+                                source-field-id-name (:name (lib.metadata/field query source-field-id))
+                                ;; make sure the implicit join name is unique.
+                                source-alias         (unique-name-fn
+                                                      (lib.join/implicit-join-name table-name source-field-id-name))]
+                            (for [field (source-table-default-fields query table-id unique-name-fn)
+                                  :let  [field (assoc field
+                                                      :fk_field_id              source-field-id
+                                                      :lib/source               :source/implicitly-joinable
+                                                      :lib/source-column-alias  (:name field))
+                                         field (lib.join/with-join-alias field source-alias)]]
+                              (assoc field :lib/desired-column-alias (unique-name-fn
+                                                                      (lib.field/desired-alias field))))))))
           column-metadatas)))
 
 (mu/defn visible-columns :- StageMetadataColumns
   "Columns that are visible inside a given stage of a query. Ignores `:fields`, `:breakout`, and `:aggregation`.
   Includes columns that are implicitly joinable from other Tables."
   [query stage-number]
-  (let [query   (lib.util/update-query-stage query stage-number dissoc :fields :breakout :aggregation)
-        columns (default-columns query stage-number)]
+  (let [query          (lib.util/update-query-stage query stage-number dissoc :fields :breakout :aggregation)
+        unique-name-fn (unique-name-generator)
+        columns        (default-columns query stage-number unique-name-fn)]
     (concat
      columns
-     (implicitly-joinable-columns query columns))))
+     (implicitly-joinable-columns query columns unique-name-fn))))
 
 (mu/defn append-stage :- ::lib.schema/query
   "Adds a new blank stage to the end of the pipeline"
diff --git a/src/metabase/lib/util.cljc b/src/metabase/lib/util.cljc
index 18627fd0324e7b2245d31dc8a529e5324841f719..30c1a68512a4f6d2e7e9535735ca4d1ab3367b79 100644
--- a/src/metabase/lib/util.cljc
+++ b/src/metabase/lib/util.cljc
@@ -5,15 +5,20 @@
    [clojure.string :as str]
    [metabase.lib.options :as lib.options]
    [metabase.lib.schema :as lib.schema]
+   [metabase.lib.schema.common :as lib.schema.common]
    [metabase.lib.schema.id :as lib.schema.id]
    [metabase.shared.util.i18n :as i18n]
    [metabase.util.malli :as mu]
    #?@(:clj
        ([potemkin :as p]))
    #?@(:cljs
-       ([goog.string :as gstring]
+       (["crc-32" :as CRC32]
+        [goog.string :as gstring]
         [goog.string.format :as gstring.format]))))
 
+#?(:clj
+   (set! *warn-on-reflection* true))
+
 ;; The formatting functionality is only loaded if you depend on goog.string.format.
 #?(:cljs (comment gstring.format/keep-me))
 
@@ -267,6 +272,83 @@
                      update-joins)]
          (f query stage-number stage))))))
 
+(mu/defn ^:private string-byte-count :- [:int {:min 0}]
+  "Number of bytes in a string using UTF-8 encoding."
+  [s :- :string]
+  #?(:clj (count (.getBytes (str s) "UTF-8"))
+     :cljs (.. (js/TextEncoder.) (encode s) -length)))
+
+#?(:clj
+   (mu/defn ^:private string-character-at :- [:string {:min 0, :max 1}]
+     [s :- :string
+      i :-[:int {:min 0}]]
+     (str (.charAt ^String s i))))
+
+(mu/defn ^:private truncate-string-to-byte-count :- :string
+  "Truncate string `s` to `max-length-bytes` UTF-8 bytes (as opposed to truncating to some number of
+  *characters*)."
+  [s                :- :string
+   max-length-bytes :- [:int {:min 1}]]
+  #?(:clj
+     (loop [i 0, cumulative-byte-count 0]
+       (cond
+         (= cumulative-byte-count max-length-bytes) (subs s 0 i)
+         (> cumulative-byte-count max-length-bytes) (subs s 0 (dec i))
+         (>= i (count s))                           s
+         :else                                      (recur (inc i)
+                                                           (+
+                                                            cumulative-byte-count
+                                                            (string-byte-count (string-character-at s i))))))
+
+     :cljs
+     (let [buf (js/Uint8Array. max-length-bytes)
+           result (.encodeInto (js/TextEncoder.) s buf)] ;; JS obj {read: chars_converted, write: bytes_written}
+       (subs s 0 (.-read result)))))
+
+(def ^:private truncate-alias-max-length-bytes
+  "Length to truncate column and table identifiers to. See [[metabase.driver.impl/default-alias-max-length-bytes]] for
+  reasoning."
+  60)
+
+(def ^:private truncated-alias-hash-suffix-length
+  "Length of the hash suffixed to truncated strings by [[truncate-alias]]."
+  ;; 8 bytes for the CRC32 plus one for the underscore
+  9)
+
+(mu/defn ^:private crc32-checksum :- [:string {:min 8, :max 8}]
+  "Return a 4-byte CRC-32 checksum of string `s`, encoded as an 8-character hex string."
+  [s :- :string]
+  (let [s #?(:clj (Long/toHexString (.getValue (doto (java.util.zip.CRC32.)
+                                                 (.update (.getBytes ^String s "UTF-8")))))
+             :cljs (-> (CRC32/str s 0)
+                       (unsigned-bit-shift-right 0) ; see https://github.com/SheetJS/js-crc32#signed-integers
+                       (.toString 16)))]
+    ;; pad to 8 characters if needed. Might come out as less than 8 if the first byte is `00` or `0x` or something.
+    (loop [s s]
+      (if (< (count s) 8)
+        (recur (str \0 s))
+        s))))
+
+(mu/defn truncate-alias :- ::lib.schema.common/non-blank-string
+  "Truncate string `s` if it is longer than [[truncate-alias-max-length-bytes]] and append a hex-encoded CRC-32
+  checksum of the original string. Truncated string is truncated to [[truncate-alias-max-length-bytes]]
+  minus [[truncated-alias-hash-suffix-length]] characters so the resulting string is
+  exactly [[truncate-alias-max-length-bytes]]. The goal here is that two really long strings that only differ at the
+  end will still have different resulting values.
+
+    (truncate-alias \"some_really_long_string\" 15) ;   -> \"some_r_8e0f9bc2\"
+    (truncate-alias \"some_really_long_string_2\" 15) ; -> \"some_r_2a3c73eb\""
+  ([s]
+   (truncate-alias s truncate-alias-max-length-bytes))
+
+  ([s         :- ::lib.schema.common/non-blank-string
+    max-bytes :- [:int {:min 0}]]
+   (if (<= (string-byte-count s) max-bytes)
+     s
+     (let [checksum  (crc32-checksum s)
+           truncated (truncate-string-to-byte-count s (- max-bytes truncated-alias-hash-suffix-length))]
+       (str truncated \_ checksum)))))
+
 (mu/defn string-table-id->card-id :- [:maybe ::lib.schema.id/card]
   "If `table-id` is a `card__<id>`-style string, parse the `<id>` part to an integer Card ID."
   [table-id]
diff --git a/test/metabase/api/metric_test.clj b/test/metabase/api/metric_test.clj
index c6159a8cec0e6e355ee0a79eb69e2efd4a8fea20..3d7b51a5109336363b28ec7841ba351ce23748b8 100644
--- a/test/metabase/api/metric_test.clj
+++ b/test/metabase/api/metric_test.clj
@@ -412,7 +412,7 @@
                {:name                   "Metric B"
                 :id                     id-2
                 :creator                {}
-                :definition_description "Venues, Sum of Name, Filtered by Price equals 4 and Segment"}]
+                :definition_description "Venues, Sum of Categories → Name, Filtered by Price equals 4 and Segment"}]
               (filter (fn [{metric-id :id}]
                         (contains? #{id-1 id-2 id-3} metric-id))
                       (mt/user-http-request :rasta :get 200 "metric/")))))))
diff --git a/test/metabase/api/segment_test.clj b/test/metabase/api/segment_test.clj
index c00f22b7bb116fa167d6502957056f7de6b5aef3..ecf9ce90224a3618ad9ad17bafc496a8533afc0d 100644
--- a/test/metabase/api/segment_test.clj
+++ b/test/metabase/api/segment_test.clj
@@ -425,7 +425,7 @@
                 :name                   "Segment 2"
                 :definition             {}
                 :creator                {}
-                :definition_description "Filtered by Price equals 4 and Name equals \"BBQ\""}]
+                :definition_description "Filtered by Price equals 4 and Categories → Name equals \"BBQ\""}]
               (filter (fn [{segment-id :id}]
                         (contains? #{id-1 id-2 id-3} segment-id))
                       (mt/user-http-request :rasta :get 200 "segment/")))))))
diff --git a/test/metabase/driver/impl_test.clj b/test/metabase/driver/impl_test.clj
index c4b5b0c5ae9acaae17c0343bd92541a296153a30..78f10362c05e64f75ea3ad1744ee5c5e62ba5a37 100644
--- a/test/metabase/driver/impl_test.clj
+++ b/test/metabase/driver/impl_test.clj
@@ -1,7 +1,6 @@
 (ns metabase.driver.impl-test
   (:require
    [clojure.core.async :as a]
-   [clojure.string :as str]
    [clojure.test :refer :all]
    [metabase.driver :as driver]
    [metabase.driver.impl :as driver.impl]
@@ -9,7 +8,7 @@
 
 (set! *warn-on-reflection* true)
 
-(deftest driver->expected-namespace-test
+(deftest ^:parallel driver->expected-namespace-test
   (testing "expected namespace for a non-namespaced driver should be `metabase.driver.<driver>`"
     (is (= 'metabase.driver.sql-jdbc
            (#'driver.impl/driver->expected-namespace :sql-jdbc))))
@@ -37,81 +36,3 @@
                  (driver/the-initialized-driver ::race-condition-test)))
           (is (= true
                  @finished-loading)))))))
-
-(deftest truncate-string-to-byte-count-test
-  (letfn [(truncate-string-to-byte-count [s byte-length]
-            (let [^String truncated (#'driver.impl/truncate-string-to-byte-count s byte-length)]
-              (is (<= (count (.getBytes truncated "UTF-8")) byte-length))
-              (is (str/starts-with? s truncated))
-              truncated))]
-    (doseq [[s max-length->expected] {"12345"
-                                      {0  ""
-                                       1  "1"
-                                       2  "12"
-                                       3  "123"
-                                       4  "1234"
-                                       5  "12345"
-                                       6  "12345"
-                                       10 "12345"}
-
-                                      "가나다라"
-                                      {0  ""
-                                       1  ""
-                                       2  ""
-                                       3  "ê°€"
-                                       4  "ê°€"
-                                       5  "ê°€"
-                                       6  "가나"
-                                       7  "가나"
-                                       8  "가나"
-                                       9  "가나다"
-                                       10 "가나다"
-                                       11 "가나다"
-                                       12 "가나다라"
-                                       13 "가나다라"
-                                       15 "가나다라"
-                                       20 "가나다라"}}
-            [max-length expected] max-length->expected]
-      (testing (pr-str (list `driver.impl/truncate-string-to-byte-count s max-length))
-        (is (= expected
-               (truncate-string-to-byte-count s max-length)))))))
-
-(deftest truncate-alias-test
-  (letfn [(truncate-alias [s max-bytes]
-            (let [truncated (driver.impl/truncate-alias s max-bytes)]
-              (is (<= (count (.getBytes truncated "UTF-8")) max-bytes))
-              truncated))]
-    (doseq [[s max-bytes->expected] { ;; 20-character plain ASCII string
-                                     "01234567890123456789"
-                                     {12 "012_fc89bad5"
-                                      15 "012345_fc89bad5"
-                                      20 "01234567890123456789"}
-
-                                     ;; two strings that only differ after the point they get truncated
-                                     "0123456789abcde" {12 "012_1629bb92"}
-                                     "0123456789abcdE" {12 "012_2d479b5a"}
-
-                                     ;; Unicode string: 14 characters, 42 bytes
-                                     "가나다라마바사아자차카타파하"
-                                     {12 "ê°€_b9c95392"
-                                      13 "ê°€_b9c95392"
-                                      14 "ê°€_b9c95392"
-                                      15 "가나_b9c95392"
-                                      20 "가나다_b9c95392"
-                                      30 "가나다라마바사_b9c95392"
-                                      40 "가나다라마바사아자차_b9c95392"
-                                      50 "가나다라마바사아자차카타파하"}
-
-                                     ;; Mixed string: 17 characters, 33 bytes
-                                     "a가b나c다d라e마f바g사h아i"
-                                     {12 "a_99a0fe0c"
-                                      13 "aê°€_99a0fe0c"
-                                      14 "aê°€b_99a0fe0c"
-                                      15 "aê°€b_99a0fe0c"
-                                      20 "a가b나c_99a0fe0c"
-                                      30 "a가b나c다d라e마f_99a0fe0c"
-                                      40 "a가b나c다d라e마f바g사h아i"}}
-            [max-bytes expected] max-bytes->expected]
-      (testing (pr-str (list `driver.impl/truncate-alias s max-bytes))
-        (is (= expected
-               (truncate-alias s max-bytes)))))))
diff --git a/test/metabase/lib/aggregation_test.cljc b/test/metabase/lib/aggregation_test.cljc
index 449eb278e501f15b000a2646e9486875286175d7..7325f1c904153e45ab3d37f402095e207638e807 100644
--- a/test/metabase/lib/aggregation_test.cljc
+++ b/test/metabase/lib/aggregation_test.cljc
@@ -50,10 +50,10 @@
     {:column-name "count", :display-name "Count"}
 
     [:distinct {} (lib.tu/field-clause :venues :id)]
-    {:column-name "distinct_id", :display-name "Distinct values of ID"}
+    {:column-name "distinct_ID", :display-name "Distinct values of ID"}
 
     [:sum {} (lib.tu/field-clause :venues :id)]
-    {:column-name "sum_id", :display-name "Sum of ID"}
+    {:column-name "sum_ID", :display-name "Sum of ID"}
 
     [:+ {} [:count {}] 1]
     {:column-name "count_plus_1", :display-name "Count + 1"}
@@ -62,7 +62,7 @@
      {}
      [:min {} (lib.tu/field-clause :venues :id)]
      [:* {} 2 [:avg {} (lib.tu/field-clause :venues :price)]]]
-    {:column-name  "min_id_plus_2_times_avg_price"
+    {:column-name  "min_ID_plus_2_times_avg_PRICE"
      :display-name "Min of ID + (2 × Average of Price)"}
 
     [:+
@@ -74,7 +74,7 @@
       [:avg {} (lib.tu/field-clause :venues :price)]
       3
       [:- {} [:max {} (lib.tu/field-clause :venues :category-id)] 4]]]
-    {:column-name  "min_id_plus_2_times_avg_price_times_3_times_max_category_id_minus_4"
+    {:column-name  "min_ID_plus_2_times_avg_PRICE_times_3_times_max_CATEGORY_ID_minus_4"
      :display-name "Min of ID + (2 × Average of Price × 3 × (Max of Category ID - 4))"}
 
     ;; user-specified names
@@ -94,7 +94,7 @@
      {:display-name "User-specified Name"}
      [:min {} (lib.tu/field-clause :venues :id)]
      [:* {} 2 [:avg {} (lib.tu/field-clause :venues :price)]]]
-    {:column-name  "min_id_plus_2_times_avg_price"
+    {:column-name  "min_ID_plus_2_times_avg_PRICE"
      :display-name "User-specified Name"}))
 
 ;;; the following tests use raw legacy MBQL because they're direct ports of JavaScript tests from MLv1 and I wanted to
@@ -144,16 +144,16 @@
     ;; :sum
     [:sum {} [:+ {} (lib.tu/field-clause :venues :price) 1]]
     {:base_type    :type/Integer
-     :name         "sum_price_plus_1"
+     :name         "sum_PRICE_plus_1"
      :display_name "Sum of Price + 1"}
 
     ;; options map
     [:sum
      {:name "sum_2", :display-name "My custom name", :base-type :type/BigInteger}
      (lib.tu/field-clause :venues :price)]
-    {:base_type     :type/BigInteger
-     :name          "sum_2"
-     :display_name  "My custom name"}))
+    {:base_type    :type/BigInteger
+     :name         "sum_2"
+     :display_name "My custom name"}))
 
 (deftest ^:parallel col-info-named-aggregation-test
   (testing "col info for an `expression` aggregation w/ a named expression should work as expected"
@@ -173,12 +173,12 @@
 (deftest ^:parallel aggregate-test
   (let [q (lib/query-for-table-name meta/metadata-provider "VENUES")
         result-query
-        {:lib/type :mbql/query,
-         :database (meta/id) ,
-         :type :pipeline,
-         :stages [{:lib/type :mbql.stage/mbql,
-                   :source-table (meta/id :venues) ,
-                   :lib/options {:lib/uuid string?},
+        {:lib/type :mbql/query
+         :database (meta/id)
+         :type :pipeline
+         :stages [{:lib/type :mbql.stage/mbql
+                   :source-table (meta/id :venues)
+                   :lib/options {:lib/uuid string?}
                    :aggregation [[:sum {:lib/uuid string?}
                                   [:field
                                    {:base-type :type/Integer, :lib/uuid string?}
@@ -248,7 +248,7 @@
       (is (=? {:settings     {:is_priceless true}
                :lib/type     :metadata/field
                :base_type    :type/Integer
-               :name         "sum_price"
+               :name         "sum_PRICE"
                :display_name "Sum of Price"
                :lib/source   :source/aggregations}
               (lib.metadata.calculation/metadata query (first (lib/aggregations query -1))))))))
diff --git a/test/metabase/lib/expression_test.cljc b/test/metabase/lib/expression_test.cljc
index d30c00c02e492f7ff398f4a7bb52a6ba1dfee126..53228f9022957da6fdbb1bd218c485fb7a323840 100644
--- a/test/metabase/lib/expression_test.cljc
+++ b/test/metabase/lib/expression_test.cljc
@@ -119,7 +119,7 @@
                 (lib.tu/field-clause :checkins :date {:base-type :type/Date})
                 -1
                 :day]]
-    (is (= "date_minus_1_day"
+    (is (= "DATE_minus_1_day"
            (lib.metadata.calculation/column-name lib.tu/venues-query -1 clause)))
     (is (= "Date - 1 day"
            (lib.metadata.calculation/display-name lib.tu/venues-query -1 clause)))))
@@ -141,7 +141,7 @@
 
 (deftest ^:parallel coalesce-names-test
   (let [clause [:coalesce {} (lib.tu/field-clause :venues :name) "<Venue>"]]
-    (is (= "name"
+    (is (= "NAME"
            (lib.metadata.calculation/column-name lib.tu/venues-query -1 clause)))
     (is (= "Name"
            (lib.metadata.calculation/display-name lib.tu/venues-query -1 clause)))))
diff --git a/test/metabase/lib/field_test.cljc b/test/metabase/lib/field_test.cljc
index 596e7c86753a0377600a6f2c0e4d1007196341be..1d450d8faf7b19a382b6298d86d897acde4139ea 100644
--- a/test/metabase/lib/field_test.cljc
+++ b/test/metabase/lib/field_test.cljc
@@ -1,6 +1,7 @@
 (ns metabase.lib.field-test
   (:require
    [clojure.test :refer [deftest is testing]]
+   [medley.core :as m]
    [metabase.lib.core :as lib]
    [metabase.lib.metadata :as lib.metadata]
    [metabase.lib.metadata.calculation :as lib.metadata.calculation]
@@ -151,6 +152,29 @@
       (is (= "Date (year)"
              (lib.metadata.calculation/display-name query -1 field))))))
 
+(deftest ^:parallel joined-field-column-name-test
+  (let [card  {:dataset_query {:database (meta/id)
+                               :type     :query
+                               :query    {:source-table (meta/id :venues)
+                                          :joins        [{:fields       :all
+                                                          :source-table (meta/id :categories)
+                                                          :condition    [:=
+                                                                         [:field (meta/id :venues :category-id) nil]
+                                                                         [:field (meta/id :categories :id) {:join-alias "Cat"}]]
+                                                          :alias        "Cat"}]}}}
+        query (lib/saved-question-query
+               meta/metadata-provider
+               card)]
+    (is (=? [{:lib/desired-column-alias "ID"}
+             {:lib/desired-column-alias "NAME"}
+             {:lib/desired-column-alias "CATEGORY_ID"}
+             {:lib/desired-column-alias "LATITUDE"}
+             {:lib/desired-column-alias "LONGITUDE"}
+             {:lib/desired-column-alias "PRICE"}
+             {:lib/desired-column-alias "Cat__ID"}
+             {:lib/desired-column-alias "Cat__NAME"}]
+            (lib.metadata.calculation/metadata query)))))
+
 (deftest ^:parallel field-ref-type-of-test
   (testing "Make sure we can calculate field ref type information correctly"
     (let [clause [:field {:lib/uuid (str (random-uuid))} (meta/id :venues :id)]]
@@ -159,6 +183,17 @@
       (is (= :type/BigInteger
              (lib.metadata.calculation/type-of lib.tu/venues-query clause))))))
 
+(deftest ^:parallel implicitly-joinable-field-display-name-test
+  (testing "Should be able to calculate a display name for an implicitly joinable Field"
+    (let [query           (lib/query-for-table-name meta/metadata-provider "VENUES")
+          categories-name (m/find-first #(= (:id %) (meta/id :categories :name))
+                                        (lib/orderable-columns query))]
+      (is (= "Categories → Name"
+             (lib/display-name query categories-name)))
+      (let [query' (lib/order-by query categories-name)]
+        (is (= "Venues, Sorted by Categories → Name ascending"
+               (lib/describe-query query')))))))
+
 (deftest ^:parallel source-card-table-display-info-test
   (let [query (assoc lib.tu/venues-query :lib/metadata lib.tu/metadata-provider-with-card)
         field (lib.metadata.calculation/metadata query (assoc (lib.metadata/field query (meta/id :venues :name))
diff --git a/test/metabase/lib/metadata/jvm_test.clj b/test/metabase/lib/metadata/jvm_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..fc73d59e2280d33a9e758a44cc47c7501a683fa3
--- /dev/null
+++ b/test/metabase/lib/metadata/jvm_test.clj
@@ -0,0 +1,30 @@
+(ns metabase.lib.metadata.jvm-test
+  (:require
+   [clojure.test :refer :all]
+   [metabase.lib.core :as lib]
+   [metabase.lib.metadata.calculation :as lib.metadata.calculation]
+   [metabase.lib.metadata.jvm :as sut]
+   [metabase.test :as mt]))
+
+(deftest ^:parallel saved-question-metadata-test
+  (let [card  {:dataset_query {:database (mt/id)
+                               :type     :query
+                               :query    {:source-table (mt/id :venues)
+                                          :joins        [{:fields       :all
+                                                          :source-table (mt/id :categories)
+                                                          :condition    [:=
+                                                                         [:field (mt/id :venues :category_id) nil]
+                                                                         [:field (mt/id :categories :id) {:join-alias "Cat"}]]
+                                                          :alias        "Cat"}]}}}
+        query (lib/saved-question-query
+               (metabase.lib.metadata.jvm/application-database-metadata-provider (mt/id))
+               card)]
+    (is (=? [{:lib/desired-column-alias "ID"}
+             {:lib/desired-column-alias "NAME"}
+             {:lib/desired-column-alias "CATEGORY_ID"}
+             {:lib/desired-column-alias "LATITUDE"}
+             {:lib/desired-column-alias "LONGITUDE"}
+             {:lib/desired-column-alias "PRICE"}
+             {:lib/desired-column-alias "Cat__ID"}
+             {:lib/desired-column-alias "Cat__NAME"}]
+            (lib.metadata.calculation/metadata query)))))
diff --git a/test/metabase/lib/order_by_test.cljc b/test/metabase/lib/order_by_test.cljc
index 21c22685a9050ca16d049da0ed1b927c59c184d7..6fd10546608c38b064aa8f1ce9091025797d8d18 100644
--- a/test/metabase/lib/order_by_test.cljc
+++ b/test/metabase/lib/order_by_test.cljc
@@ -148,12 +148,12 @@
                   :base_type          :type/Integer}
                  {:lib/type     :metadata/field
                   :base_type    :type/Integer
-                  :name         "sum_price"
+                  :name         "sum_PRICE"
                   :display_name "Sum of Price"
                   :lib/source   :source/aggregations}
                  {:lib/type     :metadata/field
                   :base_type    :type/Float
-                  :name         "avg_price_plus_1"
+                  :name         "avg_PRICE_plus_1"
                   :display_name "Average of Price + 1"
                   :lib/source   :source/aggregations}]
                 (lib/orderable-columns query)))))))
@@ -174,53 +174,69 @@
 (deftest ^:parallel orderable-columns-test
   (let [query (lib/query-for-table-name meta/metadata-provider "VENUES")]
     (testing (lib.util/format "Query =\n%s" (u/pprint-to-str query))
-      (is (=? [{:lib/type     :metadata/field
-                :name         "ID"
-                :display_name "ID"
-                :id           (meta/id :venues :id)
-                :table_id     (meta/id :venues)
-                :base_type    :type/BigInteger}
-               {:lib/type     :metadata/field
-                :name         "NAME"
-                :display_name "Name"
-                :id           (meta/id :venues :name)
-                :table_id     (meta/id :venues)
-                :base_type    :type/Text}
-               {:lib/type     :metadata/field
-                :name         "CATEGORY_ID"
-                :display_name "Category ID"
-                :id           (meta/id :venues :category-id)
-                :table_id     (meta/id :venues)}
-               {:lib/type     :metadata/field
-                :name         "LATITUDE"
-                :display_name "Latitude"
-                :id           (meta/id :venues :latitude)
-                :table_id     (meta/id :venues)
-                :base_type    :type/Float}
-               {:lib/type     :metadata/field
-                :name         "LONGITUDE"
-                :display_name "Longitude"
-                :id           (meta/id :venues :longitude)
-                :table_id     (meta/id :venues)
-                :base_type    :type/Float}
-               {:lib/type     :metadata/field
-                :name         "PRICE"
-                :display_name "Price"
-                :id           (meta/id :venues :price)
-                :table_id     (meta/id :venues)
-                :base_type    :type/Integer}
-               {:lib/type     :metadata/field
-                :name         "ID"
-                :display_name "ID"
-                :id           (meta/id :categories :id)
-                :table_id     (meta/id :categories)
-                :base_type    :type/BigInteger}
-               {:lib/type     :metadata/field
-                :name         "NAME"
-                :display_name "Name"
-                :id           (meta/id :categories :name)
-                :table_id     (meta/id :categories)
-                :base_type    :type/Text}]
+      (is (=? [{:lib/type                 :metadata/field
+                :name                     "ID"
+                :display_name             "ID"
+                :id                       (meta/id :venues :id)
+                :table_id                 (meta/id :venues)
+                :base_type                :type/BigInteger
+                :lib/source-column-alias  "ID"
+                :lib/desired-column-alias "ID"}
+               {:lib/type                 :metadata/field
+                :name                     "NAME"
+                :display_name             "Name"
+                :id                       (meta/id :venues :name)
+                :table_id                 (meta/id :venues)
+                :base_type                :type/Text
+                :lib/source-column-alias  "NAME"
+                :lib/desired-column-alias "NAME"}
+               {:lib/type                 :metadata/field
+                :name                     "CATEGORY_ID"
+                :display_name             "Category ID"
+                :id                       (meta/id :venues :category-id)
+                :table_id                 (meta/id :venues)
+                :lib/source-column-alias  "CATEGORY_ID"
+                :lib/desired-column-alias "CATEGORY_ID"}
+               {:lib/type                 :metadata/field
+                :name                     "LATITUDE"
+                :display_name             "Latitude"
+                :id                       (meta/id :venues :latitude)
+                :table_id                 (meta/id :venues)
+                :base_type                :type/Float
+                :lib/source-column-alias  "LATITUDE"
+                :lib/desired-column-alias "LATITUDE"}
+               {:lib/type                 :metadata/field
+                :name                     "LONGITUDE"
+                :display_name             "Longitude"
+                :id                       (meta/id :venues :longitude)
+                :table_id                 (meta/id :venues)
+                :base_type                :type/Float
+                :lib/source-column-alias  "LONGITUDE"
+                :lib/desired-column-alias "LONGITUDE"}
+               {:lib/type                 :metadata/field
+                :name                     "PRICE"
+                :display_name             "Price"
+                :id                       (meta/id :venues :price)
+                :table_id                 (meta/id :venues)
+                :base_type                :type/Integer
+                :lib/source-column-alias  "PRICE"
+                :lib/desired-column-alias "PRICE"}
+               {:lib/type                 :metadata/field
+                :name                     "ID"
+                :display_name             "ID"
+                :id                       (meta/id :categories :id)
+                :table_id                 (meta/id :categories)
+                :base_type                :type/BigInteger
+                :lib/source-column-alias  "ID"
+                :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__ID"}
+               {:lib/type                 :metadata/field
+                :name                     "NAME"
+                :display_name             "Name"
+                :id                       (meta/id :categories :name)
+                :table_id                 (meta/id :categories)
+                :base_type                :type/Text
+                :lib/source-column-alias  "NAME"
+                :lib/desired-column-alias "CATEGORIES__via__CATEGORY_ID__NAME"}]
               (lib/orderable-columns query))))))
 
 (deftest ^:parallel orderable-expressions-test
@@ -336,32 +352,83 @@
                     [:field {:lib/uuid string? :base-type :type/Text} (meta/id :venues :name)]]]
                   (lib/order-bys query'))))))))
 
+(deftest ^:parallel orderable-columns-with-join-test
+  (is (=? [{:name                     "ID"
+            :lib/source-column-alias  "ID"
+            :lib/desired-column-alias "ID"
+            :lib/source               :source/table-defaults}
+           {:name                     "NAME"
+            :lib/source-column-alias  "NAME"
+            :lib/desired-column-alias "NAME"
+            :lib/source               :source/table-defaults}
+           {:name                     "CATEGORY_ID"
+            :lib/source-column-alias  "CATEGORY_ID"
+            :lib/desired-column-alias "CATEGORY_ID"
+            :lib/source               :source/table-defaults}
+           {:name                     "LATITUDE"
+            :lib/source-column-alias  "LATITUDE"
+            :lib/desired-column-alias "LATITUDE"
+            :lib/source               :source/table-defaults}
+           {:name                     "LONGITUDE"
+            :lib/source-column-alias  "LONGITUDE"
+            :lib/desired-column-alias "LONGITUDE"
+            :lib/source               :source/table-defaults}
+           {:name                     "PRICE"
+            :lib/source-column-alias  "PRICE"
+            :lib/desired-column-alias "PRICE"
+            :lib/source               :source/table-defaults}
+           {:name                     "ID"
+            :lib/source-column-alias  "ID"
+            :lib/desired-column-alias "Cat__ID"
+            :lib/source               :source/joins}
+           {:name                     "NAME"
+            :lib/source-column-alias  "NAME"
+            :lib/desired-column-alias "Cat__NAME"
+            :lib/source               :source/joins}]
+          (-> (lib/query-for-table-name meta/metadata-provider "VENUES")
+              (lib/join (-> (lib/join-clause
+                             (meta/table-metadata :categories)
+                             (lib/=
+                              (lib/field "VENUES" "CATEGORY_ID")
+                              (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")))
+                            (lib/with-join-alias "Cat")
+                            (lib/with-join-fields :all)))
+              (lib/fields [(lib/field "VENUES" "ID")
+                           (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")])
+              (lib/orderable-columns)))))
+
 (deftest ^:parallel order-bys-with-duplicate-column-names-test
   (testing "Order by stuff should work with two different columns named ID (#29702)"
-    (is (=? [{:id             (meta/id :venues :id)
-              :name           "ID"
-              :lib/source     :source/previous-stage
-              :lib/type       :metadata/field
-              :base_type      :type/BigInteger
-              :effective_type :type/BigInteger
-              :display_name   "ID"
-              :table_id       (meta/id :venues)}
-             {:id             (meta/id :categories :id)
-              :name           "ID_2"
-              :lib/source     :source/previous-stage
-              :lib/type       :metadata/field
-              :base_type      :type/BigInteger
-              :effective_type :type/BigInteger
-              :display_name   "ID"
-              :table_id       (meta/id :categories)}]
+    (is (=? [{:id                       (meta/id :venues :id)
+              :name                     "ID"
+              :lib/source               :source/previous-stage
+              :lib/type                 :metadata/field
+              :base_type                :type/BigInteger
+              :effective_type           :type/BigInteger
+              :display_name             "ID"
+              :table_id                 (meta/id :venues)
+              :lib/source-column-alias  "ID"
+              :lib/desired-column-alias "ID"}
+             {:id                       (meta/id :categories :id)
+              :name                     "ID"
+              :lib/source               :source/previous-stage
+              :lib/type                 :metadata/field
+              :base_type                :type/BigInteger
+              :effective_type           :type/BigInteger
+              :display_name             "Categories → ID"
+              :table_id                 (meta/id :categories)
+              :lib/source-column-alias  "Cat__ID"
+              :lib/desired-column-alias "Cat__ID"}]
             (-> (lib/query-for-table-name meta/metadata-provider "VENUES")
                 (lib/join (-> (lib/join-clause
                                (meta/table-metadata :categories)
                                (lib/=
                                 (lib/field "VENUES" "CATEGORY_ID")
-                                (lib/field "CATEGORIES" "ID")))
+                                (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")))
+                              (lib/with-join-alias "Cat")
                               (lib/with-join-fields :all)))
-                (lib/fields [(lib/field "VENUES" "ID")  (lib/field "CATEGORIES" "ID")])
+                (lib/fields [(lib/field "VENUES" "ID")
+                             (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")])
                 (lib/append-stage)
                 (lib/orderable-columns))))))
 
@@ -463,7 +530,7 @@
                                               {:source-field (meta/id :venues :category-id)}
                                               (meta/id :categories :name)]]]}]}
               query))
-      (is (= "Venues, Sorted by Name ascending"
+      (is (= "Venues, Sorted by Categories → Name ascending"
              (lib/describe-query query)))
       (is (=? [{:display_name "ID",          :lib/source :source/table-defaults}
                {:display_name "Name",        :lib/source :source/table-defaults}
@@ -504,17 +571,16 @@
   (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES")
                   (lib/expression "expr" (lib/absolute-datetime "2020" :month))
                   (lib/fields [(lib/field "VENUES" "ID")]))]
-    (is (= [{:id (meta/id :venues :id),          :name "ID",          :display_name "ID",          :lib/source :source/table-defaults}
-            {:id (meta/id :venues :name),        :name "NAME",        :display_name "Name",        :lib/source :source/table-defaults}
-            {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display_name "Category ID", :lib/source :source/table-defaults}
-            {:id (meta/id :venues :latitude),    :name "LATITUDE",    :display_name "Latitude",    :lib/source :source/table-defaults}
-            {:id (meta/id :venues :longitude),   :name "LONGITUDE",   :display_name "Longitude",   :lib/source :source/table-defaults}
-            {:id (meta/id :venues :price),       :name "PRICE",       :display_name "Price",       :lib/source :source/table-defaults}
-            {:name "expr", :display_name "expr", :lib/source :source/expressions}
-            {:id (meta/id :categories :id),   :name "ID",   :display_name "ID",   :lib/source :source/implicitly-joinable}
-            {:id (meta/id :categories :name), :name "NAME", :display_name "Name", :lib/source :source/implicitly-joinable}]
-           (map #(select-keys % [:id :name :display_name :lib/source])
-                (lib/orderable-columns query))))
+    (is (=? [{:id (meta/id :venues :id),          :name "ID",          :display_name "ID",          :lib/source :source/table-defaults}
+             {:id (meta/id :venues :name),        :name "NAME",        :display_name "Name",        :lib/source :source/table-defaults}
+             {:id (meta/id :venues :category-id), :name "CATEGORY_ID", :display_name "Category ID", :lib/source :source/table-defaults}
+             {:id (meta/id :venues :latitude),    :name "LATITUDE",    :display_name "Latitude",    :lib/source :source/table-defaults}
+             {:id (meta/id :venues :longitude),   :name "LONGITUDE",   :display_name "Longitude",   :lib/source :source/table-defaults}
+             {:id (meta/id :venues :price),       :name "PRICE",       :display_name "Price",       :lib/source :source/table-defaults}
+             {:name "expr", :display_name "expr", :lib/source :source/expressions}
+             {:id (meta/id :categories :id),   :name "ID",   :display_name "ID",   :lib/source :source/implicitly-joinable}
+             {:id (meta/id :categories :name), :name "NAME", :display_name "Name", :lib/source :source/implicitly-joinable}]
+            (lib/orderable-columns query)))
     (let [expr (m/find-first #(= (:name %) "expr") (lib/orderable-columns query))]
       (is (=? {:lib/type   :metadata/field
                :lib/source :source/expressions
diff --git a/test/metabase/lib/stage_test.cljc b/test/metabase/lib/stage_test.cljc
index 61a0edfd5f5c06fbe6e1348b01804f460df0e03b..b475fa578d2f5aaa4209bd99d139a56016845217 100644
--- a/test/metabase/lib/stage_test.cljc
+++ b/test/metabase/lib/stage_test.cljc
@@ -36,18 +36,22 @@
       (let [query (lib.tu/venues-query-with-last-stage
                    {:aggregation [[:*
                                    {}
-                                   0.9
+                                   0.8
                                    [:avg {} (lib.tu/field-clause :venues :price)]]
                                   [:*
                                    {}
                                    0.8
                                    [:avg {} (lib.tu/field-clause :venues :price)]]]})]
-        (is (=? [{:base_type    :type/Float
-                  :name         "0_9_times_avg_price"
-                  :display_name "0.9 × Average of Price"}
-                 {:base_type    :type/Float
-                  :name         "0_8_times_avg_price"
-                  :display_name "0.8 × Average of Price"}]
+        (is (=? [{:base_type                :type/Float
+                  :name                     "0_8_times_avg_PRICE"
+                  :display_name             "0.8 × Average of Price"
+                  :lib/source-column-alias  "0_8_times_avg_PRICE"
+                  :lib/desired-column-alias "0_8_times_avg_PRICE"}
+                 {:base_type                :type/Float
+                  :name                     "0_8_times_avg_PRICE"
+                  :display_name             "0.8 × Average of Price"
+                  :lib/source-column-alias  "0_8_times_avg_PRICE"
+                  :lib/desired-column-alias "0_8_times_avg_PRICE_2"}]
                 (lib.metadata.calculation/metadata query -1 query)))))))
 
 (deftest ^:parallel stage-display-name-card-source-query
@@ -81,9 +85,9 @@
                    :stages       [{:lib/type     :mbql.stage/mbql
                                    :lib/options  {:lib/uuid (str (random-uuid))}
                                    :source-table "card__1"}]}]
-        (is (= (for [col (:columns meta/results-metadata)]
-                 (assoc col :lib/source :source/card))
-               (lib.metadata.calculation/metadata query -1 query)))))))
+        (is (=? (for [col (:columns meta/results-metadata)]
+                  (assoc col :lib/source :source/card))
+                (lib.metadata.calculation/metadata query -1 query)))))))
 
 (deftest ^:parallel adding-and-removing-stages
   (let [query (lib/query-for-table-name meta/metadata-provider "VENUES")
@@ -147,16 +151,20 @@
                {:id (meta/id :venues :price),       :name "PRICE",       :lib/source :source/table-defaults}
                {:name "ID + 1", :lib/source :source/expressions}
                {:name "ID + 2", :lib/source :source/expressions}
-               {:id           (meta/id :categories :id)
-                :name         "ID_2"
-                :lib/source   :source/joins
-                :source_alias "Cat"
-                :display_name "Categories → ID"}
-               {:id           (meta/id :categories :name)
-                :name         "NAME_2"
-                :lib/source   :source/joins
-                :source_alias "Cat"
-                :display_name "Categories → Name"}]
+               {:id                       (meta/id :categories :id)
+                :name                     "ID"
+                :lib/source               :source/joins
+                :source_alias             "Cat"
+                :display_name             "Categories → ID"
+                :lib/source-column-alias  "ID"
+                :lib/desired-column-alias "Cat__ID"}
+               {:id                       (meta/id :categories :name)
+                :name                     "NAME"
+                :lib/source               :source/joins
+                :source_alias             "Cat"
+                :display_name             "Categories → Name"
+                :lib/source-column-alias  "NAME"
+                :lib/desired-column-alias "Cat__NAME"}]
               (lib.metadata.calculation/metadata query))))))
 
 (deftest ^:parallel metadata-with-fields-only-include-expressions-in-fields-test
@@ -185,3 +193,36 @@
             (let [[id-plus-1] (lib.metadata.calculation/metadata query')]
               (is (=? [:expression {:base-type :type/Integer, :effective-type :type/Integer} "ID + 1"]
                       (lib/ref id-plus-1))))))))))
+
+(deftest ^:parallel joins-source-and-desired-aliases-test
+  (let [query (-> (lib/query-for-table-name meta/metadata-provider "VENUES")
+                  (lib/join (-> (lib/join-clause
+                                 (meta/table-metadata :categories)
+                                 (lib/=
+                                  (lib/field "VENUES" "CATEGORY_ID")
+                                  (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")))
+                                (lib/with-join-alias "Cat")
+                                (lib/with-join-fields :all)))
+                  (lib/fields [(lib/field "VENUES" "ID")
+                               (lib/with-join-alias (lib/field "CATEGORIES" "ID") "Cat")]))]
+    (is (=? [{:name                     "ID"
+              :lib/source-column-alias  "ID"
+              :lib/desired-column-alias "ID"
+              :lib/source               :source/fields}
+             {:name                          "ID"
+              :lib/source-column-alias       "ID"
+              :lib/desired-column-alias      "Cat__ID"
+              :metabase.lib.field/join-alias "Cat"
+              :lib/source                    :source/fields}]
+            (lib.metadata.calculation/metadata query)))
+    (testing "Introduce a new stage"
+      (let [query' (lib/append-stage query)]
+        (is (=? [{:name                     "ID"
+                  :lib/source-column-alias  "ID"
+                  :lib/desired-column-alias "ID"
+                  :lib/source               :source/previous-stage}
+                 {:name                          "ID"
+                  :lib/source-column-alias       "Cat__ID"
+                  :lib/desired-column-alias      "Cat__ID"
+                  :lib/source                    :source/previous-stage}]
+                (lib.metadata.calculation/metadata query')))))))
diff --git a/test/metabase/lib/util_test.cljc b/test/metabase/lib/util_test.cljc
index a1554b85d51d8f432d3406bfa59519b21f73e283..e70d37a08f24ae01ce462b1dfd06ddded001b861 100644
--- a/test/metabase/lib/util_test.cljc
+++ b/test/metabase/lib/util_test.cljc
@@ -1,5 +1,6 @@
 (ns metabase.lib.util-test
   (:require
+   [clojure.string :as str]
    [clojure.test :refer [are deftest is testing]]
    [metabase.lib.test-metadata :as meta]
    [metabase.lib.util :as lib.util]
@@ -216,3 +217,92 @@
     ["a" "b"]         "a and b"
     ["a" "b" "c"]     "a, b, and c"
     ["a" "b" "c" "d"] "a, b, c, and d"))
+
+(deftest ^:parallel crc32-checksum-test
+  (are [s checksum] (= checksum
+                       (#'lib.util/crc32-checksum s))
+    "YMRZFRTHUBOUZHPTZGPD" "2694651f"
+    "MEBRXTJEPWOJJXVZIPDA" "048132cb"
+    "UIOJOTPGUIROVRJYAFPO" "0085cacb"
+    "UCVEWTGNBDANGMZPGNQC" "000e32a0"
+    "ZAFVKSVXQKJNGANBQZMX" "0000d5b8"
+    "NCTFDMQNUEQLJUMAGSYG" "000000ea"
+    "YHQJXDIXGGQTSARGOQZZ" "000000c1"
+    "0601246074"           "00000001"
+    "2915035893"           "00000000"))
+
+(deftest ^:parallel truncate-string-to-byte-count-test
+  (letfn [(truncate-string-to-byte-count [s byte-length]
+            (let [truncated (#'lib.util/truncate-string-to-byte-count s byte-length)]
+              (is (<= (#'lib.util/string-byte-count truncated) byte-length))
+              (is (str/starts-with? s truncated))
+              truncated))]
+    (doseq [[s max-length->expected] {"12345"
+                                      {1  "1"
+                                       2  "12"
+                                       3  "123"
+                                       4  "1234"
+                                       5  "12345"
+                                       6  "12345"
+                                       10 "12345"}
+
+                                      "가나다라"
+                                      {1  ""
+                                       2  ""
+                                       3  "ê°€"
+                                       4  "ê°€"
+                                       5  "ê°€"
+                                       6  "가나"
+                                       7  "가나"
+                                       8  "가나"
+                                       9  "가나다"
+                                       10 "가나다"
+                                       11 "가나다"
+                                       12 "가나다라"
+                                       13 "가나다라"
+                                       15 "가나다라"
+                                       20 "가나다라"}}
+            [max-length expected] max-length->expected]
+      (testing (pr-str (list `lib.util/truncate-string-to-byte-count s max-length))
+        (is (= expected
+               (truncate-string-to-byte-count s max-length)))))))
+
+(deftest ^:parallel truncate-alias-test
+  (letfn [(truncate-alias [s max-bytes]
+            (let [truncated (lib.util/truncate-alias s max-bytes)]
+              (is (<= (#'lib.util/string-byte-count truncated) max-bytes))
+              truncated))]
+    (doseq [[s max-bytes->expected] { ;; 20-character plain ASCII string
+                                     "01234567890123456789"
+                                     {12 "012_fc89bad5"
+                                      15 "012345_fc89bad5"
+                                      20 "01234567890123456789"}
+
+                                     ;; two strings that only differ after the point they get truncated
+                                     "0123456789abcde" {12 "012_1629bb92"}
+                                     "0123456789abcdE" {12 "012_2d479b5a"}
+
+                                     ;; Unicode string: 14 characters, 42 bytes
+                                     "가나다라마바사아자차카타파하"
+                                     {12 "ê°€_b9c95392"
+                                      13 "ê°€_b9c95392"
+                                      14 "ê°€_b9c95392"
+                                      15 "가나_b9c95392"
+                                      20 "가나다_b9c95392"
+                                      30 "가나다라마바사_b9c95392"
+                                      40 "가나다라마바사아자차_b9c95392"
+                                      50 "가나다라마바사아자차카타파하"}
+
+                                     ;; Mixed string: 17 characters, 33 bytes
+                                     "a가b나c다d라e마f바g사h아i"
+                                     {12 "a_99a0fe0c"
+                                      13 "aê°€_99a0fe0c"
+                                      14 "aê°€b_99a0fe0c"
+                                      15 "aê°€b_99a0fe0c"
+                                      20 "a가b나c_99a0fe0c"
+                                      30 "a가b나c다d라e마f_99a0fe0c"
+                                      40 "a가b나c다d라e마f바g사h아i"}}
+            [max-bytes expected] max-bytes->expected]
+      (testing (pr-str (list `lib.util/truncate-alias s max-bytes))
+        (is (= expected
+               (truncate-alias s max-bytes)))))))
diff --git a/test/metabase/models/metric_test.clj b/test/metabase/models/metric_test.clj
index b4c37b26869871bed7f1a2dd6831b56fb943d794..13cf23a62881a9745811bd8121652bc3f530a825 100644
--- a/test/metabase/models/metric_test.clj
+++ b/test/metabase/models/metric_test.clj
@@ -144,7 +144,7 @@
                                                                  :filter      [:and
                                                                                [:= $price 4]
                                                                                [:segment segment-id]]}))}]
-    (is (= "Venues, Sum of Name, Filtered by Price equals 4 and Checkins with ID = 1"
+    (is (= "Venues, Sum of Categories → Name, Filtered by Price equals 4 and Checkins with ID = 1"
            (:definition_description (t2/hydrate metric :definition_description))))))
 
 (deftest definition-description-missing-source-table-test
@@ -154,7 +154,7 @@
                                             :definition (mt/$ids venues
                                                           {:aggregation [[:sum $category_id->categories.name]]
                                                            :filter      [:= $price 4]})}]
-      (is (= "Venues, Sum of Name, Filtered by Price equals 4"
+      (is (= "Venues, Sum of Categories → Name, Filtered by Price equals 4"
              (:definition_description (t2/hydrate metric :definition_description)))))))
 
 (deftest definition-description-invalid-query-test
diff --git a/test/metabase/models/segment_test.clj b/test/metabase/models/segment_test.clj
index b9eeae877cbc997debaef71a6b4b9a5668342bb7..2de2445c9e4c45d50d6cfa7f31f9f0db894b5a94 100644
--- a/test/metabase/models/segment_test.clj
+++ b/test/metabase/models/segment_test.clj
@@ -131,7 +131,7 @@
                                                                    [:and
                                                                     [:= $price 4]
                                                                     [:= $category_id->categories.name "BBQ"]]}))}]
-    (is (= "Filtered by Price equals 4 and Name equals \"BBQ\""
+    (is (= "Filtered by Price equals 4 and Categories → Name equals \"BBQ\""
            (:definition_description (t2/hydrate segment :definition_description))))
     (testing "Segments that reference other Segments (inception)"
       (t2.with-temp/with-temp [Segment segment-2 {:name "Segment 2"
diff --git a/test/metabase/query_processor_test/explicit_joins_test.clj b/test/metabase/query_processor_test/explicit_joins_test.clj
index 166598c4c673fa83efafb1fb51dd2a86ed809b41..0b91aecfc9577fd2b8654463502de9c072690917 100644
--- a/test/metabase/query_processor_test/explicit_joins_test.clj
+++ b/test/metabase/query_processor_test/explicit_joins_test.clj
@@ -822,7 +822,7 @@
   (str/join (for [i (range length)]
               (nth charset (mod i (count charset))))))
 
-(deftest very-long-join-name-test
+(deftest ^:parallel very-long-join-name-test
   (mt/test-drivers (mt/normal-drivers-with-feature :left-join)
     (testing "Drivers should work correctly even if joins have REALLLLLLY long names (#15978)"
       (doseq [[charset-name charset] charsets
diff --git a/yarn.lock b/yarn.lock
index ab03bda00e527ad8d5ba641e2402db5f134b6b5a..83b5c06bd148d0d5e8e6f6e2c63298d0867dd449 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9354,6 +9354,11 @@ cpy@^8.1.2:
     p-filter "^2.1.0"
     p-map "^3.0.0"
 
+crc-32@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+  integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+
 crc-32@~1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208"