diff --git a/project.clj b/project.clj
index 1223baf874495873dd8085dadbd7225d30cb4ede..61cae437805ce044bee4d0657ae2ad5bec4eb3bf 100644
--- a/project.clj
+++ b/project.clj
@@ -9,7 +9,6 @@
             "generate-sample-dataset" ["with-profile" "+generate-sample-dataset" "run"]}
   :dependencies [[org.clojure/clojure "1.7.0"]
                  [org.clojure/core.async "0.1.346.0-17112a-alpha"]
-                 [org.clojure/core.logic "0.8.10"]
                  [org.clojure/core.match "0.3.0-alpha4"]              ; optimized pattern matching library for Clojure
                  [org.clojure/core.memoize "0.5.7"]                   ; needed by core.match; has useful FIFO, LRU, etc. caching mechanisms
                  [org.clojure/data.csv "0.1.3"]                       ; CSV parsing / generation
@@ -17,7 +16,6 @@
                  [org.clojure/java.jdbc "0.4.2"]                      ; basic jdbc access from clojure
                  [org.clojure/math.numeric-tower "0.0.4"]             ; math functions like `ceil`
                  [org.clojure/tools.logging "0.3.1"]                  ; logging framework
-                 [org.clojure/tools.macro "0.1.5"]                    ; tools for writing macros
                  [org.clojure/tools.namespace "0.2.10"]
                  [amalloy/ring-gzip-middleware "0.1.3"]               ; Ring middleware to GZIP responses if client can handle it
                  [cheshire "5.5.0"]                                   ; fast JSON encoding (used by Ring JSON middleware)
diff --git a/src/metabase/driver/query_processor/annotate.clj b/src/metabase/driver/query_processor/annotate.clj
index 46e2657ee45e41164ae0fabd1d8fd90c62dace39..e0e55ea2a2a684108987e06cd40919bc34ef9746 100644
--- a/src/metabase/driver/query_processor/annotate.clj
+++ b/src/metabase/driver/query_processor/annotate.clj
@@ -1,17 +1,14 @@
 (ns metabase.driver.query-processor.annotate
   (:refer-clojure :exclude [==])
-  (:require [clojure.core.logic :refer :all]
-            (clojure.core.logic [arithmetic :as ar]
-                                [fd :as fd])
-            [clojure.tools.macro :refer [macrolet]]
-            (clojure [set :as set]
+  (:require (clojure [set :as set]
                      [string :as s])
+            [clojure.tools.logging :as log]
+            [medley.core :as m]
             [metabase.db :refer [sel]]
             [metabase.driver.query-processor.interface :as i]
             (metabase.models [field :refer [Field], :as field]
                              [foreign-key :refer [ForeignKey]])
-            [metabase.util :as u]
-            [metabase.util.logic :refer :all]))
+            [metabase.util :as u]))
 
 ;; Fields should be returned in the following order:
 ;; 1.  Breakout Fields
@@ -35,246 +32,219 @@
 ;;         When two Fields have the same :position and :special_type "group", fall back to sorting Fields alphabetically by name.
 ;;         This is arbitrary, but it makes the QP deterministic by keeping the results in a consistent order, which makes it testable.
 
-;;; # ---------------------------------------- FIELD COLLECTION  ----------------------------------------
-
-;; Walk the expanded query and collect the fields found therein. Associate some additional info to each that we'll pass to core.logic so it knows
-;; how to order the results
-
-(defn- field-qualify-name [field]
-  (assoc field :field-name (keyword (apply str (->> (rest (i/qualified-name-components field))
-                                                    (interpose "."))))))
-
-(defn- flatten-collect-fields [form]
-  (let [fields (transient [])]
-    (clojure.walk/prewalk (fn [f]
-                            (cond
-                              (= (type f) metabase.driver.query_processor.interface.Field)
-                              (do
-                                (conj! fields f)
-                                ;; HACK !!!
-                                ;; Nested Mongo fields come back inside of their parent when you specify them in the fields clause
-                                ;; e.g. (Q fields venue...name) will return rows like {:venue {:name "Kyle's Low-Carb Grill"}}
-                                ;; Until we fix this the right way we'll just include the parent Field in the :query-fields list so the pattern
-                                ;; matching works correctly.
-                                ;; (This hack was part of the old annotation code too, it just sticks out better because it's no longer hidden amongst the others)
-                                (when (:parent f)
-                                  (conj! fields (:parent f))))
-
-                              ;; For a DateTimeField we'll flatten it back into regular Field but include the :unit info for the frontend
-                              ;; Recurse so this fn will handle the resulting Field normally
-                              (= (type f) metabase.driver.query_processor.interface.DateTimeField)
-                              (recur (assoc (:field f)
-                                            :unit (:unit f)))
-
-                              :else f))
-                          form)
-    (->> (persistent! fields)
-         distinct
-         (map field-qualify-name)
-         (mapv (u/rpartial dissoc :parent :parent-id :table-name)))))
-
-(defn- flatten-collect-ids-domain [form]
-  (apply fd/domain (sort (map :field-id (flatten-collect-fields form)))))
-
-
-;;; # ---------------------------------------- COLUMN RESOLUTION & ORDERING (CORE.LOGIC)  ----------------------------------------
-
-;; Use core.logic to determine the appropriate ordering / result Fields
-
-(defn- field-name° [field field-name]
-  (featurec field {:field-name field-name}))
-
-(defn- make-field-in° [items]
-  (if-not (seq items)
-    (constantly fail)
-    (let [ids-domain (flatten-collect-ids-domain items)]
-      (fn [field]
-        (fresh [id]
-          (featurec field {:field-id id})
-          (fd/in id ids-domain))))))
-
-(defn- breakout-field° [{:keys [breakout]}]
-  (make-field-in° breakout))
-
-(defn- explicit-fields-field° [{:keys [fields-is-implicit fields], :as query}]
-  (if fields-is-implicit (constantly fail)
-      (make-field-in° fields)))
-
-(defn- aggregate-field° [{{ag-type :aggregation-type, ag-field :field} :aggregation}]
-  (if-not (contains? #{:avg :count :distinct :stddev :sum} ag-type)
-    (constantly fail)
-    (let [ag-field (if (contains? #{:count :distinct} ag-type)
-                     {:base-type    :IntegerField
-                      :field-name   :count
-                      :field-display-name "count"
-                      :special-type :number}
-                     (-> ag-field
-                         (select-keys [:base-type :special-type])
-                         (assoc :field-name (if (= ag-type :distinct) :count
-                                                ag-type))
-                         (assoc :field-display-name (if (= ag-type :distinct) "count"
-                                                        (name ag-type)))))]
-      (fn [out]
-        (trace-lvars "*" out)
-        (== out ag-field)))))
-
-(defn- unknown-field° [field-name out]
-  (all
-   (== out {:base-type    :UnknownField
-            :special-type nil
-            :field-name   field-name
-            :field-display-name field-name})
-   (trace-lvars "UNKNOWN FIELD - NOT PRESENT IN EXPANDED QUERY (!)" out)))
-
-(defn- field° [query]
-  (let [ag-field°     (aggregate-field° query)
-        normal-field° (let [field-name->field (let [fields (flatten-collect-fields query)]
-                                                (zipmap (map :field-name fields) fields))]
-                        (fn [field-name out]
-                          (if-let [field (field-name->field field-name)]
-                            (== out field)
-                            fail)))]
-    (fn [field-name field]
-      (conda
-        ((normal-field° field-name field))
-        ((ag-field° field))))))
-
-(def ^:const ^:private field-groups
-  {:breakout        0
-   :aggregation     1
-   :explicit-fields 2
-   :other           3})
-
-(defn- field-group° [query]
-  (let [breakout° (breakout-field° query)
-        agg°      (aggregate-field° query)
-        xfields°  (explicit-fields-field° query)]
-    (fn [field out]
-      (conda
-        ((breakout° field) (== out (field-groups :breakout)))
-        ((agg° field)      (== out (field-groups :aggregation)))
-        ((xfields° field)  (== out (field-groups :explicit-fields)))
-        (s#                (== out (field-groups :other)))))))
-
-(defn- field-position° [field out]
-  (featurec field {:position out}))
-
-(def ^:const ^:private special-type-groups
-  {:id    0
-   :name  1
-   :other 2})
-
-(defn- special-type-group° [field out]
-  (conda
-   ((featurec field {:special-type :id})   (== out (special-type-groups :id)))
-   ((featurec field {:special-type :name}) (== out (special-type-groups :name)))
-   (s#                                     (== out (special-type-groups :other)))))
-
-(defn- field-name< [query]
-  (fn [f1 f2]
-    (fresh [name-1 name-2]
-      (field-name° f1 name-1)
-      (field-name° f2 name-2)
-      (matches-seq-order° name-1 name-2 (:result-keys query)))))
-
-(defn- clause-position< [query]
-  (let [group°          (field-group° query)
-        breakout-fields (flatten-collect-fields (:breakout query))
-        fields-fields   (flatten-collect-fields (:fields query))]
-    (fn [f1 f2]
-      (conda
-       ((group° f1 (field-groups :breakout))        (matches-seq-order° f1 f2 breakout-fields))
-       ((group° f1 (field-groups :explicit-fields)) (matches-seq-order° f1 f2 fields-fields))))))
-
-(defn- fields-sorted° [query]
-  (let [group°      (field-group° query)
-        name<       (field-name< query)
-        clause-pos< (clause-position< query)]
-    (fn [f1 f2]
-      (macrolet [(<-or-== [f & ==-clauses] `(conda
-                                              ((fresh [v#]
-                                                 (~f ~'f1 v#)
-                                                 (~f ~'f2 v#)) ~@==-clauses)
-                                              ((fresh [v1# v2#]
-                                                 (~f ~'f1 v1#)
-                                                 (~f ~'f2 v2#)
-                                                 (ar/< v1# v2#)) ~'s#)))]
-        (<-or-== group°
-          (<-or-== field-position°
-            (conda
-              ((group° f1 (field-groups :other)) (<-or-== special-type-group°
-                                                   (name< f1 f2)))
-              ((clause-pos< f1 f2)))))))))
-
-(defn- resolve+order-cols [{:keys [result-keys], :as query}]
-  (when (seq result-keys)
-    (first (let [fields       (vec (lvars (count result-keys)))
-                 known-field° (field° query)]
-             (run 1 [q]
-               (everyg (fn [[result-key field]]
-                         (conda
-                           ((known-field°   result-key field))
-                           ((unknown-field° result-key field))))
-                       (zipmap result-keys fields))
-               (sorted-permutation° (fields-sorted° query) fields q))))))
-
-
-;;; # ---------------------------------------- COLUMN DETAILS  ----------------------------------------
-
-;; Format the results in the way the front-end expects.
-
-(defn- format-col [col]
-  (merge {:description nil
-          :id          nil
-          :table_id    nil}
-         (-> col
-             (set/rename-keys  {:base-type          :base_type
-                                :field-id           :id
-                                :field-name         :name
-                                :field-display-name :display_name
-                                :schema-name        :schema_name
-                                :special-type       :special_type
-                                :preview-display    :preview_display
-                                :table-id           :table_id})
-             (dissoc :position))))
-
-(defn- add-fields-extra-info
+;;; ## Field Resolution
+
+(defn- collect-fields
+  "Return a sequence of all the `Fields` inside THIS, recursing as needed for collections.
+   For maps, add or `conj` to property `:path`, recording the keypath used to reach each `Field.`
+
+     (collect-fields {:name \"id\", ...})     -> [{:name \"id\", ...}]
+     (collect-fields [{:name \"id\", ...}])   -> [{:name \"id\", ...}]
+     (collect-fields {:a {:name \"id\", ...}) -> [{:name \"id\", :path [:a], ...}]"
+  [this]
+  {:post [(every? (partial instance? metabase.driver.query_processor.interface.Field) %)]}
+  (condp instance? this
+    ;; For a DateTimeField we'll flatten it back into regular Field but include the :unit info for the frontend.
+    ;; Recurse so it is otherwise handled normally
+    metabase.driver.query_processor.interface.DateTimeField
+    (let [{:keys [field unit]} this]
+      (collect-fields (assoc field :unit unit)))
+
+    metabase.driver.query_processor.interface.Field
+    (if-let [parent (:parent this)]
+      ;; Nested Mongo fields come back inside of their parent when you specify them in the fields clause
+      ;; e.g. (Q fields venue...name) will return rows like {:venue {:name "Kyle's Low-Carb Grill"}}
+      ;; Until we fix this the right way we'll just include the parent Field in the :query-fields list so the pattern
+      ;; matching works correctly.
+      [this parent]
+      [this])
+
+    clojure.lang.IPersistentMap
+    (for [[k v] (seq this)
+          field (collect-fields v)
+          :when field]
+      (assoc field :source k))
+
+    clojure.lang.Sequential
+    (for [[i field] (m/indexed (mapcat collect-fields this))]
+      (assoc field :clause-position i))
+
+    nil))
+
+(defn- qualify-field-name
+  "Update the `field-name` to reflect the name we expect to see coming back from the query.
+   (This is for handling Mongo nested Fields, I think (?))"
+  [field]
+  {:post [(keyword? (:field-name %))]}
+  (assoc field :field-name (->> (rest (i/qualified-name-components field))
+                                (interpose ".")
+                                (apply str)
+                                keyword)))
+
+(defn- add-aggregate-field-if-needed
+  "Add a Field containing information about an aggregate column such as `:count` or `:distinct` if needed."
+  [{{ag-type :aggregation-type, ag-field :field, :as ag} :aggregation} fields]
+  (if (or (not ag-type)
+          (= ag-type :rows))
+    fields
+    (conj fields (merge {:source :aggregation}
+                        (if (contains? #{:count :distinct} ag-type)
+                          {:base-type          :IntegerField
+                           :field-name         :count
+                           :field-display-name :count
+                           :special-type       :number}
+                          (merge (select-keys ag-field [:base-type :special-type])
+                                 {:field-name         ag-type
+                                  :field-display-name ag-type}))))))
+
+(defn- add-unknown-fields-if-needed
+  "When create info maps for any fields we didn't expect to come back from the query.
+   Ideally, this should never happen, but on the off chance it does we still want to return it in the results."
+  [actual-keys fields]
+  {:pre [(set? actual-keys)
+         (every? keyword? actual-keys)]}
+  (let [expected-keys (set (map :field-name fields))
+        _             (assert (every? keyword? expected-keys))
+        missing-keys  (set/difference actual-keys expected-keys)]
+    (when (seq missing-keys)
+      (log/error (u/format-color 'red "Unknown fields - returned by results but not present in expanded query: %s\nExpected: %s\nActual: %s"
+                   missing-keys expected-keys actual-keys)))
+    (concat fields (for [k missing-keys]
+                     {:base-type          :UnknownField
+                      :special-type       nil
+                      :field-name         k
+                      :field-display-name k}))))
+
+
+;;; ## Field Sorting
+
+;; We sort Fields with a "importance" vector like [source-importance position special-type-importance name]
+
+(defn- source-importance-fn
+  "Create a function to return a importance for FIELD based on its source clause in the query.
+   e.g. if a Field comes from a `:breakout` clause, we should return that column first in the results."
+  [{:keys [fields-is-implicit]}]
+  (fn [{:keys [source]}]
+    (or (when (= source :breakout)
+          :0-breakout)
+        (when (= source :aggregation)
+          :1-aggregation)
+        (when-not fields-is-implicit
+          (when (= source :fields)
+            :2-fields))
+        :3-other)))
+
+(defn- special-type-importance
+  "Return a importance for FIELD based on the relative importance of its `:special-type`.
+   i.e. a Field with special type `:id` should be sorted ahead of all other Fields in the results."
+  [{:keys [special-type]}]
+  (condp = special-type
+    :id   :0-id
+    :name :1-name
+          :2-other))
+
+(defn- field-importance-fn
+  "Create a function to return an \"importance\" vector for use in sorting FIELD."
+  [query]
+  (let [source-importance (source-importance-fn query)]
+    (fn [{:keys [position clause-position field-name source], :as field}]
+      [(source-importance field)
+       (or position
+           (when (= source :fields)
+             clause-position)
+           Integer/MAX_VALUE)
+       (special-type-importance field)
+       field-name])))
+
+(defn- sort-fields
+  "Sort FIELDS by their \"importance\" vectors."
+  [query fields]
+  (let [field-importance (field-importance-fn query)]
+    (log/debug (u/format-color 'yellow "Sorted fields:\n%s" (u/pprint-to-str (sort (map field-importance fields)))))
+    (sort-by field-importance fields)))
+
+(defn- convert-field-to-expected-format
+  "Rename keys, provide default values, etc. for FIELD so it is in the format expected by the frontend."
+  [field]
+  {:pre  [field]
+   :post [(keyword? (:name %))]}
+  (let [defaults {:description nil
+                  :id          nil
+                  :table_id    nil}]
+    (-> (merge defaults field)
+        (update :field-display-name name)
+        (set/rename-keys  {:base-type          :base_type
+                           :field-id           :id
+                           :field-name         :name
+                           :field-display-name :display_name
+                           :schema-name        :schema_name
+                           :special-type       :special_type
+                           :preview-display    :preview_display
+                           :table-id           :table_id})
+        (dissoc :position :clause-position :source :parent :parent-id :table-name))))
+
+(defn- fk-field->dest-fn
+  "Fetch fk info and return a function that returns the destination Field of a given Field."
+  ([fields]
+   (or (fk-field->dest-fn fields (for [{:keys [special_type id]} fields
+                                       :when (= special_type :fk)]
+                                   id))
+       (constantly nil)))
+  ;; Fetch the ForeignKey objects whose origin is in the returned Fields, create a map of origin-field-id->destination-field-id
+  ([fields fk-ids]
+   (when (seq fk-ids)
+     (fk-field->dest-fn fields fk-ids (sel :many :field->field [ForeignKey :origin_id :destination_id]
+                                           :origin_id      [in fk-ids]
+                                           :destination_id [not= nil]))))
+  ;; Fetch the destination Fields referenced by the ForeignKeys
+  ([fields fk-ids id->dest-id]
+   (when (seq (vals id->dest-id))
+     (fk-field->dest-fn fields fk-ids id->dest-id (sel :many :id->fields [Field :id :name :display_name :table_id :description :base_type :special_type :preview_display]
+                                                       :id [in (vals id->dest-id)]))))
+  ;; Return a function that will return the corresponding destination Field for a given Field
+  ([fields fk-ids id->dest-id dest-id->field]
+   (fn [{:keys [id]}]
+     (some-> id id->dest-id dest-id->field))))
+
+(defn- add-extra-info-to-fk-fields
   "Add `:extra_info` about `ForeignKeys` to `Fields` whose `special_type` is `:fk`."
   [fields]
-  ;; Get a sequence of add Field IDs that have a :special_type of FK
-  (let [fk-field-ids            (->> fields
-                                     (filter #(= (:special_type %) :fk))
-                                     (map :id)
-                                     (filter identity))
-        ;; Look up the Foreign keys info if applicable.
-        ;; Build a map of FK Field IDs -> Destination Field IDs
-        field-id->dest-field-id (when (seq fk-field-ids)
-                                  (sel :many :field->field [ForeignKey :origin_id :destination_id], :origin_id [in fk-field-ids], :destination_id [not= nil]))
-
-        ;; Build a map of Destination Field IDs -> Destination Fields
-        dest-field-id->field    (when (and (seq fk-field-ids)
-                                           (seq (vals field-id->dest-field-id)))
-                                  (sel :many :id->fields [Field :id :name :display_name :table_id :description :base_type :special_type :preview_display], :id [in (vals field-id->dest-field-id)]))]
-
-    ;; Add the :extra_info + :target to every Field. For non-FK Fields, these are just {} and nil, respectively.
-    (vec (for [{field-id :id, :as field} fields]
-           (let [dest-field (when (seq fk-field-ids)
-                              (some->> field-id
-                                       field-id->dest-field-id
-                                       dest-field-id->field))]
-             (assoc field
-                    :target     dest-field
-                    :extra_info (if-not dest-field {}
-                                        {:target_table_id (:table_id dest-field)})))))))
-
-(defn post-annotate [qp]
+  (let [field->dest (fk-field->dest-fn fields)]
+    (for [field fields]
+      (let [{:keys [table_id], :as dest-field} (field->dest field)]
+        (assoc field
+               :target     dest-field
+               :extra_info (if table_id {:target_table_id table_id} {}))))))
+
+(defn- resolve-sort-and-format-columns
+  "Collect the Fields referenced in QUERY, sort them according to the rules at the top
+   of this page, format them as expected by the frontend, and return the results."
+  [query result-keys]
+  {:pre [(set? result-keys)]}
+  (when (seq result-keys)
+    (->> (collect-fields query)
+         (map qualify-field-name)
+         (add-aggregate-field-if-needed query)
+         (map (u/rpartial update :field-name keyword))
+         (add-unknown-fields-if-needed result-keys)
+         (sort-fields query)
+         (map convert-field-to-expected-format)
+         (filter (comp (partial contains? result-keys) :name))
+         (m/distinct-by :name)
+         add-extra-info-to-fk-fields)))
+
+(defn post-annotate
+  "QP middleware that runs directly after the the query is ran. This stage:
+
+  1.  Sorts the results according to the rules at the top of this page
+  2.  Resolves the Fields returned in the results and adds information like `:columns` and `:cols`
+      expected by the frontend."
+  [qp]
   (fn [query]
-    (let [results (qp query)
-          cols    (->> (assoc (:query query) :result-keys (vec (sort (keys (first results)))))
-                       resolve+order-cols
-                       (map format-col)
-                       add-fields-extra-info)
-          columns (map :name cols)]
+    (let [results     (qp query)
+          result-keys (set (keys (first results)))
+          cols        (resolve-sort-and-format-columns (:query query) result-keys)
+          columns     (mapv :name cols)]
       {:cols    (vec (for [col cols]
                        (update col :name name)))
        :columns (mapv name columns)
diff --git a/src/metabase/util.clj b/src/metabase/util.clj
index f32855f8dbbb7a9863b565516a4ccf8cdb2f8b8d..74f4ebcf9bc71be8a93ff8c307b128805d095c41 100644
--- a/src/metabase/util.clj
+++ b/src/metabase/util.clj
@@ -3,10 +3,10 @@
   (:require [clojure.java.jdbc :as jdbc]
             [clojure.pprint :refer [pprint]]
             [clojure.tools.logging :as log]
-            [colorize.core :as color]
-            [medley.core :as m]
+            [clj-time.coerce :as coerce]
             [clj-time.format :as time]
-            [clj-time.coerce :as coerce])
+            [colorize.core :as color]
+            [medley.core :as m])
   (:import (java.net Socket
                      InetSocketAddress
                      InetAddress)
@@ -237,10 +237,13 @@
 
 (defn format-color
   "Like `format`, but uses a function in `colorize.core` to colorize the output.
-   COLOR-SYMB should be a symbol like `green`.
+   COLOR-SYMB should be a quoted symbol like `green`, `red`, `yellow`, `blue`,
+   `cyan`, `magenta`, etc. See the entire list of avaliable colors
+   [here](https://github.com/ibdknox/colorize/blob/master/src/colorize/core.clj).
 
      (format-color 'red \"Fatal error: %s\" error-message)"
   [color-symb format-string & args]
+  {:pre [(symbol? color-symb)]}
   ((ns-resolve 'colorize.core color-symb) (apply format format-string args)))
 
 (defn pprint-to-str
@@ -267,31 +270,81 @@
   [^Throwable e]
   (when e
     (when-let [stacktrace (.getStackTrace e)]
-      (->> (map str (.getStackTrace e))
-           (filterv (partial re-find #"metabase"))))))
-
-(defmacro try-apply
-  "Call F with PARAMS inside a try-catch block and log exceptions caught."
-  [f & params]
-  `(try
-     (~f ~@params)
-     (catch java.sql.SQLException e#
-       (log/error (color/red ~(format "Caught exception in %s: " f)
-                             (with-out-str (jdbc/print-sql-exception-chain e#))
-                             (pprint-to-str (filtered-stacktrace e#)))))
-     (catch Throwable e#
-       (log/error (color/red ~(format "Caught exception in %s: " f)
-                             (or (.getMessage e#) e#)
-                             (pprint-to-str (filtered-stacktrace e#)))))))
+      (filterv (partial re-find #"metabase")
+               (map str (.getStackTrace e))))))
+
+(defn wrap-try-catch
+  "Returns a new function that wraps F in a `try-catch`. When an exception is caught, it is logged
+   with `log/error` and returns `nil`."
+  ([f]
+   (wrap-try-catch f nil))
+  ([f f-name]
+   (let [exception-message (if f-name
+                             (format "Caught exception in %s: " f-name)
+                             "Caught exception: ")]
+     (fn [& args]
+       (try
+         (apply f args)
+         (catch java.sql.SQLException e
+           (log/error (color/red exception-message "\n"
+                                 (with-out-str (jdbc/print-sql-exception-chain e)) "\n"
+                                 (pprint-to-str (filtered-stacktrace e)))))
+         (catch Throwable e
+           (log/error (color/red exception-message (or (.getMessage e) e) "\n"
+                                 (pprint-to-str (filtered-stacktrace e))))))))))
+
+(defn try-apply
+  "Like `apply`, but wraps F inside a `try-catch` block and logs exceptions caught."
+  [^clojure.lang.IFn f & args]
+  (apply (wrap-try-catch f) args))
+
+(defn wrap-try-catch!
+  "Re-intern FN-SYMB as a new fn that wraps the original with a `try-catch`. Intended for debugging.
+
+     (defn z [] (throw (Exception. \"!\")))
+     (z) ; -> exception
+
+     (wrap-try-catch! 'z)
+     (z) ; -> nil; exception logged with log/error"
+  [fn-symb]
+  {:pre [(symbol? fn-symb)
+         (fn? @(resolve fn-symb))]}
+  (let [varr                    (resolve fn-symb)
+        {nmspc :ns, symb :name} (meta varr)]
+    (println (format "wrap-try-catch! %s/%s" nmspc symb))
+    (intern nmspc symb (wrap-try-catch @varr fn-symb))))
+
+(defn ns-wrap-try-catch!
+  "Re-intern all functions in NAMESPACE as ones that wrap the originals with a `try-catch`.
+   Defaults to the current namespace. You may optionally exclude a set of symbols using the kwarg `:exclude`.
+
+     (ns-wrap-try-catch!)
+     (ns-wrap-try-catch! 'metabase.driver)
+     (ns-wrap-try-catch! 'metabase.driver :exclude 'query-complete)
+
+   Intended for debugging."
+  {:arglists '([namespace? :exclude & excluded-symbs])}
+  [& args]
+  (let [[nmspc args] (optional #(try-apply the-ns [%]) args *ns*)
+        excluded     (when (= (first args) :exclude)
+                       (set (rest args)))]
+    (doseq [[symb varr] (ns-interns nmspc)]
+      (when (fn? @varr)
+        (when-not (contains? excluded symb)
+          (wrap-try-catch! (symbol (str (ns-name nmspc) \/ symb))))))))
+
+(defn deref-with-timeout
+  "Call `deref` on a FUTURE and throw an exception if it takes more than TIMEOUT-MS."
+  [futur timeout-ms]
+  (let [result (deref futur timeout-ms ::timeout)]
+    (when (= result ::timeout)
+      (throw (Exception. (format "Timed out after %d milliseconds." timeout-ms))))
+    result))
 
 (defmacro with-timeout
   "Run BODY in a `future` and throw an exception if it fails to complete after TIMEOUT-MS."
   [timeout-ms & body]
-  `(let [future# (future ~@body)
-         result# (deref future# ~timeout-ms :timeout)]
-     (when (= result# :timeout)
-       (throw (Exception. (format "Timed out after %d milliseconds." ~timeout-ms))))
-     result#))
+  `(deref-with-timeout (future ~@body) ~timeout-ms))
 
 (defmacro cond-as->
   "Anaphoric version of `cond->`. Binds EXPR to NAME through a series
diff --git a/src/metabase/util/logic.clj b/src/metabase/util/logic.clj
deleted file mode 100644
index 1acdb153a9c394e4405b8715ee79747db21563eb..0000000000000000000000000000000000000000
--- a/src/metabase/util/logic.clj
+++ /dev/null
@@ -1,61 +0,0 @@
-(ns metabase.util.logic
-  "Useful relations for `core.logic`."
-  (:refer-clojure :exclude [==])
-  (:require [clojure.core.logic :refer :all]))
-
-(defna butlast°
-  "A relation such that BUSTLASTV is all items but the last item LASTV of list L."
-  [butlastv lastv l]
-  ([[]   ?x [?x]])
-  ([_    _  [?x . ?more]] (fresh [more-butlast]
-                            (butlast° more-butlast lastv ?more)
-                            (conso ?x more-butlast butlastv))))
-
-(defna split°
-  "A relation such that HALF1 and HALF2 are even divisions of list L.
-   If L has an odd number of items, HALF1 will have one more item than HALF2."
-  [half1 half2 l]
-  ([[]   []   []])
-  ([[?x] []   [?x]])
-  ([[?x] [?y] [?x ?y]])
-  ([[?x ?y . ?more-half1-butlast] [?more-half1-last . ?more-half2] [?x ?y . ?more]]
-   (fresh [more-half1]
-     (split° more-half1 ?more-half2 ?more)
-     (butlast° ?more-half1-butlast ?more-half1-last more-half1))))
-
-(defn sorted-into°
-  "A relation such that OUT is the list L with V sorted into it doing comparisons with PRED-F."
-  [pred-f l v out]
-  (matche [l]
-    ([[]]           (== out [v]))
-    ([[?x . ?more]] (conda
-                     ((pred-f v ?x) (conso v (lcons ?x ?more) out))
-                     (s#            (fresh [more]
-                                      (sorted-into° pred-f ?more v more)
-                                      (conso ?x more out)))))))
-
-(defna sorted-permutation°
-  "A relation such that OUT is a permutation of L where all items are sorted by PRED-F."
-  [pred-f l out]
-  ([_ [] []])
-  ([_ [?x . ?more] _] (fresh [more]
-                        (sorted-permutation° pred-f ?more more)
-                        (sorted-into° pred-f more ?x out))))
-
-(defn matches-seq-order°
-  "A relation such that V1 is present and comes before V2 in list L."
-  [v1 v2 l]
-  (conda
-    ;; This is just an optimization for cases where L isn't a logic var; it's much faster <3
-    ((nonlvaro l)  ((fn -ordered° [[item & more]]
-                      (conda
-                        ((== v1 item)         s#)
-                        ((== v2 item)         fail)
-                        ((when (seq more) s#) (-ordered° more))))
-                    l))
-    (s#            (conda
-                     ((firsto l v1))
-                     ((firsto l v2) fail)
-                     ((fresh [more]
-                        (resto l more)
-                        (matches-seq-order° v1 v2 more)))))))
diff --git a/test/metabase/driver/query_processor_test.clj b/test/metabase/driver/query_processor_test.clj
index afcf729fb6cc11a3174da9bcb2255c788e9058e0..6136414b384f194a4a7de5875094f80ee4b2c20d 100644
--- a/test/metabase/driver/query_processor_test.clj
+++ b/test/metabase/driver/query_processor_test.clj
@@ -1304,3 +1304,39 @@
                      :aggregation  ["count"]
                      :filter       ["TIME_INTERVAL" (id :checkins :timestamp) "last" "week"]}})
         :data :rows first first)))
+
+;; Make sure that when referencing the same field multiple times with different units we return the one
+;; that actually reflects the units the results are in.
+;; eg when we breakout by one unit and filter by another, make sure the results and the col info
+;; use the unit used by breakout
+(defn- date-bucketing-unit-when-you [& {:keys [breakout-by filter-by]}]
+  (with-temp-db [_ (checkins:1-per-day)]
+    (let [results (driver/process-query
+                   {:database (db-id)
+                    :type     :query
+                    :query     {:source_table (id :checkins)
+                                :aggregation  ["count"]
+                                :breakout     [["datetime_field" (id :checkins :timestamp) "as" breakout-by]]
+                                :filter       ["TIME_INTERVAL" (id :checkins :timestamp) "current" filter-by]}})]
+      {:rows (-> results :row_count)
+       :unit (-> results :data :cols first :unit)})))
+
+(datasets/expect-with-datasets sql-engines
+  {:rows 1, :unit :day}
+  (date-bucketing-unit-when-you :breakout-by "day", :filter-by "day"))
+
+(datasets/expect-with-datasets sql-engines
+  {:rows 7, :unit :day}
+  (date-bucketing-unit-when-you :breakout-by "day", :filter-by "week"))
+
+(datasets/expect-with-datasets sql-engines
+  {:rows 1, :unit :week}
+  (date-bucketing-unit-when-you :breakout-by "week", :filter-by "day"))
+
+(datasets/expect-with-datasets sql-engines
+  {:rows 1, :unit :quarter}
+  (date-bucketing-unit-when-you :breakout-by "quarter", :filter-by "day"))
+
+(datasets/expect-with-datasets sql-engines
+  {:rows 1, :unit :hour}
+  (date-bucketing-unit-when-you :breakout-by "hour", :filter-by "day"))