diff --git a/e2e/test/scenarios/admin/datamodel/field.cy.spec.js b/e2e/test/scenarios/admin/datamodel/field.cy.spec.js
index 8076868c03f9a4ae6e2b03fb57f3c36b774fbd66..ddfed4dc3dd56b3c3f23927cbe5ddaa7ee7643b0 100644
--- a/e2e/test/scenarios/admin/datamodel/field.cy.spec.js
+++ b/e2e/test/scenarios/admin/datamodel/field.cy.spec.js
@@ -3,9 +3,11 @@ import {
   withDatabase,
   visitAlias,
   popover,
+  resetTestTable,
   startNewQuestion,
+  resyncDatabase,
 } from "e2e/support/helpers";
-import { SAMPLE_DB_ID } from "e2e/support/cypress_data";
+import { SAMPLE_DB_ID, WRITABLE_DB_ID } from "e2e/support/cypress_data";
 import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
 
 const { ORDERS, ORDERS_ID } = SAMPLE_DATABASE;
@@ -133,3 +135,52 @@ describe.skip("scenarios > admin > datamodel > field", () => {
     });
   });
 });
+    
+function getUnfoldJsonContent() {
+  return cy
+    .findByText("Unfold JSON")
+    .closest("section")
+    .findByTestId("select-button-content");
+}
+
+describe("Unfold JSON", () => {
+  beforeEach(() => {
+    resetTestTable({ type: "postgres", table: "many_data_types" });
+    restore(`postgres-writable`);
+    cy.signInAsAdmin();
+    resyncDatabase({ dbId: WRITABLE_DB_ID, tableName: "many_data_types" });
+  });
+  
+  it("lets you enable/disable 'Unfold JSON' for JSON columns", () => {
+    cy.intercept("POST", `/api/database/${WRITABLE_DB_ID}/sync_schema`).as(
+      "sync_schema",
+    );
+    // Go to field settings
+    cy.visit(`/admin/datamodel/database/${WRITABLE_DB_ID}`);
+    cy.findByText(/Many Data Types/i).click();
+
+    // Check json is unfolded initially
+    cy.findByText(/json.a/i).should("be.visible");
+    cy.findByTestId("column-json").within(() => { cy.icon("gear").click(); });
+    
+    getUnfoldJsonContent().findByText(/Yes/i).click();
+    popover().within(() => {
+      cy.findByText(/No/i).click();
+    });
+
+    // Check setting has persisted
+    cy.reload();
+    getUnfoldJsonContent().findByText(/No/i);
+
+    // Sync database
+    cy.visit(`/admin/databases/${WRITABLE_DB_ID}`);
+    cy.findByText(/Sync database schema now/i).click();
+    cy.wait("@sync_schema");
+    cy.findByText("Sync triggered!");
+
+    // Check json field is not unfolded
+    cy.visit(`/admin/datamodel/database/${WRITABLE_DB_ID}`);
+    cy.findByText(/Many Data Types/i).click();
+    cy.findByText(/json.a/i).should("not.exist");
+  });
+});
diff --git a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
index 7932a66fe4042bf62ea8e59ab9f31953bc5785ce..464d0a9ef127ed7c2768de65a1ae9db614e6a411 100644
--- a/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
+++ b/frontend/src/metabase/admin/datamodel/containers/FieldApp.jsx
@@ -38,7 +38,8 @@ import { getGlobalSettingsForColumn } from "metabase/visualizations/lib/settings
 import Databases from "metabase/entities/databases";
 import Tables from "metabase/entities/tables";
 import Fields from "metabase/entities/fields";
-import { isTypeFK, isCurrency } from "metabase-lib/types/utils/isa";
+import { TYPE } from "metabase-lib/types/constants";
+import { isTypeFK, isCurrency, isa } from "metabase-lib/types/utils/isa";
 import { rescanFieldValues, discardFieldValues } from "../field";
 import UpdateCachedFieldValues from "../components/UpdateCachedFieldValues";
 import FieldRemapping from "../components/FieldRemapping";
@@ -225,6 +226,7 @@ class FieldApp extends React.Component {
                   fieldsError={fieldsError}
                   idfields={idfields}
                   table={table}
+                  database={db}
                   metadata={metadata}
                   onUpdateFieldValues={this.onUpdateFieldValues}
                   onUpdateFieldProperties={this.onUpdateFieldProperties}
@@ -255,6 +257,7 @@ const FieldGeneralPane = ({
   fieldsError,
   idfields,
   table,
+  database,
   metadata,
   onUpdateFieldValues,
   onUpdateFieldProperties,
@@ -297,6 +300,31 @@ const FieldGeneralPane = ({
       />
     </Section>
 
+    {isa(field.base_type, TYPE.JSON) &&
+      database.hasFeature("nested-field-columns") && (
+        <Section>
+          <SectionHeader
+            title={t`Unfold JSON`}
+            description={t`Unfold JSON into component fields, where each JSON key becomes a column. You can turn this off if performance is slow.`}
+          />
+          <Select
+            className="inline-block"
+            value={
+              field.json_unfolding ?? database.details["json-unfolding"] ?? true
+            }
+            onChange={({ target: { value } }) => {
+              return onUpdateFieldProperties({
+                json_unfolding: value,
+              });
+            }}
+            options={[
+              { name: t`Yes`, value: true },
+              { name: t`No`, value: false },
+            ]}
+          />
+        </Section>
+      )}
+
     {!isTypeFK(field.semantic_type) && is_coerceable(field.base_type) && (
       <Section>
         <SectionHeader title={t`Cast to a specific data type`} />
@@ -326,6 +354,7 @@ const FieldGeneralPane = ({
         />
       </Section>
     )}
+
     <Section>
       <SectionHeader
         title={t`Filtering on this field`}
diff --git a/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj b/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
index a8debfbfba56e9efab4618f3cdf3425d2fcb005e..5ecc49c91514ac6c5797f699d2a000f17ba1090f 100644
--- a/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
+++ b/modules/drivers/snowflake/test/metabase/driver/snowflake_test.clj
@@ -130,13 +130,15 @@
                          :pk?               true
                          :database-position 0
                          :database-is-auto-increment true
-                         :database-required false}
+                         :database-required false
+                         :json-unfolding    false}
                         {:name              "name"
                          :database-type     "VARCHAR"
                          :base-type         :type/Text
                          :database-position 1
                          :database-is-auto-increment false
-                         :database-required true}}}
+                         :database-required true
+                         :json-unfolding    false}}}
              (driver/describe-table :snowflake (assoc (mt/db) :name "ABC") (t2/select-one Table :id (mt/id :categories))))))))
 
 (deftest describe-table-fks-test
diff --git a/modules/drivers/sqlite/test/metabase/driver/sqlite_test.clj b/modules/drivers/sqlite/test/metabase/driver/sqlite_test.clj
index ac42dcfcb95ed5123c6ad9ec46dc7005c085c705..b483dc91ee43bbefb92b011c2cc77a6c07a4d8b8 100644
--- a/modules/drivers/sqlite/test/metabase/driver/sqlite_test.clj
+++ b/modules/drivers/sqlite/test/metabase/driver/sqlite_test.clj
@@ -143,11 +143,12 @@
               (testing "timestamp column should exist"
                 (is (= {:name "timestamp_table"
                         :schema nil
-                        :fields #{{:name "created_at"
-                                   :database-type "TIMESTAMP"
-                                   :base-type :type/DateTime
-                                   :database-position 0
-                                   :database-required false
+                        :fields #{{:name                       "created_at"
+                                   :database-type              "TIMESTAMP"
+                                   :base-type                  :type/DateTime
+                                   :database-position          0
+                                   :database-required          false
+                                   :json-unfolding             false
                                    :database-is-auto-increment false}}}
                        (driver/describe-table driver db (t2/select-one Table :id (mt/id :timestamp_table)))))))))))))
 
diff --git a/resources/migrations/000_migrations.yaml b/resources/migrations/000_migrations.yaml
index fea1db34e87a62ca4b81a84e88e00d9a5db4cddc..e25c68c0f8a2e0410ac496d6a580f29e5c85c9cb 100644
--- a/resources/migrations/000_migrations.yaml
+++ b/resources/migrations/000_migrations.yaml
@@ -14312,7 +14312,6 @@ databaseChangeLog:
       id: v47.00-001
       author: calherries
       comment: Added 0.47.0 -- set base-type to type/JSON for JSON database-types for postgres and mysql
-      validCheckSum: ANY
       changes:
         - sql:
             sql: >-
@@ -14362,6 +14361,34 @@ databaseChangeLog:
               WHEN MATCHED THEN
                 UPDATE SET f.base_type = updates.base_type;
 
+  - changeSet:
+      id: v47.00-002
+      author: calherries
+      comment: Added 0.47.0 - Add json_unfolding column to metabase_field
+      changes:
+        - addColumn:
+            columns:
+            - column:
+                remarks: 'Enable/disable JSON unfolding for a field'
+                name: json_unfolding
+                type: boolean
+                defaultValueBoolean: false
+                constraints:
+                  nullable: false
+            tableName: metabase_field
+
+  - changeSet:
+      id: v47.00-003
+      author: calherries
+      comment: Added 0.47.0 - Populate metabase_field.json_unfolding based on base_type
+      changes:
+        - sql:
+            sql: >-
+              UPDATE metabase_field
+              SET json_unfolding = true
+              WHERE base_type = 'type/JSON';
+      rollback: # nothing to do, since json_unfolding is new in 47
+
   - changeSet:
       id: v47.00-004
       author: qnkhuat
diff --git a/src/metabase/api/field.clj b/src/metabase/api/field.clj
index 916e6bbd63634fd20f1fc81008b6fce460e411ff..40cb8e1876df9ab8a02100195819b2d7c37cc2bc 100644
--- a/src/metabase/api/field.clj
+++ b/src/metabase/api/field.clj
@@ -73,8 +73,7 @@
 (defn- clear-dimension-on-fk-change! [{:keys [dimensions], :as _field}]
   (doseq [{dimension-id :id, dimension-type :type} dimensions]
     (when (and dimension-id (= :external dimension-type))
-      (t2/delete! Dimension :id dimension-id)))
-  true)
+      (t2/delete! Dimension :id dimension-id))))
 
 (defn- removed-fk-semantic-type? [old-semantic-type new-semantic-type]
   (and (not= old-semantic-type new-semantic-type)
@@ -97,14 +96,36 @@
     (when (and old-dim-id
                (= :internal old-dim-type)
                (not (internal-remapping-allowed? base-type new-semantic-type)))
-      (t2/delete! Dimension :id old-dim-id)))
-  true)
+      (t2/delete! Dimension :id old-dim-id))))
+
+(defn- update-nested-fields-on-json-unfolding-change!
+  "If JSON unfolding was enabled for a JSON field, it activates previously synced nested fields from the JSON field.
+   If JSON unfolding was disabled for that field, it inactivates the nested fields from the JSON field.
+   Returns nil."
+  [old-field new-json-unfolding]
+  (when (not= new-json-unfolding (:json_unfolding old-field))
+    (if new-json-unfolding
+      (let [update-result (t2/update! Field
+                                      :table_id (:table_id old-field)
+                                      :nfc_path [:like (str "[\"" (:name old-field) "\",%]")]
+                                      {:active true})]
+        (when (zero? update-result)
+          ;; Sync the table if no nested fields exist. This means the table hasn't previously
+          ;; been synced when JSON unfolding was enabled. This assumes the JSON field is already updated to have
+          ;; JSON unfolding enabled.
+          (let [table (field/table old-field)]
+            (sync.concurrent/submit-task (fn [] (sync/sync-table! table))))))
+      (t2/update! Field
+                  :table_id (:table_id old-field)
+                  :nfc_path [:like (str "[\"" (:name old-field) "\",%]")]
+                  {:active false})))
+  nil)
 
 #_{:clj-kondo/ignore [:deprecated-var]}
 (api/defendpoint-schema PUT "/:id"
   "Update `Field` with ID."
   [id :as {{:keys [caveats description display_name fk_target_field_id points_of_interest semantic_type
-                   coercion_strategy visibility_type has_field_values settings nfc_path]
+                   coercion_strategy visibility_type has_field_values settings nfc_path json_unfolding]
             :as   body} :body}]
   {caveats            (s/maybe su/NonBlankString)
    description        (s/maybe su/NonBlankString)
@@ -116,9 +137,10 @@
    visibility_type    (s/maybe FieldVisibilityType)
    has_field_values   (s/maybe (apply s/enum (map name field/has-field-values-options)))
    settings           (s/maybe su/Map)
-   nfc_path           (s/maybe [su/NonBlankString])}
-  (let [field              (hydrate (api/write-check Field id) :dimensions)
-        new-semantic-type  (keyword (get body :semantic_type (:semantic_type field)))
+   nfc_path           (s/maybe [su/NonBlankString])
+   json_unfolding     (s/maybe s/Bool)}
+  (let [field             (hydrate (api/write-check Field id) :dimensions)
+        new-semantic-type (keyword (get body :semantic_type (:semantic_type field)))
         [effective-type coercion-strategy]
         (or (when-let [coercion_strategy (keyword coercion_strategy)]
               (let [effective (types/effective-type-for-coercion coercion_strategy)]
@@ -137,19 +159,19 @@
     ;; everything checks out, now update the field
     (api/check-500
      (t2/with-transaction [_conn]
-       (and
-        (if removed-fk?
-          (clear-dimension-on-fk-change! field)
-          true)
-        (clear-dimension-on-type-change! field (:base_type field) new-semantic-type)
-        (t2/update! Field id
-          (u/select-keys-when (assoc body
-                                     :fk_target_field_id (when-not removed-fk? fk-target-field-id)
-                                     :effective_type effective-type
-                                     :coercion_strategy coercion-strategy)
-            :present #{:caveats :description :fk_target_field_id :points_of_interest :semantic_type :visibility_type
-                       :coercion_strategy :effective_type :has_field_values :nfc_path}
-            :non-nil #{:display_name :settings})))))
+       (when removed-fk?
+         (clear-dimension-on-fk-change! field))
+       (clear-dimension-on-type-change! field (:base_type field) new-semantic-type)
+       (t2/update! Field id
+                   (u/select-keys-when (assoc body
+                                              :fk_target_field_id (when-not removed-fk? fk-target-field-id)
+                                              :effective_type effective-type
+                                              :coercion_strategy coercion-strategy)
+                                       :present #{:caveats :description :fk_target_field_id :points_of_interest :semantic_type :visibility_type
+                                                  :coercion_strategy :effective_type :has_field_values :nfc_path :json_unfolding}
+                                       :non-nil #{:display_name :settings}))))
+    (when (some? json_unfolding)
+      (update-nested-fields-on-json-unfolding-change! field json_unfolding))
     ;; return updated field. note the fingerprint on this might be out of date if the task below would replace them
     ;; but that shouldn't matter for the datamodel page
     (u/prog1 (hydrate (t2/select-one Field :id id) :dimensions)
diff --git a/src/metabase/db/custom_migrations.clj b/src/metabase/db/custom_migrations.clj
index 25e704d07d71a63fdd8767e0b092891f41051e38..8f7aed4f36e562a831cdef736534f91b0b5f2957 100644
--- a/src/metabase/db/custom_migrations.clj
+++ b/src/metabase/db/custom_migrations.clj
@@ -8,6 +8,7 @@
 
    If you need to use code from elsewhere, consider copying it into this namespace to minimize risk of the code changing behaviour."
   (:require
+   [cheshire.core :as json]
    [clojure.set :as set]
    [clojurewerkz.quartzite.jobs :as jobs]
    [clojurewerkz.quartzite.scheduler :as qs]
@@ -146,3 +147,26 @@
     (qs/delete-trigger scheduler (triggers/key "metabase.task.abandonment-emails.trigger"))
     (qs/delete-job scheduler (jobs/key "metabase.task.abandonment-emails.job"))
     (qs/shutdown scheduler)))
+
+(define-migration FillJSONUnfoldingDefault
+  (let [db-ids-to-not-update (->> (t2/query {:select [:id :details]
+                                             :from   [:metabase_database]})
+                                  ;; if json-unfolding is nil it's treated as if it were true
+                                  ;; so we need to remove databases that have it set to false
+                                  (filter (fn [{:keys [details]}]
+                                            (when details
+                                              (false? (:json-unfolding (json/parse-string details true))))))
+                                  (map :id))
+        field-ids-to-update  (->> (t2/query {:select [:f.id]
+                                             :from   [[:metabase_field :f]]
+                                             :join   [[:metabase_table :t] [:= :t.id :f.table_id]]
+                                             :where  (if (seq db-ids-to-not-update)
+                                                       [:and
+                                                        [:not-in :t.db_id db-ids-to-not-update]
+                                                        [:= :f.base_type "type/JSON"]]
+                                                       [:= :f.base_type "type/JSON"])})
+                                  (map :id))]
+    (when (seq field-ids-to-update)
+      (t2/query-one {:update :metabase_field
+                     :set    {:json_unfolding true}
+                     :where  [:in :metabase_field.id field-ids-to-update]}))))
diff --git a/src/metabase/driver/common.clj b/src/metabase/driver/common.clj
index 67f39d29669cbbfd8967b6ec96e133760b54865e..1e2efaaa020e7866bcdab7e2bc12b9f86e550109 100644
--- a/src/metabase/driver/common.clj
+++ b/src/metabase/driver/common.clj
@@ -174,12 +174,13 @@
 (def json-unfolding
   "Map representing the `json-unfolding` option in a DB connection form"
   {:name         "json-unfolding"
-   :display-name (deferred-tru "Unfold JSON Columns")
+   :display-name (deferred-tru "Allow unfolding of JSON columns")
    :type         :boolean
    :visible-if   {"advanced-options" true}
    :description  (deferred-tru
-                   (str "We unfold JSON columns into component fields."
-                        "This is on by default but you can turn it off if performance is slow."))
+                   (str "This enables unfolding JSON columns into their component fields. "
+                        "Disable unfolding if performance is slow. If enabled, you can still disable unfolding for "
+                        "individual fields in their settings."))
    :default      true})
 
 (def refingerprint
@@ -450,3 +451,14 @@
   Sunday), then the offset should be `-1`, because `:monday` returned by the driver (`2`) minus `1` = `1`."
   [driver]
   (start-of-week-offset-for-day (driver/db-start-of-week driver)))
+
+(defn json-unfolding-default
+  "Returns true if JSON fields should be unfolded by default for this database, and false otherwise."
+  [database]
+  ;; This allows adding support for nested-field-columns for drivers in the future and
+  ;; have json-unfolding enabled by default, without
+  ;; needing a migration to add the `json-unfolding=true` key to the database details.
+  (let [json-unfolding (get-in database [:details :json-unfolding])]
+    (if (nil? json-unfolding)
+      true
+      json-unfolding)))
diff --git a/src/metabase/driver/mysql.clj b/src/metabase/driver/mysql.clj
index a439f636ab7a26c0b46e15520bfb39bfb3dc8025..0ba029faffd64a5ee4301e83ad541d5c723f9a13 100644
--- a/src/metabase/driver/mysql.clj
+++ b/src/metabase/driver/mysql.clj
@@ -48,11 +48,11 @@
 
 (defmethod driver/display-name :mysql [_] "MySQL")
 
-(defmethod driver/database-supports? [:mysql :nested-field-columns] [_ _ database]
-  (let [json-setting (get-in database [:details :json-unfolding])]
-    (if (nil? json-setting)
-      true
-      json-setting)))
+;; This is a bit of a lie since the JSON type was introduced for MySQL since 5.7.8.
+;; And MariaDB doesn't have the JSON type at all, though `JSON` was introduced as an alias for LONGTEXT in 10.2.7.
+;; But since JSON unfolding will only apply columns with JSON types, this won't cause any problems during sync.
+(defmethod driver/database-supports? [:mysql :nested-field-columns] [_driver _feat db]
+  (driver.common/json-unfolding-default db))
 
 (defmethod driver/database-supports? [:mysql :persist-models] [_driver _feat _db] true)
 
diff --git a/src/metabase/driver/postgres.clj b/src/metabase/driver/postgres.clj
index 7c017f286b48f57ab31a9781bd565702af8448ad..d1b45813be44042ed9f73ba8a7ddc5f83a84bd22 100644
--- a/src/metabase/driver/postgres.clj
+++ b/src/metabase/driver/postgres.clj
@@ -46,18 +46,16 @@
 
 (driver/register! :postgres, :parent :sql-jdbc)
 
-(defmethod driver/database-supports? [:postgres :nested-field-columns] [_ _ database]
-  (let [json-setting (get-in database [:details :json-unfolding])
-        ;; If not set at all, default to true, actually
-        setting-nil? (nil? json-setting)]
-    (or json-setting setting-nil?)))
-
 ;;; +----------------------------------------------------------------------------------------------------------------+
 ;;; |                                             metabase.driver impls                                              |
 ;;; +----------------------------------------------------------------------------------------------------------------+
 
 (defmethod driver/display-name :postgres [_] "PostgreSQL")
 
+(defmethod driver/database-supports? [:postgres :nested-field-columns]
+  [_driver _feat db]
+  (driver.common/json-unfolding-default db))
+
 (defmethod driver/database-supports? [:postgres :datetime-diff]
   [_driver _feat _db]
   true)
diff --git a/src/metabase/driver/sql_jdbc/sync/describe_table.clj b/src/metabase/driver/sql_jdbc/sync/describe_table.clj
index 635f3c77dba9dc564f968df9cf34713139f453cf..78d8d53e050fc39f67f5272fa4388293f9ca686f 100644
--- a/src/metabase/driver/sql_jdbc/sync/describe_table.clj
+++ b/src/metabase/driver/sql_jdbc/sync/describe_table.clj
@@ -13,10 +13,12 @@
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
    [metabase.driver.sql.query-processor :as sql.qp]
    [metabase.mbql.schema :as mbql.s]
+   [metabase.models :refer [Field]]
    [metabase.models.table :as table]
    [metabase.util :as u]
    [metabase.util.honeysql-extensions :as hx]
-   [metabase.util.log :as log])
+   [metabase.util.log :as log]
+   [toucan2.core :as t2])
   (:import
    (java.sql Connection DatabaseMetaData ResultSet)))
 
@@ -154,20 +156,19 @@
   "Returns a transducer for computing metatdata about the fields in `table`."
   [driver table]
   (map-indexed (fn [i {:keys [database-type], column-name :name, :as col}]
-                 (let [base-type (database-type->base-type-or-warn driver database-type)
-                       semantic-type (calculated-semantic-type driver column-name database-type)]
+                 (let [base-type      (database-type->base-type-or-warn driver database-type)
+                       semantic-type  (calculated-semantic-type driver column-name database-type)
+                       db             (table/database table)
+                       json?          (isa? base-type :type/JSON)]
                    (merge
                     (u/select-non-nil-keys col [:name :database-type :field-comment :database-required :database-is-auto-increment])
                     {:base-type         base-type
-                     :database-position i}
+                     :database-position i
+                     ;; json-unfolding is true by default for JSON fields, but this can be overridden at the DB level
+                     :json-unfolding    json?}
                     (when semantic-type
                       {:semantic-type semantic-type})
-                    (when (and
-                           (isa? base-type :type/JSON)
-                           (driver/database-supports?
-                            driver
-                            :nested-field-columns
-                            (table/database table)))
+                    (when (and json? (driver/database-supports? driver :nested-field-columns db))
                       {:visibility-type :details-only}))))))
 
 (defmulti describe-table-fields
@@ -385,6 +386,7 @@
                             :base-type         curr-type
                             ;; Postgres JSONB field, which gets most usage, doesn't maintain JSON object ordering...
                             :database-position 0
+                            :json-unfolding    false
                             :visibility-type   :normal
                             :nfc-path          field-path})))
         field-hash   (apply hash-set (filter some? valid-fields))]
@@ -403,19 +405,27 @@
       (if (nil? (seq json-fields))
         #{}
         (sql.qp/with-driver-honey-sql-version driver
-          (let [json-field-names (mapv #(apply hx/identifier :field (into table-identifier-info [(:name %)])) json-fields)
-                table-identifier (apply hx/identifier :table table-identifier-info)
-                sql-args         (sql.qp/format-honeysql driver {:select (mapv sql.qp/maybe-wrap-unaliased-expr json-field-names)
-                                                                 :from   [(sql.qp/maybe-wrap-unaliased-expr table-identifier)]
-                                                                 :limit  metadata-queries/nested-field-sample-limit})
-                query            (jdbc/reducible-query spec sql-args {:identifiers identity})
-                field-types      (transduce describe-json-xform describe-json-rf query)
-                fields           (field-types->fields field-types)]
-            (if (> (count fields) max-nested-field-columns)
-              (do
-                (log/warn
-                 (format
-                  "More nested field columns detected than maximum. Limiting the number of nested field columns to %d."
-                  max-nested-field-columns))
-                (set (take max-nested-field-columns fields)))
-              fields)))))))
+          (let [existing-fields-by-name (m/index-by :name (t2/select Field :table_id (u/the-id table)))
+                unfold-json-fields      (remove (fn [field]
+                                                  (when-let [existing-field (existing-fields-by-name (:name field))]
+                                                    (false? (:json_unfolding existing-field))))
+                                                json-fields)]
+            (if (empty? unfold-json-fields)
+              #{}
+              (binding [hx/*honey-sql-version* (sql.qp/honey-sql-version driver)]
+                (let [json-field-names (mapv #(apply hx/identifier :field (into table-identifier-info [(:name %)])) unfold-json-fields)
+                      table-identifier (apply hx/identifier :table table-identifier-info)
+                      sql-args         (sql.qp/format-honeysql driver {:select (mapv sql.qp/maybe-wrap-unaliased-expr json-field-names)
+                                                                       :from   [(sql.qp/maybe-wrap-unaliased-expr table-identifier)]
+                                                                       :limit  metadata-queries/nested-field-sample-limit})
+                      query            (jdbc/reducible-query spec sql-args {:identifiers identity})
+                      field-types      (transduce describe-json-xform describe-json-rf query)
+                      fields           (field-types->fields field-types)]
+                  (if (> (count fields) max-nested-field-columns)
+                    (do
+                      (log/warn
+                       (format
+                        "More nested field columns detected than maximum. Limiting the number of nested field columns to %d."
+                        max-nested-field-columns))
+                      (set (take max-nested-field-columns fields)))
+                    fields))))))))))
diff --git a/src/metabase/models/database.clj b/src/metabase/models/database.clj
index c8b1aaf23cec9d0b6deef49402b959b569056e6f..cec4f3af7811832fefdd7ff363105ce7655041f0 100644
--- a/src/metabase/models/database.clj
+++ b/src/metabase/models/database.clj
@@ -176,7 +176,13 @@
                       {:status-code     400
                        :existing-engine existing-engine
                        :new-engine      new-engine}))
-      (u/prog1 (handle-secrets-changes database)
+      (u/prog1  (-> database
+                    (cond->
+                      ;; If the engine doesn't support nested field columns, `json_unfolding` must be nil
+                      (and (some? (:details database))
+                           (not (driver/database-supports? (or new-engine existing-engine) :nested-field-columns database)))
+                      (update :details dissoc :json_unfolding))
+                    handle-secrets-changes)
         ;; TODO - this logic would make more sense in post-update if such a method existed
         ;; if the sync operation schedules have changed, we need to reschedule this DB
         (when (or new-metadata-schedule new-fieldvalues-schedule)
diff --git a/src/metabase/sync/interface.clj b/src/metabase/sync/interface.clj
index 14ea6ae12364a30a14a5966b53f8495a88e08b4c..317bc187f05ec8227c7596409dd18c21006fb480 100644
--- a/src/metabase/sync/interface.clj
+++ b/src/metabase/sync/interface.clj
@@ -33,6 +33,7 @@
    (s/optional-key :field-comment)              (s/maybe su/NonBlankString)
    (s/optional-key :pk?)                        s/Bool
    (s/optional-key :nested-fields)              #{(s/recursive #'TableMetadataField)}
+   (s/optional-key :json-unfolding)             s/Bool
    (s/optional-key :nfc-path)                   [s/Any]
    (s/optional-key :custom)                     {s/Any s/Any}
    (s/optional-key :database-is-auto-increment) s/Bool
diff --git a/src/metabase/sync/sync_metadata/fields/fetch_metadata.clj b/src/metabase/sync/sync_metadata/fields/fetch_metadata.clj
index f1a769b6134f3603a2d9ffaf7e79ff1ff2889e3d..4672ecf446f94d54f0d7d0613325896c67c64503 100644
--- a/src/metabase/sync/sync_metadata/fields/fetch_metadata.clj
+++ b/src/metabase/sync/sync_metadata/fields/fetch_metadata.clj
@@ -33,6 +33,7 @@
           :semantic-type             (:semantic_type field)
           :pk?                       (isa? (:semantic_type field) :type/PK)
           :field-comment             (:description field)
+          :json-unfolding            (:json_unfolding field)
           :database-is-auto-increment (:database_is_auto_increment field)
           :database-position         (:database_position field)
           :database-required         (:database_required field)})
@@ -69,7 +70,8 @@
   "Fetch active Fields from the Metabase application database for a given `table`."
   [table :- i/TableInstance]
  (t2/select [Field :name :database_type :base_type :effective_type :coercion_strategy :semantic_type
-             :parent_id :id :description :database_position :nfc_path :database_is_auto_increment :database_required]
+             :parent_id :id :description :database_position :nfc_path :database_is_auto_increment :database_required
+             :json_unfolding]
      :table_id  (u/the-id table)
      :active    true
      {:order-by table/field-order-rule}))
diff --git a/src/metabase/sync/sync_metadata/fields/sync_instances.clj b/src/metabase/sync/sync_metadata/fields/sync_instances.clj
index 85a31f7353e8a2205d944468dd357abd2742cef1..cf7e2622d8a4ab36fb20fb91065cd63d5f7aee3d 100644
--- a/src/metabase/sync/sync_metadata/fields/sync_instances.clj
+++ b/src/metabase/sync/sync_metadata/fields/sync_instances.clj
@@ -42,36 +42,38 @@
   (when (seq new-field-metadatas)
     (t2/insert-returning-pks! Field
       (for [{:keys [database-type database-is-auto-increment database-required base-type effective-type coercion-strategy
-                    field-comment database-position nfc-path visibility-type], field-name :name :as field} new-field-metadatas]
+                    field-comment database-position nfc-path visibility-type json-unfolding]
+             field-name :name :as field} new-field-metadatas]
         (do
-         (when (and effective-type
-                    base-type
-                    (not= effective-type base-type)
-                    (nil? coercion-strategy))
-           (log/warn (u/format-color 'red
-                                     (str
-                                      "WARNING: Field `%s`: effective type `%s` provided but no coercion strategy provided."
-                                      " Using base-type: `%s`")
-                                     field-name
-                                     effective-type
-                                     base-type)))
-         {:table_id                  (u/the-id table)
-          :name                      field-name
-          :display_name              (humanization/name->human-readable-name field-name)
-          :database_type             (or database-type "NULL") ; placeholder for Fields w/ no type info (e.g. Mongo) & all NULL
-          :base_type                 base-type
-          ;; todo test this?
-          :effective_type            (if (and effective-type coercion-strategy) effective-type base-type)
-          :coercion_strategy         (when effective-type coercion-strategy)
-          :semantic_type             (common/semantic-type field)
-          :parent_id                 parent-id
-          :nfc_path                  nfc-path
-          :description               field-comment
-          :position                  database-position
-          :database_position         database-position
-          :database_is_auto_increment (or database-is-auto-increment false)
-          :database_required         (or database-required false)
-          :visibility_type           (or visibility-type :normal)})))))
+          (when (and effective-type
+                     base-type
+                     (not= effective-type base-type)
+                     (nil? coercion-strategy))
+            (log/warn (u/format-color 'red
+                                      (str
+                                       "WARNING: Field `%s`: effective type `%s` provided but no coercion strategy provided."
+                                       " Using base-type: `%s`")
+                                      field-name
+                                      effective-type
+                                      base-type)))
+          {:table_id                   (u/the-id table)
+           :name                       field-name
+           :display_name               (humanization/name->human-readable-name field-name)
+           :database_type              (or database-type "NULL") ; placeholder for Fields w/ no type info (e.g. Mongo) & all NULL
+           :base_type                  base-type
+           ;; todo test this?
+           :effective_type             (if (and effective-type coercion-strategy) effective-type base-type)
+           :coercion_strategy          (when effective-type coercion-strategy)
+           :semantic_type              (common/semantic-type field)
+           :parent_id                  parent-id
+           :nfc_path                   nfc-path
+           :description                field-comment
+           :position                   database-position
+           :database_position          database-position
+           :json_unfolding             (or json-unfolding false)
+           :database_is_auto_increment (or database-is-auto-increment false)
+           :database_required          (or database-required false)
+           :visibility_type            (or visibility-type :normal)})))))
 
 (s/defn ^:private create-or-reactivate-fields! :- (s/maybe [i/FieldInstance])
   "Create (or reactivate) Metabase Field object(s) for any Fields in `new-field-metadatas`. Does *NOT* recursively
diff --git a/test/metabase/api/field_test.clj b/test/metabase/api/field_test.clj
index e9526f54c1dab7e0758ea643aa7d951d3cc3cdfc..80b29871605a239f8359b0e2dcb498cd9071cc5f 100644
--- a/test/metabase/api/field_test.clj
+++ b/test/metabase/api/field_test.clj
@@ -4,6 +4,8 @@
    [clojure.test :refer :all]
    [medley.core :as m]
    [metabase.api.field :as api.field]
+   [metabase.driver :as driver]
+   [metabase.driver.mysql-test :as mysql-test]
    [metabase.driver.util :as driver.u]
    [metabase.models :refer [Database Field FieldValues Table]]
    [metabase.sync :as sync]
@@ -16,6 +18,8 @@
    [toucan.hydrate :refer [hydrate]]
    [toucan2.core :as t2]))
 
+(set! *warn-on-reflection* true)
+
 (use-fixtures :once (fixtures/initialize :plugins))
 
 ;; Helper Fns
@@ -84,6 +88,7 @@
                       :description
                       :visibility_type
                       :semantic_type
+                      :json_unfolding
                       :fk_target_field_id
                       :nfc_path]))
 
@@ -103,6 +108,7 @@
                     :description        nil
                     :semantic_type      nil
                     :visibility_type    :normal
+                    :json_unfolding     false
                     :fk_target_field_id nil
                     :nfc_path           nil}
                    original-val)))
@@ -111,6 +117,7 @@
                                                                                   :display_name    "yay"
                                                                                   :description     "foobar"
                                                                                   :semantic_type   :type/Name
+                                                                                  :json_unfolding  true
                                                                                   :visibility_type :sensitive
                                                                                   :nfc_path        ["bob" "dobbs"]})
           (let [updated-val (simple-field-details (t2/select-one Field :id field-id))]
@@ -120,6 +127,7 @@
                       :description        "foobar"
                       :semantic_type      :type/Name
                       :visibility_type    :sensitive
+                      :json_unfolding     true
                       :fk_target_field_id nil
                       :nfc_path           ["bob" "dobbs"]}
                      updated-val)))
@@ -133,6 +141,7 @@
                       :description        nil
                       :semantic_type      nil
                       :visibility_type    :sensitive
+                      :json_unfolding     true
                       :fk_target_field_id nil
                       :nfc_path           nil}
                      (simple-field-details (t2/select-one Field :id field-id)))))))))
@@ -508,6 +517,7 @@
                 :visibility_type    :normal
                 :semantic_type      :type/FK
                 :fk_target_field_id true
+                :json_unfolding     false
                 :nfc_path           nil}
                (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2))))))
       (mt/user-http-request :crowberto :put 200 (format "field/%d" field-id-2) {:semantic_type nil})
@@ -518,6 +528,7 @@
                 :visibility_type    :normal
                 :semantic_type      nil
                 :fk_target_field_id false
+                :json_unfolding     false
                 :nfc_path           nil}
                (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2)))))))))
 
@@ -536,6 +547,7 @@
                   :visibility_type    :normal
                   :semantic_type      :type/FK
                   :fk_target_field_id true
+                  :json_unfolding     false
                   :nfc_path           nil}
                  (mt/boolean-ids-and-timestamps before-change))))
         (mt/user-http-request :crowberto :put 200 (format "field/%d" field-id-3) {:fk_target_field_id field-id-2})
@@ -547,6 +559,7 @@
                     :visibility_type    :normal
                     :semantic_type      :type/FK
                     :fk_target_field_id true
+                    :json_unfolding     false
                     :nfc_path           nil}
                    (mt/boolean-ids-and-timestamps after-change)))
             (is (not= (:fk_target_field_id before-change)
@@ -564,6 +577,7 @@
                 :visibility_type    :normal
                 :semantic_type      nil
                 :fk_target_field_id false
+                :json_unfolding     false
                 :nfc_path           nil}
                (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2))))))
       (mt/user-http-request :crowberto :put 200 (format "field/%d" field-id-2) {:semantic_type      :type/FK
@@ -575,6 +589,7 @@
                 :visibility_type    :normal
                 :semantic_type      :type/FK
                 :fk_target_field_id true
+                :json_unfolding     false
                 :nfc_path           nil}
                (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2)))))))))
 
@@ -592,6 +607,7 @@
                   :visibility_type    :normal
                   :semantic_type      :type/FK
                   :fk_target_field_id true
+                  :json_unfolding     false
                   :nfc_path           nil}
                  (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2))))))
         (mt/user-http-request :crowberto :put 200 (format "field/%d" field-id-2) {:description "foo"})
@@ -602,6 +618,7 @@
                   :visibility_type    :normal
                   :semantic_type      :type/FK
                   :fk_target_field_id true
+                  :json_unfolding     false
                   :nfc_path           nil}
                  (mt/boolean-ids-and-timestamps (simple-field-details (t2/select-one Field :id field-id-2))))))))))
 
@@ -726,3 +743,90 @@
                                     [2 "Small Marble Shoes"]
                                     [3 "Synergistic Granite Chair"]]}
                           (mt/user-http-request :crowberto :get 200 (format "field/%d/values" (mt/id :orders :product_id)))))))))))
+
+(deftest json-unfolding-initially-true-test
+  (mt/test-drivers (mt/normal-drivers-with-feature :nested-field-columns)
+    (when-not (mysql-test/is-mariadb? (u/id (mt/db)))
+      (mt/dataset json
+        ;; Create a new database with the same details as the json dataset, with json unfolding enabled
+        (let [database (t2/select-one Database :id (mt/id))]
+          (mt/with-temp* [Database [database {:engine driver/*driver*, :details (assoc (:details database) :json-unfolding true)}]]
+            (mt/with-db database
+              ;; Sync the new database
+              (sync/sync-database! database)
+              (let [field (t2/select-one Field :id (mt/id :json :json_bit))
+                    get-database (fn [] (t2/select-one Database :id (mt/id)))
+                    set-json-unfolding-for-field! (fn [v]
+                                                    (mt/user-http-request :crowberto :put 200 (format "field/%d" (mt/id :json :json_bit))
+                                                                          (assoc field :json_unfolding v)))
+                    set-json-unfolding-for-db! (fn [v]
+                                                 (let [updated-db (into {} (assoc-in database [:details :json-unfolding] v))]
+                                                   (mt/user-http-request :crowberto :put 200 (format "database/%d" (:id database))
+                                                                         updated-db)))
+                    nested-fields          (fn []
+                                             (->> (t2/select Field :table_id (mt/id :json) :active true :nfc_path [:not= nil])
+                                                  (filter (fn [field] (= (first (:nfc_path field)) "json_bit")))))]
+                (testing "json_unfolding is enabled by default at the field level"
+                  (is (true? (:json_unfolding field))))
+                (testing "nested fields are present since json unfolding is enabled by default"
+                  (is (seq (nested-fields))))
+                (testing "nested fields are removed when json unfolding is disabled for the DB"
+                  (set-json-unfolding-for-db! false)
+                  (sync/sync-database! (get-database))
+                  (is (empty? (nested-fields))))
+                (testing "nested fields are added when json unfolding is enabled again for the DB"
+                  (set-json-unfolding-for-db! true)
+                  (sync/sync-database! (get-database))
+                  (is (seq (nested-fields))))
+                (testing "nested fields are removed when json unfolding is disabled for the field"
+                  (set-json-unfolding-for-field! false)
+                  (is (empty? (nested-fields))))
+                (testing "nested fields are added when json unfolding is enabled again for the field"
+                  (set-json-unfolding-for-field! true)
+                  (is (seq (nested-fields))))))))))))
+
+(deftest json-unfolding-initially-false-test
+  (mt/test-drivers (mt/normal-drivers-with-feature :nested-field-columns)
+    (when-not (mysql-test/is-mariadb? (u/id (mt/db)))
+      (mt/dataset json
+        (let [database (t2/select-one Database :id (mt/id))]
+          (testing "When json_unfolding is disabled at the DB level on the first sync"
+            ;; Create a new database with the same details as the json dataset, with json unfolding disabled
+            (mt/with-temp* [Database [database {:engine driver/*driver*, :details (assoc (:details database) :json-unfolding false)}]]
+              (mt/with-db database
+                ;; Sync the new database
+                (sync/sync-database! database)
+                (let [get-field (fn [] (t2/select-one Field :id (mt/id :json :json_bit)))
+                      get-database (fn [] (t2/select-one Database :id (mt/id)))
+                      set-json-unfolding-for-field! (fn [v]
+                                                      (mt/user-http-request :crowberto :put 200 (format "field/%d" (mt/id :json :json_bit))
+                                                                            (assoc (get-field) :json_unfolding v)))
+                      set-json-unfolding-for-db! (fn [v]
+                                                   (let [updated-db (into {} (assoc-in database [:details :json-unfolding] v))]
+                                                     (mt/user-http-request :crowberto :put 200 (format "database/%d" (:id database))
+                                                                           updated-db)))
+                      nested-fields (fn []
+                                      (->> (t2/select Field :table_id (mt/id :json) :active true :nfc_path [:not= nil])
+                                           (filter (fn [field] (= (first (:nfc_path field)) "json_bit")))))]
+                  (testing "nested fields are not created"
+                    (is (empty? (nested-fields))))
+                  (testing "yet json_unfolding is enabled by default at the field level"
+                    (is (true? (:json_unfolding (get-field)))))
+                  (testing "nested fields are added automatically when json unfolding is enabled for the field,
+                            and json unfolding is alread enabled for the DB"
+                    (set-json-unfolding-for-field! false)
+                    (set-json-unfolding-for-db! true)
+                    (set-json-unfolding-for-field! true)
+                    ;; Wait for the sync to finish
+                    (Thread/sleep 500)
+                    (is (seq (nested-fields))))
+                  (testing "nested fields are added when json unfolding is enabled for the DB"
+                    (set-json-unfolding-for-db! true)
+                    (is (true? (:json-unfolding (:details (get-database)))))
+                    (is (true? (:json_unfolding (get-field))))
+                    (sync/sync-database! (get-database))
+                    (is (seq (nested-fields))))
+                  (testing "nested fields are removed when json unfolding is disabled again"
+                    (set-json-unfolding-for-db! false)
+                    (sync/sync-database! (get-database))
+                    (is (empty? (nested-fields)))))))))))))
diff --git a/test/metabase/cmd/dump_to_h2_test.clj b/test/metabase/cmd/dump_to_h2_test.clj
index 128e8d66d62702d4d69fd320ca92aaeb737e4334..c0ef21b3de574195d56c3b6fd0266355aa617d3b 100644
--- a/test/metabase/cmd/dump_to_h2_test.clj
+++ b/test/metabase/cmd/dump_to_h2_test.clj
@@ -81,7 +81,7 @@
               (load-from-h2/load-from-h2! h2-fixture-db-file)
               (encryption-test/with-secret-key "89ulvIGoiYw6mNELuOoEZphQafnF/zYe+3vT+v70D1A="
                 (t2/insert! Setting {:key "my-site-admin", :value "baz"})
-                (t2/update! Database 1 {:details "{\"db\":\"/tmp/test.db\"}"})
+                (t2/update! Database 1 {:details {:db "/tmp/test.db"}})
                 (dump-to-h2/dump-to-h2! h2-file-plaintext {:dump-plaintext? true})
                 (dump-to-h2/dump-to-h2! h2-file-enc {:dump-plaintext? false})
                 (dump-to-h2/dump-to-h2! h2-file-default-enc))
diff --git a/test/metabase/cmd/rotate_encryption_key_test.clj b/test/metabase/cmd/rotate_encryption_key_test.clj
index 7b52b8ff17060671d3c55d966664ff82d7a901b2..6ca754f531cd15e0454be7a4cd86ecb35105e426 100644
--- a/test/metabase/cmd/rotate_encryption_key_test.clj
+++ b/test/metabase/cmd/rotate_encryption_key_test.clj
@@ -112,7 +112,7 @@
                 (reset! secret-id-unenc (u/the-id secret)))
               (encryption-test/with-secret-key k1
                 (t2/insert! Setting {:key "k1crypted", :value "encrypted with k1"})
-                (t2/update! Database 1 {:details "{\"db\":\"/tmp/test.db\"}"})
+                (t2/update! Database 1 {:details {:db "/tmp/test.db"}})
                 (let [secret (first (t2/insert-returning-instances! Secret {:name       "My Secret (encrypted)"
                                                                             :kind       "password"
                                                                             :value      (.getBytes secret-val StandardCharsets/UTF_8)
@@ -153,11 +153,11 @@
 
               (testing "full rollback when a database details looks encrypted with a different key than the current one"
                 (encryption-test/with-secret-key k3
-                  (let [db (first (t2/insert-returning-instances! Database {:name "k3", :engine :mysql, :details "{\"db\":\"/tmp/k3.db\"}"}))]
+                  (let [db (first (t2/insert-returning-instances! Database {:name "k3", :engine :mysql, :details {:db "/tmp/k3.db"}}))]
                     (is (=? {:name "k3"}
                             db))))
                 (encryption-test/with-secret-key k2
-                  (let [db (first (t2/insert-returning-instances! Database {:name "k2", :engine :mysql, :details "{\"db\":\"/tmp/k2.db\"}"}))]
+                  (let [db (first (t2/insert-returning-instances! Database {:name "k2", :engine :mysql, :details {:db "/tmp/k3.db"}}))]
                     (is (=? {:name "k2"}
                             db)))
                   (is (thrown-with-msg?
@@ -169,8 +169,8 @@
                   (is (= {:db "/tmp/k3.db"} (t2/select-one-fn :details Database :name "k3")))))
 
               (testing "rotate-encryption-key! to nil decrypts the encrypted keys"
-                (t2/update! Database 1 {:details "{\"db\":\"/tmp/test.db\"}"})
-                (t2/update! Database {:name "k3"} {:details "{\"db\":\"/tmp/test.db\"}"})
+                (t2/update! Database 1 {:details {:db "/tmp/test.db"}})
+                (t2/update! Database {:name "k3"} {:details {:db "/tmp/test.db"}})
                 (encryption-test/with-secret-key k2 ; with the last key that we rotated to in the test
                   (rotate-encryption-key! nil))
                 (is (= "unencrypted value" (raw-value "nocrypt")))
diff --git a/test/metabase/driver/common_test.clj b/test/metabase/driver/common_test.clj
index 466b8db81b74d9c6bad37721737011a170eda808..69ecac7adf5fde2c3a1fe423852a26b7d10cd715 100644
--- a/test/metabase/driver/common_test.clj
+++ b/test/metabase/driver/common_test.clj
@@ -65,3 +65,12 @@
             ip-address-field (first
                               (filter #(= (:name %) "cloud-ip-address-info") connection-props))]
         (is (re-find #"If your database is behind a firewall" (:placeholder ip-address-field)))))))
+
+(deftest ^:parallel json-unfolding-default-test
+  (testing "JSON Unfolding database support details behave as they're supposed to"
+    (are [details expected] (= expected
+                               (driver.common/json-unfolding-default {:details details}))
+      {}                      true
+      {:json-unfolding nil}   true
+      {:json-unfolding true}  true
+      {:json-unfolding false} false)))
diff --git a/test/metabase/driver/mysql_test.clj b/test/metabase/driver/mysql_test.clj
index 976b625cd68b3e598bf7b5d9dc60812dd3fefb1f..156ab2372d156edfabfa19573319f71d194f3d0d 100644
--- a/test/metabase/driver/mysql_test.clj
+++ b/test/metabase/driver/mysql_test.clj
@@ -438,60 +438,69 @@
                     :database-type "timestamp",
                     :base-type :type/DateTime,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "1234123412314"]}
                    {:name "json_bit → boop",
                     :database-type "timestamp",
                     :base-type :type/DateTime,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "boop"]}
                    {:name "json_bit → genres",
                     :database-type "text",
                     :base-type :type/Array,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "genres"]}
                    {:name "json_bit → 1234",
                     :database-type "bigint",
                     :base-type :type/Integer,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "1234"]}
                    {:name "json_bit → doop",
                     :database-type "text",
                     :base-type :type/Text,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "doop"]}
                    {:name "json_bit → noop",
                     :database-type "timestamp",
                     :base-type :type/DateTime,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "noop"]}
                    {:name "json_bit → zoop",
                     :database-type "timestamp",
                     :base-type :type/DateTime,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "zoop"]}
                    {:name "json_bit → published",
                     :database-type "text",
                     :base-type :type/Text,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "published"]}
                    {:name "json_bit → title",
                     :database-type "text",
                     :base-type :type/Text,
                     :database-position 0,
+                    :json-unfolding false,
                     :visibility-type :normal,
                     :nfc-path [:json_bit "title"]}}
                  (sql-jdbc.sync/describe-nested-field-columns
                    :mysql
                    (mt/db)
-                   {:name "json"}))))))))
+                   {:name "json" :id (mt/id "json")}))))))))
 
 (deftest big-nested-field-column-test
   (mt/test-driver :mysql
@@ -502,7 +511,7 @@
                  (count (sql-jdbc.sync/describe-nested-field-columns
                          :mysql
                          (mt/db)
-                         {:name "big_json"})))))))))
+                         {:name "big_json" :id (mt/id "big_json")})))))))))
 
 (deftest json-query-test
   (let [boop-identifier (h2x/identifier :field "boop" "bleh -> meh")]
@@ -589,12 +598,14 @@
                     :database-type     "boolean"
                     :base-type         :type/Boolean
                     :database-position 0
+                    :json-unfolding    false
                     :visibility-type   :normal
                     :nfc-path          [:jsoncol "mybool"]}
                    {:name              "jsoncol → myint"
                     :database-type     "double precision"
                     :base-type         :type/Number
                     :database-position 0
+                    :json-unfolding    false
                     :visibility-type   :normal
                     :nfc-path          [:jsoncol "myint"]}}
                  (sql-jdbc.sync/describe-nested-field-columns
@@ -602,20 +613,6 @@
                   (mt/db)
                   (t2/select-one Table :db_id (mt/id) :name "bigint-and-bool-table")))))))))
 
-(deftest can-shut-off-json-unwrapping
-  (mt/test-driver :mysql
-    ;; in here we fiddle with the mysql db details
-    (let [db (t2/select-one Database :id (mt/id))]
-      (try
-        (t2/update! Database (mt/id) {:details (assoc (:details db) :json-unfolding true)})
-        (is (= true (driver/database-supports? :mysql :nested-field-columns (mt/db))))
-        (t2/update! Database (mt/id) {:details (assoc (:details db) :json-unfolding false)})
-        (is (= false (driver/database-supports? :mysql :nested-field-columns (mt/db))))
-        (t2/update! Database (mt/id) {:details (assoc (:details db) :json-unfolding nil)})
-        (is (= true (driver/database-supports? :mysql :nested-field-columns (mt/db))))
-        ;; un fiddle with the mysql db details.
-        (finally (t2/update! Database (mt/id) {:details (:details db)}))))))
-
 (deftest ddl-execute-with-timeout-test1
   (mt/test-driver :mysql
     (mt/dataset json
diff --git a/test/metabase/driver/postgres_test.clj b/test/metabase/driver/postgres_test.clj
index 637ebb287c751f403fc8832b9a814a190a09e37a..c4615a0b8fce7559407102776759ad61c463cc71 100644
--- a/test/metabase/driver/postgres_test.clj
+++ b/test/metabase/driver/postgres_test.clj
@@ -27,6 +27,7 @@
    [metabase.query-processor.store :as qp.store]
    [metabase.sync :as sync]
    [metabase.sync.sync-metadata :as sync-metadata]
+   [metabase.sync.sync-metadata.tables :as sync-tables]
    [metabase.sync.util :as sync-util]
    [metabase.test :as mt]
    [metabase.util :as u]
@@ -347,14 +348,6 @@
 
 ;;; ----------------------------------------- Tests for exotic column types ------------------------------------------
 
-(deftest ^:parallel json-query-support-test
-  (testing "JSON database support options behave as they're supposed to"
-    (are [details expected] (= expected
-                               (driver/database-supports? :postgres :nested-field-columns {:details details}))
-      {}                      true
-      {:json-unfolding true}  true
-      {:json-unfolding false} false)))
-
 (deftest ^:parallel json-query-test
   (let [boop-identifier (h2x/identifier :field "boop" "bleh -> meh")]
     (testing "Transforming MBQL query with JSON in it to postgres query works"
@@ -496,53 +489,60 @@
             spec    (sql-jdbc.conn/connection-details->spec :postgres details)]
         (jdbc/with-db-connection [_conn (sql-jdbc.conn/connection-details->spec :postgres details)]
           (jdbc/execute! spec [describe-json-table-sql]))
-        (mt/with-temp Database [database {:engine :postgres, :details details}]
-          (is (= [:type/JSON :type/SerializedJSON]
-                 (->> (sql-jdbc.sync/describe-table :postgres database {:name "describe_json_table"})
-                      (:fields)
-                      (:take 1)
-                      (first)
-                      ((juxt :base-type :semantic-type)))))
-          (is (= '#{{:name              "incoherent_json_val → b",
-                     :database-type     "text",
-                     :base-type         :type/Text,
-                     :database-position 0,
-                     :nfc-path          [:incoherent_json_val "b"]
-                     :visibility-type   :normal}
-                    {:name              "coherent_json_val → a",
-                     :database-type     "bigint",
-                     :base-type         :type/Integer,
-                     :database-position 0,
-                     :nfc-path          [:coherent_json_val "a"]
-                     :visibility-type   :normal}
-                    {:name              "coherent_json_val → b",
-                     :database-type     "bigint",
-                     :base-type         :type/Integer,
-                     :database-position 0,
-                     :nfc-path          [:coherent_json_val "b"]
-                     :visibility-type   :normal}
-                    {:name "coherent_json_val → c",
-                     :database-type     "timestamp",
-                     :base-type         :type/DateTime,
-                     :database-position 0,
-                     :visibility-type   :normal,
-                     :nfc-path          [:coherent_json_val "c"]}
-                    {:name              "incoherent_json_val → c",
-                     :database-type     "double precision",
-                     :base-type         :type/Number,
-                     :database-position 0,
-                     :visibility-type   :normal,
-                     :nfc-path          [:incoherent_json_val "c"]}
-                    {:name              "incoherent_json_val → d",
-                     :database-type     "bigint",
-                     :base-type         :type/Integer,
-                     :database-position 0,
-                     :visibility-type   :normal,
-                     :nfc-path          [:incoherent_json_val "d"]}}
-                 (sql-jdbc.sync/describe-nested-field-columns
-                  :postgres
-                  database
-                  {:name "describe_json_table"}))))))))
+        (mt/with-temp* [Database [database {:engine :postgres, :details details}]]
+          (mt/with-db database
+            (is (= [:type/JSON :type/SerializedJSON]
+                   (-> (sql-jdbc.sync/describe-table :postgres database {:name "describe_json_table"})
+                       :fields
+                       first
+                       ((juxt :base-type :semantic-type)))))
+            (sync-tables/sync-tables-and-database! database)
+            (is (= '#{{:name              "incoherent_json_val → b",
+                       :database-type     "text",
+                       :base-type         :type/Text,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :nfc-path          [:incoherent_json_val "b"]
+                       :visibility-type   :normal}
+                      {:name              "coherent_json_val → a",
+                       :database-type     "bigint",
+                       :base-type         :type/Integer,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :nfc-path          [:coherent_json_val "a"]
+                       :visibility-type   :normal}
+                      {:name              "coherent_json_val → b",
+                       :database-type     "bigint",
+                       :base-type         :type/Integer,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :nfc-path          [:coherent_json_val "b"]
+                       :visibility-type   :normal}
+                      {:name "coherent_json_val → c",
+                       :database-type     "timestamp",
+                       :base-type         :type/DateTime,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :visibility-type   :normal,
+                       :nfc-path          [:coherent_json_val "c"]}
+                      {:name              "incoherent_json_val → c",
+                       :database-type     "double precision",
+                       :base-type         :type/Number,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :visibility-type   :normal,
+                       :nfc-path          [:incoherent_json_val "c"]}
+                      {:name              "incoherent_json_val → d",
+                       :database-type     "bigint",
+                       :base-type         :type/Integer,
+                       :database-position 0,
+                       :json-unfolding    false
+                       :visibility-type   :normal,
+                       :nfc-path          [:incoherent_json_val "d"]}}
+                   (sql-jdbc.sync/describe-nested-field-columns
+                    :postgres
+                    database
+                    {:name "describe_json_table" :id (mt/id "describe_json_table")})))))))))
 
 (deftest describe-nested-field-columns-identifier-test
   (mt/test-driver :postgres
@@ -556,16 +556,19 @@
                                     "CREATE TABLE bobdobbs.describe_json_table (trivial_json JSONB NOT NULL);"
                                     "INSERT INTO bobdobbs.describe_json_table (trivial_json) VALUES ('{\"a\": 1}');")]))
         (mt/with-temp Database [database {:engine :postgres, :details details}]
-          (is (= #{{:name              "trivial_json → a",
-                    :database-type     "bigint",
-                    :base-type         :type/Integer,
-                    :database-position 0,
-                    :visibility-type   :normal,
-                    :nfc-path          [:trivial_json "a"]}}
-                 (sql-jdbc.sync/describe-nested-field-columns
-                  :postgres
-                  database
-                  {:schema "bobdobbs" :name "describe_json_table"}))))))))
+          (mt/with-db database
+            (sync-tables/sync-tables-and-database! database)
+            (is (= #{{:name              "trivial_json → a",
+                      :database-type     "bigint",
+                      :base-type         :type/Integer,
+                      :database-position 0,
+                      :json-unfolding    false,
+                      :visibility-type   :normal,
+                      :nfc-path          [:trivial_json "a"]}}
+                   (sql-jdbc.sync/describe-nested-field-columns
+                    :postgres
+                    database
+                    {:schema "bobdobbs" :name "describe_json_table" :id (mt/id "describe_json_table")})))))))))
 
 (deftest describe-funky-name-table-nested-field-columns-test
   (mt/test-driver :postgres
@@ -579,16 +582,19 @@
                                     "CREATE TABLE \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json JSONB NOT NULL);"
                                     "INSERT INTO \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json) VALUES ('{\"a\": 1}');")]))
         (mt/with-temp Database [database {:engine :postgres, :details details}]
-          (is (= #{{:name              "trivial_json → a",
-                    :database-type     "bigint",
-                    :base-type         :type/Integer,
-                    :database-position 0,
-                    :visibility-type   :normal,
-                    :nfc-path          [:trivial_json "a"]}}
-                 (sql-jdbc.sync/describe-nested-field-columns
-                  :postgres
-                  database
-                  {:schema "AAAH_#" :name "dESCribe_json_table_%"}))))))))
+          (mt/with-db database
+            (sync-tables/sync-tables-and-database! database)
+            (is (= #{{:name              "trivial_json → a",
+                      :database-type     "bigint",
+                      :base-type         :type/Integer,
+                      :database-position 0,
+                      :json-unfolding    false,
+                      :visibility-type   :normal,
+                      :nfc-path          [:trivial_json "a"]}}
+                   (sql-jdbc.sync/describe-nested-field-columns
+                    :postgres
+                    database
+                    {:schema "AAAH_#" :name "dESCribe_json_table_%" :id (mt/id "dESCribe_json_table_%")})))))))))
 
 (deftest describe-big-nested-field-columns-test
   (mt/test-driver :postgres
@@ -604,19 +610,21 @@
         (jdbc/with-db-connection [_conn (sql-jdbc.conn/connection-details->spec :postgres details)]
           (jdbc/execute! spec [sql]))
         (mt/with-temp Database [database {:engine :postgres, :details details}]
-          (is (= sql-jdbc.describe-table/max-nested-field-columns
-                 (count
-                  (sql-jdbc.sync/describe-nested-field-columns
-                   :postgres
-                   database
-                   {:name "big_json_table"}))))
-          (is (str/includes?
-                (get-in (mt/with-log-messages-for-level :warn
-                              (sql-jdbc.sync/describe-nested-field-columns
-                                :postgres
-                                database
-                                {:name "big_json_table"})) [0 2])
-                "More nested field columns detected than maximum.")))))))
+          (mt/with-db database
+            (sync-tables/sync-tables-and-database! database)
+            (is (= sql-jdbc.describe-table/max-nested-field-columns
+                   (count
+                    (sql-jdbc.sync/describe-nested-field-columns
+                     :postgres
+                     database
+                     {:name "big_json_table" :id (mt/id "big_json_table")}))))
+            (is (str/includes?
+                 (get-in (mt/with-log-messages-for-level :warn
+                           (sql-jdbc.sync/describe-nested-field-columns
+                            :postgres
+                            database
+                            {:name "big_json_table" :id (mt/id "big_json_table")})) [0 2])
+                 "More nested field columns detected than maximum."))))))))
 
 (mt/defdataset with-uuid
   [["users"
@@ -807,25 +815,28 @@
 
         (testing "check that describe-table properly describes the database & base types of the enum fields"
           (is (= {:name   "birds"
-                  :fields #{{:name                      "name"
-                             :database-type             "varchar"
-                             :base-type                 :type/Text
-                             :pk?                       true
-                             :database-position         0
-                             :database-required         true
-                             :database-is-auto-increment false}
-                            {:name                      "status"
-                             :database-type             "bird_status"
-                             :base-type                 :type/PostgresEnum
-                             :database-position         1
-                             :database-required         true
-                             :database-is-auto-increment false}
-                            {:name                      "type"
-                             :database-type             "bird type"
-                             :base-type                 :type/PostgresEnum
-                             :database-position         2
-                             :database-required         true
-                             :database-is-auto-increment false}}}
+                  :fields #{{:name                       "name"
+                             :database-type              "varchar"
+                             :base-type                  :type/Text
+                             :pk?                        true
+                             :database-position          0
+                             :database-required          true
+                             :database-is-auto-increment false
+                             :json-unfolding             false}
+                            {:name                       "status"
+                             :database-type              "bird_status"
+                             :base-type                  :type/PostgresEnum
+                             :database-position          1
+                             :database-required          true
+                             :database-is-auto-increment false
+                             :json-unfolding             false}
+                            {:name                       "type"
+                             :database-type              "bird type"
+                             :base-type                  :type/PostgresEnum
+                             :database-position          2
+                             :database-required          true
+                             :database-is-auto-increment false
+                             :json-unfolding             false}}}
                  (driver/describe-table :postgres db {:name "birds"}))))
 
         (testing "check that when syncing the DB the enum types get recorded appropriately"
diff --git a/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj b/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj
index 9f18ced8c117c9b0ffb1f31b43ac32be14c990cb..8de3e2e1586869472ae9df47d20f2d73ca2c9ad0 100644
--- a/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj
+++ b/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj
@@ -27,12 +27,12 @@
 (deftest describe-table-test
   (is (= {:name "VENUES",
           :fields
-          #{{:name "ID", :database-type "BIGINT", :base-type :type/BigInteger, :database-position 0, :pk? true :database-required false :database-is-auto-increment true}
-            {:name "NAME", :database-type "CHARACTER VARYING", :base-type :type/Text, :database-position 1 :database-required false :database-is-auto-increment false}
-            {:name "CATEGORY_ID", :database-type "INTEGER", :base-type :type/Integer, :database-position 2 :database-required false :database-is-auto-increment false}
-            {:name "LATITUDE", :database-type "DOUBLE PRECISION", :base-type :type/Float, :database-position 3 :database-required false :database-is-auto-increment false}
-            {:name "LONGITUDE", :database-type "DOUBLE PRECISION", :base-type :type/Float, :database-position 4 :database-required false :database-is-auto-increment false}
-            {:name "PRICE", :database-type "INTEGER", :base-type :type/Integer, :database-position 5 :database-required false :database-is-auto-increment false}}}
+          #{{:name "ID", :database-type "BIGINT", :base-type :type/BigInteger, :database-position 0, :pk? true :database-required false :database-is-auto-increment true :json-unfolding false}
+            {:name "NAME", :database-type "CHARACTER VARYING", :base-type :type/Text, :database-position 1 :database-required false :database-is-auto-increment false :json-unfolding false}
+            {:name "CATEGORY_ID", :database-type "INTEGER", :base-type :type/Integer, :database-position 2 :database-required false :database-is-auto-increment false :json-unfolding false}
+            {:name "LATITUDE", :database-type "DOUBLE PRECISION", :base-type :type/Float, :database-position 3 :database-required false :database-is-auto-increment false :json-unfolding false}
+            {:name "LONGITUDE", :database-type "DOUBLE PRECISION", :base-type :type/Float, :database-position 4 :database-required false :database-is-auto-increment false :json-unfolding false}
+            {:name "PRICE", :database-type "INTEGER", :base-type :type/Integer, :database-position 5 :database-required false :database-is-auto-increment false :json-unfolding false}}}
          (sql-jdbc.describe-table/describe-table :h2 (mt/id) {:name "VENUES"}))))
 
 (deftest describe-auto-increment-on-non-pk-field-test
@@ -54,19 +54,22 @@
                          :database-required         false
                          :database-type             "INTEGER"
                          :name                      "id"
-                         :pk?                       true}
+                         :pk?                       true
+                         :json-unfolding            false}
                         {:base-type                 :type/Integer
                          :database-is-auto-increment true
                          :database-position         1
                          :database-required         false
                          :database-type             "INTEGER"
-                         :name                      "count"}
+                         :name                      "count"
+                         :json-unfolding            false}
                         {:base-type                 :type/Integer
                          :database-is-auto-increment false
                          :database-position         2
                          :database-required         true
                          :database-type             "INTEGER"
-                         :name                      "rank"}}
+                         :name                      "rank"
+                         :json-unfolding            false}}
               :name "employee_counter"}
              (sql-jdbc.describe-table/describe-table :h2 (mt/id) {:name "employee_counter"}))))))
 
@@ -174,12 +177,13 @@
       (is (= types (#'sql-jdbc.describe-table/row->types row)))))
   (testing "JSON row->types handles bigint that comes in and gets interpreted as Java bigint OK (#22732)"
     (let [int-row   {:zlob {"blob" (java.math.BigInteger. "123124124312134235234235345344324352")}}]
-      (is (= #{{:name "zlob → blob",
-                :database-type "decimal",
-                :base-type :type/BigInteger,
+      (is (= #{{:name              "zlob → blob",
+                :database-type     "decimal",
+                :base-type         :type/BigInteger,
                 :database-position 0,
-                :visibility-type :normal,
-                :nfc-path [:zlob "blob"]}}
+                :json-unfolding    false
+                :visibility-type   :normal,
+                :nfc-path          [:zlob "blob"]}}
              (-> int-row
                  (#'sql-jdbc.describe-table/row->types)
                  (#'sql-jdbc.describe-table/field-types->fields)))))))
diff --git a/test/metabase/driver/sql_jdbc_test.clj b/test/metabase/driver/sql_jdbc_test.clj
index 9518d8a98c29ab04756764393921d84165787c37..d34ce4cba2ff0afd71feb02e10c7a6561953ef2f 100644
--- a/test/metabase/driver/sql_jdbc_test.clj
+++ b/test/metabase/driver/sql_jdbc_test.clj
@@ -27,37 +27,43 @@
                      :pk?               true
                      :database-position 0
                      :database-required false
-                     :database-is-auto-increment true}
+                     :database-is-auto-increment true
+                     :json-unfolding    false}
                     {:name              "NAME"
                      :database-type     "CHARACTER VARYING"
                      :base-type         :type/Text
                      :database-position 1
                      :database-required false
-                     :database-is-auto-increment false}
+                     :database-is-auto-increment false
+                     :json-unfolding    false}
                     {:name              "CATEGORY_ID"
                      :database-type     "INTEGER"
                      :base-type         :type/Integer
                      :database-position 2
                      :database-required false
-                     :database-is-auto-increment false}
+                     :database-is-auto-increment false
+                     :json-unfolding    false}
                     {:name              "LATITUDE"
                      :database-type     "DOUBLE PRECISION"
                      :base-type         :type/Float
                      :database-position 3
                      :database-required false
-                     :database-is-auto-increment false}
+                     :database-is-auto-increment false
+                     :json-unfolding    false}
                     {:name              "LONGITUDE"
                      :database-type     "DOUBLE PRECISION"
                      :base-type         :type/Float
                      :database-position 4
                      :database-required false
-                     :database-is-auto-increment false}
+                     :database-is-auto-increment false
+                     :json-unfolding    false}
                     {:name              "PRICE"
                      :database-type     "INTEGER"
                      :base-type         :type/Integer
                      :database-position 5
                      :database-required false
-                     :database-is-auto-increment false}}}
+                     :database-is-auto-increment false
+                     :json-unfolding    false}}}
          (driver/describe-table :h2 (mt/db) (t2/select-one Table :id (mt/id :venues))))))
 
 (deftest ^:parallel describe-table-fks-test
diff --git a/test/metabase/sync/sync_dynamic_test.clj b/test/metabase/sync/sync_dynamic_test.clj
index a91e314623bd90d2ba428fffe99c3cb12b9806af..7e039018e2fd9fb5177b00873f9dd1556336d56c 100644
--- a/test/metabase/sync/sync_dynamic_test.clj
+++ b/test/metabase/sync/sync_dynamic_test.clj
@@ -22,7 +22,14 @@
                            (for [field fields]
                              (u/select-non-nil-keys
                               field
-                              [:table_id :name :fk_target_field_id :parent_id :base_type :database_type :database_is_auto_increment]))))))))
+                              [:table_id
+                               :name
+                               :fk_target_field_id
+                               :parent_id
+                               :base_type
+                               :database_type
+                               :database_is_auto_increment
+                               :json-unfolding]))))))))
 
 (defn- get-tables [database-or-id]
   (->> (hydrate (t2/select Table, :db_id (u/the-id database-or-id), {:order-by [:id]}) :fields)
diff --git a/test/metabase/sync/sync_metadata/fields/fetch_metadata_test.clj b/test/metabase/sync/sync_metadata/fields/fetch_metadata_test.clj
index 899d89c572c4b0b2032c9d51429fe8bd4b8a79c8..0018b66df555bc9463778b979a70564eeb8a39c6 100644
--- a/test/metabase/sync/sync_metadata/fields/fetch_metadata_test.clj
+++ b/test/metabase/sync/sync_metadata/fields/fetch_metadata_test.clj
@@ -23,7 +23,8 @@
               :semantic-type     :type/PK
               :pk?               true
               :database-required false
-              :database-is-auto-increment true}
+              :database-is-auto-increment true
+              :json-unfolding    false}
              {:name              "buyer"
               :database-type     "OBJECT"
               :base-type         :type/Dictionary
@@ -31,12 +32,14 @@
               :pk?               false
               :database-required false
               :database-is-auto-increment false
+              :json-unfolding    false
               :nested-fields     #{{:name              "name"
                                     :database-type     "VARCHAR"
                                     :base-type         :type/Text
                                     :effective-type    :type/Text
                                     :pk?               false
                                     :database-required false
+                                    :json-unfolding    false
                                     :database-is-auto-increment false}
                                    {:name              "cc"
                                     :database-type     "VARCHAR"
@@ -44,6 +47,7 @@
                                     :effective-type    :type/Text
                                     :pk?               false
                                     :database-required false
+                                    :json-unfolding    false
                                     :database-is-auto-increment false}}}
              {:name              "ts"
               :database-type     "BIGINT"
@@ -52,6 +56,7 @@
               :coercion-strategy :Coercion/UNIXMilliSeconds->DateTime
               :pk?               false
               :database-is-auto-increment false
+              :json-unfolding    false
               :database-required false}
              {:name              "toucan"
               :database-type     "OBJECT"
@@ -60,12 +65,14 @@
               :pk?               false
               :database-required false
               :database-is-auto-increment false
+              :json-unfolding    false
               :nested-fields     #{{:name              "name"
                                     :database-type     "VARCHAR"
                                     :base-type         :type/Text
                                     :effective-type    :type/Text
                                     :pk?               false
                                     :database-required false
+                                    :json-unfolding    false
                                     :database-is-auto-increment false}
                                    {:name              "details"
                                     :database-type     "OBJECT"
@@ -73,6 +80,7 @@
                                     :effective-type    :type/Dictionary
                                     :pk?               false
                                     :database-required false
+                                    :json-unfolding    false
                                     :database-is-auto-increment false
                                     :nested-fields     #{{:name              "weight"
                                                           :database-type     "DECIMAL"
@@ -81,6 +89,7 @@
                                                           :semantic-type     :type/Category
                                                           :pk?               false
                                                           :database-required false
+                                                          :json-unfolding    false
                                                           :database-is-auto-increment false}
                                                          {:name              "age"
                                                           :database-type     "INT"
@@ -88,6 +97,7 @@
                                                           :effective-type    :type/Integer
                                                           :pk?               false
                                                           :database-required false
+                                                          :json-unfolding    false
                                                           :database-is-auto-increment false}}}}}}
 
            (let [transactions-table-id   (u/the-id (t2/select-one-pk Table :db_id (u/the-id db), :name "transactions"))
diff --git a/test/metabase/sync/sync_metadata/fields/sync_metadata_test.clj b/test/metabase/sync/sync_metadata/fields/sync_metadata_test.clj
index 605c26cc36f79150a102dac86f18b22d128f3dda..ca3f673624dd7678b3abade9de4d09991ac52d11 100644
--- a/test/metabase/sync/sync_metadata/fields/sync_metadata_test.clj
+++ b/test/metabase/sync/sync_metadata/fields/sync_metadata_test.clj
@@ -22,54 +22,54 @@
   (testing "test that if database-type changes we will update it in the DB"
     (is (= [["Field" 1 {:database_type "Integer"}]]
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment false}
-            {:name              "My Field"
-             :database-type     "NULL"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "NULL"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment false})))))
 
 (deftest database-required-changed-test
   (testing "test that if database-required changes we will update it in the DB"
     (is (= [["Field" 1 {:database_required false}]]
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment false}
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required true
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          true
              :database-is-auto-increment false})))))
 
 (deftest database-is-auto-increment-changed-test
   (testing "test that if database-required changes we will update it in the DB"
     (is (= [["Field" 1 {:database_is_auto_increment true}]]
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment true}
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment false})))
     (is (= [["Field" 1 {:database_is_auto_increment false}]]
            (updates-that-will-be-performed
@@ -79,31 +79,53 @@
              :database-position 0
              ;; no :database-is-auto-increment key to test case where describe-table does not not return it
              :database-required false}
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required false
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
              :database-is-auto-increment true})))))
 
+(deftest json-unfolding-test
+  (testing "test that if json-unfolding changes the DB doesn't get updated"
+    (is (= []
+           (updates-that-will-be-performed
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
+             :json-unfolding             true
+             :database-is-auto-increment false}
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
+             :json-unfolding             false
+             :database-is-auto-increment false})))))
+
 (deftest no-op-test
   (testing "no changes should be made (i.e., no calls to `update!`) if nothing changes"
     (is (= []
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
-             :database-is-auto-increment true}
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required false
-             :database-is-auto-increment true})))))
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
+             :database-is-auto-increment true
+             :json-unfolding             false}
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
+             :database-is-auto-increment true
+             :json-unfolding             false})))))
 
 (deftest nil-database-type-test
   (testing (str "test that if `database-type` comes back as `nil` in the metadata from the sync process, we won't try "
@@ -111,37 +133,41 @@
                 "`TableMetadataField` schema.")
     (is (= [["Field" 1 {:database_type "NULL"}]]
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     nil
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
-             :database-is-auto-increment false}
-            {:name              "My Field"
-             :database-type     "Integer"
-             :base-type         :type/Integer
-             :database-position 0
-             :id                1
-             :database-required false
-             :database-is-auto-increment false}))))
+            {:name                       "My Field"
+             :database-type              nil
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
+             :database-is-auto-increment false
+             :json-unfolding             false}
+            {:name                       "My Field"
+             :database-type              "Integer"
+             :base-type                  :type/Integer
+             :database-position          0
+             :id                         1
+             :database-required          false
+             :database-is-auto-increment false
+             :json-unfolding             false}))))
 
   (testing (str "if `database-type` comes back as `nil` and was already saved in application DB as `NULL` no changes "
                 "should be made")
     (is (= []
            (updates-that-will-be-performed
-            {:name              "My Field"
-             :database-type     nil
-             :base-type         :type/Integer
-             :database-position 0
-             :database-required false
-             :database-is-auto-increment false}
-            {:name              "My Field"
-             :database-type     "NULL"
-             :base-type         :type/Integer
-             :id                1
-             :database-position 0
-             :database-required false
-             :database-is-auto-increment false})))))
+            {:name                       "My Field"
+             :database-type              nil
+             :base-type                  :type/Integer
+             :database-position          0
+             :database-required          false
+             :database-is-auto-increment false
+             :json-unfolding             false}
+            {:name                       "My Field"
+             :database-type              "NULL"
+             :base-type                  :type/Integer
+             :id                         1
+             :database-position          0
+             :database-required          false
+             :database-is-auto-increment false
+             :json-unfolding             false})))))
 
 (deftest dont-overwrite-semantic-type-test
   (testing "We should not override non-nil `semantic_type`s"
@@ -152,6 +178,7 @@
              :base-type         :type/Integer
              :semantic-type     nil
              :database-position 0
+             :json-unfolding    false
              :database-required false
              :database-is-auto-increment false}
             {:name              "My Field"
@@ -160,5 +187,6 @@
              :semantic-type     :type/Price
              :id                1
              :database-position 0
+             :json-unfolding    false
              :database-required false
              :database-is-auto-increment false})))))
diff --git a/test/metabase/sync_test.clj b/test/metabase/sync_test.clj
index 488799f62ead849ee88d7766719d85cf17021ce2..03441a8b148228e2043627d4abb59e725a6a6ce7 100644
--- a/test/metabase/sync_test.clj
+++ b/test/metabase/sync_test.clj
@@ -32,17 +32,20 @@
                         :base-type         :type/Integer
                         :semantic-type     :type/PK
                         :database-is-auto-increment true
+                        :json-unfolding    false
                         :database-position 0}
                        {:name              "title"
                         :database-type     "VARCHAR"
                         :base-type         :type/Text
                         :semantic-type     :type/Title
                         :database-is-auto-increment false
+                        :json-unfolding    false
                         :database-position 1}
                        {:name              "studio"
                         :database-type     "VARCHAR"
                         :base-type         :type/Text
                         :database-is-auto-increment false
+                        :json-unfolding    false
                         :database-position 2}}
              :description nil}
    "studio" {:name   "studio"
@@ -52,11 +55,13 @@
                         :base-type         :type/Text
                         :semantic-type     :type/PK
                         :database-is-auto-increment false
+                        :json-unfolding    false
                         :database-position 0}
                        {:name              "name"
                         :database-type     "VARCHAR"
                         :base-type         :type/Text
                         :database-is-auto-increment false
+                        :json-unfolding    false
                         :database-position 1}}
              :description ""}})
 
@@ -120,6 +125,7 @@
     :last_analyzed       false
     :parent_id           false
     :position            0
+    :json_unfolding      false
     :table_id            true
     :updated_at          true}))
 
diff --git a/test/metabase/test/mock/toucanery.clj b/test/metabase/test/mock/toucanery.clj
index f45f5ab01ba2f2edba309ba1a5643b85d33b9e47..8616373540925995ac2246ccf933afc6c7b0233c 100644
--- a/test/metabase/test/mock/toucanery.clj
+++ b/test/metabase/test/mock/toucanery.clj
@@ -9,60 +9,72 @@
 (def toucanery-tables
   {"transactions" {:name   "transactions"
                    :schema nil
-                   :fields #{{:name          "id"
-                              :pk?           true
-                              :database-type "SERIAL"
-                              :base-type     :type/Integer
+                   :fields #{{:name                       "id"
+                              :pk?                        true
+                              :database-type              "SERIAL"
+                              :base-type                  :type/Integer
+                              :json-unfolding             false
                               :database-is-auto-increment true}
-                             {:name          "ts"
-                              :database-type "BIGINT"
-                              :base-type     :type/BigInteger
-                              :effective-type :type/DateTime
-                              :coercion-strategy :Coercion/UNIXMilliSeconds->DateTime
+                             {:name                       "ts"
+                              :database-type              "BIGINT"
+                              :base-type                  :type/BigInteger
+                              :effective-type             :type/DateTime
+                              :coercion-strategy          :Coercion/UNIXMilliSeconds->DateTime
+                              :json-unfolding             false
                               :database-is-auto-increment false}
-                             {:name          "toucan"
-                              :database-type "OBJECT"
-                              :base-type     :type/Dictionary
+                             {:name                       "toucan"
+                              :database-type              "OBJECT"
+                              :base-type                  :type/Dictionary
+                              :json-unfolding             false
                               :database-is-auto-increment false
-                              :nested-fields #{{:name          "name"
-                                                :database-type "VARCHAR"
-                                                :base-type     :type/Text
-                                                :database-is-auto-increment false}
-                                               {:name          "details"
-                                                :database-type "OBJECT"
-                                                :base-type     :type/Dictionary
-                                                :database-is-auto-increment false
-                                                :nested-fields #{{:name          "age"
-                                                                  :database-type "INT"
-                                                                  :database-is-auto-increment false
-                                                                  :base-type     :type/Integer}
-                                                                 {:name          "weight"
-                                                                  :database-type "DECIMAL"
-                                                                  :database-is-auto-increment false
-                                                                  :semantic-type :type/Category
-                                                                  :base-type     :type/Decimal}}}}}
-                             {:name          "buyer"
-                              :database-type "OBJECT"
+                              :nested-fields              #{{:name                       "name"
+                                                             :database-type              "VARCHAR"
+                                                             :base-type                  :type/Text
+                                                             :json-unfolding             false
+                                                             :database-is-auto-increment false}
+                                                            {:name                       "details"
+                                                             :database-type              "OBJECT"
+                                                             :base-type                  :type/Dictionary
+                                                             :json-unfolding             false
+                                                             :database-is-auto-increment false
+                                                             :nested-fields              #{{:name                       "age"
+                                                                                            :database-type              "INT"
+                                                                                            :database-is-auto-increment false
+                                                                                            :json-unfolding             false
+                                                                                            :base-type                  :type/Integer}
+                                                                                           {:name                       "weight"
+                                                                                            :database-type              "DECIMAL"
+                                                                                            :database-is-auto-increment false
+                                                                                            :json-unfolding             false
+                                                                                            :semantic-type              :type/Category
+                                                                                            :base-type                  :type/Decimal}}}}}
+                             {:name           "buyer"
+                              :database-type  "OBJECT"
                               :database-is-auto-increment false
-                              :base-type     :type/Dictionary
-                              :nested-fields #{{:name          "name"
-                                                :database-type "VARCHAR"
-                                                :database-is-auto-increment false
-                                                :base-type     :type/Text}
-                                               {:name          "cc"
-                                                :database-type "VARCHAR"
-                                                :database-is-auto-increment false
-                                                :base-type     :type/Text}}}}}
+                              :json-unfolding false
+                              :base-type      :type/Dictionary
+                              :nested-fields  #{{:name                       "name"
+                                                 :database-type              "VARCHAR"
+                                                 :json-unfolding             false
+                                                 :base-type                  :type/Text
+                                                 :database-is-auto-increment false}
+                                                {:name                       "cc"
+                                                 :database-type              "VARCHAR"
+                                                 :json-unfolding             false
+                                                 :base-type                  :type/Text
+                                                 :database-is-auto-increment false}}}}}
    "employees"    {:name   "employees"
                    :schema nil
-                   :fields #{{:name          "id"
-                              :database-type "SERIAL"
+                   :fields #{{:name                       "id"
+                              :database-type              "SERIAL"
+                              :json-unfolding             false
                               :database-is-auto-increment true
-                              :base-type     :type/Integer}
-                             {:name          "name"
-                              :database-type "VARCHAR"
+                              :base-type                  :type/Integer}
+                             {:name                       "name"
+                              :database-type              "VARCHAR"
+                              :json-unfolding             false
                               :database-is-auto-increment false
-                              :base-type     :type/Text}}}})
+                              :base-type                  :type/Text}}}})
 
 (driver/register! ::toucanery, :abstract? true)
 
diff --git a/test/metabase/test/mock/util.clj b/test/metabase/test/mock/util.clj
index 309635b0d54468a9e04ed15d9ce16a1ff1c47724..2598e4faeef93bb9353234a915b34f73e764eb68 100644
--- a/test/metabase/test/mock/util.clj
+++ b/test/metabase/test/mock/util.clj
@@ -33,7 +33,8 @@
    :position           0
    :visibility_type    :normal
    :preview_display    true
-   :created_at         true})
+   :created_at         true
+   :json_unfolding     false})
 
 (def pulse-channel-defaults
   {:schedule_frame nil