/src/lobos/backends/sqlite.clj

http://github.com/budu/lobos · Clojure · 168 lines · 137 code · 20 blank · 11 comment · 18 complexity · bf856eb8affcbf54523e86306e40b7df 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.sqlite
  9. "Compiler implementation for SQLite."
  10. (:refer-clojure :exclude [compile defonce])
  11. (:require (lobos [schema :as schema]))
  12. (:use (lobos analyzer compiler connectivity internal metadata utils))
  13. (:import (lobos.ast AlterTableStatement
  14. AutoIncClause
  15. CreateSchemaStatement
  16. CreateTableStatement
  17. DataTypeClause
  18. DropStatement
  19. IdentifierExpression)
  20. (lobos.schema DataType
  21. ForeignKeyConstraint
  22. Schema
  23. UniqueConstraint)))
  24. ;; -----------------------------------------------------------------------------
  25. ;; ## Analyzer
  26. (def ^{:private true} analyzer-data-type-aliases
  27. {:time-with-time-zone :time
  28. :timestamp-with-time-zone :timestamp})
  29. (defmethod analyze [:sqlite DataType]
  30. [_ column-meta]
  31. (let [dtype (-> column-meta :type_name as-keyword)
  32. tz? #{:time-with-time-zone :timestamp-with-time-zone}
  33. [dtype options] (if (tz? dtype)
  34. [dtype {:time-zone true}]
  35. [dtype nil])
  36. dtype (first (replace analyzer-data-type-aliases [dtype]))
  37. args (analyze-data-type-args dtype column-meta)]
  38. (schema/data-type
  39. dtype
  40. (if (#{:decimal :numeric} dtype)
  41. [(first args)]
  42. args)
  43. options)))
  44. (defmethod analyze [:sqlite UniqueConstraint]
  45. [_ sname tname cname index-meta]
  46. (let [columns (vec (map #(-> % :column_name keyword)
  47. index-meta))]
  48. (UniqueConstraint.
  49. (make-index-name tname :unique columns)
  50. :unique
  51. columns)))
  52. (defn- analyze-primary-keys [tname]
  53. (let [columns (reduce
  54. #(conj %1 (-> %2 :column_name keyword))
  55. []
  56. (resultset-seq
  57. (.getPrimaryKeys (db-meta) nil nil (name tname))))]
  58. (when (not-empty columns)
  59. [(UniqueConstraint.
  60. (make-index-name tname :primary-key columns)
  61. :primary-key
  62. columns)])))
  63. (defn- analyze-foreign-keys [tname]
  64. (let [fks (group-by :id (try
  65. (raw-query (format "pragma foreign_key_list(%s);"
  66. (name tname)))
  67. (catch Exception _)))]
  68. (for [fk fks]
  69. (let [fk (second fk)
  70. pcolumns (reduce #(conj %1 (-> %2 :to keyword)) [] fk)
  71. fcolumns (reduce #(conj %1 (-> %2 :from keyword)) [] fk)
  72. fk (first fk)
  73. ptable (keyword (:table fk))
  74. match (as-keyword (:match fk))
  75. match (when-not (= match :none) match)
  76. on-delete (as-keyword (:on_delete fk))
  77. on-delete (when-not (= on-delete :no-action) on-delete)
  78. on-delete (when on-delete [:on-delete on-delete])
  79. on-update (as-keyword (:on_update fk))
  80. on-update (when-not (= on-update :no-action) on-update)
  81. on-update (when on-delete [:on-update on-update])]
  82. (ForeignKeyConstraint.
  83. (make-index-name tname :fkey fcolumns)
  84. fcolumns
  85. ptable
  86. pcolumns
  87. match
  88. (into {} [on-delete on-update]))))))
  89. (defn- analyze-unique [sname tname]
  90. (map (fn [[cname meta]] (analyze UniqueConstraint sname tname cname meta))
  91. (indexes-meta sname tname #(let [nu (:non_unique %)]
  92. (or (false? nu) (= nu 0))))))
  93. (defmethod analyze [:sqlite :constraints]
  94. [_ sname tname]
  95. (concat (analyze-unique sname tname)
  96. (analyze-primary-keys tname)
  97. (analyze-foreign-keys tname)))
  98. (defmethod analyze [:sqlite Schema]
  99. [_ sname]
  100. (let [db-spec (db-meta-spec)
  101. sname (or sname
  102. (->> db-spec
  103. :subname
  104. (re-find #"^\./(.*)\.\w+$")
  105. second))]
  106. (analyze [:lobos.analyzer/standard Schema] sname)))
  107. ;; -----------------------------------------------------------------------------
  108. ;; ## Compiler
  109. (defmethod compile [:sqlite IdentifierExpression]
  110. [identifier]
  111. (as-str (:name identifier)))
  112. (defmethod compile [:sqlite DataTypeClause]
  113. [expression]
  114. (let [{:keys [dtype args options]} expression
  115. {:keys [collate time-zone]} options]
  116. (unsupported (and (#{:decimal :numeric} dtype) (= (count args) 2))
  117. "Doesn't support scale argument.")
  118. (join \space
  119. (str (as-sql-keyword dtype) (as-list args))
  120. (when collate (str "COLLATE " (as-str collate)))
  121. (when time-zone "WITH TIME ZONE"))))
  122. (defmethod compile [:sqlite AutoIncClause]
  123. [_]
  124. nil)
  125. (defmethod compile [:sqlite CreateSchemaStatement]
  126. [statement]
  127. (let [{:keys [db-spec sname elements]} statement]
  128. (map compile elements)))
  129. (defn- drop-schema [db-spec]
  130. (map (comp compile
  131. #(schema/build-drop-statement % :cascade db-spec)
  132. #(schema/table %)
  133. :name)
  134. (with-connection db-spec
  135. (raw-query
  136. (format "select name from sqlite_master where type <> 'index';")))))
  137. (defmethod compile [:sqlite DropStatement]
  138. [statement]
  139. (let [{:keys [db-spec otype oname behavior]} statement]
  140. (if (= otype :schema)
  141. (drop-schema db-spec)
  142. (join \space
  143. "DROP"
  144. (as-sql-keyword otype)
  145. (as-identifier db-spec oname)))))
  146. (defmethod compile [:sqlite AlterTableStatement]
  147. [statement]
  148. (unsupported "Alter statement unsupported."))