PageRenderTime 57ms CodeModel.GetById 15ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lobos/backends/mysql.clj

http://github.com/budu/lobos
Clojure | 192 lines | 160 code | 20 blank | 12 comment | 1 complexity | 9f8ef1fdd09e56a2a9cd2a5274bfd198 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.backends.mysql
 10  "Compiler implementation for MySQL."
 11  (:refer-clojure :exclude [compile defonce])
 12  (:require (lobos [schema :as schema]
 13                   [ast :as ast]))
 14  (:use (lobos [schema :only [build-definition]]
 15               analyzer
 16               compiler
 17               metadata
 18               utils))
 19  (:import (lobos.schema Column
 20                         DataType
 21                         Schema
 22                         UniqueConstraint)))
 23
 24(ast/import-all)
 25
 26;; -----------------------------------------------------------------------------
 27
 28;; ## Analyzer
 29
 30(def ^{:private true} analyzer-data-type-aliases
 31  {:bit :boolean
 32   :int :integer
 33   :text :clob
 34   :tinyblob :blob
 35   :tinytext :clob})
 36
 37(defmethod analyze [:mysql DataType]
 38  [_ column-meta]
 39  (let [dtype (-> column-meta :type_name as-keyword)
 40        dtype (first (replace analyzer-data-type-aliases [dtype]))]
 41    (schema/data-type
 42     dtype
 43     (if (#{:time :timestamp} dtype)
 44       []
 45       (analyze-data-type-args dtype column-meta)))))
 46
 47(defmethod analyze [:mysql UniqueConstraint]
 48  [_ sname tname cname index-meta]
 49  (let [pkeys (primary-keys sname tname)
 50        pkey (pkeys (keyword cname))
 51        columns (vec (map #(-> % :column_name keyword)
 52                          index-meta))]
 53    (UniqueConstraint.
 54     (if pkey
 55       (make-index-name tname :primary-key columns)
 56       (keyword cname))
 57     (if pkey
 58       :primary-key
 59       :unique)
 60     columns)))
 61
 62(defn- analyze-column [sname tname cname]
 63  (let [schema (if sname (name sname) "")]
 64    (analyze Column
 65      (first
 66       (resultset-seq
 67        (.getColumns (db-meta) schema nil (name tname) (name cname)))))))
 68
 69(defmethod analyze [:mysql Schema]
 70  [_ sname]
 71  (let [db-spec (db-meta-spec)
 72        sname (or sname
 73                  (->> db-spec
 74                       :subname
 75                       (re-find #"//.*/(.+)$")
 76                       second))]
 77    (when-not sname
 78      (throw (java.sql.SQLException. "No database selected")))
 79    (analyze [:lobos.analyzer/standard Schema] sname)))
 80
 81;; -----------------------------------------------------------------------------
 82
 83;; ## Compiler
 84
 85(defmethod compile [:mysql IdentifierExpression]
 86  [identifier]
 87  (let [{:keys [db-spec name qualifiers]} identifier]
 88    (join* \. (->> (concat qualifiers [name])
 89                   (filter identity)
 90                   (map #(when % (as-str \` % \`)))))))
 91
 92(def ^{:private true} compiler-data-type-aliases
 93  {:clob :text
 94   :nclob :text})
 95
 96(defmethod compile [:mysql DataTypeClause]
 97  [expression]
 98  (let [{:keys [dtype args options]} expression
 99        {:keys [encoding collate]} options
100        encoding (when (= dtype :nclob) "UTF8")
101        dtype (first (replace compiler-data-type-aliases [dtype]))
102        args (if (#{:time :timestamp} dtype) [] args)]
103    (unsupported (= dtype :real)
104      "Use double instead.")
105    (unsupported (:time-zone options)
106      "Time zones not supported.")
107    (join \space
108      (str (as-sql-keyword dtype) (as-list args))
109      (when encoding (str "CHARACTER SET " (as-str encoding)))
110      (when collate (str "COLLATE " (as-str collate))))))
111
112(defmethod compile [:mysql AutoIncClause]
113  [_]
114  "AUTO_INCREMENT")
115
116(defmethod compile [:mysql CreateSchemaStatement]
117  [statement]
118  (let [{:keys [db-spec sname elements]} statement
119        [elements foreign-keys] (extract-foreign-keys elements)
120        alters (map compile (build-alter-add-statements
121                             (assoc db-spec :schema sname)
122                             foreign-keys))]
123    (conj (concat (map (comp compile
124                             #(assoc-in % [:db-spec :schema] sname))
125                       elements)
126                  alters)
127          (str "CREATE SCHEMA "
128               (as-identifier db-spec sname)))))
129
130(defmethod compile [:mysql DropStatement]
131  [statement]
132  (let [{:keys [db-spec otype oname behavior options]} statement]
133    (if (= otype :index)
134      (join \space
135        "DROP INDEX"
136        (as-identifier db-spec oname)
137        "ON"
138        (as-identifier db-spec (:tname options) (:schema db-spec)))
139      (join \space
140        "DROP"
141        (as-sql-keyword otype)
142        (as-identifier db-spec oname (:schema db-spec))
143        (when (and behavior (#{:table} otype))
144          [(as-sql-keyword behavior)])))))
145
146(defmethod compile [:mysql AlterDropAction]
147  [action]
148  (let [{:keys [db-spec element]} action
149        is-unique (instance? UniqueConstraintDefinition element)
150        is-pkey (and is-unique (= (:type element) :primary-key))]
151    (join \space
152          "DROP"
153          (cond (instance? ColumnDefinition element) "COLUMN"
154                (instance? ForeignKeyConstraintDefinition element) "FOREIGN KEY"
155                (and is-unique (= (:ctype element) :unique)) "INDEX"
156                is-pkey "PRIMARY KEY")
157          (when-not is-pkey
158            (as-identifier db-spec (:cname element))))))
159
160(defmethod compile [:mysql AlterModifyDataTypeAndOptionsAction]
161  [action]
162  (let [{:keys [db-spec element]} action
163        column (with-db-meta db-spec
164                 (analyze-column (:sname element)
165                                 (:tname element)
166                                 (:cname element)))
167        actual (build-definition column db-spec)
168        ;; HACK: ignore actual default clause if data-type changed
169        actual (if (= (:data-type actual)
170                      (:data-type element))
171                 actual
172                 (assoc actual :default nil))
173        element (merge-with
174                 #(if (nil? %2) % %2)
175                 actual
176                 element)]
177    (str "MODIFY " (compile element))))
178
179(defmethod compile [:mysql AlterRenameAction]
180  [action]
181  (let [{:keys [db-spec element]} action
182        old-name (:cname element)
183        new-name (:others element)
184        column (with-db-meta db-spec
185                 (assoc (analyze-column (:sname element)
186                                        (:tname element)
187                                        old-name)
188                   :cname new-name))]
189    (join \space
190          "CHANGE"
191          (as-identifier db-spec old-name)
192          (compile (build-definition column db-spec)))))