/src/lobos/metadata.clj
Clojure | 146 lines | 110 code | 22 blank | 14 comment | 11 complexity | 6dd2a1c79d661cca5efeee7a3207187d MD5 | raw file
1; Copyright (c) Nicolas Buduroi. All rights reserved. 2; The use and distribution terms for this software are covered by the 3; Eclipse Public License 1.0 which can be found in the file 4; epl-v10.html at the root of this distribution. By using this software 5; in any fashion, you are agreeing to be bound by the terms of this 6; license. 7; You must not remove this notice, or any other, from this software. 8 9(ns lobos.metadata 10 "Helpers to query the database's meta-data." 11 (:require (lobos [compiler :as compiler] 12 [connectivity :as conn] 13 [schema :as schema])) 14 (:import (java.sql DatabaseMetaData))) 15 16;; ----------------------------------------------------------------------------- 17 18;; ## Database Metadata 19 20(def ^{:dynamic true :private true} *db-meta* nil) 21(def ^{:dynamic true :private true} *db-meta-spec* nil) 22 23(defn db-meta 24 "Returns the binded DatabaseMetaData object found in *db-meta* or get 25 one from the default connection if not available." 26 [] 27 (or (when (conn/connection) 28 (.getMetaData (conn/connection))) 29 *db-meta* 30 (conn/with-connection :default-connection 31 (.getMetaData (conn/connection))))) 32 33(defn db-meta-spec 34 [] 35 (or (conn/jdbc-db-spec) 36 *db-meta-spec* 37 (conn/get-db-spec :default-connection))) 38 39(defmacro with-db-meta 40 "Evaluates body in the context of a new connection or a named global 41 connection to a database then closes the connection while binding its 42 DatabaseMetaData object to *db-meta*." 43 [connection-info & body] 44 `(if ~connection-info 45 (conn/with-connection ~connection-info 46 (binding [*db-meta-spec* (conn/get-db-spec ~connection-info) 47 *db-meta* (.getMetaData (conn/connection))] 48 ~@body)) 49 (do ~@body))) 50 51;; ----------------------------------------------------------------------------- 52 53;; ## Predicates 54 55(defn supports-catalogs 56 "Returns the term used for catalogs if the underlying database supports 57 that concept." 58 [] 59 (when (.supportsCatalogsInDataManipulation (db-meta)) 60 (.getCatalogTerm (db-meta)))) 61 62(defn supports-schemas 63 "Returns the term used for schemas if the underlying database supports 64 that concept." 65 [] 66 (when (.supportsSchemasInDataManipulation (db-meta)) 67 (.getSchemaTerm (db-meta)))) 68 69(defmacro meta-call [method sname & args] 70 `(doall 71 (resultset-seq 72 (~method (db-meta) 73 (when (and ~sname (not (supports-schemas))) (name ~sname)) 74 (when (and ~sname (supports-schemas)) (name ~sname)) 75 ~@args)))) 76 77;; ----------------------------------------------------------------------------- 78 79;; ## Database Objects 80 81(defn catalogs 82 "Returns a list of catalog names as keywords." 83 [] 84 (map #(-> % :table_cat keyword) 85 (doall (resultset-seq (.getCatalogs (db-meta)))))) 86 87(defn schemas 88 "Returns a list of schema names as keywords." 89 [] 90 (cond (supports-schemas) 91 (map #(-> % :table_schem keyword) 92 (doall (resultset-seq (.getSchemas (db-meta))))) 93 (supports-catalogs) (catalogs))) 94 95(defn default-schema [] 96 (when (supports-schemas) 97 (->> (doall (resultset-seq (.getSchemas (db-meta)))) 98 (filter :is_default) 99 first 100 :table_schem 101 keyword))) 102 103(defn tables 104 "Returns a list of table names as keywords for the specified schema." 105 [& [sname]] 106 (map #(-> % :table_name keyword) 107 (meta-call .getTables sname nil (into-array ["TABLE"])))) 108 109(defn primary-keys 110 "Returns primary key names as a set of keywords for the specified 111 table." 112 [sname tname] 113 (set 114 (map #(-> % :pk_name keyword) 115 (meta-call .getPrimaryKeys sname (name tname))))) 116 117(defn indexes-meta 118 "Returns metadata maps for indexes of the specified table. Results are 119 sorted by ordinal position, grouped by index name and can be filtered 120 by the given function f. Doesn't returns indexes that have 121 tableIndexStatistic type." 122 [sname tname & [f]] 123 (try 124 (group-by :index_name 125 (filter #(let [type (:type %) 126 ;; HACK: new MySQL drivers return a string here! 127 type (if (string? type) 128 (Integer/parseInt type) 129 (short type))] 130 (and (> type DatabaseMetaData/tableIndexStatistic) 131 ((or f identity) %))) 132 (meta-call .getIndexInfo sname (name tname) false false))) 133 (catch java.sql.SQLException _ nil))) 134 135(defn references-meta 136 "Returns metadata maps for cross reference of the specified foreign 137 table. Results are sorted by their ordinal position and grouped by 138 foreign key name." 139 [sname tname] 140 (group-by :fk_name 141 (meta-call .getImportedKeys sname (name tname)))) 142 143(defn columns-meta 144 "Returns metadata maps for each columns of the specified table." 145 [sname tname] 146 (meta-call .getColumns sname (name tname) nil))