diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 264cdbc..4d91167 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -16,7 +16,8 @@ :refer {:level :warning - :exclude [clojure.test]} + :exclude [clojure.test + macaw.test.util]} :docstring-leading-trailing-whitespace {:level :warning} :keyword-binding {:level :warning} diff --git a/src/macaw/rewrite.clj b/src/macaw/rewrite.clj index 4d0c6db..0557ea7 100644 --- a/src/macaw/rewrite.clj +++ b/src/macaw/rewrite.clj @@ -94,6 +94,12 @@ (throw (ex-info (str "Unknown rename: " unknown) {:type k :rename unknown})))))) +(defn- index-by-instances [xs] + (into {} (for [x xs + :let [c (:component x)] + i (:instances c)] + [i c]))) + (defn replace-names "Given a SQL query and its corresponding (untransformed) AST, apply the given table and column renames." [sql parsed-ast {schema-renames :schemas @@ -101,12 +107,8 @@ column-renames :columns :as renames}] (let [comps (collect/query->components parsed-ast {:with-instance true}) - columns (into {} (for [c (:columns comps) - i (:instances (:component c))] - [i (:component c)])) - tables (into {} (for [t (:tables comps) - i (:instances (:component t))] - [i (:component t)])) + columns (index-by-instances (:columns comps)) + tables (index-by-instances (:tables comps)) ;; execute rename updated-nodes (volatile! []) res (-> parsed-ast diff --git a/test/macaw/core_test.clj b/test/macaw/core_test.clj index a2497c1..04014ee 100644 --- a/test/macaw/core_test.clj +++ b/test/macaw/core_test.clj @@ -1,17 +1,14 @@ (ns ^:parallel macaw.core-test (:require - [clojure.string :as str] [clojure.test :refer [deftest is testing]] [macaw.core :as m] + [macaw.test.util :refer [ws=]] [macaw.walk :as mw]) (:import (net.sf.jsqlparser.schema Table))) (set! *warn-on-reflection* true) -(defn- normalize-ws [s] - (str/replace s #"\s\s+" " ")) - (defn- and* [x y] (and x y)) @@ -236,34 +233,33 @@ (m/replace-names "SELECT p.id, q.id FROM public.orders p join private.orders q" {:tables {{:schema "public" :table "orders"} "whatever"}}))) - (is (= (normalize-ws "SELECT SUM(public.orders.total) AS s, - MAX(orders.total) AS max, - MIN(total) AS min - FROM public.orders") - (m/replace-names - (normalize-ws "SELECT SUM(public.orders.amount) AS s, - MAX(orders.amount) AS max, - MIN(amount) AS min - FROM public.orders") - {:columns {{:schema "public" :table "orders" :column "amount"} "total"}}))) + (is (ws= "SELECT SUM(public.orders.total) AS s, + MAX(orders.total) AS max, + MIN(total) AS min + FROM public.orders" + (m/replace-names + "SELECT SUM(public.orders.amount) AS s, + MAX(orders.amount) AS max, + MIN(amount) AS min + FROM public.orders" + {:columns {{:schema "public" :table "orders" :column "amount"} "total"}}))) - (is (= (normalize-ws "SELECT *, sturmunddrang - , oink AS oink - FROM /* /* lore */ - floor_muser, - user, /* more */ vigilant_user ;") - (m/replace-names - (normalize-ws - "SELECT *, boink - , yoink AS oink + (is (ws= "SELECT *, sturmunddrang + , oink AS oink FROM /* /* lore */ - core_user, - bore_user, /* more */ snore_user ;") - {:tables {{:schema "public" :table "core_user"} "floor_muser" - {:schema "public" :table "bore_user"} "user" - {:schema "public" :table "snore_user"} "vigilant_user"} - :columns {{:schema "public" :table "core_user" :column "boink"} "sturmunddrang" - {:schema "public" :table "snore_user" :column "yoink"} "oink"}}))) + floor_muser, + user, /* more */ vigilant_user ;" + (m/replace-names + "SELECT *, boink + , yoink AS oink + FROM /* /* lore */ + core_user, + bore_user, /* more */ snore_user ;" + {:tables {{:schema "public" :table "core_user"} "floor_muser" + {:schema "public" :table "bore_user"} "user" + {:schema "public" :table "snore_user"} "vigilant_user"} + :columns {{:schema "public" :table "core_user" :column "boink"} "sturmunddrang" + {:schema "public" :table "snore_user" :column "yoink"} "oink"}}))) (is (thrown? Exception #"Unknown rename" (m/replace-names "SELECT 1" {:tables {{:schema "public" :table "a"} "aa"}})))) diff --git a/test/macaw/test/util.clj b/test/macaw/test/util.clj new file mode 100644 index 0000000..f2ec536 --- /dev/null +++ b/test/macaw/test/util.clj @@ -0,0 +1,28 @@ +(ns macaw.test.util + (:require + [clojure.string :as str] + [clojure.test :refer :all] + [clojure.walk :as walk])) + +(defn- indentation [s] + (count (re-find #"^\s*" s))) + +(defn- trim-indent* [margin s] + (if (< (count s) margin) + "" + (subs s margin))) + +(defn trim-indent + "Given a multi-line string, remove the common margin from the remaining lines. + Used so that strings with significant whitespace may be visually aligned." + [s] + (let [lines (str/split-lines s) + margin (->> (rest lines) + (remove str/blank?) + (transduce (map indentation) min Integer/MAX_VALUE))] + (str/join "\n" (cons (first lines) (map (partial trim-indent* margin) (rest lines)))))) + +(defmacro ws= + "Trim the extra indentation from all string literals before evaluation a given equality form." + [& xs] + `(= ~@(walk/postwalk #(cond-> % (string? %) trim-indent) xs))) diff --git a/test/macaw/test/util_test.clj b/test/macaw/test/util_test.clj new file mode 100644 index 0000000..95e5895 --- /dev/null +++ b/test/macaw/test/util_test.clj @@ -0,0 +1,24 @@ +(ns macaw.test.util-test + (:require + [clojure.string :as str] + [clojure.test :refer :all] + [macaw.test.util :refer [ws=]])) + +(deftest ^:parallel ws=-test + (testing "Code indentation is ignored" + (is (ws= "ABC + DEF + XXX" + (str/replace "ABC + DEF + GHI" + "GHI" "XXX")))) + + (testing "Comparison is still whitespace sensitive" + (is (not (ws= "A B" "A B"))) + (is (not (ws= "A + B + C" + "A + B + C")))))