PageRenderTime 37ms CodeModel.GetById 18ms app.highlight 14ms RepoModel.GetById 2ms app.codeStats 0ms

/src/lobos/metadata.clj

http://github.com/budu/lobos
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))