diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn
index d14e7a0f7e7aa54512c41ea4fe3e7071000dd85f..f8fdabc586bc64b1d3a6c73b5d1592c12a88b66f 100644
--- a/.clj-kondo/config.edn
+++ b/.clj-kondo/config.edn
@@ -123,49 +123,52 @@
     schema.core/either                                                           {:namespaces ["metabase.*"]}}}
 
   :discouraged-var
-  {clojure.core/print                  {:message "Use clojure.tools.logging instead of clojure.core/print"}
-   clojure.core/println                {:message "Use clojure.tools.logging instead of clojure.core/println"}
-   clojure.core/printf                 {:message "Use clojure.tools.logging instead of clojure.core/printf"}
-   clojure.core/prn                    {:message "Use clojure.tools.logging instead of clojure.core/prn"}
-   clojure.core/pr                     {:message "Use clojure.tools.logging instead of clojure.core/pr"}
-   clojure.pprint/pprint               {:message "Use clojure.tools.logging instead of clojure.pprint/pprint"}
-   clojure.string/lower-case           {:message "Use metabase.util/lower-case-en instead of clojure.string/lower-case"}
-   clojure.string/upper-case           {:message "Use metabase.util/upper-case-en instead of clojure.string/upper-case"}
-   honeysql.core/call                  {:message "Use hx/call instead because it is Honey SQL 2 friendly"}
-   honeysql.core/raw                   {:message "Use hx/raw instead because it is Honey SQL 2 friendly"}
-   java-time/with-clock                {:message "Use mt/with-clock"}
-   metabase.test/with-temp             {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp"}
-   ;; metabase.test/with-temp*            {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp*"}
-   toucan.db/select                    {:message "Use t2/select instead of toucan.db/select"}
-   toucan.db/select-one                {:message "Use t2/select-one instead of toucan.db/select-one"}
-   toucan.db/select-one-id             {:message "Use t2/select-one-pk instead of toucan.db/select-one-id"}
-   toucan.db/select-one-field          {:message "Use t2/select-one-fn instead of toucan.db/select-one-field"}
-   toucan.db/select-ids                {:message "Use t2/select-pks-set instead of toucan.db/select-ids"}
-   toucan.db/select-field              {:message "Use t2/select-fn instead of toucan.db/select-field"}
-   toucan.db/select-field->field       {:message "Use t2/select-fn->fn instead of toucan.db/select-field->field"}
-   toucan.db/select-field->id          {:message "Use t2/select-fn->pk instead of toucan.db/select-field->id"}
-   toucan.db/select-id->field          {:message "Use t2/select-pk->field instead of toucan.db/select-id->field"}
-   toucan.db/simple-insert!            {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert!"}
-   toucan.db/simple-insert-many!       {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert-many!"}
-   toucan.db/simple-delete!            {:message "Use t2/delete! with table name instead of toucan.db/simple-delete!"}
-   toucan.db/delete!                   {:message "Use t2/delete! instead of toucan.db/delete!"}
-   toucan.db/insert!                   {:message "Use t2/insert-returning-instances! instead of toucan.db/insert!"}
-   toucan.db/update!                   {:message "Use t2/update! instead of toucan.db/update!"}
-   toucan.db/update-where!             {:message "Use t2/update! instead of toucan.db/update-where!"}
-   toucan.db/query                     {:message "Use mdb.query/query instead of toucan.db/query"}
-   toucan.db/count                     {:message "Use t2/count instead of toucan.db/count"}
-   toucan.db/exists?                   {:message "Use t2/exists? instead of toucan.db/exists?"}
-   toucan.db/transaction               {:message "Use t2/with-transaction instead of toucan.db/transaction"}
-   toucan.db/with-call-counting        {:message "Use t2/with-call-count instead of toucan.db/with-call-counting"}
-   toucan.db/execute!                  {:message "Use t2/query-one instead of toucan.db/execute!"}
-   toucan.db/reducible-query           {:message "Use mdb.query/reducible-query instead of toucan.db/reducible-query"}
-   toucan.db/resolve-model             {:message "Use metabase.db.util/resolve-model instead of toucan.db/resolve-model"}
-   toucan.models/primary-key           {:message "Use metabase.db.util/primary-key instead of toucan.models/primary-key"}
-   toucan.models/model?                {:message "Use mdb.u/toucan-model? instead of toucan.models/model?"}
-   toucan.hydrate/hydrate              {:message "Use t2/hydrate instead of toucan.hydrate/hydrate"}
-   toucan.util.test/with-temp-defaults {:message "Use t2.with-temp/with-temp-defaults instead of toucan.util.test/with-temp-defaults"}
-   toucan.util.test/with-temp          {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp"}
-   toucan.util.test/with-temp*         {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp*"}}
+  {clojure.core/print                   {:message "Use clojure.tools.logging instead of clojure.core/print"}
+   clojure.core/println                 {:message "Use clojure.tools.logging instead of clojure.core/println"}
+   clojure.core/printf                  {:message "Use clojure.tools.logging instead of clojure.core/printf"}
+   clojure.core/prn                     {:message "Use clojure.tools.logging instead of clojure.core/prn"}
+   clojure.core/pr                      {:message "Use clojure.tools.logging instead of clojure.core/pr"}
+   clojure.java.jdbc/get-connection     {:message "Use sql-jdbc.execute/do-with-connection-with-options (for drivers) or t2/with-connection (for the app DB) instead of jdbc/get-connection"}
+   clojure.java.jdbc/with-db-connection {:message "Use sql-jdbc.execute/do-with-connection-with-options (for drivers) or t2/with-connection (for the app DB) instead of jdbc/with-db-connection"}
+   clojure.java.jdbc/with-db-metadata   {:message "Use sql-jdbc.execute/do-with-connection-with-options + .getMetaData instead of jdbc/with-db-metadata"}
+   clojure.pprint/pprint                {:message "Use clojure.tools.logging instead of clojure.pprint/pprint"}
+   clojure.string/lower-case            {:message "Use metabase.util/lower-case-en instead of clojure.string/lower-case"}
+   clojure.string/upper-case            {:message "Use metabase.util/upper-case-en instead of clojure.string/upper-case"}
+   honeysql.core/call                   {:message "Use hx/call instead because it is Honey SQL 2 friendly"}
+   honeysql.core/raw                    {:message "Use hx/raw instead because it is Honey SQL 2 friendly"}
+   java-time/with-clock                 {:message "Use mt/with-clock"}
+   metabase.test/with-temp              {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp"}
+   ;; metabase.test/with-temp*             {:message "Use t2.with-temp/with-temp instead of metabase.test/with-temp*"}
+   toucan.db/select                     {:message "Use t2/select instead of toucan.db/select"}
+   toucan.db/select-one                 {:message "Use t2/select-one instead of toucan.db/select-one"}
+   toucan.db/select-one-id              {:message "Use t2/select-one-pk instead of toucan.db/select-one-id"}
+   toucan.db/select-one-field           {:message "Use t2/select-one-fn instead of toucan.db/select-one-field"}
+   toucan.db/select-ids                 {:message "Use t2/select-pks-set instead of toucan.db/select-ids"}
+   toucan.db/select-field               {:message "Use t2/select-fn instead of toucan.db/select-field"}
+   toucan.db/select-field->field        {:message "Use t2/select-fn->fn instead of toucan.db/select-field->field"}
+   toucan.db/select-field->id           {:message "Use t2/select-fn->pk instead of toucan.db/select-field->id"}
+   toucan.db/select-id->field           {:message "Use t2/select-pk->field instead of toucan.db/select-id->field"}
+   toucan.db/simple-insert!             {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert!"}
+   toucan.db/simple-insert-many!        {:message "Use t2/insert-returning-pks! with table name instead of toucan.db/simple-insert-many!"}
+   toucan.db/simple-delete!             {:message "Use t2/delete! with table name instead of toucan.db/simple-delete!"}
+   toucan.db/delete!                    {:message "Use t2/delete! instead of toucan.db/delete!"}
+   toucan.db/insert!                    {:message "Use t2/insert-returning-instances! instead of toucan.db/insert!"}
+   toucan.db/update!                    {:message "Use t2/update! instead of toucan.db/update!"}
+   toucan.db/update-where!              {:message "Use t2/update! instead of toucan.db/update-where!"}
+   toucan.db/query                      {:message "Use mdb.query/query instead of toucan.db/query"}
+   toucan.db/count                      {:message "Use t2/count instead of toucan.db/count"}
+   toucan.db/exists?                    {:message "Use t2/exists? instead of toucan.db/exists?"}
+   toucan.db/transaction                {:message "Use t2/with-transaction instead of toucan.db/transaction"}
+   toucan.db/with-call-counting         {:message "Use t2/with-call-count instead of toucan.db/with-call-counting"}
+   toucan.db/execute!                   {:message "Use t2/query-one instead of toucan.db/execute!"}
+   toucan.db/reducible-query            {:message "Use mdb.query/reducible-query instead of toucan.db/reducible-query"}
+   toucan.db/resolve-model              {:message "Use metabase.db.util/resolve-model instead of toucan.db/resolve-model"}
+   toucan.models/primary-key            {:message "Use metabase.db.util/primary-key instead of toucan.models/primary-key"}
+   toucan.models/model?                 {:message "Use mdb.u/toucan-model? instead of toucan.models/model?"}
+   toucan.hydrate/hydrate               {:message "Use t2/hydrate instead of toucan.hydrate/hydrate"}
+   toucan.util.test/with-temp-defaults  {:message "Use t2.with-temp/with-temp-defaults instead of toucan.util.test/with-temp-defaults"}
+   toucan.util.test/with-temp           {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp"}
+   toucan.util.test/with-temp*          {:message "Use t2.with-temp/with-temp instead of toucan.util.test/with-temp*"}}
 
   :discouraged-namespace
   {camel-snake-kebab.core {:message "CSK is not Turkish-safe, use the versions in metabase.util instead."}
diff --git a/dev/src/dev.clj b/dev/src/dev.clj
index 9ba4951a9a89a75e5a47a36830d0741176f5093d..c9f233d89f9c44873fcaa0aa6e55972e6c06edd6 100644
--- a/dev/src/dev.clj
+++ b/dev/src/dev.clj
@@ -142,12 +142,17 @@
     (try
       (driver/with-driver driver
         (letfn [(thunk []
-                  (with-open [conn (sql-jdbc.execute/connection-with-timezone driver (mt/db) (qp.timezone/report-timezone-id-if-supported driver (mt/db)))
-                              stmt (sql-jdbc.execute/prepared-statement driver conn sql params)
-                              rs   (sql-jdbc.execute/execute-prepared-statement! driver stmt)]
-                    (let [rsmeta (.getMetaData rs)]
-                      {:cols (sql-jdbc.execute/column-metadata driver rsmeta)
-                       :rows (reduce conj [] (sql-jdbc.execute/reducible-rows driver rs rsmeta canceled-chan))})))]
+                  (let [db (mt/db)]
+                    (sql-jdbc.execute/do-with-connection-with-options
+                     driver
+                     db
+                     {:session-timezone (qp.timezone/report-timezone-id-if-supported driver db)}
+                     (fn [conn]
+                       (with-open [stmt (sql-jdbc.execute/prepared-statement driver conn sql params)
+                                   rs   (sql-jdbc.execute/execute-prepared-statement! driver stmt)]
+                         (let [rsmeta (.getMetaData rs)]
+                           {:cols (sql-jdbc.execute/column-metadata driver rsmeta)
+                            :rows (reduce conj [] (sql-jdbc.execute/reducible-rows driver rs rsmeta canceled-chan))}))))))]
           (if dataset
             (data.impl/do-with-dataset (data.impl/resolve-dataset-definition *ns* dataset) thunk)
             (thunk))))
diff --git a/docs/developers-guide/driver-changelog.md b/docs/developers-guide/driver-changelog.md
index bb0074963aa1a21f9a371e6750b5cba74d39fcf2..2de54e04b3e82a5db49633fa6d663fe4d4bfdff4 100644
--- a/docs/developers-guide/driver-changelog.md
+++ b/docs/developers-guide/driver-changelog.md
@@ -15,7 +15,15 @@ title: Driver interface changelog
   An implemention of the multimethod `metabase.driver/database-supports?` for `:schemas` is required only if the
   database doesn't store tables in schemas.
 
-- The multimethod `metabase.driver/supports?` has been deprecated in favor of `metabase.driver/database-supports?`. The existing default implementation of `database-supports?` currently calls `supports?`, but it will be removed in 0.55.0.
+- The multimethod `metabase.driver/supports?` has been deprecated in favor of `metabase.driver/database-supports?`.
+  The existing default implementation of `database-supports?` currently calls `supports?`, but it will be removed in
+  0.50.0.
+
+- `metabase.driver.sql-jdbc.execute/connection-with-timezone` has been marked deprecated and is scheduled for removal
+  in Metabase 0.50.0. The new method `metabase.driver.sql-jdbc.execute/do-with-connection-with-options` replaces it.
+  Migration to the new method is straightforward. See PR [#22166](https://github.com/metabase/metabase/pull/22166) for
+  more information. You should use `metabase.driver.sql-jdbc.execute/do-with-connection-with-options` instead of
+  `clojure.java.jdbc/with-db-connection` or `clojure.java.jdbc/get-connection` going forward.
 
 ## Metabase 0.46.0
 
diff --git a/modules/drivers/athena/src/metabase/driver/athena.clj b/modules/drivers/athena/src/metabase/driver/athena.clj
index 6f29ac3730480dfca0e1d2a7bb2bbbfbce359806..326070bdfde6cf4c3dbf40cdfe8465c9e0e585fe 100644
--- a/modules/drivers/athena/src/metabase/driver/athena.clj
+++ b/modules/drivers/athena/src/metabase/driver/athena.clj
@@ -22,7 +22,7 @@
    [metabase.util.i18n :refer [trs]]
    [metabase.util.log :as log])
   (:import
-   (java.sql DatabaseMetaData)
+   (java.sql Connection DatabaseMetaData)
    (java.time OffsetDateTime ZonedDateTime)))
 
 (set! *warn-on-reflection* true)
@@ -368,12 +368,17 @@
 
 (defmethod driver/describe-table :athena
   [driver {{:keys [catalog]} :details, :as database} table]
-  (jdbc/with-db-metadata [metadata (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (assoc (select-keys table [:name :schema])
-           :fields (try
-                     (describe-table-fields metadata database driver table catalog)
-                     (catch Throwable _
-                       (set nil))))))
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^Connection conn]
+     (let [metadata (.getMetaData conn)]
+       (assoc (select-keys table [:name :schema])
+              :fields (try
+                        (describe-table-fields metadata database driver table catalog)
+                        (catch Throwable _
+                          (set nil))))))))
 
 (defn- get-tables
   "Athena can query EXTERNAL and MANAGED tables."
@@ -424,8 +429,13 @@
 ; If we want to limit the initial connection to a specific database/schema, I think we'd have to do that here...
 (defmethod driver/describe-database :athena
   [driver {details :details, :as database}]
-  {:tables (jdbc/with-db-metadata [metadata (sql-jdbc.conn/db->pooled-connection-spec database)]
-             (fast-active-tables driver metadata details))})
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^Connection conn]
+     (let [metadata (.getMetaData conn)]
+       {:tables (fast-active-tables driver metadata details)}))))
 
 ; Unsure if this is the right way to approach building the parameterized query...but it works
 (defn- prepare-query [driver {query :native, :as outer-query}]
diff --git a/modules/drivers/athena/test/metabase/test/data/athena.clj b/modules/drivers/athena/test/metabase/test/data/athena.clj
index 392030490a76dac3dd1ba7615e8d3ad582d6527f..6335b15f383b4426b75e4b59ce203639499c2dc3 100644
--- a/modules/drivers/athena/test/metabase/test/data/athena.clj
+++ b/modules/drivers/athena/test/metabase/test/data/athena.clj
@@ -7,6 +7,7 @@
    [metabase.driver.athena :as athena]
    [metabase.driver.ddl.interface :as ddl.i]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
    [metabase.driver.sql.util.unprepare :as unprepare]
    [metabase.test.data.interface :as tx]
@@ -211,10 +212,14 @@
 (defn- existing-databases
   "Set of databases that already exist in our S3 bucket, so we don't try to create them a second time."
   []
-  (jdbc/with-db-connection [conn (server-connection-spec)]
-    (let [dbs (into #{} (map :database_name) (jdbc/query conn ["SHOW DATABASES;"]))]
-      (log/infof "The following Athena databases have already been created: %s" (pr-str (sort dbs)))
-      dbs)))
+  (sql-jdbc.execute/do-with-connection-with-options
+   :athena
+   (server-connection-spec)
+   nil
+   (fn [^java.sql.Connection conn]
+     (let [dbs (into #{} (map :database_name) (jdbc/query {:connection conn} ["SHOW DATABASES;"]))]
+       (log/infof "The following Athena databases have already been created: %s" (pr-str (sort dbs)))
+       dbs))))
 
 (def ^:private ^:dynamic *allow-database-creation*
   "Whether to allow database creation. This is normally disabled to prevent people from accidentally loading duplicate
diff --git a/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj b/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
index 1c3462e6b7741b7dc07493d48a2a79e55549f47c..04f810dfae474d451cd2d43194f6ae5a018764c1 100644
--- a/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
+++ b/modules/drivers/presto-jdbc/src/metabase/driver/presto_jdbc.clj
@@ -35,19 +35,8 @@
   (:import
    (com.facebook.presto.jdbc PrestoConnection)
    (com.mchange.v2.c3p0 C3P0ProxyConnection)
-   (java.sql
-     Connection
-     PreparedStatement
-     ResultSet
-     ResultSetMetaData
-     Time
-     Types)
-   (java.time
-     LocalDateTime
-     LocalTime
-     OffsetDateTime
-     OffsetTime
-     ZonedDateTime)
+   (java.sql Connection PreparedStatement ResultSet ResultSetMetaData Time Types)
+   (java.time LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime)
    (java.time.format DateTimeFormatter)
    (java.time.temporal ChronoField Temporal)))
 
@@ -587,28 +576,34 @@
 
 (defmethod driver/describe-database :presto-jdbc
   [driver {{:keys [catalog schema] :as _details} :details :as database}]
-  (with-open [conn (-> (sql-jdbc.conn/db->pooled-connection-spec database)
-                       jdbc/get-connection)]
-    (let [schemas (if schema #{(describe-schema driver conn catalog schema)}
-                             (all-schemas driver conn catalog))]
-      {:tables (reduce set/union schemas)})))
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^Connection conn]
+     (let [schemas (if schema #{(describe-schema driver conn catalog schema)}
+                       (all-schemas driver conn catalog))]
+       {:tables (reduce set/union schemas)}))))
 
 (defmethod driver/describe-table :presto-jdbc
   [driver {{:keys [catalog] :as _details} :details :as database} {schema :schema, table-name :name}]
-  (with-open [conn (-> (sql-jdbc.conn/db->pooled-connection-spec database)
-                     jdbc/get-connection)]
-    (let [sql (describe-table-sql driver catalog schema table-name)]
-      (log/trace (trs "Running statement in describe-table: {0}" sql))
-      {:schema schema
-       :name   table-name
-       :fields (into
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^Connection conn]
+     (let [sql (describe-table-sql driver catalog schema table-name)]
+       (log/trace (trs "Running statement in describe-table: {0}" sql))
+       {:schema schema
+        :name   table-name
+        :fields (into
                  #{}
                  (map-indexed (fn [idx {:keys [column type] :as _col}]
-                                {:name column
-                                 :database-type type
+                                {:name              column
+                                 :database-type     type
                                  :base-type         (presto-type->base-type type)
                                  :database-position idx}))
-                 (jdbc/reducible-query {:connection conn} sql))})))
+                 (jdbc/reducible-query {:connection conn} sql))}))))
 
 ;;; The Presto JDBC driver DOES NOT support the `.getImportedKeys` method so just return `nil` here so the `:sql-jdbc`
 ;;; implementation doesn't try to use it.
@@ -657,26 +652,47 @@
   ^PrestoConnection [^C3P0ProxyConnection pooled-conn]
   (.unwrap pooled-conn PrestoConnection))
 
-(defmethod sql-jdbc.execute/connection-with-timezone :presto-jdbc
-  [driver database ^String timezone-id]
-  ;; Presto supports setting the session timezone via a `PrestoConnection` instance method. Under the covers,
-  ;; this is equivalent to the `X-Presto-Time-Zone` header in the HTTP request.
-  (let [conn            (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))
-        underlying-conn (pooled-conn->presto-conn conn)]
-    (try
-      (sql-jdbc.execute/set-best-transaction-level! driver conn)
-      (when-not (str/blank? timezone-id)
-        ;; set session time zone if defined
-        (.setTimeZoneId underlying-conn timezone-id))
+;;; for some insane reason Presto's JDBC driver does not seem to work properly when reusing a Connection to execute
+;;; multiple PreparedStatements... this breaks the test-data loading code. To work around that, we'll track the original
+;;; Connection spec from the top-level call, and if the `:presto-jdbc/force-fresh?` is passed in to recursive calls
+;;; we'll create a NEW connection using the original spec every time. See for example the code
+;;; in [[metabase.test.data.presto-jdbc]]
+(def ^:dynamic ^:private *original-connection-spec* nil)
+
+(defn- set-connection-options! [driver ^java.sql.Connection conn {:keys [^String session-timezone write?], :as _options}]
+  (let [underlying-conn (pooled-conn->presto-conn conn)]
+    (sql-jdbc.execute/set-best-transaction-level! driver conn)
+    (when-not (str/blank? session-timezone)
+      ;; set session time zone if defined
+      (.setTimeZoneId underlying-conn session-timezone))
+    ;; as with statement and prepared-statement, cannot set holdability on the connection level
+    (let [read-only? (not write?)]
       (try
-        (.setReadOnly conn true)
+        (.setReadOnly conn read-only?)
         (catch Throwable e
-          (log/debug e (trs "Error setting connection to read-only"))))
-      ;; as with statement and prepared-statement, cannot set holdability on the connection level
-      conn
-      (catch Throwable e
-        (.close conn)
-        (throw e)))))
+          (log/debug e (trs "Error setting connection read-only to {0}" (pr-str read-only?))))))))
+
+(defmethod sql-jdbc.execute/do-with-connection-with-options :presto-jdbc
+  [driver db-or-id-or-spec options f]
+  ;; Presto supports setting the session timezone via a `PrestoConnection` instance method. Under the covers,
+  ;; this is equivalent to the `X-Presto-Time-Zone` header in the HTTP request.
+  (cond
+    (nil? *original-connection-spec*)
+    (binding [*original-connection-spec* db-or-id-or-spec]
+      (sql-jdbc.execute/do-with-connection-with-options driver db-or-id-or-spec options f))
+
+    (:presto-jdbc/force-fresh? options)
+    (sql-jdbc.execute/do-with-connection-with-options driver *original-connection-spec* (dissoc options :presto-jdbc/force-fresh?) f)
+
+    :else
+    (sql-jdbc.execute/do-with-resolved-connection
+     driver
+     db-or-id-or-spec
+     (dissoc options :session-timezone)
+     (fn [^java.sql.Connection conn]
+       (when-not (sql-jdbc.execute/recursive-connection?)
+         (set-connection-options! driver conn options))
+       (f conn)))))
 
 (defn- date-time->substitution [ts-str]
   (sql.params.substitution/make-stmt-subs "from_iso8601_timestamp(?)" [ts-str]))
diff --git a/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj b/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
index 9bd821b290b9d3d0f8c2cd70ca4c110f8e9e05d9..7974e9883723e61430122f94853f50f50fc73eff 100644
--- a/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
+++ b/modules/drivers/presto-jdbc/test/metabase/driver/presto_jdbc_test.clj
@@ -1,6 +1,5 @@
 (ns metabase.driver.presto-jdbc-test
   (:require
-   [clojure.java.jdbc :as jdbc]
    [clojure.test :refer :all]
    [honeysql.format :as hformat]
    [java-time :as t]
@@ -9,6 +8,7 @@
    [metabase.driver :as driver]
    [metabase.driver.presto-jdbc :as presto-jdbc]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.query-processor :as sql.qp]
    [metabase.models.database :refer [Database]]
    [metabase.models.field :refer [Field]]
@@ -185,11 +185,14 @@
 
 (defn- execute-ddl! [ddl-statements]
   (mt/with-driver :presto-jdbc
-    (let [jdbc-spec (sql-jdbc.conn/connection-details->spec :presto-jdbc (clone-db-details))]
-      (with-open [conn (jdbc/get-connection jdbc-spec)]
-        (doseq [ddl-stmt ddl-statements]
-          (with-open [stmt (.prepareStatement conn ddl-stmt)]
-            (.executeUpdate stmt)))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     :presto-jdbc
+     (sql-jdbc.conn/connection-details->spec :presto-jdbc (clone-db-details))
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (doseq [ddl-stmt ddl-statements]
+         (with-open [stmt (.prepareStatement conn ddl-stmt)]
+           (.executeUpdate stmt)))))))
 
 (deftest specific-schema-sync-test
   (mt/test-driver :presto-jdbc
diff --git a/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
index f7bdfe26d50da6aa743076106a38ff9d1badf9cb..36abfdb311b4a61dc171a847f7ce2a4b0688aa31 100644
--- a/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
+++ b/modules/drivers/presto-jdbc/test/metabase/test/data/presto_jdbc.clj
@@ -4,7 +4,6 @@
    [clojure.string :as str]
    [clojure.test :refer :all]
    [metabase.config :as config]
-   [metabase.connection-pool :as connection-pool]
    [metabase.driver :as driver]
    [metabase.driver.ddl.interface :as ddl.i]
    [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
@@ -17,7 +16,7 @@
    [metabase.test.data.sql.ddl :as ddl]
    [metabase.util.log :as log])
   (:import
-   (java.sql Connection DriverManager PreparedStatement)))
+   (java.sql Connection PreparedStatement)))
 
 (set! *warn-on-reflection* true)
 
@@ -117,29 +116,25 @@
   [_ dbdef tabledef]
   (load-data dbdef tabledef))
 
-(defn- jdbc-spec->connection
-  "This is to work around some weird interplay between clojure.java.jdbc caching behavior of connections based on URL,
-  combined with the fact that the Presto driver apparently closes the connection when it closes a prepare statement.
-  Therefore, create a fresh connection from the DriverManager."
-  ^Connection [jdbc-spec]
-  (DriverManager/getConnection (format "jdbc:%s:%s" (:subprotocol jdbc-spec) (:subname jdbc-spec))
-    (connection-pool/map->properties (select-keys jdbc-spec [:user :SSL]))))
-
 (defmethod load-data/do-insert! :presto-jdbc
   [driver spec table-identifier row-or-rows]
   (let [statements (ddl/insert-rows-ddl-statements driver table-identifier row-or-rows)]
-    (with-open [conn (jdbc-spec->connection spec)]
-      (doseq [[^String sql & params] statements]
-        (try
-          (with-open [^PreparedStatement stmt (.prepareStatement conn sql)]
-            (sql-jdbc.execute/set-parameters! driver stmt params)
-            (let [tbl-nm        ((comp last :components) (into {} table-identifier))
-                  rows-affected (.executeUpdate stmt)]
-              (log/infof "[%s] Inserted %d rows into %s." driver rows-affected tbl-nm)))
-          (catch Throwable e
-            (throw (ex-info (format "[%s] Error executing SQL: %s" driver (ex-message e))
-                     {:driver driver, :sql sql, :params params}
-                     e))))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     spec
+     {:write? true, :presto-jdbc/force-fresh? true}
+     (fn [^Connection conn]
+       (doseq [[^String sql & params] statements]
+         (try
+           (with-open [^PreparedStatement stmt (.prepareStatement conn sql)]
+             (sql-jdbc.execute/set-parameters! driver stmt params)
+             (let [tbl-nm        ((comp last :components) (into {} table-identifier))
+                   rows-affected (.executeUpdate stmt)]
+               (log/infof "[%s] Inserted %d rows into %s." driver rows-affected tbl-nm)))
+           (catch Throwable e
+             (throw (ex-info (format "[%s] Error executing SQL: %s" driver (ex-message e))
+                             {:driver driver, :sql sql, :params params}
+                             e)))))))))
 
 (defmethod sql.tx/drop-db-if-exists-sql :presto-jdbc [_ _] nil)
 (defmethod sql.tx/create-db-sql         :presto-jdbc [_ _] nil)
diff --git a/modules/drivers/redshift/src/metabase/driver/redshift.clj b/modules/drivers/redshift/src/metabase/driver/redshift.clj
index ab5afe7b98541aee68ff7f181a3b40697307ea6b..ccf939d8898152955291afd00e8835a0fd45df94 100644
--- a/modules/drivers/redshift/src/metabase/driver/redshift.clj
+++ b/modules/drivers/redshift/src/metabase/driver/redshift.clj
@@ -130,22 +130,23 @@
   [_]
   "SET TIMEZONE TO %s;")
 
-;; This impl is basically the same as the default impl in `sql-jdbc.execute`, but doesn't attempt to make the
-;; connection read-only, because that seems to be causing problems for people
-(defmethod sql-jdbc.execute/connection-with-timezone :redshift
-  [driver database ^String timezone-id]
-  (let [conn (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))]
-    (try
-      (sql-jdbc.execute/set-best-transaction-level! driver conn)
-      (sql-jdbc.execute/set-time-zone-if-supported! driver conn timezone-id)
-      (try
-        (.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)
-        (catch Throwable e
-          (log/debug e (trs "Error setting default holdability for connection"))))
-      conn
-      (catch Throwable e
-        (.close ^Connection conn)
-        (throw e)))))
+;; This impl is basically the same as the default impl in [[metabase.driver.sql-jdbc.execute]], but doesn't attempt to
+;; make the connection read-only, because that seems to be causing problems for people
+(defmethod sql-jdbc.execute/do-with-connection-with-options :redshift
+  [driver db-or-id-or-spec {:keys [^String session-timezone], :as options} f]
+  (sql-jdbc.execute/do-with-resolved-connection
+   driver
+   db-or-id-or-spec
+   options
+   (fn [^Connection conn]
+     (when-not (sql-jdbc.execute/recursive-connection?)
+       (sql-jdbc.execute/set-best-transaction-level! driver conn)
+       (sql-jdbc.execute/set-time-zone-if-supported! driver conn session-timezone)
+       (try
+         (.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)
+         (catch Throwable e
+           (log/debug e (trs "Error setting default holdability for connection")))))
+     (f conn))))
 
 (defn- prepare-statement ^PreparedStatement [^Connection conn sql]
   (.prepareStatement conn
@@ -167,10 +168,13 @@
 (defn- quote-literal-for-database
   "This function invokes quote-literal-for-connection with a connection for the given database. See its docstring for
   more detail."
-  [database s]
-  (let [jdbc-spec (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (with-open [conn (jdbc/get-connection jdbc-spec)]
-      (quote-literal-for-connection conn s))))
+  [driver database s]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [conn]
+     (quote-literal-for-connection conn s))))
 
 (defmethod sql.qp/->honeysql [:redshift :regex-match-first]
   [driver [_ arg pattern]]
@@ -179,7 +183,7 @@
    ;; the parameter to REGEXP_SUBSTR can only be a string literal; neither prepared statement parameters nor encoding/
    ;; decoding functions seem to work (fails with java.sql.SQLExcecption: "The pattern must be a valid UTF-8 literal
    ;; character expression"), hence we will use a different function to safely escape it before splicing here
-   [:raw (quote-literal-for-database (qp.store/database) pattern)]])
+   [:raw (quote-literal-for-database driver (qp.store/database) pattern)]])
 
 (defmethod sql.qp/->honeysql [:redshift :replace]
   [driver [_ arg pattern replacement]]
diff --git a/modules/drivers/redshift/test/metabase/driver/redshift_test.clj b/modules/drivers/redshift/test/metabase/driver/redshift_test.clj
index 3dd46b6c8dfef89b6885b55d7d975b862452ca87..bdab08c1caaef9d5faf95c01e23fce290762ec21 100644
--- a/modules/drivers/redshift/test/metabase/driver/redshift_test.clj
+++ b/modules/drivers/redshift/test/metabase/driver/redshift_test.clj
@@ -311,18 +311,22 @@
         (try
           (binding [redshift.test/*use-original-filtered-syncable-schemas-impl?* true]
             (t2.with-temp/with-temp [Database db {:engine :redshift, :details (assoc db-det :user temp-username :password user-pw)}]
-              (with-open [conn (jdbc/get-connection (sql-jdbc.conn/db->pooled-connection-spec db))]
-                (let [schemas (reduce conj
-                                      #{}
-                                      (sql-jdbc.sync/filtered-syncable-schemas :redshift
-                                                                               conn
-                                                                               (.getMetaData conn)
-                                                                               nil
-                                                                               nil))]
-                  (testing "filtered-syncable-schemas for the user should contain the newly created random schema"
-                    (is (contains? schemas random-schema)))
-                  (testing "should not contain the current session-schema name (since that was never granted)"
-                    (is (not (contains? schemas (redshift.test/unique-session-schema)))))))))
+              (sql-jdbc.execute/do-with-connection-with-options
+               :redshift
+               db
+               nil
+               (fn [^java.sql.Connection conn]
+                 (let [schemas (reduce conj
+                                       #{}
+                                       (sql-jdbc.sync/filtered-syncable-schemas :redshift
+                                                                                conn
+                                                                                (.getMetaData conn)
+                                                                                nil
+                                                                                nil))]
+                   (testing "filtered-syncable-schemas for the user should contain the newly created random schema"
+                     (is (contains? schemas random-schema)))
+                   (testing "should not contain the current session-schema name (since that was never granted)"
+                     (is (not (contains? schemas (redshift.test/unique-session-schema))))))))))
           (finally
             (execute! (str "REVOKE USAGE ON SCHEMA %s FROM %s;%n"
                            "DROP USER IF EXISTS %s;%n"
@@ -341,19 +345,22 @@
                                                                    (eduction
                                                                     cat
                                                                     [(orig metadata) [fake-schema-name]])))]
-            (let [jdbc-spec (sql-jdbc.conn/db->pooled-connection-spec (mt/db))]
-              (with-open [conn (jdbc/get-connection jdbc-spec)]
-                (letfn [(schemas []
-                          (reduce
-                           conj
-                           #{}
-                           (sql-jdbc.sync/filtered-syncable-schemas :redshift conn (.getMetaData conn) nil nil)))]
-                  (testing "if schemas-with-usage-permissions is disabled, the ::fake-schema should come back"
-                    (with-redefs [redshift/reducible-schemas-with-usage-permissions (fn [_ reducible]
-                                                                                      reducible)]
-                      (is (contains? (schemas) fake-schema-name))))
-                  (testing "normally, ::fake-schema should be filtered out (because it does not exist)"
-                    (is (not (contains? (schemas) fake-schema-name)))))))))))))
+            (sql-jdbc.execute/do-with-connection-with-options
+             :redshift
+             (mt/db)
+             nil
+             (fn [^java.sql.Connection conn]
+               (letfn [(schemas []
+                         (reduce
+                          conj
+                          #{}
+                          (sql-jdbc.sync/filtered-syncable-schemas :redshift conn (.getMetaData conn) nil nil)))]
+                 (testing "if schemas-with-usage-permissions is disabled, the ::fake-schema should come back"
+                   (with-redefs [redshift/reducible-schemas-with-usage-permissions (fn [_ reducible]
+                                                                                     reducible)]
+                     (is (contains? (schemas) fake-schema-name))))
+                 (testing "normally, ::fake-schema should be filtered out (because it does not exist)"
+                   (is (not (contains? (schemas) fake-schema-name)))))))))))))
 
 (mt/defdataset numeric-unix-timestamps
   [["timestamps"
diff --git a/modules/drivers/redshift/test/metabase/test/data/redshift.clj b/modules/drivers/redshift/test/metabase/test/data/redshift.clj
index d9b88d4e37c6d48b7ad9fcf4fcbd3f985b03bac4..6150e213a68117fe833f61ce251e965490bffd59 100644
--- a/modules/drivers/redshift/test/metabase/test/data/redshift.clj
+++ b/modules/drivers/redshift/test/metabase/test/data/redshift.clj
@@ -1,9 +1,9 @@
 (ns metabase.test.data.redshift
   (:require
-   [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [java-time :as t]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync :as sql-jdbc.sync]
    [metabase.driver.sql.test-util.unique-prefix :as sql.tu.unique-prefix]
    [metabase.test.data.interface :as tx]
@@ -165,11 +165,14 @@
       (.execute stmt sql))))
 
 (defmethod tx/before-run :redshift
-  [_driver]
-  (with-open [conn (jdbc/get-connection
-                    (sql-jdbc.conn/connection-details->spec :redshift @db-connection-details))]
-    (delete-old-schemas! conn)
-    (create-session-schema! conn)))
+  [driver]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   (sql-jdbc.conn/connection-details->spec driver @db-connection-details)
+   {:write? true}
+   (fn [conn]
+     (delete-old-schemas! conn)
+     (create-session-schema! conn))))
 
 (defonce ^:private ^{:arglists '([driver connection metadata _ _])}
   original-filtered-syncable-schemas
diff --git a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
index a166d59405d523a850db3dd9ee7b2090e0e9fd6b..590bbed066be52bd81207e310acca65e12addf93 100644
--- a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
+++ b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj
@@ -388,42 +388,49 @@
                                                (sql.qp/with-driver-honey-sql-version driver
                                                  (sql.qp/->honeysql driver table)))]]}))
 
-(defmethod driver/describe-database :snowflake [driver database]
+(defmethod driver/describe-database :snowflake
+  [driver database]
   (let [db-name          (db-name database)
         excluded-schemas (set (sql-jdbc.sync/excluded-schemas driver))]
     (qp.store/with-store
       (qp.store/fetch-and-store-database! (u/the-id database))
-      (let [spec            (sql-jdbc.conn/db->pooled-connection-spec database)
-            sql             (format "SHOW OBJECTS IN DATABASE \"%s\"" db-name)
+      (let [sql             (format "SHOW OBJECTS IN DATABASE \"%s\"" db-name)
             schema-patterns (driver.s/db-details->schema-filter-patterns "schema-filters" database)
             [inclusion-patterns exclusion-patterns] schema-patterns]
         (log/tracef "[Snowflake] %s" sql)
-        (with-open [conn (jdbc/get-connection spec)]
-          {:tables (into
-                    #{}
-                    (comp (filter (fn [{schema :schema_name, table-name :name}]
-                                    (and (not (contains? excluded-schemas schema))
-                                         (driver.s/include-schema? inclusion-patterns
-                                                                   exclusion-patterns
-                                                                   schema)
-                                         (sql-jdbc.sync/have-select-privilege? driver conn schema table-name))))
-                          (map (fn [{schema :schema_name, table-name :name, remark :comment}]
-                                 {:name        table-name
-                                  :schema      schema
-                                  :description (not-empty remark)})))
-                    (try
-                      (jdbc/reducible-query {:connection conn} sql)
-                      (catch Throwable e
-                        (throw (ex-info (trs "Error executing query: {0}" (ex-message e)) {:sql sql} e)))))})))))
+        (sql-jdbc.execute/do-with-connection-with-options
+         driver
+         database
+         nil
+         (fn [^Connection conn]
+           {:tables (into
+                     #{}
+                     (comp (filter (fn [{schema :schema_name, table-name :name}]
+                                     (and (not (contains? excluded-schemas schema))
+                                          (driver.s/include-schema? inclusion-patterns
+                                                                    exclusion-patterns
+                                                                    schema)
+                                          (sql-jdbc.sync/have-select-privilege? driver conn schema table-name))))
+                           (map (fn [{schema :schema_name, table-name :name, remark :comment}]
+                                  {:name        table-name
+                                   :schema      schema
+                                   :description (not-empty remark)})))
+                     (try
+                       (jdbc/reducible-query {:connection conn} sql)
+                       (catch Throwable e
+                         (throw (ex-info (trs "Error executing query: {0}" (ex-message e)) {:sql sql} e)))))}))))))
 
 (defmethod driver/describe-table :snowflake
   [driver database table]
-  (let [spec (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (with-open [conn (jdbc/get-connection spec)]
-      (->> (assoc (select-keys table [:name :schema])
-                  :fields (sql-jdbc.sync/describe-table-fields driver conn table (db-name database)))
-           ;; find PKs and mark them
-           (sql-jdbc.sync/add-table-pks driver conn (db-name database))))))
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^Connection conn]
+     (->> (assoc (select-keys table [:name :schema])
+                 :fields (sql-jdbc.sync/describe-table-fields driver conn table (db-name database)))
+          ;; find PKs and mark them
+          (sql-jdbc.sync/add-table-pks driver conn (db-name database))))))
 
 (defn- escape-name-for-metadata [entity-name]
   (when entity-name
@@ -471,9 +478,12 @@
   [driver db-or-id-or-spec-or-conn table & [db-name-or-nil]]
   (if (instance? Connection db-or-id-or-spec-or-conn)
     (describe-table-fks* driver db-or-id-or-spec-or-conn table db-name-or-nil)
-    (let [spec (sql-jdbc.conn/db->pooled-connection-spec db-or-id-or-spec-or-conn)]
-      (with-open [conn (jdbc/get-connection spec)]
-        (describe-table-fks* driver conn table db-name-or-nil)))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     db-or-id-or-spec-or-conn
+     nil
+     (fn [conn]
+       (describe-table-fks* driver conn table db-name-or-nil)))))
 
 (defmethod driver/describe-table-fks :snowflake
   [driver database table]
diff --git a/modules/drivers/snowflake/test/metabase/test/data/snowflake.clj b/modules/drivers/snowflake/test/metabase/test/data/snowflake.clj
index edde99360ac8c03a9a289d450d3a367f71064c01..92c89259849271b9f59d6be01785d93a45941cfb 100644
--- a/modules/drivers/snowflake/test/metabase/test/data/snowflake.clj
+++ b/modules/drivers/snowflake/test/metabase/test/data/snowflake.clj
@@ -3,6 +3,7 @@
    [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.test-util.unique-prefix :as sql.tu.unique-prefix]
    [metabase.driver.sql.util.unprepare :as unprepare]
    [metabase.test.data.interface :as tx]
@@ -87,18 +88,22 @@
 (defn- old-dataset-names
   "Return a collection of all dataset names that are old -- prefixed with a date two days ago or older?"
   []
-  (with-open [conn (jdbc/get-connection (no-db-connection-spec))]
-    (let [metadata (.getMetaData conn)]
-      (with-open [rset (.getCatalogs metadata)]
-        (loop [acc []]
-          (if-not (.next rset)
-            acc
-            ;; for whatever dumb reason the Snowflake JDBC driver always returns these as uppercase despite us making
-            ;; them all lower-case
-            (let [catalog (u/lower-case-en (.getString rset "TABLE_CAT"))
-                  acc     (cond-> acc
-                            (sql.tu.unique-prefix/old-dataset-name? catalog) (conj catalog))]
-              (recur acc))))))))
+  (sql-jdbc.execute/do-with-connection-with-options
+   :snowflake
+   (no-db-connection-spec)
+   {:write? true}
+   (fn [^java.sql.Connection conn]
+     (let [metadata (.getMetaData conn)]
+       (with-open [rset (.getCatalogs metadata)]
+         (loop [acc []]
+           (if-not (.next rset)
+             acc
+             ;; for whatever dumb reason the Snowflake JDBC driver always returns these as uppercase despite us making
+             ;; them all lower-case
+             (let [catalog (u/lower-case-en (.getString rset "TABLE_CAT"))
+                   acc     (cond-> acc
+                             (sql.tu.unique-prefix/old-dataset-name? catalog) (conj catalog))]
+               (recur acc)))))))))
 
 (defn- delete-old-datasets!
   "Delete any datasets prefixed by a date that is two days ago or older. See comments above."
@@ -109,19 +114,23 @@
   #_{:clj-kondo/ignore [:discouraged-var]}
   (println "[Snowflake] deleting old datasets...")
   (when-let [old-datasets (not-empty (old-dataset-names))]
-    (with-open [conn (jdbc/get-connection (no-db-connection-spec))
-                stmt (.createStatement conn)]
-      (doseq [dataset-name old-datasets]
-        #_{:clj-kondo/ignore [:discouraged-var]}
-        (println "[Snowflake] Deleting old dataset:" dataset-name)
-        (try
-          (.execute stmt (format "DROP DATABASE \"%s\";" dataset-name))
-          ;; if this fails for some reason it's probably just because some other job tried to delete the dataset at the
-          ;; same time. No big deal. Just log this and carry on trying to delete the other datasets. If we don't end up
-          ;; deleting anything it's not the end of the world because it won't affect our ability to run our tests
-          (catch Throwable e
-            #_{:clj-kondo/ignore [:discouraged-var]}
-            (println "[Snowflake] Error deleting old dataset:" (ex-message e))))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     :snowflake
+     (no-db-connection-spec)
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (with-open [stmt (.createStatement conn)]
+         (doseq [dataset-name old-datasets]
+           #_{:clj-kondo/ignore [:discouraged-var]}
+           (println "[Snowflake] Deleting old dataset:" dataset-name)
+           (try
+             (.execute stmt (format "DROP DATABASE \"%s\";" dataset-name))
+             ;; if this fails for some reason it's probably just because some other job tried to delete the dataset at the
+             ;; same time. No big deal. Just log this and carry on trying to delete the other datasets. If we don't end up
+             ;; deleting anything it's not the end of the world because it won't affect our ability to run our tests
+             (catch Throwable e
+               #_{:clj-kondo/ignore [:discouraged-var]}
+               (println "[Snowflake] Error deleting old dataset:" (ex-message e))))))))))
 
 (defonce ^:private deleted-old-datasets?
   (atom false))
diff --git a/modules/drivers/sparksql/src/metabase/driver/sparksql.clj b/modules/drivers/sparksql/src/metabase/driver/sparksql.clj
index c167d436740c5cbecda2a31715fa5343a27c5168..463038a71cacc35e428ca9825f466b6e56c5e147 100644
--- a/modules/drivers/sparksql/src/metabase/driver/sparksql.clj
+++ b/modules/drivers/sparksql/src/metabase/driver/sparksql.clj
@@ -114,14 +114,18 @@
 
 ;; workaround for SPARK-9686 Spark Thrift server doesn't return correct JDBC metadata
 (defmethod driver/describe-database :sparksql
-  [_ database]
+  [driver database]
   {:tables
-   (with-open [conn (jdbc/get-connection (sql-jdbc.conn/db->pooled-connection-spec database))]
-     (set
-      (for [{:keys [database tablename tab_name], table-namespace :namespace} (jdbc/query {:connection conn} ["show tables"])]
-        {:name   (or tablename tab_name) ; column name differs depending on server (SparkSQL, hive, Impala)
-         :schema (or (not-empty database)
-                     (not-empty table-namespace))})))})
+   (sql-jdbc.execute/do-with-connection-with-options
+    driver
+    database
+    nil
+    (fn [^Connection conn]
+      (set
+       (for [{:keys [database tablename tab_name], table-namespace :namespace} (jdbc/query {:connection conn} ["show tables"])]
+         {:name   (or tablename tab_name) ; column name differs depending on server (SparkSQL, hive, Impala)
+          :schema (or (not-empty database)
+                      (not-empty table-namespace))}))))})
 
 ;; Hive describe table result has commented rows to distinguish partitions
 (defn- valid-describe-table-row? [{:keys [col_name data_type]}]
@@ -135,19 +139,23 @@
   {:name   table-name
    :schema schema
    :fields
-   (with-open [conn (jdbc/get-connection (sql-jdbc.conn/db->pooled-connection-spec database))]
-     (let [results (jdbc/query {:connection conn} [(format
-                                                    "describe %s"
-                                                    (sql.u/quote-name driver :table
-                                                      (dash-to-underscore schema)
-                                                      (dash-to-underscore table-name)))])]
-       (set
-        (for [[idx {col-name :col_name, data-type :data_type, :as result}] (m/indexed results)
-              :when (valid-describe-table-row? result)]
-          {:name              col-name
-           :database-type     data-type
-           :base-type         (sql-jdbc.sync/database-type->base-type :hive-like (keyword data-type))
-           :database-position idx}))))})
+   (sql-jdbc.execute/do-with-connection-with-options
+    driver
+    database
+    nil
+    (fn [^Connection conn]
+      (let [results (jdbc/query {:connection conn} [(format
+                                                     "describe %s"
+                                                     (sql.u/quote-name driver :table
+                                                                       (dash-to-underscore schema)
+                                                                       (dash-to-underscore table-name)))])]
+        (set
+         (for [[idx {col-name :col_name, data-type :data_type, :as result}] (m/indexed results)
+               :when (valid-describe-table-row? result)]
+           {:name              col-name
+            :database-type     data-type
+            :base-type         (sql-jdbc.sync/database-type->base-type :hive-like (keyword data-type))
+            :database-position idx})))))})
 
 ;; bound variables are not supported in Spark SQL (maybe not Hive either, haven't checked)
 (defmethod driver/execute-reducible-query :sparksql
@@ -167,15 +175,16 @@
 ;; 2.  SparkSQL doesn't support session timezones (at least our driver doesn't support it)
 ;; 3.  SparkSQL doesn't support making connections read-only
 ;; 4.  SparkSQL doesn't support setting the default result set holdability
-(defmethod sql-jdbc.execute/connection-with-timezone :sparksql
-  [driver database _timezone-id]
-  (let [conn (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))]
-    (try
-      (.setTransactionIsolation conn Connection/TRANSACTION_READ_UNCOMMITTED)
-      conn
-      (catch Throwable e
-        (.close conn)
-        (throw e)))))
+(defmethod sql-jdbc.execute/do-with-connection-with-options :sparksql
+  [driver db-or-id-or-spec options f]
+  (sql-jdbc.execute/do-with-resolved-connection
+   driver
+   db-or-id-or-spec
+   options
+   (fn [^Connection conn]
+     (when-not (sql-jdbc.execute/recursive-connection?)
+       (.setTransactionIsolation conn Connection/TRANSACTION_READ_UNCOMMITTED))
+     (f conn))))
 
 ;; 1.  SparkSQL doesn't support setting holdability type to `CLOSE_CURSORS_AT_COMMIT`
 (defmethod sql-jdbc.execute/prepared-statement :sparksql
diff --git a/modules/drivers/sparksql/test/metabase/test/data/sparksql.clj b/modules/drivers/sparksql/test/metabase/test/data/sparksql.clj
index 7dbb67c8732acd635a1a5e52070affc47ac2bf09..7385e657a64e4e08d5e8f4c12c0697064fb6ee5d 100644
--- a/modules/drivers/sparksql/test/metabase/test/data/sparksql.clj
+++ b/modules/drivers/sparksql/test/metabase/test/data/sparksql.clj
@@ -5,6 +5,7 @@
    [metabase.config :as config]
    [metabase.driver :as driver]
    [metabase.driver.ddl.interface :as ddl.i]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.util :as sql.u]
    [metabase.driver.sql.util.unprepare :as unprepare]
    [metabase.test.data.interface :as tx]
@@ -84,15 +85,19 @@
 (defmethod load-data/do-insert! :sparksql
   [driver spec table-identifier row-or-rows]
   (let [statements (ddl/insert-rows-ddl-statements driver table-identifier row-or-rows)]
-    (with-open [conn (jdbc/get-connection spec)]
-      (try
-        (.setAutoCommit conn false)
-        (doseq [sql+args statements]
-          (jdbc/execute! {:connection conn} sql+args {:transaction? false}))
-        (catch java.sql.SQLException e
-          (log/infof "Error inserting data: %s" (u/pprint-to-str 'red statements))
-          (jdbc/print-sql-exception-chain e)
-          (throw e))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     spec
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (try
+         (.setAutoCommit conn false)
+         (doseq [sql+args statements]
+           (jdbc/execute! {:connection conn} sql+args {:transaction? false}))
+         (catch java.sql.SQLException e
+           (log/infof "Error inserting data: %s" (u/pprint-to-str 'red statements))
+           (jdbc/print-sql-exception-chain e)
+           (throw e)))))))
 
 (defmethod load-data/load-data! :sparksql [& args]
   (apply load-data/load-data-add-ids! args))
diff --git a/modules/drivers/sqlite/src/metabase/driver/sqlite.clj b/modules/drivers/sqlite/src/metabase/driver/sqlite.clj
index f0f1a87cc90766b7a7f0012f91fd81da43c075c3..21846773c8b66ba17a573522ba0eaba555b8b506 100644
--- a/modules/drivers/sqlite/src/metabase/driver/sqlite.clj
+++ b/modules/drivers/sqlite/src/metabase/driver/sqlite.clj
@@ -409,15 +409,16 @@
 ;; SQLite's JDBC driver is fussy and won't let you change connections to read-only after you create them. So skip that
 ;; step. SQLite doesn't have a notion of session timezones so don't do that either. The only thing we're doing here from
 ;; the default impl is setting the transaction isolation level
-(defmethod sql-jdbc.execute/connection-with-timezone :sqlite
-  [driver database _timezone-id]
-  (let [conn (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))]
-    (try
-      (sql-jdbc.execute/set-best-transaction-level! driver conn)
-      conn
-      (catch Throwable e
-        (.close conn)
-        (throw e)))))
+(defmethod sql-jdbc.execute/do-with-connection-with-options :sqlite
+  [driver db-or-id-or-spec options f]
+  (sql-jdbc.execute/do-with-resolved-connection
+   driver
+   db-or-id-or-spec
+   options
+   (fn [^Connection conn]
+     (when-not (sql-jdbc.execute/recursive-connection?)
+       (sql-jdbc.execute/set-best-transaction-level! driver conn))
+     (f conn))))
 
 ;; SQLite's JDBC driver is dumb and complains if you try to call `.setFetchDirection` on the Connection
 (defmethod sql-jdbc.execute/prepared-statement :sqlite
diff --git a/modules/drivers/sqlserver/test/metabase/driver/sqlserver_test.clj b/modules/drivers/sqlserver/test/metabase/driver/sqlserver_test.clj
index 09ce34524ee20ba814705088a0496f5d0f8b4312..05b670f4d5807d98942dca1f815d2a311d622198 100644
--- a/modules/drivers/sqlserver/test/metabase/driver/sqlserver_test.clj
+++ b/modules/drivers/sqlserver/test/metabase/driver/sqlserver_test.clj
@@ -190,26 +190,30 @@
       ;; we're doing things here with low-level calls to HoneySQL (emulating what the QP does) instead of using normal
       ;; QP pathways because `SET LANGUAGE` doesn't seem to persist to subsequent executions so to test that things
       ;; are working we need to add to in from of the query we're trying to check
-      (with-open [conn (sql-jdbc.execute/connection-with-timezone :sqlserver (mt/db) (qp.timezone/report-timezone-id-if-supported :sqlserver (mt/db)))]
-        (.setAutoCommit conn false)
-        (try
-          (doseq [[sql & params] [["DROP TABLE IF EXISTS temp;"]
-                                  ["CREATE TABLE temp (d DATETIME2);"]
-                                  ["INSERT INTO temp (d) VALUES (?)" #t "2019-02-08T00:00:00Z"]
-                                  ["SET LANGUAGE Italian;"]]]
-            (with-open [stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql params)]
-              (.execute stmt)))
-          (let [[sql & params] (hsql/format {:select [[(sql.qp/date :sqlserver :month :temp.d) :my-date]]
-                                             :from   [:temp]}
-                                 :quoting :ansi, :allow-dashed-names? true)]
-            (with-open [stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql params)
-                        rs   (sql-jdbc.execute/execute-prepared-statement! :sqlserver stmt)]
-              (let [row-thunk (sql-jdbc.execute/row-thunk :sqlserver rs (.getMetaData rs))]
-                (is (= [#t "2019-02-01"]
-                       (row-thunk))))))
-          ;; rollback transaction so `temp` table gets discarded
-          (finally
-            (.rollback conn)))))))
+      (sql-jdbc.execute/do-with-connection-with-options
+       :sqlserver
+       (mt/db)
+       {:session-timezone (qp.timezone/report-timezone-id-if-supported :sqlserver (mt/db))}
+       (fn [^java.sql.Connection conn]
+         (.setAutoCommit conn false)
+         (try
+           (doseq [[sql & params] [["DROP TABLE IF EXISTS temp;"]
+                                   ["CREATE TABLE temp (d DATETIME2);"]
+                                   ["INSERT INTO temp (d) VALUES (?)" #t "2019-02-08T00:00:00Z"]
+                                   ["SET LANGUAGE Italian;"]]]
+             (with-open [stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql params)]
+               (.execute stmt)))
+           (let [[sql & params] (hsql/format {:select [[(sql.qp/date :sqlserver :month :temp.d) :my-date]]
+                                              :from   [:temp]}
+                                             :quoting :ansi, :allow-dashed-names? true)]
+             (with-open [stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql params)
+                         rs   (sql-jdbc.execute/execute-prepared-statement! :sqlserver stmt)]
+               (let [row-thunk (sql-jdbc.execute/row-thunk :sqlserver rs (.getMetaData rs))]
+                 (is (= [#t "2019-02-01"]
+                        (row-thunk))))))
+           ;; rollback transaction so `temp` table gets discarded
+           (finally
+             (.rollback conn))))))))
 
 (deftest unprepare-test
   (mt/test-driver :sqlserver
@@ -232,13 +236,17 @@
           #_{:clj-kondo/ignore [:discouraged-var]}
           (testing (format "Convert %s to SQL literal" (colorize/magenta (with-out-str (pr t))))
             (let [sql (format "SELECT %s AS t;" (unprepare/unprepare-value :sqlserver t))]
-              (with-open [conn (sql-jdbc.execute/connection-with-timezone :sqlserver (mt/db) nil)
-                          stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql nil)
-                          rs   (sql-jdbc.execute/execute-prepared-statement! :sqlserver stmt)]
-                (let [row-thunk (sql-jdbc.execute/row-thunk :sqlserver rs (.getMetaData rs))]
-                  (is (= [expected]
-                         (row-thunk))
-                      (format "SQL %s should return %s" (colorize/blue (pr-str sql)) (colorize/green expected))))))))))))
+              (sql-jdbc.execute/do-with-connection-with-options
+               :sqlserver
+               (mt/db)
+               nil
+               (fn [^java.sql.Connection conn]
+                 (with-open [stmt (sql-jdbc.execute/prepared-statement :sqlserver conn sql nil)
+                             rs   (sql-jdbc.execute/execute-prepared-statement! :sqlserver stmt)]
+                   (let [row-thunk (sql-jdbc.execute/row-thunk :sqlserver rs (.getMetaData rs))]
+                     (is (= [expected]
+                            (row-thunk))
+                         (format "SQL %s should return %s" (colorize/blue (pr-str sql)) (colorize/green expected))))))))))))))
 
 (defn- pretty-sql [s]
   (str/replace s #"\"" ""))
diff --git a/modules/drivers/vertica/test/metabase/test/data/vertica.clj b/modules/drivers/vertica/test/metabase/test/data/vertica.clj
index cb6e9e0d4f72a7393f83dd519bf2c735af2429dc..798254f385c0e873599eb7fca015ccf1fbc5c868 100644
--- a/modules/drivers/vertica/test/metabase/test/data/vertica.clj
+++ b/modules/drivers/vertica/test/metabase/test/data/vertica.clj
@@ -8,6 +8,7 @@
    [java-time :as t]
    [medley.core :as m]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.test :as mt]
    [metabase.test.data.interface :as tx]
    [metabase.test.data.sql :as sql.tx]
@@ -123,42 +124,46 @@
 
 (defn- load-rows-from-csv!
   "Load rows from a CSV file into a Table."
-  [_driver {:keys [database-name], :as _dbdef} {:keys [table-name rows], :as _tabledef} filename]
-  (let [table-identifier (sql.tx/qualify-and-quote :vertica database-name table-name)]
-    (with-open [conn (jdbc/get-connection (dbspec))]
-      (letfn [(execute! [sql]
-                (try
-                  (jdbc/execute! {:connection conn} sql)
-                  (catch Throwable e
-                    (throw (ex-info "Error executing SQL" {:sql sql, :spec (dbspec)} e)))))
-              (actual-rows []
-                (u/ignore-exceptions
-                  (jdbc/query {:connection conn}
-                              (format "SELECT * FROM %s ORDER BY id ASC;" table-identifier))))]
-        (try
-          ;; make sure the Table is empty
-          (execute! (format "TRUNCATE TABLE %s" table-identifier))
-          ;; load the rows from the CSV file
-          (let [[num-rows-inserted] (execute! (format "COPY %s FROM LOCAL '%s' DELIMITER ','"
-                                                      table-identifier
-                                                      filename))]
-            ;; it should return the number of rows inserted; make sure this matches what we expected
-            (when-not (= num-rows-inserted (count rows))
-              (throw (ex-info (format "Expected %d rows to be inserted, but only %d were" (count rows) num-rows-inserted)
-                              {:inserted-rows (take 100 (actual-rows))}))))
-          ;; make sure SELECT COUNT(*) matches as well
-          (let [[{actual-num-rows :count}] (jdbc/query {:connection conn}
-                                                       (format "SELECT count(*) FROM %s;" table-identifier))]
-            (when-not (= actual-num-rows (count rows))
-              (throw (ex-info (format "Expected count(*) to return %d, but only got %d" (count rows) actual-num-rows)
-                              {:inserted-rows (take 100 (actual-rows))}))))
-          ;; success!
-          :ok
-          (catch Throwable e
-            (throw (ex-info "Error loading rows from CSV file"
-                            {:filename filename
-                             :rows     (take 10 (str/split-lines (slurp filename)))}
-                            e))))))))
+  [driver {:keys [database-name], :as _dbdef} {:keys [table-name rows], :as _tabledef} filename]
+  (let [table-identifier (sql.tx/qualify-and-quote driver database-name table-name)]
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     (dbspec)
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (letfn [(execute! [sql]
+                 (try
+                   (jdbc/execute! {:connection conn} sql)
+                   (catch Throwable e
+                     (throw (ex-info "Error executing SQL" {:sql sql, :spec (dbspec)} e)))))
+               (actual-rows []
+                 (u/ignore-exceptions
+                   (jdbc/query {:connection conn}
+                               (format "SELECT * FROM %s ORDER BY id ASC;" table-identifier))))]
+         (try
+           ;; make sure the Table is empty
+           (execute! (format "TRUNCATE TABLE %s" table-identifier))
+           ;; load the rows from the CSV file
+           (let [[num-rows-inserted] (execute! (format "COPY %s FROM LOCAL '%s' DELIMITER ','"
+                                                       table-identifier
+                                                       filename))]
+             ;; it should return the number of rows inserted; make sure this matches what we expected
+             (when-not (= num-rows-inserted (count rows))
+               (throw (ex-info (format "Expected %d rows to be inserted, but only %d were" (count rows) num-rows-inserted)
+                               {:inserted-rows (take 100 (actual-rows))}))))
+           ;; make sure SELECT COUNT(*) matches as well
+           (let [[{actual-num-rows :count}] (jdbc/query {:connection conn}
+                                                        (format "SELECT count(*) FROM %s;" table-identifier))]
+             (when-not (= actual-num-rows (count rows))
+               (throw (ex-info (format "Expected count(*) to return %d, but only got %d" (count rows) actual-num-rows)
+                               {:inserted-rows (take 100 (actual-rows))}))))
+           ;; success!
+           :ok
+           (catch Throwable e
+             (throw (ex-info "Error loading rows from CSV file"
+                             {:filename filename
+                              :rows     (take 10 (str/split-lines (slurp filename)))}
+                             e)))))))))
 
 (defmethod load-data/load-data! :vertica
   [driver dbdef {:keys [rows], :as tabledef}]
diff --git a/src/metabase/db/liquibase.clj b/src/metabase/db/liquibase.clj
index 4b3ec2763830271c22e7148cd17294a6e0606193..2d9e1e521a44770046d2d2d84550964377941f2c 100644
--- a/src/metabase/db/liquibase.clj
+++ b/src/metabase/db/liquibase.clj
@@ -194,7 +194,7 @@
   [db-type :- s/Keyword
    conn    :- java.sql.Connection]
   (let [liquibase-table-name (changelog-table-name db-type)
-        fresh-install?       (jdbc/with-db-metadata [meta {:connection conn}] ; don't migrate on fresh install
+        fresh-install?       (let [meta (.getMetaData conn)] ; don't migrate on fresh install
                                (empty? (jdbc/metadata-query
                                         (.getTables meta nil nil liquibase-table-name (u/varargs String ["TABLE"])))))
         statement            (format "UPDATE %s SET FILENAME = ?" liquibase-table-name)]
diff --git a/src/metabase/db/setup.clj b/src/metabase/db/setup.clj
index df471fe10f3f443e5502be31d76a164c60af6dd4..77067cb71ee069d726a09b5f0212d5262ffb8c3a 100644
--- a/src/metabase/db/setup.clj
+++ b/src/metabase/db/setup.clj
@@ -68,8 +68,7 @@
    & args      :- [s/Any]]
   (with-open [conn (.getConnection data-source)]
     (.setAutoCommit conn false)
-      ;; Set up liquibase and let it do its thing
-
+    ;; Set up liquibase and let it do its thing
     (log/info (trs "Setting up Liquibase..."))
     (liquibase/with-liquibase [liquibase conn]
       (try
diff --git a/src/metabase/driver.clj b/src/metabase/driver.clj
index 0466de95b923b3d69cf34b8da535709aa67d1c7c..7d937c5f36ffa5bb80fa435e9f8cd1ddbc74d7aa 100644
--- a/src/metabase/driver.clj
+++ b/src/metabase/driver.clj
@@ -830,18 +830,21 @@
   dispatch-on-initialized-driver
   :hierarchy #'hierarchy)
 
+;;; FIXME -- should be named `create-table!`
 (defmulti create-table
   "Create a table named `table-name`. If the table already exists it will throw an error."
   {:added "0.47.0", :arglists '([driver db-id table-name col->type])}
   dispatch-on-initialized-driver
   :hierarchy #'hierarchy)
 
+;;; FIXME -- should be named `drop-table!`
 (defmulti drop-table
   "Drop a table named `table-name`. If the table doesn't exist it will not be dropped."
   {:added "0.47.0", :arglists '([driver db-id table-name])}
   dispatch-on-initialized-driver
   :hierarchy #'hierarchy)
 
+;;; FIXME -- should be named `insert-into!`
 (defmulti insert-into
   "Insert `values` into a table named `table-name`. `values` is a sequence of rows, where each row's order matches
    `column-names`."
diff --git a/src/metabase/driver/h2.clj b/src/metabase/driver/h2.clj
index 0ccf6f49d130df0adf549bc5d73a078b451181da..095c40bc3bfc7083d552d01242612d7dcafadddb 100644
--- a/src/metabase/driver/h2.clj
+++ b/src/metabase/driver/h2.clj
@@ -478,19 +478,20 @@
   [_]
   #{"INFORMATION_SCHEMA"})
 
-(defmethod sql-jdbc.execute/connection-with-timezone :h2
-  [driver database ^String _timezone-id]
+(defmethod sql-jdbc.execute/do-with-connection-with-options :h2
+  [driver db-or-id-or-spec {:keys [write?], :as options} f]
   ;; h2 doesn't support setting timezones, or changing the transaction level without admin perms, so we can skip those
   ;; steps that are in the default impl
-  (let [conn (.getConnection (sql-jdbc.execute/datasource-with-diagnostic-info! driver database))]
-    (try
-      ;; in H2, setting readOnly to true doesn't prevent writes
-      ;; see https://github.com/h2database/h2database/issues/1163
-      (doto conn
-        (.setReadOnly true))
-      (catch Throwable e
-        (.close conn)
-        (throw e)))))
+  (sql-jdbc.execute/do-with-resolved-connection
+   driver
+   db-or-id-or-spec
+   (dissoc options :session-timezone)
+   (fn [^java.sql.Connection conn]
+     (when-not (sql-jdbc.execute/recursive-connection?)
+       ;; in H2, setting readOnly to true doesn't prevent writes
+       ;; see https://github.com/h2database/h2database/issues/1163
+       (.setReadOnly conn (not write?)))
+     (f conn))))
 
 ;; de-CLOB any CLOB values that come back
 (defmethod sql-jdbc.execute/read-column-thunk :h2
diff --git a/src/metabase/driver/mysql.clj b/src/metabase/driver/mysql.clj
index b158d85e4b2e10a4b1e6016f6dbd92fd63189444..fb329e4ec31a508229948f5d95b402c3d5b4b2e4 100644
--- a/src/metabase/driver/mysql.clj
+++ b/src/metabase/driver/mysql.clj
@@ -102,18 +102,22 @@
 
 (defn- warn-on-unsupported-versions [driver details]
   (sql-jdbc.conn/with-connection-spec-for-testing-connection [jdbc-spec [driver details]]
-    (jdbc/with-db-metadata [metadata jdbc-spec]
-      (when (unsupported-version? metadata)
-        (log/warn
-         (u/format-color 'red
-                         (str
-                          "\n\n********************************************************************************\n"
-                          (trs "WARNING: Metabase only officially supports MySQL {0}/MariaDB {1} and above."
-                               min-supported-mysql-version
-                               min-supported-mariadb-version)
-                          "\n"
-                          (trs "All Metabase features may not work properly when using an unsupported version.")
-                          "\n********************************************************************************\n")))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     jdbc-spec
+     nil
+     (fn [^java.sql.Connection conn]
+       (when (unsupported-version? (.getMetaData conn))
+         (log/warn
+          (u/format-color 'red
+                          (str
+                           "\n\n********************************************************************************\n"
+                           (trs "WARNING: Metabase only officially supports MySQL {0}/MariaDB {1} and above."
+                                min-supported-mysql-version
+                                min-supported-mariadb-version)
+                           "\n"
+                           (trs "All Metabase features may not work properly when using an unsupported version.")
+                           "\n********************************************************************************\n"))))))))
 
 (defmethod driver/can-connect? :mysql
   [driver details]
diff --git a/src/metabase/driver/mysql/actions.clj b/src/metabase/driver/mysql/actions.clj
index 6b1b1111f79f107fd539a201ab667a3ad8144ef0..ec08c89ba3910c8fdeb02c0a28f3262a95bdaaf3 100644
--- a/src/metabase/driver/mysql/actions.clj
+++ b/src/metabase/driver/mysql/actions.clj
@@ -5,6 +5,7 @@
    [clojure.string :as str]
    [metabase.driver.sql-jdbc.actions :as sql-jdbc.actions]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.query-processor :as sql.qp]
    [metabase.util :as u]
    [metabase.util.i18n :refer [tru]]
@@ -133,13 +134,20 @@
   [_driver _conn thunk]
   (thunk))
 
-(defn- get-primary-keys [db-spec table-components]
+(defn- primary-keys [driver jdbc-spec table-components]
   (let [schema (when (next table-components) (first table-components))
-        table (last table-components)]
-    (jdbc/with-db-metadata [md db-spec]
-      (->> (.getPrimaryKeys md nil schema table)
-           jdbc/metadata-result
-           (mapv :column_name)))))
+        table  (last table-components)]
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     jdbc-spec
+     nil
+     (fn [^java.sql.Connection conn]
+       (let [metadata (.getMetaData conn)]
+         (with-open [rset (.getPrimaryKeys metadata nil schema table)]
+           (loop [acc []]
+             (if-not (.next rset)
+               acc
+               (recur (conj acc (.getString rset "COLUMN_NAME")))))))))))
 
 ;;; MySQL returns the generated ID (of which cannot be more than one)
 ;;; as insert_id. If this is not null, we determine the name of the
@@ -151,9 +159,9 @@
 ;;; warning and return nil.
 (defmethod sql-jdbc.actions/select-created-row :mysql
   [driver create-hsql conn {:keys [insert_id] :as results}]
-  (let [db-spec {:connection conn}
+  (let [jdbc-spec {:connection conn}
         table-components (-> create-hsql :insert-into :components)
-        pks (get-primary-keys db-spec table-components)
+        pks (primary-keys driver jdbc-spec table-components)
         where-clause (if insert_id
                        [:= (-> pks first keyword) insert_id]
                        (into [:and]
@@ -165,7 +173,7 @@
                                :from [(:insert-into create-hsql)]
                                :where where-clause))
         select-sql-args (sql.qp/format-honeysql driver select-hsql)
-        query-results (jdbc/query db-spec
+        query-results (jdbc/query jdbc-spec
                                   select-sql-args
                                   {:identifiers identity, :transaction? false})]
     (if (next query-results)
diff --git a/src/metabase/driver/mysql/ddl.clj b/src/metabase/driver/mysql/ddl.clj
index be38b455cf3211ff17d0ebee09279b549cf008c1..0eeb7472cf81697d50eac2588d65b08e09150264 100644
--- a/src/metabase/driver/mysql/ddl.clj
+++ b/src/metabase/driver/mysql/ddl.clj
@@ -1,12 +1,12 @@
 (ns metabase.driver.mysql.ddl
   (:require
    [clojure.core.async :as a]
-   [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [honey.sql :as sql]
    [java-time :as t]
    [metabase.driver.ddl.interface :as ddl.i]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.ddl :as sql.ddl]
    [metabase.public-settings :as public-settings]
    [metabase.query-processor :as qp]
@@ -17,21 +17,25 @@
 
 (set! *warn-on-reflection* true)
 
-(defn- exec-async [conn-chan db-spec sql+params]
+(defn- exec-async [driver conn-chan db-spec sql+params]
   (a/thread
-    (jdbc/with-db-connection [conn db-spec]
-      (try
-        (let [pid (:pid (first (sql.ddl/jdbc-query conn ["select connection_id() pid"])))]
-          (a/put! conn-chan pid)
-          (sql.ddl/jdbc-query conn sql+params))
-        (catch SQLNonTransientConnectionException _e
-          ;; Our connection may be killed due to timeout, `kill` will throw an appropriate exception
-          nil)
-        (catch Exception e
-          (log/warn e)
-          e)))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     db-spec
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (try
+         (let [pid (:pid (first (sql.ddl/jdbc-query conn ["select connection_id() pid"])))]
+           (a/put! conn-chan pid)
+           (sql.ddl/jdbc-query conn sql+params))
+         (catch SQLNonTransientConnectionException _e
+           ;; Our connection may be killed due to timeout, [[kill!]] will throw an appropriate exception
+           nil)
+         (catch Exception e
+           (log/warn e)
+           e))))))
 
-(defn- kill [conn pid]
+(defn- kill! [conn pid]
   (let [results (sql.ddl/jdbc-query conn ["show processlist"])
         result? (some (fn [r]
                         (and (= (:id r) pid)
@@ -46,43 +50,54 @@
   "Spins up another channel to execute the statement.
    If `timeout-ms` passes, send a kill statement to stop execution and throw exception
    Otherwise return results returned by channel."
-  [conn db-spec timeout-ms sql+params]
-  (let [conn-chan    (a/chan)
-        exec-chan    (exec-async conn-chan db-spec sql+params)
+  [driver conn db-spec timeout-ms sql+params]
+  (let [conn-chan    (a/promise-chan)
+        exec-chan    (exec-async driver conn-chan db-spec sql+params)
         pid          (a/<!! conn-chan)
+        _            (a/close! conn-chan)
         timeout-chan (a/timeout timeout-ms)
-        [v port] (a/alts!! [timeout-chan exec-chan])]
+        [v port]     (a/alts!! [timeout-chan exec-chan])]
+    (a/close! exec-chan)
     (cond
-      (= port timeout-chan) (kill conn pid)
+      (= port timeout-chan) (kill! conn pid)
+      (= port exec-chan)    (if (instance? Exception v)
+                              (throw v)
+                              v))))
 
-      (= port exec-chan) (if (instance? Exception v)
-                           (throw v)
-                           v))))
-
-(defmethod ddl.i/refresh! :mysql [_driver database definition dataset-query]
+(defmethod ddl.i/refresh! :mysql
+  [driver database definition dataset-query]
   (let [{:keys [query params]} (qp/compile dataset-query)
         db-spec (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (jdbc/with-db-connection [conn db-spec]
-      (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table-name definition))])
-      ;; It is possible that this fails and rollback would not restore the table.
-      ;; That is ok, the persisted-info will be marked inactive and the next refresh will try again.
-      (execute-with-timeout! conn
-                             db-spec
-                             (.toMillis (t/minutes 10))
-                             (into [(sql.ddl/create-table-sql database definition query)] params))
-      {:state :success})))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     database
+     {:write? true}
+     (fn [conn]
+       (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table-name definition))])
+       ;; It is possible that this fails and rollback would not restore the table.
+       ;; That is ok, the persisted-info will be marked inactive and the next refresh will try again.
+       (execute-with-timeout! driver
+                              conn
+                              db-spec
+                              (.toMillis (t/minutes 10))
+                              (into [(sql.ddl/create-table-sql database definition query)] params))
+       {:state :success}))))
 
 (defmethod ddl.i/unpersist! :mysql
-  [_driver database persisted-info]
-  (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (try
-      (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table_name persisted-info))])
-      (catch Exception e
-        (log/warn e)
-        (throw e)))))
+  [driver database persisted-info]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   {:write? true}
+   (fn [^java.sql.Connection conn]
+     (try
+       (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table_name persisted-info))])
+       (catch Exception e
+         (log/warn e)
+         (throw e))))))
 
 (defmethod ddl.i/check-can-persist :mysql
-  [database]
+  [{driver :engine, :as database}]
   (let [schema-name (ddl.i/schema-name database (public-settings/site-uuid))
         table-name (format "persistence_check_%s" (rand-int 10000))
         db-spec (sql-jdbc.conn/db->pooled-connection-spec database)
@@ -98,7 +113,8 @@
                   (sql.ddl/execute! conn [(sql.ddl/drop-schema-sql database)]))]
                [:persist.check/create-table
                 (fn create-table [conn]
-                  (execute-with-timeout! conn
+                  (execute-with-timeout! driver
+                                         conn
                                          db-spec
                                          (.toMillis (t/minutes 10))
                                          [(sql.ddl/create-table-sql
@@ -134,28 +150,32 @@
                                           {:dialect :mysql})))]]]
     ;; Unlike postgres, mysql ddl clauses will not rollback in a transaction.
     ;; So we keep track of undo-steps to manually rollback previous, completed steps.
-    (jdbc/with-db-connection [conn db-spec]
-      (loop [[[step stepfn undofn] & remaining] steps
-             undo-steps []]
-        (let [result (try (stepfn conn)
-                          (log/info (trs "Step {0} was successful for db {1}"
-                                         step (:name database)))
-                          ::valid
-                          (catch Exception e
-                            (log/warn (trs "Error in `{0}` while checking for model persistence permissions." step))
-                            (log/warn e)
-                            (try
-                              (doseq [[undo-step undofn] (reverse undo-steps)]
-                                (log/warn (trs "Undoing step `{0}` for db {1}" undo-step (:name database)))
-                                (undofn conn))
-                              (catch Exception _e
-                                (log/warn (trs "Unable to rollback database check for model persistence"))))
-                            step))]
-          (cond (and (= result ::valid) remaining)
-                (recur remaining (conj undo-steps [step undofn]))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     db-spec
+     {:write? true}
+     (fn [conn]
+       (loop [[[step stepfn undofn] & remaining] steps
+              undo-steps []]
+         (let [result (try (stepfn conn)
+                           (log/info (trs "Step {0} was successful for db {1}"
+                                          step (:name database)))
+                           ::valid
+                           (catch Exception e
+                             (log/warn (trs "Error in `{0}` while checking for model persistence permissions." step))
+                             (log/warn e)
+                             (try
+                               (doseq [[undo-step undofn] (reverse undo-steps)]
+                                 (log/warn (trs "Undoing step `{0}` for db {1}" undo-step (:name database)))
+                                 (undofn conn))
+                               (catch Exception _e
+                                 (log/warn (trs "Unable to rollback database check for model persistence"))))
+                             step))]
+           (cond (and (= result ::valid) remaining)
+                 (recur remaining (conj undo-steps [step undofn]))
 
-                (= result ::valid)
-                [true :persist.check/valid]
+                 (= result ::valid)
+                 [true :persist.check/valid]
 
-                :else
-                [false step]))))))
+                 :else
+                 [false step])))))))
diff --git a/src/metabase/driver/postgres.clj b/src/metabase/driver/postgres.clj
index 0b5c1db1cf92e65d22081fffacf863ff6e1f1301..35c03b22523bf6ae4c7f292bdb866d8c8b62a57e 100644
--- a/src/metabase/driver/postgres.clj
+++ b/src/metabase/driver/postgres.clj
@@ -620,7 +620,7 @@
     (default-base-types column)))
 
 (defmethod sql-jdbc.sync/column->semantic-type :postgres
-  [_ database-type _]
+  [_driver database-type _column-name]
   ;; this is really, really simple right now.  if its postgres :json type then it's :type/SerializedJSON semantic-type
   (case database-type
     "json"  :type/SerializedJSON
@@ -807,15 +807,19 @@
 
 (defmethod driver/insert-into :postgres
   [driver db-id table-name column-names values]
-  (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec db-id)]
-    (let [copy-manager (CopyManager. (.unwrap (jdbc/get-connection conn) PgConnection))
-          [sql & _]    (sql/format {::copy       (keyword table-name)
-                                    :columns     (map keyword column-names)
-                                    ::from-stdin "''"}
-                                   :quoted true
-                                   :dialect (sql.qp/quote-style driver))
-          tsvs         (->> values
-                            (map row->tsv)
-                            (str/join "\n")
-                            (StringReader.))]
-      (.copyIn copy-manager ^String sql tsvs))))
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   db-id
+   {:write? true}
+   (fn [^java.sql.Connection conn]
+     (let [copy-manager (CopyManager. (.unwrap conn PgConnection))
+           [sql & _]    (sql/format {::copy       (keyword table-name)
+                                     :columns     (map keyword column-names)
+                                     ::from-stdin "''"}
+                                    :quoted true
+                                    :dialect (sql.qp/quote-style driver))
+           tsvs         (->> values
+                             (map row->tsv)
+                             (str/join "\n")
+                             (StringReader.))]
+       (.copyIn copy-manager ^String sql tsvs)))))
diff --git a/src/metabase/driver/postgres/ddl.clj b/src/metabase/driver/postgres/ddl.clj
index 596a55440b9755de49be5200abd11c2ca7840d58..8f0d10169cecd3a5b59ba1b38c87fd887cee142b 100644
--- a/src/metabase/driver/postgres/ddl.clj
+++ b/src/metabase/driver/postgres/ddl.clj
@@ -4,7 +4,7 @@
    [honey.sql :as sql]
    [java-time :as t]
    [metabase.driver.ddl.interface :as ddl.i]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.ddl :as sql.ddl]
    [metabase.public-settings :as public-settings]
    [metabase.query-processor :as qp]
@@ -36,26 +36,35 @@
     ;; Can't use a prepared parameter with these statements
     (sql.ddl/execute! tx [(format "SET LOCAL statement_timeout TO '%s'" (str new-timeout))])))
 
-(defmethod ddl.i/refresh! :postgres [_driver database definition dataset-query]
+(defmethod ddl.i/refresh! :postgres
+  [driver database definition dataset-query]
   (let [{:keys [query params]} (qp/compile dataset-query)]
-    (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec database)]
-      (jdbc/with-db-transaction [tx conn]
-        (set-statement-timeout! tx)
-        (sql.ddl/execute! tx [(sql.ddl/drop-table-sql database (:table-name definition))])
-        (sql.ddl/execute! tx (into [(sql.ddl/create-table-sql database definition query)] params)))
-      {:state :success})))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     database
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (jdbc/with-db-transaction [tx {:connection conn}]
+         (set-statement-timeout! tx)
+         (sql.ddl/execute! tx [(sql.ddl/drop-table-sql database (:table-name definition))])
+         (sql.ddl/execute! tx (into [(sql.ddl/create-table-sql database definition query)] params)))
+       {:state :success}))))
 
 (defmethod ddl.i/unpersist! :postgres
-  [_driver database persisted-info]
-  (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec database)]
-    (try
-      (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table_name persisted-info))])
-      (catch Exception e
-        (log/warn e)
-        (throw e)))))
+  [driver database persisted-info]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   {:write? true}
+   (fn [conn]
+     (try
+       (sql.ddl/execute! conn [(sql.ddl/drop-table-sql database (:table_name persisted-info))])
+       (catch Exception e
+         (log/warn e)
+         (throw e))))))
 
 (defmethod ddl.i/check-can-persist :postgres
-  [database]
+  [{driver :engine, :as database}]
   (let [schema-name (ddl.i/schema-name database (public-settings/site-uuid))
         table-name  (format "persistence_check_%s" (rand-int 10000))
         steps       [[:persist.check/create-schema
@@ -93,23 +102,27 @@
                                                 (ddl.i/populate-kv-table-honey-sql-form
                                                  schema-name)
                                                 {:dialect :ansi})))]]]
-    (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec database)]
-      (jdbc/with-db-transaction
-        [tx conn]
-        (set-statement-timeout! tx)
-        (loop [[[step stepfn] & remaining] steps]
-          (let [result (try (stepfn tx)
-                            (log/info (trs "Step {0} was successful for db {1}"
-                                           step (:name database)))
-                            ::valid
-                            (catch Exception e
-                              (log/warn (trs "Error in `{0}` while checking for model persistence permissions." step))
-                              (log/warn e)
-                              step))]
-            (cond (and (= result ::valid) remaining)
-                  (recur remaining)
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     database
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (jdbc/with-db-transaction
+         [tx {:connection conn}]
+         (set-statement-timeout! tx)
+         (loop [[[step stepfn] & remaining] steps]
+           (let [result (try (stepfn tx)
+                             (log/info (trs "Step {0} was successful for db {1}"
+                                            step (:name database)))
+                             ::valid
+                             (catch Exception e
+                               (log/warn (trs "Error in `{0}` while checking for model persistence permissions." step))
+                               (log/warn e)
+                               step))]
+             (cond (and (= result ::valid) remaining)
+                   (recur remaining)
 
-                  (= result ::valid)
-                  [true :persist.check/valid]
+                   (= result ::valid)
+                   [true :persist.check/valid]
 
-                  :else [false step])))))))
+                   :else [false step]))))))))
diff --git a/src/metabase/driver/sql/ddl.clj b/src/metabase/driver/sql/ddl.clj
index 66b6ac090e9b26c666bdc41f50703f8f7c34336f..61604d25b4b474e600d87506437689b6308d7ec2 100644
--- a/src/metabase/driver/sql/ddl.clj
+++ b/src/metabase/driver/sql/ddl.clj
@@ -13,15 +13,23 @@
   (str "-- Metabase\n"
        sql-str))
 
+(defn- jdbc-spec [connection-or-spec]
+  (cond
+    (instance? java.sql.Connection connection-or-spec) {:connection connection-or-spec}
+    (map? connection-or-spec)                          connection-or-spec
+    :else                                              (throw (ex-info "Invalid JDBC connection spec" {:spec connection-or-spec}))))
+
+;;; TODO -- move the JDBC stuff to something like [[metabase.driver.sql-jdbc.ddl]]. JDBC-specific stuff does not belong
+;;; IN [[metabase.driver.sql]] !!
 (defn execute!
   "Executes sql and params with a standard remark prepended to the statement."
-  [conn [sql & params]]
-  (jdbc/execute! conn (into [(add-remark sql)] params)))
+  [connection-or-spec [sql & params]]
+  (jdbc/execute! (jdbc-spec connection-or-spec) (into [(add-remark sql)] params)))
 
 (defn jdbc-query
   "Queries sql and params with a standard remark prepended to the statement."
-  [conn [sql & params]]
-  (jdbc/query conn (into [(add-remark sql)] params)))
+  [connection-or-spec [sql & params]]
+  (jdbc/query (jdbc-spec connection-or-spec) (into [(add-remark sql)] params)))
 
 (defn create-schema-sql
   "SQL string to create a schema suitable"
diff --git a/src/metabase/driver/sql_jdbc.clj b/src/metabase/driver/sql_jdbc.clj
index c5c51ec987775ce2dc4e68533d4aa4984d887fd2..7eb27c8d9afc9b7529bbac2ad0cda9f7d3f05300 100644
--- a/src/metabase/driver/sql_jdbc.clj
+++ b/src/metabase/driver/sql_jdbc.clj
@@ -145,7 +145,11 @@
 
 (defmethod driver/syncable-schemas :sql-jdbc
   [driver database]
-  (with-open [conn ^java.sql.Connection (jdbc/get-connection (sql-jdbc.conn/db->pooled-connection-spec database))]
-    (let [[inclusion-patterns
-           exclusion-patterns] (driver.s/db-details->schema-filter-patterns database)]
-      (into #{} (sql-jdbc.sync.interface/filtered-syncable-schemas driver conn (.getMetaData conn) inclusion-patterns exclusion-patterns)))))
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   database
+   nil
+   (fn [^java.sql.Connection conn]
+     (let [[inclusion-patterns
+            exclusion-patterns] (driver.s/db-details->schema-filter-patterns database)]
+       (into #{} (sql-jdbc.sync.interface/filtered-syncable-schemas driver conn (.getMetaData conn) inclusion-patterns exclusion-patterns))))))
diff --git a/src/metabase/driver/sql_jdbc/actions.clj b/src/metabase/driver/sql_jdbc/actions.clj
index f8b7de6c2e3a36f7e694f6958e9355dd40e70282..db5b0e8977055248015b269cc3c5c71f87d16cd0 100644
--- a/src/metabase/driver/sql_jdbc/actions.clj
+++ b/src/metabase/driver/sql_jdbc/actions.clj
@@ -8,7 +8,6 @@
    [metabase.actions :as actions]
    [metabase.db.util :as mdb.u]
    [metabase.driver :as driver]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
    [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql.query-processor :as sql.qp]
    [metabase.driver.util :as driver.u]
@@ -117,25 +116,27 @@
   [database-id f]
   (if *connection*
     (f *connection*)
-    (let [jdbc-spec (sql-jdbc.conn/db->pooled-connection-spec database-id)]
-      (with-open [conn (jdbc/get-connection jdbc-spec)]
-        ;; execute inside of a transaction.
-        (.setAutoCommit conn false)
-        (log/tracef "BEGIN transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
-        ;; use the strictest transaction isolation level possible to avoid dirty, non-repeatable, and phantom reads
-        (sql-jdbc.execute/set-best-transaction-level! (driver.u/database->driver database-id) conn)
-        (try
-          (let [result (binding [*connection* conn]
-                         (f conn))]
-            (log/debug "f completed successfully; committing transaction.")
-            (log/tracef "COMMIT transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
-            (.commit conn)
-            result)
-          (catch Throwable e
-            (log/debugf "f threw Exception; rolling back transaction. Error: %s" (ex-message e))
-            (log/tracef "ROLLBACK transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
-            (.rollback conn)
-            (throw e)))))))
+    (let [driver (driver.u/database->driver database-id)]
+      (sql-jdbc.execute/do-with-connection-with-options
+       driver
+       database-id
+       {:write? true}
+       (fn [^Connection conn]
+         ;; execute inside of a transaction.
+         (.setAutoCommit conn false)
+         (log/tracef "BEGIN transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
+         (try
+           (let [result (binding [*connection* conn]
+                          (f conn))]
+             (log/debug "f completed successfully; committing transaction.")
+             (log/tracef "COMMIT transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
+             (.commit conn)
+             result)
+           (catch Throwable e
+             (log/debugf "f threw Exception; rolling back transaction. Error: %s" (ex-message e))
+             (log/tracef "ROLLBACK transaction on conn %s@0x%s" (.getCanonicalName (class conn)) (System/identityHashCode conn))
+             (.rollback conn)
+             (throw e))))))))
 
 (defmacro with-jdbc-transaction
   "Execute `f` with a JDBC Connection for the Database with `database-id`. Uses [[*connection*]] if already bound,
@@ -144,7 +145,6 @@
   [[connection-binding database-id] & body]
   `(do-with-jdbc-transaction ~database-id (fn [~(vary-meta connection-binding assoc :tag 'Connection)] ~@body)))
 
-
 (defmulti prepare-query*
   "Multimethod for preparing a honeysql query `hsql-query` for a given action type `action`.
   `action` is a keyword like `:row/create` or `:bulk/create`; `hsql-query` is a generic
diff --git a/src/metabase/driver/sql_jdbc/execute.clj b/src/metabase/driver/sql_jdbc/execute.clj
index 1148ad83425b5f59a9bf7a60ece0f0c558abcbda..a580a12ec7c93a42755d4cb24de4e1aac809d7cd 100644
--- a/src/metabase/driver/sql_jdbc/execute.clj
+++ b/src/metabase/driver/sql_jdbc/execute.clj
@@ -6,6 +6,7 @@
   `metabase.driver.sql-jdbc.execute.legacy-impl`. "
   (:require
    [clojure.core.async :as a]
+   [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [java-time :as t]
    [metabase.db.query :as mdb.query]
@@ -15,6 +16,10 @@
     :as sql-jdbc.execute.diagnostic]
    [metabase.driver.sql-jdbc.execute.old-impl :as sql-jdbc.execute.old]
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
+   [metabase.lib.schema.expression.temporal
+    :as lib.schema.expression.temporal]
+   [metabase.lib.schema.literal.jvm :as lib.schema.literal.jvm]
+   [metabase.models.database :refer [Database]]
    [metabase.models.setting :refer [defsetting]]
    [metabase.query-processor.context :as qp.context]
    [metabase.query-processor.error-type :as qp.error-type]
@@ -26,7 +31,9 @@
    [metabase.util :as u]
    [metabase.util.i18n :refer [trs tru]]
    [metabase.util.log :as log]
-   [potemkin :as p])
+   [metabase.util.malli :as mu]
+   [potemkin :as p]
+   [toucan2.core :as t2])
   (:import
    (java.sql Connection JDBCType PreparedStatement ResultSet ResultSetMetaData Statement Types)
    (java.time Instant LocalDate LocalDateTime LocalTime OffsetDateTime OffsetTime ZonedDateTime)
@@ -38,27 +45,72 @@
 ;;; |                                        SQL JDBC Reducible QP Interface                                         |
 ;;; +----------------------------------------------------------------------------------------------------------------+
 
-(defmulti connection-with-timezone
-  "Fetch a Connection for a `database` with session time zone set to `timezone-id` (if supported by the driver.) The
-  default implementation:
+(def ConnectionOptions
+  "Malli schema for the options passed to [[do-with-connection-with-options]]."
+  [:maybe
+   [:map
+    ;; a string like 'US/Pacific' or something like that.
+    [:session-timezone {:optional true} [:maybe [:ref ::lib.schema.expression.temporal/timezone-id]]]
+    ;; whether this Connection should NOT be read-only, e.g. for DDL stuff or inserting data or whatever.
+    [:write? {:optional true} [:maybe :boolean]]]])
 
-  1. Calls util fn `datasource` to get a c3p0 connection pool DataSource
-  2. Calls `.getConnection()` the normal way
-  3. Executes [[set-timezone-sql]] if implemented by the driver.
+(defmulti do-with-connection-with-options
+  "Fetch a [[java.sql.Connection]] from a `driver`/`db-or-id-or-spec`, and invoke
 
-  `timezone-id` will be `nil` if a `report-timezone` Setting is not currently set; don't change the session time zone
-  if this is the case.
+    (f connection)
 
-  For drivers that support session timezones, the default implementation and [[set-timezone-sql]] should be considered
-  deprecated in favor of implementing `connection-with-timezone` directly. This way you can set the session timezone
-  in the most efficient manner, e.g. only setting it if needed (if there's an easy way for you to check this), or by
-  setting it as a parameter of the connection itself (the default connection pools are automatically flushed when
-  `report-timezone-id` changes).
+  If `db-or-id-or-spec` is a Database or Database ID, the default implementation fetches a pooled connection spec for
+  that Database using [[datasource]].
 
-  Custom implementations should set transaction isolation to the least-locking level supported by the driver, and make
-  connections read-only (*after* setting timezone, if needed)."
-  {:added    "0.35.0"
-   :arglists '(^java.sql.Connection [driver database ^String timezone-id])}
+  If `db-or-id-or-spec` is a `clojure.java.jdbc` spec, it fetches a Connection
+  using [[clojure.java..jdbc/get-connection]]. Note that this will not be a pooled connection unless your spec is for
+  a pooled DataSource.
+
+  `options` matches the [[ConnectionOptions]] schema above.
+
+  * If `:session-timezone` is passed, it should be used to set the Session timezone for the Connection. If not passed,
+    leave as-is
+
+  * If `:write?` is NOT passed or otherwise falsey, make the connection read-only if possible; if it is truthy, make
+    the connection read-write. Note that this current does not run things inside a transaction automatically; you'll
+    have to do that yourself if you want it
+
+  The normal 'happy path' is more or less
+
+    (with-open [conn (.getConnection (datasource driver db-or-id-or-spec))]
+      (set-best-transaction-level! driver conn)
+      (set-time-zone-if-supported! driver conn session-timezone)
+      (.setReadOnly conn true)
+      (.setAutoCommit conn true) ; so the query(s) are not ran inside a transaction
+      (.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)
+      (f conn))
+
+  This default implementation is abstracted out into two functions, [[do-with-resolved-connection]]
+  and [[set-default-connection-options!]], that you can use as needed in custom implementations. See various driver
+  implementations for examples. You should only set connection options on top-level calls
+  to [[do-with-connection-with-options]]; check whether this is a [[recursive-connection?]] before setting options.
+
+  There are two usual ways to set the session timezone if your driver supports them:
+
+  1. Specifying the session timezone based on the value of [[metabase.driver/report-timezone]] as a JDBC connection
+     parameter in the JDBC connection spec returned by [[metabase.driver.sql-jdbc.connection/connection-details->spec]].
+     If the spec returned by this method changes, connection pools associated with it will be flushed automatically.
+     This is the preferred way to set session timezones; if you set them this way, you DO NOT need to implement this
+     method unless you need to do something special with regards to setting the transaction level.
+
+  2. Setting the session timezone manually on the [[java.sql.Connection]] returned by [[datasource]] based on the
+     value of `session-timezone`.
+
+    2a. The default implementation will do this for you by executing SQL if you implement
+        [[set-timezone-sql]].
+
+    2b. You can implement this method, [[do-with-connection-with-options]], yourself and set the timezone however you
+        wish. Only set it if `session-timezone` is not `nil`!
+
+   Custom implementations should set transaction isolation to the least-locking level supported by the driver, and make
+   connections read-only (*after* setting timezone, if needed)."
+  {:added    "0.47.0"
+   :arglists '([driver db-or-id-or-spec options f])}
   driver/dispatch-on-initialized-driver
   :hierarchy #'driver/hierarchy)
 
@@ -71,6 +123,10 @@
     [(driver/dispatch-on-initialized-driver driver) (class object)])
   :hierarchy #'driver/hierarchy)
 
+;; TODO -- maybe like [[do-with-connection-with-options]] we should replace [[prepared-statment]] and [[statement]]
+;; with `do-with-prepared-statement` and `do-with-statement` methods -- that way you can't accidentally forget to wrap
+;; things in a `try-catch` and call `.close`
+
 (defmulti ^PreparedStatement prepared-statement
   "Create a PreparedStatement with `sql` query, and set any `params`. You shouldn't need to override the default
   implementation for this method; if you do, take care to set options to maximize result set read performance (e.g.
@@ -135,23 +191,23 @@
 ;;; +----------------------------------------------------------------------------------------------------------------+
 
 (defn datasource
-  "Fetch the connection pool `DataSource` associated with `database`."
+  "Fetch the connection pool `DataSource` associated with `db-or-id-or-spec`."
   {:added "0.35.0"}
-  ^DataSource [database]
-  (:datasource (sql-jdbc.conn/db->pooled-connection-spec database)))
+  ^DataSource [db-or-id-or-spec]
+  (:datasource (sql-jdbc.conn/db->pooled-connection-spec db-or-id-or-spec)))
 
 (defn datasource-with-diagnostic-info!
   "Fetch the connection pool `DataSource` associated with `database`, while also recording diagnostic info for the
   pool. To be used in conjunction with `sql-jdbc.execute.diagnostic/capturing-diagnostic-info`."
   {:added "0.40.0"}
-  ^DataSource [driver database]
-  (let [ds (datasource database)]
-    (sql-jdbc.execute.diagnostic/record-diagnostic-info-for-pool driver (u/the-id database) ds)
+  ^DataSource [driver db-or-id]
+  (let [ds (datasource db-or-id)]
+    (sql-jdbc.execute.diagnostic/record-diagnostic-info-for-pool! driver (u/the-id db-or-id) ds)
     ds))
 
 (defn set-time-zone-if-supported!
   "Execute `set-timezone-sql`, if implemented by driver, to set the session time zone. This way of setting the time zone
-  should be considered deprecated in favor of implementing `connection-with-time-zone` directly."
+  should be considered deprecated in favor of implementing `connection-with-timezone` directly."
   {:deprecated "0.35.0"}
   [driver ^Connection conn ^String timezone-id]
   (when timezone-id
@@ -191,33 +247,132 @@
         (seq more)
         (recur more)))))
 
-(defmethod connection-with-timezone :sql-jdbc
-  [driver database ^String timezone-id]
-  (let [conn (.getConnection (datasource-with-diagnostic-info! driver database))]
-    (try
-      (set-best-transaction-level! driver conn)
-      (set-time-zone-if-supported! driver conn timezone-id)
+(mu/defn do-with-resolved-connection-data-source :- (lib.schema.literal.jvm/instance-of DataSource)
+  "Part of the default implementation for [[do-with-connection-with-options]]: get an appropriate `java.sql.DataSource`
+  for `db-or-id-or-spec`. Not for use with a JDBC spec wrapping a `java.sql.Connection` (a spec with the key
+  `:connection`), since we do not have control over its lifecycle and would thus not be able to use [[with-open]] with
+  Connections provided by this DataSource."
+  {:added "0.47.0", :arglists '(^javax.sql.DataSource [driver db-or-id-or-spec options])}
+  [driver           :- :keyword
+   db-or-id-or-spec :- [:and
+                        [:or :int :map]
+                        [:fn
+                         ;; can't wrap a java.sql.Connection here because we're not
+                         ;; responsible for its lifecycle and that means you can't use
+                         ;; `with-open` on the Connection you'd get from the DataSource
+                         {:error/message "Cannot be a JDBC spec wrapping a java.sql.Connection"}
+                         (complement :connection)]]
+   {:keys [^String session-timezone], :as _options} :- ConnectionOptions]
+  (if-not (u/id db-or-id-or-spec)
+    ;; not a Database or Database ID... this is a raw `clojure.java.jdbc` spec, use that
+    ;; directly.
+    (reify DataSource
+      (getConnection [_this]
+        #_{:clj-kondo/ignore [:discouraged-var]}
+        (jdbc/get-connection db-or-id-or-spec)))
+    ;; otherwise this is either a Database or Database ID.
+    (if-let [old-method-impl (get-method
+                              #_{:clj-kondo/ignore [:deprecated-var]} sql-jdbc.execute.old/connection-with-timezone
+                              driver)]
+      ;; use the deprecated impl for `connection-with-timezone` if one exists.
+      (do
+        (log/warn (trs "{0} is deprecated in Metabase 0.47.0. Implement {1} instead."
+                       #_{:clj-kondo/ignore [:deprecated-var]}
+                       `connection-with-timezone
+                       `do-with-connection-with-options))
+        ;; for compatibility, make sure we pass it an actual Database instance.
+        (let [database (if (integer? db-or-id-or-spec)
+                         (t2/select-one Database db-or-id-or-spec)
+                         db-or-id-or-spec)]
+          (reify DataSource
+            (getConnection [_this]
+              (old-method-impl driver database session-timezone)))))
+      (datasource-with-diagnostic-info! driver db-or-id-or-spec))))
+
+(def ^:private ^:dynamic ^{:added "0.47.0"} *connection-recursion-depth*
+  "In recursive calls to [[do-with-connection-with-options]] we don't want to set options AGAIN, because this might
+  break things. For example in a top-level `:write?` call, we might disable auto-commit and run things in a
+  transaction; a read-only call inside of this transaction block should not go in and change the connection to be
+  auto-commit. So only set options at the top-level call, and use this to keep track of whether we're at the top level
+  or not.
+
+  This gets incremented inside [[do-with-resolved-connection]], so the top level call with have a depth of `0`, a
+  nested call will get `1`, and so forth. This is done this way and inside [[do-with-resolved-connection]]
+  and [[set-default-connection-options!]] so drivers that implement "
+  -1)
+
+(defn recursive-connection?
+  "Whether or not we are in a recursive call to [[do-with-connection-with-options]]. If we are, you shouldn't set
+  Connection options AGAIN, as that may override previous options that we don't want to override."
+  []
+  {:added "0.47.0"}
+  (pos? *connection-recursion-depth*))
+
+(mu/defn do-with-resolved-connection
+  "Execute
+
+    (f ^java.sql.Connection conn)
+
+  with a resolved JDBC connection. Part of the default implementation for [[do-with-connection-with-options]].
+  Generally does not set any `options`, but may set session-timezone if `driver` implements the
+  deprecated [[sql-jdbc.execute.old/connection-with-timezone]] method."
+  {:added "0.47.0"}
+  [driver           :- :keyword
+   db-or-id-or-spec :- [:or :int :map]
+   options          :- ConnectionOptions
+   f                :- fn?]
+  (binding [*connection-recursion-depth* (inc *connection-recursion-depth*)]
+    (if-let [conn (:connection db-or-id-or-spec)]
+      (f conn)
+      (with-open [conn (.getConnection (do-with-resolved-connection-data-source driver db-or-id-or-spec options))]
+        (f conn)))))
+
+(mu/defn set-default-connection-options!
+  "Part of the default implementation of [[do-with-connection-with-options]]: set options for a newly fetched
+  Connection."
+  {:added "0.47.0"}
+  [driver                                                 :- :keyword
+   ^Connection conn                                       :- (lib.schema.literal.jvm/instance-of Connection)
+   {:keys [^String session-timezone write?], :as options} :- ConnectionOptions]
+  (when-not (recursive-connection?)
+    (log/tracef "Setting default connection options with options %s" (pr-str options))
+    (set-best-transaction-level! driver conn)
+    (set-time-zone-if-supported! driver conn session-timezone)
+    (let [read-only? (not write?)]
       (try
         ;; Setting the connection to read-only does not prevent writes on some databases, and is meant
         ;; to be a hint to the driver to enable database optimizations
         ;; See https://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setReadOnly-boolean-
-        (.setReadOnly conn true)
+        (log/trace (pr-str (list '.setReadOnly 'conn read-only?)))
+        (.setReadOnly conn read-only?)
         (catch Throwable e
-          (log/debug e (trs "Error setting connection to read-only"))))
+          (log/debugf e "Error setting connection readOnly to %s" (pr-str read-only?)))))
+    ;; if this is (supposedly) a read-only connection, enable auto-commit so this IS NOT ran inside of a transaction.
+    ;;
+    ;; TODO -- for `write?` connections, we should probably disable autoCommit and then manually call `.commit` at after
+    ;; `f`... we need to check and make sure that won't mess anything up, since some existing code is already doing it
+    ;; manually.
+    (when-not write?
       (try
-        ;; set autocommit to false so that pg honors fetchSize. Otherwise it commits the transaction and needs the
-        ;; entire realized result set
-        (.setAutoCommit conn false)
+        (log/trace (pr-str '(.setAutoCommit conn true)))
+        (.setAutoCommit conn true)
         (catch Throwable e
-          (log/debug e (trs "Error setting connection to autoCommit false"))))
-      (try
-        (.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)
-        (catch Throwable e
-          (log/debug e (trs "Error setting default holdability for connection"))))
-      conn
+          (log/debug e "Error enabling connection autoCommit"))))
+    (try
+      (log/trace (pr-str '(.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)))
+      (.setHoldability conn ResultSet/CLOSE_CURSORS_AT_COMMIT)
       (catch Throwable e
-        (.close conn)
-        (throw e)))))
+        (log/debug e (trs "Error setting default holdability for connection"))))))
+
+(defmethod do-with-connection-with-options :sql-jdbc
+  [driver db-or-id-or-spec options f]
+  (do-with-resolved-connection
+   driver
+   db-or-id-or-spec
+   options
+   (fn [^Connection conn]
+     (set-default-connection-options! driver conn options)
+     (f conn))))
 
 ;; TODO - would a more general method to convert a parameter to the desired class (and maybe JDBC type) be more
 ;; useful? Then we can actually do things like log what transformations are taking place
@@ -497,27 +652,54 @@
      (execute-reducible-query driver sql params max-rows context respond)))
 
   ([driver sql params max-rows context respond]
-   (with-open [conn          (connection-with-timezone driver (qp.store/database) (qp.timezone/report-timezone-id-if-supported))
-               stmt          (statement-or-prepared-statement driver conn sql params (qp.context/canceled-chan context))
-               ^ResultSet rs (try
-                               (execute-statement-or-prepared-statement! driver stmt max-rows params sql)
-                               (catch Throwable e
-                                 (throw (ex-info (tru "Error executing query: {0}" (ex-message e))
-                                                 {:driver driver
-                                                  :sql    (str/split-lines (mdb.query/format-sql sql driver))
-                                                  :params params
-                                                  :type   qp.error-type/invalid-query}
-                                                 e))))]
-     (let [rsmeta           (.getMetaData rs)
-           results-metadata {:cols (column-metadata driver rsmeta)}]
-       (respond results-metadata (reducible-rows driver rs rsmeta (qp.context/canceled-chan context)))))))
+   (do-with-connection-with-options
+    driver
+    (qp.store/database)
+    {:session-timezone (qp.timezone/report-timezone-id-if-supported driver (qp.store/database))}
+    (fn [^Connection conn]
+      (with-open [stmt          (statement-or-prepared-statement driver conn sql params (qp.context/canceled-chan context))
+                  ^ResultSet rs (try
+                                  (execute-statement-or-prepared-statement! driver stmt max-rows params sql)
+                                  (catch Throwable e
+                                    (throw (ex-info (tru "Error executing query: {0}" (ex-message e))
+                                                    {:driver driver
+                                                     :sql    (str/split-lines (mdb.query/format-sql sql driver))
+                                                     :params params
+                                                     :type   qp.error-type/invalid-query}
+                                                    e))))]
+        (let [rsmeta           (.getMetaData rs)
+              results-metadata {:cols (column-metadata driver rsmeta)}]
+          (respond results-metadata (reducible-rows driver rs rsmeta (qp.context/canceled-chan context)))))))))
+
+
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; |                                                 Actions Stuff                                                  |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(defmethod driver/execute-write-query! :sql-jdbc
+  [driver {{sql :query, :keys [params]} :native}]
+  {:pre [(string? sql)]}
+  (try
+    (do-with-connection-with-options
+     driver
+     {:session-timezone (qp.timezone/report-timezone-id-if-supported driver (qp.store/database))}
+     (fn [^Connection conn]
+       (with-open [stmt (statement-or-prepared-statement driver conn sql params nil)]
+         {:rows-affected (if (instance? PreparedStatement stmt)
+                           (.executeUpdate ^PreparedStatement stmt)
+                           (.executeUpdate stmt sql))})))
+    (catch Throwable e
+      (throw (ex-info (tru "Error executing write query: {0}" (ex-message e))
+                      {:sql sql, :params params, :type qp.error-type/invalid-query}
+                      e)))))
 
 
 ;;; +----------------------------------------------------------------------------------------------------------------+
 ;;; |                                       Convenience Imports from Old Impl                                        |
 ;;; +----------------------------------------------------------------------------------------------------------------+
 
+#_{:clj-kondo/ignore [:deprecated-var]}
 (p/import-vars
  [sql-jdbc.execute.old
-  ;; interface (set-parameter is imported as well at the top of the namespace)
+  connection-with-timezone
   set-timezone-sql])
diff --git a/src/metabase/driver/sql_jdbc/execute/diagnostic.clj b/src/metabase/driver/sql_jdbc/execute/diagnostic.clj
index d1f5618a31a5887ac48b4ad8b25538078dbc6db7..1cb8649e4885a2798c9ed29fd21f813d4fc74f17 100644
--- a/src/metabase/driver/sql_jdbc/execute/diagnostic.clj
+++ b/src/metabase/driver/sql_jdbc/execute/diagnostic.clj
@@ -24,14 +24,14 @@
   ```
   (sql-jdbc.execute.diagnostic/capturing-diagnostic-info [diag-info-fn]
     ;; various body forms
-    ;; fetch the diagnostic info, which should be available if execute code called `record-diagnostic-info-for-pool`
+    ;; fetch the diagnostic info, which should be available if execute code called `record-diagnostic-info-for-pool!`
     (diag-info-fn))
   ```"
   {:style/indent 1}
   [[diagnostic-info-fn-binding] & body]
   `(do-with-diagnostic-info (fn [~diagnostic-info-fn-binding] ~@body)))
 
-(defn record-diagnostic-info-for-pool
+(defn record-diagnostic-info-for-pool!
   "Captures diagnostic info related to the given `driver`, `database-id`, and `datasource` (which are all related).
   The current information that is captured (in a map whose keys are namespaced keywords in this ns) is:
 
diff --git a/src/metabase/driver/sql_jdbc/execute/old_impl.clj b/src/metabase/driver/sql_jdbc/execute/old_impl.clj
index 05c69d70dfa874837c2569ced2485f1203e16bf2..8f8d94afe71c9a05e6240ef55a09325bc757a085 100644
--- a/src/metabase/driver/sql_jdbc/execute/old_impl.clj
+++ b/src/metabase/driver/sql_jdbc/execute/old_impl.clj
@@ -1,14 +1,20 @@
 (ns metabase.driver.sql-jdbc.execute.old-impl
-  "Old implementation of `metabase.driver.sql-jdbc.execute` for non-reducible results (i.e., for implementing
-  `driver/execute-query` rather than `driver/execute-reducible-query`).
-
-  All methods and functions in this namespace should be considered deprecated with the exception of `set-parameter`,
-  which will be moved to `metabase.driver.sql-jdbc.execute` when this namespace is removed."
+  "Old implementations of [[metabase.driver.sql-jdbc.execute]] methods. All methods and functions in this namespace
+  should be considered deprecated and will be removed in future releases."
   (:require
    [metabase.driver :as driver]))
 
 (set! *warn-on-reflection* true)
 
+(defmulti connection-with-timezone
+  "Deprecated in Metabase 47. Implement [[metabase.driver.sql-jdbc.execute/do-with-connection-with-options]] instead.
+  This method will be removed in or after Metabase 50."
+  {:added      "0.35.0"
+   :deprecated "0.47.0"
+   :arglists   '(^java.sql.Connection [driver database ^String timezone-id])}
+  driver/dispatch-on-initialized-driver
+  :hierarchy #'driver/hierarchy)
+
 (defmulti set-timezone-sql
   "Return a format string containing a SQL statement to be used to set the timezone for the current transaction.
   The `%s` will be replaced with a string literal for a timezone, e.g. `US/Pacific.` (Timezone ID will come already
@@ -17,8 +23,8 @@
     \"SET @@session.time_zone = %s;\"
 
   This method is only called for drivers using the default implementation
-  of [[metabase.driver.sql-jdbc.execute/connection-with-timezone]]; it should be considered deprecated in favor of
-  implementing `connection-with-timezone` directly."
+  of [[metabase.driver.sql-jdbc.execute/do-with-connection-with-options]]; it should be considered deprecated in
+  favor of implementing [[metabase.driver.sql-jdbc.execute/do-with-connection-with-options]] directly."
   {:deprecated "0.35.0", :arglists '([driver])}
   driver/dispatch-on-initialized-driver
   :hierarchy #'driver/hierarchy)
diff --git a/src/metabase/driver/sql_jdbc/sync/dbms_version.clj b/src/metabase/driver/sql_jdbc/sync/dbms_version.clj
index 939f329da0a75f5d72dc26040b3b729666d5205c..9a3383bf4e823ed42fcede6aedc29c6149baa5db 100644
--- a/src/metabase/driver/sql_jdbc/sync/dbms_version.clj
+++ b/src/metabase/driver/sql_jdbc/sync/dbms_version.clj
@@ -1,14 +1,19 @@
 (ns metabase.driver.sql-jdbc.sync.dbms-version
   (:require
-   [clojure.java.jdbc :as jdbc]))
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]))
 
 (set! *warn-on-reflection* true)
 
 (defn dbms-version
   "Default implementation of `driver/dbms-version` for SQL JDBC drivers. Uses JDBC DatabaseMetaData."
-  [_driver jdbc-spec]
-  (jdbc/with-db-metadata [metadata jdbc-spec]
-    {:flavor (.getDatabaseProductName metadata)
-     :version (.getDatabaseProductVersion metadata)
-     :semantic-version [(.getDatabaseMajorVersion metadata)
-                        (.getDatabaseMinorVersion metadata)]}))
+  [driver jdbc-spec]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   jdbc-spec
+   nil
+   (fn [^java.sql.Connection conn]
+     (let [metadata (.getMetaData conn)]
+       {:flavor           (.getDatabaseProductName metadata)
+        :version          (.getDatabaseProductVersion metadata)
+        :semantic-version [(.getDatabaseMajorVersion metadata)
+                           (.getDatabaseMinorVersion metadata)]}))))
diff --git a/src/metabase/driver/sql_jdbc/sync/describe_database.clj b/src/metabase/driver/sql_jdbc/sync/describe_database.clj
index 29e19d94d6009ce25c687544960448b82e09ebc5..5235497990440006609994629d2b6da6c9681770 100644
--- a/src/metabase/driver/sql_jdbc/sync/describe_database.clj
+++ b/src/metabase/driver/sql_jdbc/sync/describe_database.clj
@@ -1,10 +1,8 @@
 (ns metabase.driver.sql-jdbc.sync.describe-database
   "SQL JDBC impl for `describe-database`."
   (:require
-   [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [metabase.driver :as driver]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
    [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync.common :as sql-jdbc.sync.common]
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
@@ -101,17 +99,17 @@
   "Fetch a JDBC Metadata ResultSet of tables in the DB, optionally limited to ones belonging to a given
   schema. Returns a reducible sequence of results."
   [driver ^DatabaseMetaData metadata ^String schema-or-nil ^String db-name-or-nil]
-  (sql-jdbc.sync.common/reducible-results
-   #(.getTables metadata db-name-or-nil (some->> schema-or-nil (driver/escape-entity-name-for-metadata driver)) "%"
-                (into-array String ["TABLE" "PARTITIONED TABLE" "VIEW" "FOREIGN TABLE" "MATERIALIZED VIEW"
-                                    "EXTERNAL TABLE"]))
-   (fn [^ResultSet rs]
-     (fn []
-       {:name        (.getString rs "TABLE_NAME")
-        :schema      (.getString rs "TABLE_SCHEM")
-        :description (when-let [remarks (.getString rs "REMARKS")]
-                       (when-not (str/blank? remarks)
-                         remarks))}))))
+  (with-open [rset (.getTables metadata db-name-or-nil (some->> schema-or-nil (driver/escape-entity-name-for-metadata driver)) "%"
+                               (into-array String ["TABLE" "PARTITIONED TABLE" "VIEW" "FOREIGN TABLE" "MATERIALIZED VIEW"
+                                                   "EXTERNAL TABLE"]))]
+    (loop [acc []]
+      (if-not (.next rset)
+        acc
+        (recur (conj acc {:name        (.getString rset "TABLE_NAME")
+                          :schema      (.getString rset "TABLE_SCHEM")
+                          :description (when-let [remarks (.getString rset "REMARKS")]
+                                         (when-not (str/blank? remarks)
+                                           remarks))}))))))
 
 (defn fast-active-tables
   "Default, fast implementation of `active-tables` best suited for DBs with lots of system tables (like Oracle). Fetch
@@ -157,23 +155,25 @@
         :else
         nil))
 
-(defn describe-database
-  "Default implementation of `driver/describe-database` for SQL JDBC drivers. Uses JDBC DatabaseMetaData."
-  [driver db-or-id-or-spec]
-  {:tables (with-open [conn (jdbc/get-connection (sql-jdbc.conn/db->pooled-connection-spec db-or-id-or-spec))]
-             ;; try to set the Connection to `READ_UNCOMMITED` if possible, or whatever the next least-locking level
-             ;; is. Not sure how much of a difference that makes since we're not running this inside a transaction,
-             ;; but better safe than sorry
-             (sql-jdbc.execute/set-best-transaction-level! driver conn)
-             (let [schema-filter-prop      (driver.u/find-schema-filters-prop driver)
-                   has-schema-filter-prop? (some? schema-filter-prop)
-                   default-active-tbl-fn   #(into #{} (sql-jdbc.sync.interface/active-tables driver conn nil nil))]
-               (if has-schema-filter-prop?
-                 (if-let [database (db-or-id-or-spec->database db-or-id-or-spec)]
-                   (let [prop-nm                                 (:name schema-filter-prop)
-                         [inclusion-patterns exclusion-patterns] (driver.s/db-details->schema-filter-patterns
-                                                                  prop-nm
-                                                                  database)]
-                     (into #{} (sql-jdbc.sync.interface/active-tables driver conn inclusion-patterns exclusion-patterns)))
-                   (default-active-tbl-fn))
-                 (default-active-tbl-fn))))})
+(mu/defn describe-database
+  "Default implementation of [[metabase.driver/describe-database]] for SQL JDBC drivers. Uses JDBC DatabaseMetaData."
+  [driver           :- :keyword
+   db-or-id-or-spec :- [:or :int :map]]
+  {:tables
+   (sql-jdbc.execute/do-with-connection-with-options
+    driver
+    db-or-id-or-spec
+    nil
+    (fn [^Connection conn]
+      (let [schema-filter-prop      (driver.u/find-schema-filters-prop driver)
+            has-schema-filter-prop? (some? schema-filter-prop)
+            default-active-tbl-fn   #(into #{} (sql-jdbc.sync.interface/active-tables driver conn nil nil))]
+        (if has-schema-filter-prop?
+          (if-let [database (db-or-id-or-spec->database db-or-id-or-spec)]
+            (let [prop-nm                                 (:name schema-filter-prop)
+                  [inclusion-patterns exclusion-patterns] (driver.s/db-details->schema-filter-patterns
+                                                           prop-nm
+                                                           database)]
+              (into #{} (sql-jdbc.sync.interface/active-tables driver conn inclusion-patterns exclusion-patterns)))
+            (default-active-tbl-fn))
+          (default-active-tbl-fn)))))})
diff --git a/src/metabase/driver/sql_jdbc/sync/describe_table.clj b/src/metabase/driver/sql_jdbc/sync/describe_table.clj
index 78d8d53e050fc39f67f5272fa4388293f9ca686f..2129afe4fc1d4836991adc26992ea1e1e0e8a50c 100644
--- a/src/metabase/driver/sql_jdbc/sync/describe_table.clj
+++ b/src/metabase/driver/sql_jdbc/sync/describe_table.clj
@@ -8,7 +8,7 @@
    [medley.core :as m]
    [metabase.db.metadata-queries :as metadata-queries]
    [metabase.driver :as driver]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync.common :as sql-jdbc.sync.common]
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
    [metabase.driver.sql.query-processor :as sql.qp]
@@ -24,7 +24,9 @@
 
 (set! *warn-on-reflection* true)
 
-(defmethod sql-jdbc.sync.interface/column->semantic-type :sql-jdbc [_ _ _] nil)
+(defmethod sql-jdbc.sync.interface/column->semantic-type :sql-jdbc
+  [_driver _database-type _column-name]
+  nil)
 
 (defn pattern-based-database-type->base-type
   "Return a `database-type->base-type` function that matches types based on a sequence of pattern / base-type pairs.
@@ -225,9 +227,12 @@
   [driver db-or-id-or-spec-or-conn table]
   (if (instance? Connection db-or-id-or-spec-or-conn)
     (describe-table* driver db-or-id-or-spec-or-conn table)
-    (let [spec (sql-jdbc.conn/db->pooled-connection-spec db-or-id-or-spec-or-conn)]
-      (with-open [conn (jdbc/get-connection spec)]
-        (describe-table* driver conn table)))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     db-or-id-or-spec-or-conn
+     nil
+     (fn [^Connection conn]
+       (describe-table* driver conn table)))))
 
 (defn- describe-table-fks*
   [_driver ^Connection conn {^String schema :schema, ^String table-name :name} & [^String db-name-or-nil]]
@@ -246,9 +251,12 @@
   [driver db-or-id-or-spec-or-conn table & [db-name-or-nil]]
   (if (instance? Connection db-or-id-or-spec-or-conn)
     (describe-table-fks* driver db-or-id-or-spec-or-conn table db-name-or-nil)
-    (let [spec (sql-jdbc.conn/db->pooled-connection-spec db-or-id-or-spec-or-conn)]
-      (with-open [conn (jdbc/get-connection spec)]
-        (describe-table-fks* driver conn table db-name-or-nil)))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     driver
+     db-or-id-or-spec-or-conn
+     nil
+     (fn [^Connection conn]
+       (describe-table-fks* driver conn table db-name-or-nil)))))
 
 (def ^:dynamic *nested-field-column-max-row-length*
   "Max string length for a row for nested field column before we just give up on parsing it.
@@ -397,35 +405,39 @@
 (defn describe-nested-field-columns
   "Default implementation of [[metabase.driver.sql-jdbc.sync.interface/describe-nested-field-columns]] for SQL JDBC
   drivers. Goes and queries the table if there are JSON columns for the nested contents."
-  [driver spec table]
-  (with-open [conn (jdbc/get-connection spec)]
-    (let [table-identifier-info [(:schema table) (:name table)]
-          table-fields          (describe-table-fields driver conn table nil)
-          json-fields           (filter #(isa? (:base-type %) :type/JSON) table-fields)]
-      (if (nil? (seq json-fields))
-        #{}
-        (sql.qp/with-driver-honey-sql-version driver
-          (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))))))))))
+  [driver jdbc-spec table]
+  (sql-jdbc.execute/do-with-connection-with-options
+   driver
+   jdbc-spec
+   nil
+   (fn [^Connection conn]
+     (let [table-identifier-info [(:schema table) (:name table)]
+           table-fields          (describe-table-fields driver conn table nil)
+           json-fields           (filter #(isa? (:base-type %) :type/JSON) table-fields)]
+       (if (nil? (seq json-fields))
+         #{}
+         (sql.qp/with-driver-honey-sql-version driver
+           (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 jdbc-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/lib/schema/expression/temporal.cljc b/src/metabase/lib/schema/expression/temporal.cljc
index db3db6ae63187e3170dca8ecddad5ef5ae94ef42..f265da03608a7a2e5d4fd0c1010775e1772201c0 100644
--- a/src/metabase/lib/schema/expression/temporal.cljc
+++ b/src/metabase/lib/schema/expression/temporal.cljc
@@ -77,7 +77,6 @@
 
 (mr/def ::timezone-id
   [:and
-
    ::common/non-blank-string
    (into [:enum
           {:error/message "valid timezone ID"
diff --git a/src/metabase/lib/schema/literal/jvm.clj b/src/metabase/lib/schema/literal/jvm.clj
index 66bc10aad98c0c6c76d559947c1cc4d81bb9391a..7dd076d082e137aef1dc34c2c9c1ff506167dd1f 100644
--- a/src/metabase/lib/schema/literal/jvm.clj
+++ b/src/metabase/lib/schema/literal/jvm.clj
@@ -6,7 +6,9 @@
 
 (set! *warn-on-reflection* true)
 
-(defn- instance-of [^Class klass]
+(defn instance-of
+  "Convenience for defining a Malli schema for an instance of a particular Class."
+  [^Class klass]
   [:fn {:error/message (str "instance of " (.getName klass))}
    #(instance? klass %)])
 
diff --git a/src/metabase/query_processor/middleware/add_implicit_joins.clj b/src/metabase/query_processor/middleware/add_implicit_joins.clj
index 45a41a783dc29d7237c96cd96c89b8d465abd8cc..4fe206b70f61a80fd4945a0bc2fe67c1c03c5735 100644
--- a/src/metabase/query_processor/middleware/add_implicit_joins.clj
+++ b/src/metabase/query_processor/middleware/add_implicit_joins.clj
@@ -119,7 +119,7 @@
               (if-not (some #{:source-metadata} &parents)
                 (let [join-alias (or (fk-field-id->join-alias (:source-field opts))
                                      (throw (ex-info (tru "Cannot find matching FK Table ID for FK Field {0}"
-                                                          (fk-field-id->join-alias (:source-field opts)))
+                                                          (pr-str (:source-field opts)))
                                                      {:resolving  &match
                                                       :candidates fk-field-id->join-alias})))]
                   [:field id-or-name (assoc opts :join-alias join-alias)])
diff --git a/test/metabase/db/fix_mysql_utf8_test.clj b/test/metabase/db/fix_mysql_utf8_test.clj
index 0a3d2376ba2680a09e549d782f1254fc54f2452e..78f1186432ad1db5330ccd162c94ba1d1faa126f 100644
--- a/test/metabase/db/fix_mysql_utf8_test.clj
+++ b/test/metabase/db/fix_mysql_utf8_test.clj
@@ -6,17 +6,24 @@
    [metabase.db.data-source :as mdb.data-source]
    [metabase.db.setup :as mdb.setup]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.models :refer [Database]]
    [metabase.test :as mt]
    [toucan.db :as db]
    [toucan2.core :as t2]))
 
 (defn- create-test-db! []
-  (jdbc/with-db-connection [server-conn (sql-jdbc.conn/connection-details->spec :mysql
-                                          (mt/dbdef->connection-details :mysql :server nil))]
-    (doseq [statement ["DROP DATABASE IF EXISTS utf8_test;"
-                       "CREATE DATABASE utf8_test;"]]
-      (jdbc/execute! server-conn statement))))
+  (let [spec (sql-jdbc.conn/connection-details->spec
+              :mysql
+              (mt/dbdef->connection-details :mysql :server nil))]
+    (sql-jdbc.execute/do-with-connection-with-options
+     :mysql
+     spec
+     {:write? true}
+     (fn [^java.sql.Connection server-conn]
+       (doseq [statement ["DROP DATABASE IF EXISTS utf8_test;"
+                          "CREATE DATABASE utf8_test;"]]
+         (jdbc/execute! {:connection server-conn} statement))))))
 
 (defn- test-data-source ^javax.sql.DataSource []
   (mdb.data-source/broken-out-details->DataSource
diff --git a/test/metabase/db/liquibase_test.clj b/test/metabase/db/liquibase_test.clj
index 20a56d64608cad444c286857771c1752516ae550..f68f0274d08e1784a1f403f18182a5945651f6cc 100644
--- a/test/metabase/db/liquibase_test.clj
+++ b/test/metabase/db/liquibase_test.clj
@@ -1,22 +1,26 @@
 (ns metabase.db.liquibase-test
   (:require
-   [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [clojure.test :refer :all]
    [metabase.db.liquibase :as liquibase]
    [metabase.db.test-util :as mdb.test-util]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
-   [metabase.test :as mt]))
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
+   [metabase.test :as mt]
+   [next.jdbc :as next.jdbc]))
 
 (deftest mysql-engine-charset-test
   (mt/test-driver :mysql
     (testing "Make sure MySQL CREATE DATABASE statements have ENGINE/CHARACTER SET appended to them (#10691)"
-      (jdbc/with-db-connection [conn (sql-jdbc.conn/connection-details->spec
-                                      :mysql
-                                      (mt/dbdef->connection-details :mysql :server nil))]
-        (doseq [statement ["DROP DATABASE IF EXISTS liquibase_test;"
-                           "CREATE DATABASE liquibase_test;"]]
-          (jdbc/execute! conn statement)))
+      (sql-jdbc.execute/do-with-connection-with-options
+       :mysql
+       (sql-jdbc.conn/connection-details->spec :mysql
+                                               (mt/dbdef->connection-details :mysql :server nil))
+       {:write? true}
+       (fn [^java.sql.Connection conn]
+         (doseq [statement ["DROP DATABASE IF EXISTS liquibase_test;"
+                            "CREATE DATABASE liquibase_test;"]]
+           (next.jdbc/execute! conn [statement]))))
       (liquibase/with-liquibase [liquibase (->> (mt/dbdef->connection-details :mysql :db {:database-name "liquibase_test"})
                                                 (sql-jdbc.conn/connection-details->spec :mysql)
                                                 mdb.test-util/->ClojureJDBCSpecDataSource)]
diff --git a/test/metabase/db/test_util.clj b/test/metabase/db/test_util.clj
index ec6820c2a5d57106066af5f38cc32dee05818722..e0d5d4d4d2722b0fbf2a20b47fbfe8c380a61ffd 100644
--- a/test/metabase/db/test_util.clj
+++ b/test/metabase/db/test_util.clj
@@ -15,6 +15,7 @@
 
   javax.sql.DataSource
   (getConnection [_]
+    #_{:clj-kondo/ignore [:discouraged-var]}
     (jdbc/get-connection jdbc-spec))
 
   (getConnection [_ _user _password]
diff --git a/test/metabase/driver/mysql_test.clj b/test/metabase/driver/mysql_test.clj
index 83ce2e2817711c04b730b5f05f27fb7d85a47e8c..228d6c813a1e2d5f079b0940074dcbc600a6f229 100644
--- a/test/metabase/driver/mysql_test.clj
+++ b/test/metabase/driver/mysql_test.clj
@@ -620,9 +620,9 @@
           (is (thrown-with-msg?
                 Exception
                 #"Killed mysql process id [\d,]+ due to timeout."
-                (#'mysql.ddl/execute-with-timeout! db-spec db-spec 10 ["select sleep(5)"]))))
+                (#'mysql.ddl/execute-with-timeout! :mysql db-spec db-spec 10 ["select sleep(5)"]))))
         (testing "When the query takes less time than the timeout, it is successful."
-          (is (some? (#'mysql.ddl/execute-with-timeout! db-spec db-spec 5000 ["select sleep(0.1) as val"]))))))))
+          (is (some? (#'mysql.ddl/execute-with-timeout! :mysql db-spec db-spec 5000 ["select sleep(0.1) as val"]))))))))
 
 (deftest syncable-schemas-test
   (mt/test-driver :mysql
diff --git a/test/metabase/driver/postgres_test.clj b/test/metabase/driver/postgres_test.clj
index 99d12ec65ea926baee56f06918faa2e9ffabd8f8..322030e915d7a2bf9e9ef9c158c608c43778947d 100644
--- a/test/metabase/driver/postgres_test.clj
+++ b/test/metabase/driver/postgres_test.clj
@@ -35,10 +35,9 @@
    #_{:clj-kondo/ignore [:discouraged-namespace]}
    [metabase.util.honeysql-extensions :as hx]
    [metabase.util.log :as log]
+   [next.jdbc :as next.jdbc]
    [toucan2.core :as t2]
-   [toucan2.tools.with-temp :as t2.with-temp])
-  (:import
-   (java.sql DatabaseMetaData)))
+   [toucan2.tools.with-temp :as t2.with-temp]))
 
 (set! *warn-on-reflection* true)
 
@@ -323,8 +322,12 @@
             spec    (sql-jdbc.conn/connection-details->spec :postgres details)]
         ;; create the postgres DB
         (drop-if-exists-and-create-db! db-name)
-        (let [major-v ((jdbc/with-db-metadata [metadata spec]
-                         #(.getDatabaseMajorVersion ^DatabaseMetaData metadata)))]
+        (let [major-v (sql-jdbc.execute/do-with-connection-with-options
+                       :postgres
+                       spec
+                       nil
+                       (fn [^java.sql.Connection conn]
+                         (.. conn getMetaData getDatabaseMajorVersion)))]
           (if (>= major-v 10)
             ;; create the DB object
             (t2.with-temp/with-temp [Database database {:engine :postgres, :details (assoc details :dbname db-name)}]
@@ -404,15 +407,14 @@
             json-part (json/generate-string {:bob :dobbs})
             insert    (str "CREATE TABLE json_alias_test (json_part JSON NOT NULL);"
                            (format "INSERT INTO json_alias_test (json_part) VALUES ('%s');" json-part))]
-        (jdbc/with-db-connection [_conn (sql-jdbc.conn/connection-details->spec :postgres details)]
-          (jdbc/execute! spec [insert]))
-        (mt/with-temp* [Database [database    {:engine :postgres, :details details}]
-                        Table    [table       {:db_id (u/the-id database) :name "json_alias_test"}]
-                        Field    [field       {:table_id (u/the-id table)
-                                               :nfc_path [:bob
-                                                          "injection' OR 1=1--' AND released = 1"
-                                                          (keyword "injection' OR 1=1--' AND released = 1")],
-                                               :name     "json_alias_test"}]]
+        (jdbc/execute! spec [insert])
+        (t2.with-temp/with-temp [Database database {:engine :postgres, :details details}
+                                 Table    table    {:db_id (u/the-id database) :name "json_alias_test"}
+                                 Field    field    {:table_id (u/the-id table)
+                                                    :nfc_path [:bob
+                                                               "injection' OR 1=1--' AND released = 1"
+                                                               (keyword "injection' OR 1=1--' AND released = 1")],
+                                                    :name     "json_alias_test"}]
           (let [field-bucketed [:field (u/the-id field)
                                 {:temporal-unit :month,
                                  :metabase.query-processor.util.add-alias-info/source-table (u/the-id table),
@@ -489,9 +491,8 @@
       (let [details (mt/dbdef->connection-details :postgres :db {:database-name "describe-json-test"
                                                                  :json-unfolding true})
             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}]]
+        (jdbc/execute! spec [describe-json-table-sql])
+        (t2.with-temp/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"})
@@ -553,10 +554,9 @@
       (let [details (mt/dbdef->connection-details :postgres :db {:database-name  "describe-json-with-schema-test"
                                                                  :json-unfolding true})
             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 [(str "CREATE SCHEMA bobdobbs;"
-                                    "CREATE TABLE bobdobbs.describe_json_table (trivial_json JSONB NOT NULL);"
-                                    "INSERT INTO bobdobbs.describe_json_table (trivial_json) VALUES ('{\"a\": 1}');")]))
+        (jdbc/execute! spec [(str "CREATE SCHEMA bobdobbs;"
+                                  "CREATE TABLE bobdobbs.describe_json_table (trivial_json JSONB NOT NULL);"
+                                  "INSERT INTO bobdobbs.describe_json_table (trivial_json) VALUES ('{\"a\": 1}');")])
         (t2.with-temp/with-temp [Database database {:engine :postgres, :details details}]
           (mt/with-db database
             (sync-tables/sync-tables-and-database! database)
@@ -579,10 +579,9 @@
       (let [details (mt/dbdef->connection-details :postgres :db {:database-name  "describe-json-funky-names-test"
                                                                  :json-unfolding true})
             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 [(str "CREATE SCHEMA \"AAAH_#\";"
-                                    "CREATE TABLE \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json JSONB NOT NULL);"
-                                    "INSERT INTO \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json) VALUES ('{\"a\": 1}');")]))
+        (jdbc/execute! spec [(str "CREATE SCHEMA \"AAAH_#\";"
+                                  "CREATE TABLE \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json JSONB NOT NULL);"
+                                  "INSERT INTO \"AAAH_#\".\"dESCribe_json_table_%\" (trivial_json) VALUES ('{\"a\": 1}');")])
         (t2.with-temp/with-temp [Database database {:engine :postgres, :details details}]
           (mt/with-db database
             (sync-tables/sync-tables-and-database! database)
@@ -609,8 +608,7 @@
             big-json (json/generate-string big-map)
             sql      (str "CREATE TABLE big_json_table (big_json JSON NOT NULL);"
                           (format "INSERT INTO big_json_table (big_json) VALUES ('%s');" big-json))]
-        (jdbc/with-db-connection [_conn (sql-jdbc.conn/connection-details->spec :postgres details)]
-          (jdbc/execute! spec [sql]))
+        (jdbc/execute! spec [sql])
         (t2.with-temp/with-temp [Database database {:engine :postgres, :details details}]
           (mt/with-db database
             (sync-tables/sync-tables-and-database! database)
@@ -716,12 +714,16 @@
 (defn- do-with-money-test-db [thunk]
   (drop-if-exists-and-create-db! "money_columns_test")
   (let [details (mt/dbdef->connection-details :postgres :db {:database-name "money_columns_test"})]
-    (jdbc/with-db-connection [conn (sql-jdbc.conn/connection-details->spec :postgres details)]
-      (doseq [sql+args [["CREATE table bird_prices (bird TEXT, price money);"]
-                        ["INSERT INTO bird_prices (bird, price) VALUES (?, ?::numeric::money), (?, ?::numeric::money);"
-                         "Lucky Pigeon"   6.0
-                         "Katie Parakeet" 23.99]]]
-        (jdbc/execute! conn sql+args)))
+    (sql-jdbc.execute/do-with-connection-with-options
+     :postgres
+     (sql-jdbc.conn/connection-details->spec :postgres details)
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (doseq [sql+args [["CREATE table bird_prices (bird TEXT, price money);"]
+                         ["INSERT INTO bird_prices (bird, price) VALUES (?, ?::numeric::money), (?, ?::numeric::money);"
+                          "Lucky Pigeon"   6.0
+                          "Katie Parakeet" 23.99]]]
+         (next.jdbc/execute! conn sql+args))))
     (t2.with-temp/with-temp [Database db {:engine :postgres, :details (assoc details :dbname "money_columns_test")}]
       (sync/sync-database! db)
       (mt/with-db db
@@ -731,12 +733,16 @@
   (mt/test-driver :postgres
     (testing "We should support the Postgres MONEY type"
       (testing "It should be possible to return money column results (#3754)"
-        (with-open [conn (sql-jdbc.execute/connection-with-timezone :postgres (mt/db) nil)
-                    stmt (sql-jdbc.execute/prepared-statement :postgres conn "SELECT 1000::money AS \"money\";" nil)
-                    rs   (sql-jdbc.execute/execute-prepared-statement! :postgres stmt)]
-          (let [row-thunk (sql-jdbc.execute/row-thunk :postgres rs (.getMetaData rs))]
-            (is (= [1000.00M]
-                   (row-thunk))))))
+        (sql-jdbc.execute/do-with-connection-with-options
+         :postgres
+         (mt/db)
+         nil
+         (fn [conn]
+           (with-open [stmt (sql-jdbc.execute/prepared-statement :postgres conn "SELECT 1000::money AS \"money\";" nil)
+                       rs   (sql-jdbc.execute/execute-prepared-statement! :postgres stmt)]
+             (let [row-thunk (sql-jdbc.execute/row-thunk :postgres rs (.getMetaData rs))]
+               (is (= [1000.00M]
+                      (row-thunk))))))))
 
       (do-with-money-test-db
        (fn []
@@ -792,8 +798,8 @@
   properly."
   []
   (drop-if-exists-and-create-db! "enums_test")
-  (jdbc/with-db-connection [conn (sql-jdbc.conn/connection-details->spec :postgres (enums-test-db-details))]
-    (jdbc/execute! conn [enums-db-sql])))
+  (let [spec (sql-jdbc.conn/connection-details->spec :postgres (enums-test-db-details))]
+    (jdbc/execute! spec [enums-db-sql])))
 
 (defn- do-with-enums-db {:style/indent 0} [f]
   (create-enums-db!)
@@ -1014,12 +1020,15 @@
                            "DROP USER IF EXISTS no_select_test_user;"
                            "CREATE USER no_select_test_user WITH PASSWORD '123456';"
                            "GRANT SELECT ON TABLE \"no-select-test\".PUBLIC.table_with_perms TO no_select_test_user;"]]
-          (jdbc/execute! spec [statement])))
+          (is (= [0]
+                 (jdbc/execute! spec [statement])))))
       (let [test-user-details (assoc (mt/dbdef->connection-details :postgres :db {:database-name "no-select-test"})
                                      :user "no_select_test_user"
                                      :password "123456")]
         (t2.with-temp/with-temp [Database database {:engine :postgres, :details test-user-details}]
-          (sync/sync-database! database)
+          ;; make sure that sync still succeeds even tho some tables are not SELECTable.
+          (binding [sync-util/*log-exceptions-and-continue?* false]
+            (is (some? (sync/sync-database! database))))
           (is (= #{"table_with_perms"}
                  (t2/select-fn-set :name Table :db_id (:id database)))))))))
 
diff --git a/test/metabase/driver/sql_jdbc/connection_test.clj b/test/metabase/driver/sql_jdbc/connection_test.clj
index ab37b963750c1131867e68c8f40b3f973a6d1252..e38661f699826241f340422d3b7ac0120276440b 100644
--- a/test/metabase/driver/sql_jdbc/connection_test.clj
+++ b/test/metabase/driver/sql_jdbc/connection_test.clj
@@ -7,6 +7,7 @@
    [metabase.db.spec :as mdb.spec]
    [metabase.driver :as driver]
    [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.test-util :as sql-jdbc.tu]
    [metabase.driver.util :as driver.u]
    [metabase.models :refer [Database Secret]]
@@ -15,6 +16,7 @@
    [metabase.test.data :as data]
    [metabase.test.fixtures :as fixtures]
    [metabase.util :as u]
+   [next.jdbc :as next.jdbc]
    [toucan2.core :as t2]
    [toucan2.tools.with-temp :as t2.with-temp]))
 
@@ -49,26 +51,30 @@
         (with-redefs [sql-jdbc.conn/destroy-pool! (fn [id destroyed-spec]
                                                     (original-destroy id destroyed-spec)
                                                     (reset! destroyed? true))]
-          (jdbc/with-db-connection [_conn spec]
-            (jdbc/execute! spec ["CREATE TABLE birds (name varchar)"])
-            (jdbc/execute! spec ["INSERT INTO birds values ('rasta'),('lucky')"])
-            (t2.with-temp/with-temp [Database database {:engine :h2, :details connection-details}]
-              (testing "database id is not in our connection map initially"
-                ;; deref'ing a var to get the atom. looks weird
-                (is (not (contains? @@#'sql-jdbc.conn/database-id->connection-pool
-                                    (u/id database)))))
-              (testing "when getting a pooled connection it is now in our connection map"
-                (let [stored-spec (sql-jdbc.conn/db->pooled-connection-spec database)
-                      birds       (jdbc/query stored-spec ["SELECT * FROM birds"])]
-                  (is (seq birds))
-                  (is (contains? @@#'sql-jdbc.conn/database-id->connection-pool
-                                 (u/id database)))))
-              (testing "and is no longer in our connection map after cleanup"
-                (#'sql-jdbc.conn/set-pool! (u/id database) nil nil)
-                (is (not (contains? @@#'sql-jdbc.conn/database-id->connection-pool
-                                    (u/id database)))))
-              (testing "the pool has been destroyed"
-                (is @destroyed?)))))))))
+          (sql-jdbc.execute/do-with-connection-with-options
+           :h2
+           spec
+           {:write? true}
+           (fn [conn]
+             (next.jdbc/execute! conn ["CREATE TABLE birds (name varchar)"])
+             (next.jdbc/execute! conn ["INSERT INTO birds values ('rasta'),('lucky')"])
+             (t2.with-temp/with-temp [Database database {:engine :h2, :details connection-details}]
+               (testing "database id is not in our connection map initially"
+                 ;; deref'ing a var to get the atom. looks weird
+                 (is (not (contains? @@#'sql-jdbc.conn/database-id->connection-pool
+                                     (u/id database)))))
+               (testing "when getting a pooled connection it is now in our connection map"
+                 (let [stored-spec (sql-jdbc.conn/db->pooled-connection-spec database)
+                       birds       (jdbc/query stored-spec ["SELECT * FROM birds"])]
+                   (is (seq birds))
+                   (is (contains? @@#'sql-jdbc.conn/database-id->connection-pool
+                                  (u/id database)))))
+               (testing "and is no longer in our connection map after cleanup"
+                 (#'sql-jdbc.conn/set-pool! (u/id database) nil nil)
+                 (is (not (contains? @@#'sql-jdbc.conn/database-id->connection-pool
+                                     (u/id database)))))
+               (testing "the pool has been destroyed"
+                 (is @destroyed?))))))))))
 
 (deftest c3p0-datasource-name-test
   (mt/test-drivers (sql-jdbc.tu/sql-jdbc-drivers)
diff --git a/test/metabase/driver/sql_jdbc/execute_test.clj b/test/metabase/driver/sql_jdbc/execute_test.clj
new file mode 100644
index 0000000000000000000000000000000000000000..6ae9b6e83389a39af49d53ef38a6aeef274e576d
--- /dev/null
+++ b/test/metabase/driver/sql_jdbc/execute_test.clj
@@ -0,0 +1,15 @@
+(ns metabase.driver.sql-jdbc.execute-test
+  (:require
+   [clojure.test :refer :all]
+   [malli.core :as mc]
+   [malli.error :as me]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]))
+
+(deftest ^:parallel ConnectionOptions-test
+  (are [options error] (= error
+                          (me/humanize (mc/explain sql-jdbc.execute/ConnectionOptions options)))
+    nil                              nil
+    {}                               nil
+    {:session-timezone nil}          nil
+    {:session-timezone "US/Pacific"} nil
+    {:session-timezone "X"}          {:session-timezone ["invalid timezone ID: \"X\""]}))
diff --git a/test/metabase/driver/sql_jdbc/sync/describe_database_test.clj b/test/metabase/driver/sql_jdbc/sync/describe_database_test.clj
index ed50020c8d5da6b3aba79444f5ddc45a309ca336..2bf1e2cbbe2dd51518d0d884a7be0c88ffdb6555 100644
--- a/test/metabase/driver/sql_jdbc/sync/describe_database_test.clj
+++ b/test/metabase/driver/sql_jdbc/sync/describe_database_test.clj
@@ -3,7 +3,7 @@
    [clojure.java.jdbc :as jdbc]
    [clojure.test :refer :all]
    [metabase.driver :as driver]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync.describe-database
     :as sql-jdbc.describe-database]
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
@@ -25,7 +25,7 @@
 
 (use-fixtures :once (fixtures/initialize :plugins))
 
-(deftest simple-select-probe-query-test
+(deftest ^:parallel simple-select-probe-query-test
   (testing "honey-sql-version produces the same query"
     (are [version] (= ["SELECT TRUE AS \"_\" FROM \"schema\".\"wow\" WHERE 1 <> 1 LIMIT 0"]
                       (binding [hx/*honey-sql-version* version]
@@ -55,22 +55,28 @@
     (descendants driver/hierarchy :sql-jdbc))))
 
 (deftest fast-active-tables-test
-  (let [spec (sql-jdbc.conn/db->pooled-connection-spec (mt/db))]
-    (with-open [conn (jdbc/get-connection spec)]
-      ;; We have to mock this to make it work with all DBs
-      (with-redefs [sql-jdbc.describe-database/all-schemas (constantly #{"PUBLIC"})]
-        (is (= ["CATEGORIES" "CHECKINS" "USERS" "VENUES"]
-               (->> (into [] (sql-jdbc.describe-database/fast-active-tables (or driver/*driver* :h2) conn nil nil))
-                    (map :name)
-                    sort)))))))
+  (is (= ["CATEGORIES" "CHECKINS" "USERS" "VENUES"]
+         (sql-jdbc.execute/do-with-connection-with-options
+          (or driver/*driver* :h2)
+          (mt/db)
+          nil
+          (fn [^java.sql.Connection conn]
+            ;; We have to mock this to make it work with all DBs
+            (with-redefs [sql-jdbc.describe-database/all-schemas (constantly #{"PUBLIC"})]
+              (->> (into [] (sql-jdbc.describe-database/fast-active-tables (or driver/*driver* :h2) conn nil nil))
+                   (map :name)
+                   sort)))))))
 
 (deftest post-filtered-active-tables-test
-  (let [spec (sql-jdbc.conn/db->pooled-connection-spec (mt/db))]
-    (with-open [conn (jdbc/get-connection spec)]
-      (is (= ["CATEGORIES" "CHECKINS" "USERS" "VENUES"]
-             (->> (into [] (sql-jdbc.describe-database/post-filtered-active-tables :h2 conn nil nil))
-                  (map :name)
-                  sort))))))
+  (is (= ["CATEGORIES" "CHECKINS" "USERS" "VENUES"]
+         (sql-jdbc.execute/do-with-connection-with-options
+          :h2
+          (mt/db)
+          nil
+          (fn [^java.sql.Connection conn]
+            (->> (into [] (sql-jdbc.describe-database/post-filtered-active-tables :h2 conn nil nil))
+                 (map :name)
+                 sort))))))
 
 (deftest describe-database-test
   (is (= {:tables #{{:name "USERS", :schema "PUBLIC", :description nil}
@@ -94,10 +100,14 @@
       ;; taking advantage of the fact that `sql-jdbc.describe-database/describe-database` can accept JBDC connections
       ;; instead of databases; by doing this we can keep the connection open and check whether resultsets are still
       ;; open before they would normally get closed
-      (jdbc/with-db-connection [conn (sql-jdbc.conn/db->pooled-connection-spec db)]
-        (sql-jdbc.describe-database/describe-database driver conn)
-        (reduce + (for [^ResultSet rs @resultsets]
-                    (if (.isClosed rs) 0 1)))))))
+      (sql-jdbc.execute/do-with-connection-with-options
+       driver
+       db
+       nil
+       (fn [conn]
+         (sql-jdbc.describe-database/describe-database driver {:connection conn})
+         (reduce + (for [^ResultSet rs @resultsets]
+                     (if (.isClosed rs) 0 1))))))))
 
 (defn- count-active-tables-in-db
   [db-id]
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 8de3e2e1586869472ae9df47d20f2d73ca2c9ad0..1d4a92e2461d3cd6531c6a1e61c009bf222ce341 100644
--- a/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj
+++ b/test/metabase/driver/sql_jdbc/sync/describe_table_test.clj
@@ -4,7 +4,7 @@
    [clojure.test :refer :all]
    [metabase.driver :as driver]
    [metabase.driver.mysql-test :as mysql-test]
-   [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.driver.sql-jdbc.sync.describe-table
     :as sql-jdbc.describe-table]
    [metabase.driver.sql-jdbc.sync.interface :as sql-jdbc.sync.interface]
@@ -154,15 +154,18 @@
     (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 [spec  (sql-jdbc.conn/connection-details->spec driver/*driver* (:details (mt/db)))
-                table (t2/select-one Table :id (mt/id :json))]
-            (with-open [conn (jdbc/get-connection spec)]
-              (let [fields     (sql-jdbc.describe-table/describe-table-fields driver/*driver* conn table nil)
-                    json-field (first (filter #(= (:name %) "json_bit") fields))
-                    text-field (first (filter #(= (:name %) "bloop") fields))]
-                (is (= :details-only
-                       (:visibility-type json-field)))
-                (is (nil? (:visibility-type text-field)))))))))))
+          (let [table (t2/select-one Table :id (mt/id :json))]
+            (sql-jdbc.execute/do-with-connection-with-options
+             driver/*driver*
+             (mt/db)
+             nil
+             (fn [^java.sql.Connection conn]
+               (let [fields     (sql-jdbc.describe-table/describe-table-fields driver/*driver* conn table nil)
+                     json-field (first (filter #(= (:name %) "json_bit") fields))
+                     text-field (first (filter #(= (:name %) "bloop") fields))]
+                 (is (= :details-only
+                        (:visibility-type json-field)))
+                 (is (nil? (:visibility-type text-field))))))))))))
 
 (deftest describe-nested-field-columns-test
   (testing "flattened-row"
diff --git a/test/metabase/models/field_values_test.clj b/test/metabase/models/field_values_test.clj
index bd911daed40a0a536441c0f04d04951fdaf815b4..f04107ba6c3c3b4b49223f71bfdfcf7403ef1cbb 100644
--- a/test/metabase/models/field_values_test.clj
+++ b/test/metabase/models/field_values_test.clj
@@ -7,6 +7,7 @@
    [clojure.test :refer :all]
    [java-time :as t]
    [metabase.db.metadata-queries :as metadata-queries]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.models.database :refer [Database]]
    [metabase.models.dimension :refer [Dimension]]
    [metabase.models.field :refer [Field]]
@@ -16,10 +17,11 @@
    [metabase.sync :as sync]
    [metabase.test :as mt]
    [metabase.util :as u]
+   [next.jdbc :as next.jdbc]
    [toucan2.core :as t2]
    [toucan2.tools.with-temp :as t2.with-temp]))
 
-(deftest field-should-have-field-values?-test
+(deftest ^:parallel field-should-have-field-values?-test
   (doseq [[group input->expected] {"Text and Category Fields"
                                    {{:has_field_values :list
                                      :visibility_type  :normal
@@ -122,7 +124,7 @@
   (-> (t2/select-one FieldValues :id field-values-id)
       (select-keys [:values :human_readable_values])))
 
-(defn- sync-and-find-values [db field-values-id]
+(defn- sync-and-find-values! [db field-values-id]
   (sync/sync-database! db)
   (find-values field-values-id))
 
@@ -166,51 +168,55 @@
 (deftest update-human-readable-values-test
   (testing "Test \"fixing\" of human readable values when field values change"
     ;; Create a temp warehouse database that can have it's field values change
-    (jdbc/with-db-connection [conn {:classname "org.h2.Driver", :subprotocol "h2", :subname "mem:temp"}]
-      (jdbc/execute! conn ["drop table foo if exists"])
-      (jdbc/execute! conn ["create table foo (id integer primary key, category_id integer not null, desc text)"])
-      (jdbc/insert-multi! conn :foo [{:id 1 :category_id 1 :desc "foo"}
-                                     {:id 2 :category_id 2 :desc "bar"}
-                                     {:id 3 :category_id 3 :desc "baz"}])
-      ;; Create a new in the Database table for this newly created temp database
-      (t2.with-temp/with-temp [Database db {:engine       :h2
-                                            :name         "foo"
-                                            :is_full_sync true
-                                            :details      "{\"db\": \"mem:temp\"}"}]
-        ;; Sync the database so we have the new table and it's fields
-        (sync/sync-database! db)
-        (let [table-id        (t2/select-one-fn :id Table :db_id (u/the-id db) :name "FOO")
-              field-id        (t2/select-one-fn :id Field :table_id table-id :name "CATEGORY_ID")
-              field-values-id (t2/select-one-fn :id FieldValues :field_id field-id)]
-          ;; Add in human readable values for remapping
-          (is (t2/update! FieldValues field-values-id {:human_readable_values ["a" "b" "c"]}))
-          (let [expected-original-values {:values                [1 2 3]
-                                          :human_readable_values ["a" "b" "c"]}
-                expected-updated-values  {:values                [-2 -1 0 1 2 3]
-                                          :human_readable_values ["-2" "-1" "0" "a" "b" "c"]}]
-            (is (= expected-original-values
-                   (find-values field-values-id)))
-
-            (testing "There should be no changes to human_readable_values when resync'd"
-              (is (= expected-original-values
-                     (sync-and-find-values db field-values-id))))
-
-            (testing "Add new rows that will have new field values"
-              (jdbc/insert-multi! conn :foo [{:id 4 :category_id -2 :desc "foo"}
-                                             {:id 5 :category_id -1 :desc "bar"}
-                                             {:id 6 :category_id 0 :desc "baz"}])
-              (testing "Sync to pickup the new field values and rebuild the human_readable_values"
-                (is (= expected-updated-values
-                       (sync-and-find-values db field-values-id)))))
-
-            (testing "Resyncing this (with the new field values) should result in the same human_readable_values"
-              (is (= expected-updated-values
-                     (sync-and-find-values db field-values-id))))
-
-            (testing "Test that field values can be removed and the corresponding human_readable_values are removed as well"
-              (jdbc/delete! conn :foo ["id in (?,?,?)" 1 2 3])
-              (is (= {:values [-2 -1 0] :human_readable_values ["-2" "-1" "0"]}
-                     (sync-and-find-values db field-values-id))))))))))
+    (sql-jdbc.execute/do-with-connection-with-options
+     :h2
+     {:classname "org.h2.Driver", :subprotocol "h2", :subname "mem:temp"}
+     {:write? true}
+     (fn [^java.sql.Connection conn]
+       (next.jdbc/execute! conn ["drop table foo if exists"])
+       (next.jdbc/execute! conn ["create table foo (id integer primary key, category_id integer not null, desc text)"])
+       (jdbc/insert-multi! {:connection conn} :foo [{:id 1 :category_id 1 :desc "foo"}
+                                                    {:id 2 :category_id 2 :desc "bar"}
+                                                    {:id 3 :category_id 3 :desc "baz"}])
+       ;; Create a new in the Database table for this newly created temp database
+       (t2.with-temp/with-temp [Database db {:engine       :h2
+                                             :name         "foo"
+                                             :is_full_sync true
+                                             :details      "{\"db\": \"mem:temp\"}"}]
+         ;; Sync the database so we have the new table and it's fields
+         (sync/sync-database! db)
+         (let [table-id        (t2/select-one-fn :id Table :db_id (u/the-id db) :name "FOO")
+               field-id        (t2/select-one-fn :id Field :table_id table-id :name "CATEGORY_ID")
+               field-values-id (t2/select-one-fn :id FieldValues :field_id field-id)]
+           ;; Add in human readable values for remapping
+           (is (t2/update! FieldValues field-values-id {:human_readable_values ["a" "b" "c"]}))
+           (let [expected-original-values {:values                [1 2 3]
+                                           :human_readable_values ["a" "b" "c"]}
+                 expected-updated-values  {:values                [-2 -1 0 1 2 3]
+                                           :human_readable_values ["-2" "-1" "0" "a" "b" "c"]}]
+             (is (= expected-original-values
+                    (find-values field-values-id)))
+
+             (testing "There should be no changes to human_readable_values when resync'd"
+               (is (= expected-original-values
+                      (sync-and-find-values! db field-values-id))))
+
+             (testing "Add new rows that will have new field values"
+               (jdbc/insert-multi! {:connection conn} :foo [{:id 4 :category_id -2 :desc "foo"}
+                                                            {:id 5 :category_id -1 :desc "bar"}
+                                                            {:id 6 :category_id 0 :desc "baz"}])
+               (testing "Sync to pickup the new field values and rebuild the human_readable_values"
+                 (is (= expected-updated-values
+                        (sync-and-find-values! db field-values-id)))))
+
+             (testing "Resyncing this (with the new field values) should result in the same human_readable_values"
+               (is (= expected-updated-values
+                      (sync-and-find-values! db field-values-id))))
+
+             (testing "Test that field values can be removed and the corresponding human_readable_values are removed as well"
+               (jdbc/delete! {:connection conn} :foo ["id in (?,?,?)" 1 2 3])
+               (is (= {:values [-2 -1 0] :human_readable_values ["-2" "-1" "0"]}
+                      (sync-and-find-values! db field-values-id)))))))))))
 
 (deftest validate-human-readable-values-test
   (testing "Should validate FieldValues :human_readable_values when"
diff --git a/test/metabase/test/data/one_off_dbs.clj b/test/metabase/test/data/one_off_dbs.clj
index 5d04f36959e243ce92126a00c58dad25ef9414d1..cf8106fe71aae60314ce2a37b43e06c93351071a 100644
--- a/test/metabase/test/data/one_off_dbs.clj
+++ b/test/metabase/test/data/one_off_dbs.clj
@@ -5,6 +5,7 @@
    [clojure.java.jdbc :as jdbc]
    [clojure.string :as str]
    [metabase.db.spec :as mdb.spec]
+   [metabase.driver.sql-jdbc.execute :as sql-jdbc.execute]
    [metabase.models.database :refer [Database]]
    [metabase.sync :as sync]
    [metabase.test.data :as data]
@@ -19,13 +20,17 @@
 
 (defn do-with-blank-db
   "Impl for `with-blank-db` macro; prefer that to using this directly."
-  [f]
+  [thunk]
   (let [details {:db (str "mem:" (tu.random/random-name) ";DB_CLOSE_DELAY=10")}]
     (t2.with-temp/with-temp [Database db {:engine :h2, :details details}]
       (data/with-db db
-        (jdbc/with-db-connection [conn (mdb.spec/spec :h2 details)]
-          (binding [*conn* conn]
-            (f)))))))
+        (sql-jdbc.execute/do-with-connection-with-options
+         :h2
+         (mdb.spec/spec :h2 details)
+         {:write? true}
+         (fn [^java.sql.Connection conn]
+           (binding [*conn* {:connection conn}]
+             (thunk))))))))
 
 (defmacro with-blank-db
   "An empty canvas upon which you may paint your dreams.
diff --git a/test/metabase/test/data/sql_jdbc/load_data.clj b/test/metabase/test/data/sql_jdbc/load_data.clj
index a3e87f0d10097dc1bf466b721be1d993af1892e9..afdc2b620436f5064fca5f7fb074e9d157c80931 100644
--- a/test/metabase/test/data/sql_jdbc/load_data.clj
+++ b/test/metabase/test/data/sql_jdbc/load_data.clj
@@ -107,11 +107,11 @@
 (defn- make-insert!
   "Used by `make-load-data-fn`; creates the actual `insert!` function that gets passed to the `insert-middleware-fns`
   described above."
-  [driver conn {:keys [database-name], :as _dbdef} {:keys [table-name], :as _tabledef}]
+  [driver spec {:keys [database-name], :as _dbdef} {:keys [table-name], :as _tabledef}]
   (let [components       (for [component (sql.tx/qualified-name-components driver database-name table-name)]
                            (ddl.i/format-name driver (u/qualified-name component)))
         table-identifier (sql.qp/->honeysql driver (apply hx/identifier :table components))]
-    (partial do-insert! driver conn table-identifier)))
+    (partial do-insert! driver spec table-identifier)))
 
 (defn make-load-data-fn
   "Create an implementation of `load-data!`. This creates a function to actually insert a row or rows, wraps it with any
@@ -119,12 +119,16 @@
   [& insert-middleware-fns]
   (let [insert-middleware (apply comp insert-middleware-fns)]
     (fn [driver dbdef tabledef]
-      (jdbc/with-db-connection [conn (spec/dbdef->spec driver :db dbdef)]
-        (.setAutoCommit (jdbc/get-connection conn) false)
-        (let [insert! (insert-middleware (make-insert! driver conn dbdef tabledef))
-              rows    (load-data-get-rows driver dbdef tabledef)]
-          (log/tracef "Inserting rows like: %s" (first rows))
-          (insert! rows))))))
+      (sql-jdbc.execute/do-with-connection-with-options
+       driver
+       (spec/dbdef->spec driver :db dbdef)
+       {:write? true}
+       (fn [^java.sql.Connection conn]
+         (.setAutoCommit conn false)
+         (let [insert! (insert-middleware (make-insert! driver {:connection conn} dbdef tabledef))
+               rows    (load-data-get-rows driver dbdef tabledef)]
+           (log/tracef "Inserting rows like: %s" (first rows))
+           (insert! rows)))))))
 
 
 ;;; ------------------------------------------ Predefinied load-data! impls ------------------------------------------
diff --git a/test/metabase/upload_test.clj b/test/metabase/upload_test.clj
index c45df8b6411c4df5fdf1ddaf7d5f478282dd857c..2678ab76f639e0b84e67f066d35822a6d99f2583 100644
--- a/test/metabase/upload_test.clj
+++ b/test/metabase/upload_test.clj
@@ -16,17 +16,17 @@
    [metabase.util :as u]
    [toucan2.core :as t2])
   (:import
-   [java.io File]))
+   (java.io File)))
 
 (set! *warn-on-reflection* true)
 
-(def bool-type      :metabase.upload/boolean)
-(def int-type       :metabase.upload/int)
-(def float-type     :metabase.upload/float)
-(def vchar-type     :metabase.upload/varchar_255)
-(def date-type      :metabase.upload/date)
-(def datetime-type  :metabase.upload/datetime)
-(def text-type      :metabase.upload/text)
+(def ^:private bool-type      :metabase.upload/boolean)
+(def ^:private int-type       :metabase.upload/int)
+(def ^:private float-type     :metabase.upload/float)
+(def ^:private vchar-type     :metabase.upload/varchar_255)
+(def ^:private date-type      :metabase.upload/date)
+(def ^:private datetime-type  :metabase.upload/datetime)
+(def ^:private text-type      :metabase.upload/text)
 
 (defn- do-with-mysql-local-infile-activated
   "Helper for [[with-mysql-local-infile-activated]]"
@@ -48,7 +48,7 @@
           (jdbc/query conn-spec
                       "set global local_infile = 0"))))))
 
-(defmacro with-mysql-local-infile-activated
+(defmacro ^:private with-mysql-local-infile-activated
   "Turn on local_infile for MySQL"
   [& body]
   `(do-with-mysql-local-infile-activated (fn [] ~@body)))
@@ -129,7 +129,7 @@
           (is (= expected-value
                  (parser string-value))))))))
 
-(deftest type-coalescing-test
+(deftest ^:parallel type-coalescing-test
   (doseq [[type-a type-b expected] [[bool-type     bool-type     bool-type]
                                     [bool-type     int-type      int-type]
                                     [bool-type     date-type     vchar-type]
@@ -169,7 +169,7 @@
        (.write w contents))
      csv-file)))
 
-(deftest detect-schema-test
+(deftest ^:parallel detect-schema-test
   (testing "Well-formed CSV file"
     (is (= {"name"             vchar-type
             "age"              int-type
@@ -236,7 +236,7 @@
                             ;; comma, but blank column
                             "Sebulba, 112,"]))))))
 
-(deftest detect-schema-dates-test
+(deftest ^:parallel detect-schema-dates-test
   (testing "Dates"
     (is (= {"date"         date-type
             "not_date"     vchar-type