Skip to content

Instantly share code, notes, and snippets.

@camsaul
Last active May 8, 2024 02:35
Show Gist options
  • Save camsaul/bb46f320b557f8de05e08c0f91d6efa7 to your computer and use it in GitHub Desktop.
Save camsaul/bb46f320b557f8de05e08c0f91d6efa7 to your computer and use it in GitHub Desktop.
Load Clojure namespace dependency graph into a Postgres database
(ns metabase.deps-graph
(:require
[clojure.tools.namespace.dependency :as ns.deps]
[clojure.tools.namespace.find :as ns.find]
[clojure.tools.namespace.parse :as ns.parse]
[clojure.java.io :as io]))
(set! *warn-on-reflection* true)
(defn- find-ns-decls []
(ns.find/find-ns-decls [(io/file "/home/cam/metabase/src/metabase")]))
(defn- module [ns-symb]
(some-> (re-find #"^metabase\.[^.]+" (str ns-symb)) symbol))
(defn- ns-decls []
(for [decl (find-ns-decls)
:let [ns-symb (ns.parse/name-from-ns-decl decl)
deps (ns.parse/deps-from-ns-decl decl)]]
{:namespace ns-symb
:module (module ns-symb)
:deps (into #{}
(map (fn [dep-symb]
{:namespace dep-symb}))
deps)}))
(defn- load-data! []
(with-open [conn (java.sql.DriverManager/getConnection "jdbc:postgresql://localhost:5432/metabase_namespaces?user=cam&password=cam")]
(with-open [stmt (.createStatement conn)]
(doseq [^String sql ["DROP TABLE IF EXISTS namespace;"
"CREATE TABLE namespace (namespace VARCHAR NOT NULL UNIQUE, module VARCHAR NOT NULL);"
"DROP TABLE IF EXISTS dependency;"
"CREATE TABLE dependency (namespace VARCHAR NOT NULL, depends_on VARCHAR NOT NULL);"
"ALTER TABLE dependency ADD CONSTRAINT unique_dependency UNIQUE (namespace, depends_on);"]]
(println sql)
(.execute stmt sql)))
(with-open [namespace-stmt (.prepareStatement conn "INSERT INTO namespace (namespace, module) VALUES (?, ?);")
deps-stmt (.prepareStatement conn "INSERT INTO dependency (namespace, depends_on) VALUES (?, ?);")]
(doseq [decl (ns-decls)]
(println "Load" (:namespace decl))
(.setString namespace-stmt 1 (str (:namespace decl)))
(.setString namespace-stmt 2 (str (:module decl)))
(.execute namespace-stmt)
(doseq [dep (:deps decl)]
(.setString deps-stmt 1 (str (:namespace decl)))
(.setString deps-stmt 2 (str (:namespace dep)))
(.execute deps-stmt))))))
cnt | depends_on
-----+-------------------------
2 | metabase.search.util
1 | metabase.search.config
1 | metabase.search.filter
1 | metabase.search.scoring
(4 rows)
WITH deps AS (
SELECT
dependency.namespace,
n1.module AS module,
dependency.depends_on,
n2.module AS depends_on_module
FROM
dependency
LEFT JOIN
namespace n1
ON
dependency.namespace = n1.namespace
LEFT JOIN
namespace n2
ON
dependency.depends_on = n2.namespace
WHERE
n1.module IS NOT NULL
AND
n2.module IS NOT NULL
)
SELECT
count(*) AS cnt,
depends_on
FROM deps
WHERE depends_on_module = 'metabase.search'
AND module <> 'metabase.search'
GROUP BY depends_on
ORDER BY cnt DESC;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment