/src/lobos/backends/mysql.clj

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