/src/lobos/backends/mysql.clj
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)))))