                                                              :special_type :special-type
                                                              :base_type    :base-type
                                                              :table_id     :table-id})))]
+            (reset! *table-ids* (set (map :table-id (vals fields))))
             ;; This is performed depth-first so we don't end up walking the newly-created Field/Value objects
             ;; they may have nil values; this was we don't have to write an implementation of resolve-field for nil
             (walk/postwalk #(resolve-field % fields) expanded-query-dict))))

(defn expand
  "Expand a QUERY-DICT."
  [query-dict]
  (binding [*field-ids* (atom #{})]
    (some-> query-dict
            parse
            (resolve-fields @*field-ids*)
            resolve-database
            resolve-tables)))


;; ## -------------------- Field + Value --------------------

                                       (m/filter-vals identity <>))))

(def ^:private ^:dynamic *field-ids*
  "Bound to an atom containing a set of `Field` IDs referenced in the query being expanded."
  nil)

(def ^:private ^:dynamic *table-ids*
  "Bound to an atom containing a set of `Table` IDs referenced by `Fields` in the query being expanded."
  nil)

(defn- resolve-fields
