From 3da9a2da39dd10f4b3587dc62e8bce95acfa9779 Mon Sep 17 00:00:00 2001
From: Cam Saul <cammsaul@gmail.com>
Date: Mon, 8 Apr 2019 19:01:24 -0700
Subject: [PATCH] Code cleanup :shower:

---
 src/metabase/cmd/load_from_h2.clj | 128 +++++++++++++++++-------------
 1 file changed, 72 insertions(+), 56 deletions(-)

diff --git a/src/metabase/cmd/load_from_h2.clj b/src/metabase/cmd/load_from_h2.clj
index ad31fe39199..740d877a9d3 100644
--- a/src/metabase/cmd/load_from_h2.clj
+++ b/src/metabase/cmd/load_from_h2.clj
@@ -57,11 +57,16 @@
              [table :refer [Table]]
              [task-history :refer [TaskHistory]]
              [user :refer [User]]
-             [view-log :refer [ViewLog]]]))
+             [view-log :refer [ViewLog]]]
+            [clojure.set :as set]
+            [toucan.db :as db])
+  (:import java.sql.SQLException))
 
 (defn- println-ok [] (println (color/green "[OK]")))
 
-;;; -------------------------------------------------- Loading Data --------------------------------------------------
+(defn- dispatch-on-db-type [& _] (mdb/db-type))
+
+;;; ------------------------------------------ Models to Migrate (in order) ------------------------------------------
 
 (def ^:private entities
   "Entities in the order they should be serialized/deserialized. This is done so we make sure that we load load
@@ -102,6 +107,9 @@
    ;; above this line)
    DataMigrations])
 
+
+;;; --------------------------------------------- H2 Connection Options ----------------------------------------------
+
 (defn- add-file-prefix-if-needed [connection-string-or-filename]
   (if (str/starts-with? connection-string-or-filename "file:")
     connection-string-or-filename
@@ -112,31 +120,28 @@
     (mdb/jdbc-details {:type :h2, :db (str h2-filename ";IFEXISTS=TRUE")})))
 
 
-(defn- insert-entity! [target-db-conn entity objs]
-  (print (u/format-color 'blue "Transfering %d instances of %s..." (count objs) (:name entity))) ; TODO - I don't think the print+flush is working as intended :/
+;;; ------------------------------------------- Fetching & Inserting Rows --------------------------------------------
+
+(defn- insert-entity! [target-db-conn {:keys [table], :as entity} objs]
+  ;; TODO - I don't think the print+flush is working as intended :/
+  (print (u/format-color 'blue "Transfering %d instances of %s..." (count objs) (:name entity)))
   (flush)
-  (let [ks         (keys (first objs))
-        ;; 1) `:sizeX` and `:sizeY` come out of H2 as `:sizex` and `:sizey` because of automatic lowercasing; fix the
-        ;;    names of these before putting into the new DB
-        ;; 2) Need to wrap the column names in quotes because Postgres automatically lowercases unquoted identifiers
-        quote-char (case (config/config-kw :mb-db-type)
-                     :postgres \"
-                     :mysql    \`)
-        cols       (for [k ks]
-                     (str quote-char (name (case k
-                                             :sizex :sizeX
-                                             :sizey :sizeY
-                                             k)) quote-char))]
+  ;; 1) `:sizeX` and `:sizeY` come out of H2 as `:sizex` and `:sizey` because of automatic lowercasing; fix the
+  ;;    names of these before putting into the new DB
+  ;; 2) Need to wrap the column names in quotes because Postgres automatically lowercases unquoted identifiers
+  (let [ks   (keys (set/rename-keys (first objs) {:sizex :sizeX, :sizey :sizeY}))
+        cols (for [k ks]
+               ((db/quote-fn) (name k)))]
     ;; The connection closes prematurely on occasion when we're inserting thousands of rows at once. Break into
     ;; smaller chunks so connection stays alive
-    (doseq [chunk (partition-all 300 objs)]
+    (doseq [chunk (partition-all 300 objs)
+            :let  [vals (for [row chunk]
+                          (map row ks))]]
       (print (color/blue \.))
       (flush)
       (try
-        (jdbc/insert-multi! target-db-conn (:table entity) cols (for [row chunk]
-                                                                  (map row ks)))
-
-        (catch java.sql.SQLException e
+        (jdbc/insert-multi! target-db-conn table cols vals)
+        (catch SQLException e
           (jdbc/print-sql-exception-chain e)
           (throw e)))))
   (println-ok))
@@ -153,7 +158,16 @@
 
 ;;; ---------------------------------------- Enabling / Disabling Constraints ----------------------------------------
 
-(defn- disable-db-constraints:postgres! [target-db-conn]
+(defmulti ^:private disable-db-constraints!
+  {:arglists '([target-db-conn])}
+  dispatch-on-db-type)
+
+(defmulti ^:private reenable-db-constraints!
+  {:arglists '([target-db-conn])}
+  dispatch-on-db-type)
+
+
+(defmethod disable-db-constraints! :postgres [target-db-conn]
   ;; make all of our FK constraints deferrable. This only works on Postgres 9.4+ (December 2014)! (There's no pressing
   ;; reason to turn these back on at the conclusion of this script. It makes things more complicated since it doesn't
   ;; work if done inside the same transaction.)
@@ -166,49 +180,41 @@
   ;; now enable constraint deferring for the duration of the transaction
   (jdbc/execute! target-db-conn ["SET CONSTRAINTS ALL DEFERRED"]))
 
+(defmethod reenable-db-constraints! :postgres [_]) ; no-op
+
 
-(defn- disable-db-constraints:mysql! [target-db-conn]
+(defmethod disable-db-constraints! :mysql [target-db-conn]
   (jdbc/execute! target-db-conn ["SET FOREIGN_KEY_CHECKS=0"]))
 
-;; For MySQL we need to reënable FK checks when we're done
-(defn- reënable-db-constraints:mysql! [target-db-conn]
+;; For MySQL we need to re-enable FK checks when we're done
+(defmethod reenable-db-constraints! :mysql [target-db-conn]
   (jdbc/execute! target-db-conn ["SET FOREIGN_KEY_CHECKS=1"]))
 
 
-(defn- disable-db-constraints! [target-db-conn]
-  (println (u/format-color 'blue "Temporarily disabling DB constraints..."))
-  ((case (mdb/db-type)
-      :postgres disable-db-constraints:postgres!
-      :mysql    disable-db-constraints:mysql!) target-db-conn)
-  (println-ok))
-
-(defn- reënable-db-constraints-if-needed! [target-db-conn]
-  (when (= (mdb/db-type) :mysql)
-    (println (u/format-color 'blue "Reënabling DB constraints..."))
-    (reënable-db-constraints:mysql! target-db-conn)
-    (println-ok)))
-
-
-;;; ---------------------------------------- Fixing Postgres Sequence Values -----------------------------------------
+;;; --------------------------------------------- Fixing Sequence Values ---------------------------------------------
 
 (def ^:private entities-without-autoinc-ids
   "Entities that do NOT use an auto incrementing ID column."
   #{Setting Session DataMigrations})
 
-(defn- set-postgres-sequence-values-if-needed!
-  "When loading data into a Postgres DB, update the sequence nextvals."
-  []
-  (when (= (mdb/db-type) :postgres)
-    (jdbc/with-db-transaction [target-db-conn (mdb/jdbc-details)]
-      (println (u/format-color 'blue "Setting postgres sequence ids to proper values..."))
-      (doseq [e     entities
-              :when (not (contains? entities-without-autoinc-ids e))
-              :let  [table-name (name (:table e))
-                     seq-name   (str table-name "_id_seq")
-                     sql        (format "SELECT setval('%s', COALESCE((SELECT MAX(id) FROM %s), 1), true) as val"
-                                        seq-name (name table-name))]]
-        (jdbc/db-query-with-resultset target-db-conn [sql] :val))
-      (println-ok))))
+(defmulti ^:private update-sequence-values!
+  {:arglists '([])}
+  dispatch-on-db-type)
+
+(defmethod update-sequence-values! :mysql []) ; no-op
+
+;; Update the sequence nextvals.
+(defmethod update-sequence-values! :postgres []
+  (jdbc/with-db-transaction [target-db-conn (mdb/jdbc-details)]
+    (println (u/format-color 'blue "Setting postgres sequence ids to proper values..."))
+    (doseq [e     entities
+            :when (not (contains? entities-without-autoinc-ids e))
+            :let  [table-name (name (:table e))
+                   seq-name   (str table-name "_id_seq")
+                   sql        (format "SELECT setval('%s', COALESCE((SELECT MAX(id) FROM %s), 1), true) as val"
+                                      seq-name (name table-name))]]
+      (jdbc/db-query-with-resultset target-db-conn [sql] :val))
+    (println-ok)))
 
 
 ;;; --------------------------------------------------- Public Fns ---------------------------------------------------
@@ -220,10 +226,20 @@
   Defaults to using `@metabase.db/db-file` as the connection string."
   [h2-connection-string-or-nil]
   (mdb/setup-db!)
+
   (jdbc/with-db-transaction [target-db-conn (mdb/jdbc-details)]
     (jdbc/db-set-rollback-only! target-db-conn)
+
+    (println (u/format-color 'blue "Temporarily disabling DB constraints..."))
     (disable-db-constraints! target-db-conn)
+    (println-ok)
+
     (load-data! target-db-conn h2-connection-string-or-nil)
-    (reënable-db-constraints-if-needed! (mdb/jdbc-details))
+
+    (println (u/format-color 'blue "Re-enabling DB constraints..."))
+    (reenable-db-constraints! target-db-conn)
+    (println-ok)
+
     (jdbc/db-unset-rollback-only! target-db-conn))
-  (set-postgres-sequence-values-if-needed!))
+
+  (update-sequence-values!))
-- 
GitLab