diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index d02766d5bc0b785383270b1a99df950416c0ddf4..97b8ac9e32d193bfb02bcafcd7b49592e73e26a5 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -224,13 +224,14 @@ metabase.driver.mongo.parameters mongo.params metabase.driver.mongo.query-processor mongo.qp metabase.driver.mongo.util mongo.util + metabase.driver.sql-jdbc.common sql-jdbc.common metabase.driver.sql-jdbc.connection sql-jdbc.conn metabase.driver.sql-jdbc.execute sql-jdbc.execute metabase.driver.sql-jdbc.execute.diagnostic sql-jdbc.execute.diagnostic metabase.driver.sql-jdbc.execute.legacy-impl sql-jdbc.legacy metabase.driver.sql-jdbc.execute.old-impl sql-jdbc.execute.old metabase.driver.sql-jdbc.sync sql-jdbc.sync - metabase.driver.sql-jdbc.sync.common sql-jdbc.common + metabase.driver.sql-jdbc.sync.common sql-jdbc.sync.common metabase.driver.sql-jdbc.sync.describe-database sql-jdbc.describe-database metabase.driver.sql-jdbc.sync.describe-table sql-jdbc.describe-table metabase.driver.sql-jdbc.sync.interface sql-jdbc.sync.interface diff --git a/modules/drivers/oracle/src/metabase/driver/oracle.clj b/modules/drivers/oracle/src/metabase/driver/oracle.clj index e8f0917bfa3383906ee05efa5193cec26de447d6..f8776cbc4f3bcb42f95229be9d84e5aec6b95b9c 100644 --- a/modules/drivers/oracle/src/metabase/driver/oracle.clj +++ b/modules/drivers/oracle/src/metabase/driver/oracle.clj @@ -14,6 +14,8 @@ [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-jdbc.sync.common :as sql-jdbc.sync.common] + [metabase.driver.sql-jdbc.sync.describe-table :as sql-jdbc.describe-table] [metabase.driver.sql.query-processor :as sql.qp] [metabase.driver.sql.query-processor.empty-string-is-null :as sql.qp.empty-string-is-null] [metabase.driver.sql.util :as sql.u] @@ -25,7 +27,7 @@ [metabase.util.i18n :refer [trs]] [metabase.util.ssh :as ssh]) (:import com.mchange.v2.c3p0.C3P0ProxyConnection - [java.sql Connection ResultSet Types] + [java.sql Connection DatabaseMetaData ResultSet Types] [java.time Instant OffsetDateTime ZonedDateTime] [oracle.jdbc OracleConnection OracleTypes] oracle.sql.TIMESTAMPTZ)) @@ -449,6 +451,13 @@ [_ entity-name] (str/replace entity-name "/" "//")) +(defmethod sql-jdbc.describe-table/get-table-pks :oracle + [_driver ^Connection conn _ table] + (let [^DatabaseMetaData metadata (.getMetaData conn)] + (into #{} (sql-jdbc.sync.common/reducible-results + #(.getPrimaryKeys metadata nil nil (:name table)) + (fn [^ResultSet rs] #(.getString rs "COLUMN_NAME")))))) + (defmethod sql-jdbc.execute/set-timezone-sql :oracle [_] "ALTER session SET time_zone = %s") diff --git a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj index 92edd36fccfb8936495f5f12a33ad5b32a8ecc55..7852e4fe6d125c5518c99ddfad2929cefff16908 100644 --- a/modules/drivers/snowflake/src/metabase/driver/snowflake.clj +++ b/modules/drivers/snowflake/src/metabase/driver/snowflake.clj @@ -29,11 +29,11 @@ [metabase.util.honeysql-extensions :as hx] [metabase.util.i18n :refer [trs tru]] [ring.util.codec :as codec]) - (:import [java.sql ResultSet Types] + (:import java.io.File + java.nio.charset.StandardCharsets + [java.sql ResultSet Types] [java.time OffsetDateTime ZonedDateTime] - java.io.File - metabase.util.honeysql_extensions.Identifier - java.nio.charset.StandardCharsets)) + metabase.util.honeysql_extensions.Identifier)) (driver/register! :snowflake, :parent #{:sql-jdbc ::sql-jdbc.legacy/use-legacy-classes-for-read-and-set}) @@ -357,10 +357,7 @@ (qp.store/fetch-and-store-database! (u/the-id database)) (sql.qp/->honeysql driver table))]})) -(defmethod driver/describe-database :snowflake - [driver database] - ;; using the JDBC `.getTables` method seems to be pretty buggy -- it works sometimes but other times randomly - ;; returns nothing +(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 @@ -395,7 +392,7 @@ (->> (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 (.getMetaData conn)))))) + (sql-jdbc.sync/add-table-pks driver conn (db-name database)))))) (defmethod driver/describe-table-fks :snowflake [driver database table] diff --git a/src/metabase/driver/sql_jdbc/sync/describe_database.clj b/src/metabase/driver/sql_jdbc/sync/describe_database.clj index faf68e5adbac0792daba07f3189fd612aa5f20a8..686a9ba8c59b924a6e4e7f4925fde25861c956a9 100644 --- a/src/metabase/driver/sql_jdbc/sync/describe_database.clj +++ b/src/metabase/driver/sql_jdbc/sync/describe_database.clj @@ -6,7 +6,7 @@ [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.common] + [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] [metabase.driver.sync :as driver.s] @@ -24,7 +24,7 @@ "Get a *reducible* sequence of all string schema names for the current database from its JDBC database metadata." [^DatabaseMetaData metadata] {:added "0.39.0", :pre [(instance? DatabaseMetaData metadata)]} - (sql-jdbc.common/reducible-results + (sql-jdbc.sync.common/reducible-results #(.getSchemas metadata) (fn [^ResultSet rs] #(.getString rs "TABLE_SCHEM")))) @@ -59,7 +59,7 @@ every Table on every sync." [driver ^Connection conn [sql & params]] {:pre [(string? sql)]} - (with-open [stmt (sql-jdbc.common/prepare-statement driver conn sql params)] + (with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params)] ;; attempting to execute the SQL statement will throw an Exception if we don't have permissions; otherwise it will ;; truthy wheter or not it returns a ResultSet, but we can ignore that since we have enough info to proceed at ;; this point. @@ -87,7 +87,7 @@ "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.common/reducible-results + (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"])) diff --git a/src/metabase/driver/sql_jdbc/sync/describe_table.clj b/src/metabase/driver/sql_jdbc/sync/describe_table.clj index 99ee01c62f824959a51b80a305919a583525bbf5..b4e7d0c0dddb449eea3cf5563796fdf16e844438 100644 --- a/src/metabase/driver/sql_jdbc/sync/describe_table.clj +++ b/src/metabase/driver/sql_jdbc/sync/describe_table.clj @@ -9,7 +9,7 @@ [medley.core :as m] [metabase.driver :as driver] [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn] - [metabase.driver.sql-jdbc.sync.common :as sql-jdbc.common] + [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] [metabase.mbql.schema :as mbql.s] @@ -72,7 +72,7 @@ (let [[sql & params] (sql-jdbc.sync.interface/fallback-metadata-query driver table-schema table-name)] (reify clojure.lang.IReduceInit (reduce [_ rf init] - (with-open [stmt (sql-jdbc.common/prepare-statement driver conn sql params) + (with-open [stmt (sql-jdbc.sync.common/prepare-statement driver conn sql params) rs (.executeQuery stmt)] (let [metadata (.getMetaData rs)] (reduce @@ -85,7 +85,7 @@ (defn- jdbc-fields-metadata "Reducible metadata about the Fields belonging to a Table, fetching using JDBC DatabaseMetaData methods." [driver ^Connection conn db-name-or-nil schema table-name] - (sql-jdbc.common/reducible-results + (sql-jdbc.sync.common/reducible-results #(.getColumns (.getMetaData conn) db-name-or-nil (some->> schema (driver/escape-entity-name-for-metadata driver)) @@ -175,24 +175,40 @@ (describe-table-fields-xf driver table) (fields-metadata driver conn table db-name-or-nil))) +(defmulti get-table-pks + "Returns a set of primary keys for `table` using a JDBC DatabaseMetaData from JDBC Connection `conn`. + Note: If db-name, schema, and table-name are not passed, this may return _all_ pks that the metadata's connection can access." + {:added "0.45.0" + :arglists '([driver ^Connection conn db-name-or-nil table])} + driver/dispatch-on-initialized-driver + :hierarchy #'driver/hierarchy) + +(defmethod get-table-pks :default + [_driver ^Connection conn db-name-or-nil table] + (let [^DatabaseMetaData metadata (.getMetaData conn)] + (into #{} (sql-jdbc.sync.common/reducible-results + #(.getPrimaryKeys metadata db-name-or-nil (:schema table) (:name table)) + (fn [^ResultSet rs] #(.getString rs "COLUMN_NAME")))))) + (defn add-table-pks - "Using `metadata` find any primary keys for `table` and assoc `:pk?` to true for those columns." - [^DatabaseMetaData metadata table] - (let [pks (into #{} (sql-jdbc.common/reducible-results #(.getPrimaryKeys metadata nil nil (:name table)) - (fn [^ResultSet rs] - #(.getString rs "COLUMN_NAME"))))] + "Using `conn`, find any primary keys for `table` (or more, see: [[get-table-pks]]) and finally assoc `:pk?` to true for those columns." + [driver ^Connection conn db-name-or-nil table] + (let [pks (get-table-pks driver conn db-name-or-nil table)] (update table :fields (fn [fields] (set (for [field fields] (if-not (contains? pks (:name field)) field (assoc field :pk? true)))))))) -(defn- describe-table* [driver ^Connection conn table] - {:pre [(instance? Connection conn)]} - (->> (assoc (select-keys table [:name :schema]) - :fields (describe-table-fields driver conn table nil)) - ;; find PKs and mark them - (add-table-pks (.getMetaData conn)))) +(defn- describe-table* + ([driver ^Connection conn table] + (describe-table* driver conn nil table)) + ([driver ^Connection conn db-name-or-nil table] + {:pre [(instance? Connection conn)]} + (->> (assoc (select-keys table [:name :schema]) + :fields (describe-table-fields driver conn table nil)) + ;; find PKs and mark them + (add-table-pks driver conn db-name-or-nil)))) (defn describe-table "Default implementation of `driver/describe-table` for SQL JDBC drivers. Uses JDBC DatabaseMetaData." @@ -207,7 +223,7 @@ [_driver ^Connection conn {^String schema :schema, ^String table-name :name} & [^String db-name-or-nil]] (into #{} - (sql-jdbc.common/reducible-results #(.getImportedKeys (.getMetaData conn) db-name-or-nil schema table-name) + (sql-jdbc.sync.common/reducible-results #(.getImportedKeys (.getMetaData conn) db-name-or-nil schema table-name) (fn [^ResultSet rs] (fn [] {:fk-column-name (.getString rs "FKCOLUMN_NAME") diff --git a/src/metabase/sync/analyze.clj b/src/metabase/sync/analyze.clj index b5bc94b3d257506a0074ec1030640ba1602e5c51..f6b246fc4b7d4965c0dc402d9878db12defb5c22 100644 --- a/src/metabase/sync/analyze.clj +++ b/src/metabase/sync/analyze.clj @@ -99,7 +99,7 @@ (trs "Total number of tables classified {0}, {1} updated" total-tables tables-classified)) -(defn ^:private make-analyze-steps [tables log-fn] +(defn- make-analyze-steps [tables log-fn] [(sync-util/create-sync-step "fingerprint-fields" #(fingerprint/fingerprint-fields-for-db! % tables log-fn) fingerprint-fields-summary) diff --git a/src/metabase/sync/sync_metadata.clj b/src/metabase/sync/sync_metadata.clj index 3b3d50d300a1fa011e1ae2521810cb66bcec5dad..13f9dcef476822732c3c22b317b454d9539950fe 100644 --- a/src/metabase/sync/sync_metadata.clj +++ b/src/metabase/sync/sync_metadata.clj @@ -6,7 +6,8 @@ 2. Sync fields (`metabase.sync.sync-metadata.fields`) 3. Sync FKs (`metabase.sync.sync-metadata.fks`) 4. Sync Metabase Metadata table (`metabase.sync.sync-metadata.metabase-metadata`)" - (:require [metabase.sync.interface :as i] + (:require [metabase.sync.fetch-metadata :as fetch-metadata] + [metabase.sync.interface :as i] [metabase.sync.sync-metadata.fields :as sync-fields] [metabase.sync.sync-metadata.fks :as sync-fks] [metabase.sync.sync-metadata.metabase-metadata :as metabase-metadata] @@ -32,25 +33,26 @@ (trs "Total number of foreign keys sync''d {0}, {1} updated and {2} tables failed to update" total-fks updated-fks total-failed)) -(def ^:private sync-steps +(defn- make-sync-steps [db-metadata] [(sync-util/create-sync-step "sync-timezone" sync-tz/sync-timezone! sync-timezone-summary) ;; Make sure the relevant table models are up-to-date - (sync-util/create-sync-step "sync-tables" sync-tables/sync-tables-and-database! sync-tables-summary) + (sync-util/create-sync-step "sync-tables" #(sync-tables/sync-tables-and-database! % db-metadata) sync-tables-summary) ;; Now for each table, sync the fields (sync-util/create-sync-step "sync-fields" sync-fields/sync-fields! sync-fields-summary) ;; Now for each table, sync the FKS. This has to be done after syncing all the fields to make sure target fields exist (sync-util/create-sync-step "sync-fks" sync-fks/sync-fks! sync-fks-summary) ;; finally, sync the metadata metadata table if it exists. - (sync-util/create-sync-step "sync-metabase-metadata" metabase-metadata/sync-metabase-metadata!)]) + (sync-util/create-sync-step "sync-metabase-metadata" #(metabase-metadata/sync-metabase-metadata! % db-metadata))]) (s/defn sync-db-metadata! "Sync the metadata for a Metabase `database`. This makes sure child Table & Field objects are synchronized." [database :- i/DatabaseInstance] - (sync-util/sync-operation :sync-metadata database (format "Sync metadata for %s" (sync-util/name-for-logging database)) - (u/prog1 (sync-util/run-sync-operation "sync" database sync-steps) - (if (some sync-util/abandon-sync? (map second (:steps <>))) - (sync-util/set-initial-database-sync-aborted! database) - (sync-util/set-initial-database-sync-complete! database))))) + (let [db-metadata (fetch-metadata/db-metadata database)] + (sync-util/sync-operation :sync-metadata database (format "Sync metadata for %s" (sync-util/name-for-logging database)) + (u/prog1 (sync-util/run-sync-operation "sync" database (make-sync-steps db-metadata)) + (if (some sync-util/abandon-sync? (map second (:steps <>))) + (sync-util/set-initial-database-sync-aborted! database) + (sync-util/set-initial-database-sync-complete! database)))))) (s/defn sync-table-metadata! "Sync the metadata for an individual `table` -- make sure Fields and FKs are up-to-date." diff --git a/src/metabase/sync/sync_metadata/metabase_metadata.clj b/src/metabase/sync/sync_metadata/metabase_metadata.clj index 62aab5134977f2a685d37669b558db9a0a6639d1..54a83c44eb5457f74182d999fdc1db4afeb1e335 100644 --- a/src/metabase/sync/sync_metadata/metabase_metadata.clj +++ b/src/metabase/sync/sync_metadata/metabase_metadata.clj @@ -89,15 +89,17 @@ "Sync the `_metabase_metadata` table, a special table with Metabase metadata, if present. This table contains information about type information, descriptions, and other properties that should be set for Metabase objects like Tables and Fields." - [database :- i/DatabaseInstance] - (sync-util/with-error-handling (format "Error syncing _metabase_metadata table for %s" - (sync-util/name-for-logging database)) - (let [driver (driver.u/database->driver database)] - ;; `sync-metabase-metadata-table!` relies on `driver/table-rows-seq` being defined - (when (get-method driver/table-rows-seq driver) - ;; If there's more than one metabase metadata table (in different schemas) we'll sync each one in turn. - ;; Hopefully this is never the case. - (doseq [table (:tables (fetch-metadata/db-metadata database))] - (when (is-metabase-metadata-table? table) - (sync-metabase-metadata-table! driver database table)))) - {}))) + ([database :- i/DatabaseInstance] + (sync-metabase-metadata! database (fetch-metadata/db-metadata database))) + ([database :- i/DatabaseInstance db-metadata] + (sync-util/with-error-handling (format "Error syncing _metabase_metadata table for %s" + (sync-util/name-for-logging database)) + (let [driver (driver.u/database->driver database)] + ;; `sync-metabase-metadata-table!` relies on `driver/table-rows-seq` being defined + (when (get-method driver/table-rows-seq driver) + ;; If there's more than one metabase metadata table (in different schemas) we'll sync each one in turn. + ;; Hopefully this is never the case. + (doseq [table (:tables db-metadata)] + (when (is-metabase-metadata-table? table) + (sync-metabase-metadata-table! driver database table)))) + {})))) diff --git a/src/metabase/sync/sync_metadata/tables.clj b/src/metabase/sync/sync_metadata/tables.clj index cf0a76e678835aa92052d9119db36bb67aa91e02..cf205000e547ba7ef45fa5000ca3b286ddf822c3 100644 --- a/src/metabase/sync/sync_metadata/tables.clj +++ b/src/metabase/sync/sync_metadata/tables.clj @@ -171,42 +171,42 @@ "Sync the Tables recorded in the Metabase application database with the ones obtained by calling `database`'s driver's implementation of `describe-database`. Also syncs the database metadata taken from describe-database if there is any" - [database :- i/DatabaseInstance] - ;; determine what's changed between what info we have and what's in the DB - (let [db-metadata (fetch-metadata/db-metadata database) - db-tables (table-set db-metadata) - our-metadata (our-metadata database) - strip-desc (fn [metadata] - (set (map #(dissoc % :description) metadata))) - [new-tables old-tables] (data/diff + ([database :- i/DatabaseInstance] (sync-tables-and-database! database (fetch-metadata/db-metadata database))) + ([database :- i/DatabaseInstance db-metadata] + ;; determine what's changed between what info we have and what's in the DB + (let [db-tables (table-set db-metadata) + our-metadata (our-metadata database) + strip-desc (fn [metadata] + (set (map #(dissoc % :description) metadata))) + [new-tables old-tables] (data/diff (strip-desc db-tables) (strip-desc our-metadata)) - [changed-tables] (data/diff db-tables our-metadata)] - ;; update database metadata from database - (when (some? (:version db-metadata)) - (sync-util/with-error-handling (format "Error creating/reactivating tables for %s" - (sync-util/name-for-logging database)) - (update-database-metadata! database db-metadata))) - ;; create new tables as needed or mark them as active again - (when (seq new-tables) - (sync-util/with-error-handling (format "Error creating/reactivating tables for %s" - (sync-util/name-for-logging database)) - (create-or-reactivate-tables! database new-tables))) - ;; mark old tables as inactive - (when (seq old-tables) - (sync-util/with-error-handling (format "Error retiring tables for %s" (sync-util/name-for-logging database)) - (retire-tables! database old-tables))) - - ;; update description for changed tables - (when (seq changed-tables) - (sync-util/with-error-handling (format "Error updating table description for %s" (sync-util/name-for-logging database)) - (update-table-description! database changed-tables))) - - ;; update native download perms for all groups if any tables were added or removed - (when (or (seq new-tables) (seq old-tables)) - (sync-util/with-error-handling (format "Error updating native download perms for %s" (sync-util/name-for-logging database)) - (doseq [{id :id} (perms-group/non-admin-groups)] - (perms/update-native-download-permissions! id (u/the-id database))))) - - {:updated-tables (+ (count new-tables) (count old-tables)) - :total-tables (count our-metadata)})) + [changed-tables] (data/diff db-tables our-metadata)] + ;; update database metadata from database + (when (some? (:version db-metadata)) + (sync-util/with-error-handling (format "Error creating/reactivating tables for %s" + (sync-util/name-for-logging database)) + (update-database-metadata! database db-metadata))) + ;; create new tables as needed or mark them as active again + (when (seq new-tables) + (sync-util/with-error-handling (format "Error creating/reactivating tables for %s" + (sync-util/name-for-logging database)) + (create-or-reactivate-tables! database new-tables))) + ;; mark old tables as inactive + (when (seq old-tables) + (sync-util/with-error-handling (format "Error retiring tables for %s" (sync-util/name-for-logging database)) + (retire-tables! database old-tables))) + + ;; update description for changed tables + (when (seq changed-tables) + (sync-util/with-error-handling (format "Error updating table description for %s" (sync-util/name-for-logging database)) + (update-table-description! database changed-tables))) + + ;; update native download perms for all groups if any tables were added or removed + (when (or (seq new-tables) (seq old-tables)) + (sync-util/with-error-handling (format "Error updating native download perms for %s" (sync-util/name-for-logging database)) + (doseq [{id :id} (perms-group/non-admin-groups)] + (perms/update-native-download-permissions! id (u/the-id database))))) + + {:updated-tables (+ (count new-tables) (count old-tables)) + :total-tables (count our-metadata)}))) diff --git a/test/metabase/sync/util_test.clj b/test/metabase/sync/util_test.clj index 3885651076d85792266635679238f1e1a0b5de5f..4c12141bd605f5954ea866fa088329cadfda0871 100644 --- a/test/metabase/sync/util_test.clj +++ b/test/metabase/sync/util_test.clj @@ -37,8 +37,8 @@ (deftest concurrent-sync-test (testing "only one sync process be going on at a time" - ;; describe-database gets called twice during a single sync process, once for syncing tables and a second time for - ;; syncing the _metabase_metadata table + ;; describe-database gets called once during a single sync process, and the results are used for syncing tables + ;; and syncing the _metabase_metadata table. (tt/with-temp* [Database [db {:engine ::concurrent-sync-test}]] (reset! calls-to-describe-database 0) ;; start a sync processes in the background. It should take 1000 ms to finish @@ -54,9 +54,7 @@ ;; make sure both of the futures have finished (deref f1) (deref f2) - ;; Check the number of syncs that took place. Should be 2 (just the first) - (is (= 2 - @calls-to-describe-database)))))) + (is (= 1 @calls-to-describe-database)))))) (defn- call-with-operation-info "Call `f` with `log-sync-summary` and `store-sync-summary!` redef'd. For `log-sync-summary`, it intercepts the step @@ -285,9 +283,10 @@ (testing "If a non-recoverable error occurs during sync, `initial-sync-status` on the database is set to `aborted`" (let [_ (db/update! Database (:id (mt/db)) :initial_sync_status "incomplete") db (db/select-one Database :id (:id (mt/db)))] - (with-redefs [sync-metadata/sync-steps [(sync-util/create-sync-step - "fake-step" - (fn [_] (throw (java.net.ConnectException.))))]] + (with-redefs [sync-metadata/make-sync-steps (fn [_] + [(sync-util/create-sync-step + "fake-step" + (fn [_] (throw (java.net.ConnectException.))))])] (sync/sync-database! db) (is (= "aborted" (db/select-one-field :initial_sync_status Database :id (:id db)))))))