Addresses #293 by adding alter table stuff
And documenting more of the DDL.
This commit is contained in:
parent
49d8365bfb
commit
167d7cee0c
4 changed files with 138 additions and 28 deletions
|
|
@ -11,12 +11,45 @@ a space (e.g., `:left-join` is formatted as `LEFT JOIN`).
|
|||
Except as noted, these clauses apply to all the SQL
|
||||
dialects that HoneySQL supports.
|
||||
|
||||
## alter-table
|
||||
## alter-table, add-column, drop-column, modify-column, rename-column
|
||||
|
||||
## create-table
|
||||
## add-index, drop-index
|
||||
|
||||
## create-table, with-columns
|
||||
|
||||
`:create-table` can accept a single table name or a pair
|
||||
containing a table name and a flag indicating the creation
|
||||
should be conditional (`:if-not-exists` or the symbol `if-not-exists`,
|
||||
although any truthy value will work). `:create-table` should
|
||||
be used with `:with-columns` to specify the actual columns
|
||||
in the table:
|
||||
|
||||
```clojure
|
||||
user=> (sql/format {:create-table :fruit
|
||||
:with-columns
|
||||
[[:id :int [:not nil]]
|
||||
[:name [:varchar 32] [:not nil]]
|
||||
[:cost :float :null]]})
|
||||
;; \n has been replaced by an actual newline here for clarity:
|
||||
["CREATE TABLE fruit (
|
||||
id INT NOT NULL,
|
||||
name VARCHAR(32) NOT NULL,
|
||||
cost FLOAT NULL
|
||||
)"]
|
||||
```
|
||||
|
||||
## create-view
|
||||
|
||||
`:create-view` accepts a single view name:
|
||||
|
||||
```clojure
|
||||
user=> (sql/format {:create-view :products
|
||||
:select [:*]
|
||||
:from [:items]
|
||||
:where [:= :category "product"]})
|
||||
["CREATE VIEW products AS SELECT * FROM items WHERE category = ?" "product"]
|
||||
```
|
||||
|
||||
## drop-table
|
||||
|
||||
`:drop-table` can accept a single table name or a sequence of
|
||||
|
|
@ -31,6 +64,8 @@ user=> (sql/format {:drop-table [:foo :bar]})
|
|||
["DROP TABLE foo, bar"]
|
||||
```
|
||||
|
||||
## rename-table
|
||||
|
||||
## nest
|
||||
|
||||
This is pseudo-syntax that lets you wrap a substatement
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@
|
|||
(def ^:private default-clause-order
|
||||
"The (default) order for known clauses. Can have items added and removed."
|
||||
[;; DDL comes first (these don't really have a precedence):
|
||||
:alter-table :create-table :with-columns :create-view :drop-table
|
||||
:alter-table :add-column :drop-column :rename-column
|
||||
:add-index :drop-index :rename-table
|
||||
:create-table :with-columns :create-view :drop-table
|
||||
;; then SQL clauses in priority order:
|
||||
:nest :with :with-recursive :intersect :union :union-all :except :except-all
|
||||
:select :select-distinct :insert-into :update :delete :delete-from :truncate
|
||||
|
|
@ -466,7 +468,19 @@
|
|||
[(str (sql-kw k) " " e " = EXCLUDED." e)])
|
||||
(format-set-exprs k x)))
|
||||
|
||||
(defn- format-alter-table [k [x]] ["ALTER TABLE"])
|
||||
(defn- format-simple-clause [c]
|
||||
(let [[x & y] (format-dsl c)]
|
||||
(when (seq y)
|
||||
(throw (ex-info "column/index operations must be simple clauses"
|
||||
{:clause c :params y})))
|
||||
x))
|
||||
|
||||
(defn- format-alter-table [k x]
|
||||
(if (sequential? x)
|
||||
[(str (sql-kw k) " " (format-entity (first x))
|
||||
(when-let [clauses (next x)]
|
||||
(str " " (str/join ", " (map #'format-simple-clause clauses)))))]
|
||||
[(str (sql-kw k) " " (format-entity x))]))
|
||||
|
||||
(defn- format-create-table [k table]
|
||||
(let [[table if-not-exists] (if (sequential? table) table [table])]
|
||||
|
|
@ -485,21 +499,28 @@
|
|||
(when if-exists (str (sql-kw :if-exists) " "))
|
||||
(str/join ", " (map #'format-entity tables)))]))
|
||||
|
||||
(defn- format-simple-expr [e]
|
||||
(let [[x & y] (format-expr e)]
|
||||
(when (seq y)
|
||||
(throw (ex-info "column elements must be simple expressions"
|
||||
{:expr e :params y})))
|
||||
x))
|
||||
|
||||
(defn- format-single-column [xs]
|
||||
(binding [*inline* true]
|
||||
(str/join " " (let [[id & spec] (map #'format-simple-expr xs)]
|
||||
(cons id (map upper-case spec))))))
|
||||
|
||||
(defn- format-table-columns [k xs]
|
||||
(let [simple-expr (fn [e]
|
||||
(let [[x & y] (format-expr e)]
|
||||
(when (seq y)
|
||||
(throw (ex-info "column elements must be simple expressions"
|
||||
{:expr e :params y})))
|
||||
x))]
|
||||
(binding [*inline* true]
|
||||
[(str "(\n "
|
||||
(str/join ",\n "
|
||||
(map #(str/join " "
|
||||
(let [[id & spec] (map simple-expr %)]
|
||||
(cons id (map upper-case spec))))
|
||||
xs))
|
||||
"\n)")])))
|
||||
[(str "(\n "
|
||||
(str/join ",\n " (map #'format-single-column xs))
|
||||
"\n)")])
|
||||
|
||||
(defn- format-add-item [k spec]
|
||||
[(str (sql-kw k) " " (format-single-column spec))])
|
||||
|
||||
(defn- format-rename-item [k [x y]]
|
||||
[(str (sql-kw k) " " (format-entity x) " TO " (format-entity y))])
|
||||
|
||||
(def ^:private base-clause-order
|
||||
"The (base) order for known clauses. Can have items added and removed.
|
||||
|
|
@ -517,6 +538,12 @@
|
|||
"The (default) behavior for each known clause. Can also have items added
|
||||
and removed."
|
||||
(atom {:alter-table #'format-alter-table
|
||||
:add-column #'format-add-item
|
||||
:drop-column #'format-selector
|
||||
:rename-column #'format-rename-item
|
||||
:add-index #'format-add-item
|
||||
:drop-index #'format-selector
|
||||
:rename-table #'format-rename-item
|
||||
:create-table #'format-create-table
|
||||
:with-columns #'format-table-columns
|
||||
:create-view #'format-create-view
|
||||
|
|
|
|||
|
|
@ -43,11 +43,17 @@
|
|||
(assoc data k arg)
|
||||
(assoc {} k data)))
|
||||
|
||||
(defn alter-table [& args] (generic :nest args))
|
||||
(defn create-table [& args] (generic :nest args))
|
||||
(defn with-columns [& args] (generic :nest args))
|
||||
(defn create-view [& args] (generic :nest args))
|
||||
(defn drop-table [& args] (generic :nest args))
|
||||
(defn alter-table [& args] (generic :alter-table args))
|
||||
(defn add-column [& args] (generic :add-column args))
|
||||
(defn drop-column [& args] (generic-1 :drop-column args))
|
||||
(defn rename-column [& args] (generic :rename-column args))
|
||||
(defn add-index [& args] (generic :add-index args))
|
||||
(defn drop-index [& args] (generic-1 :drop-index args))
|
||||
(defn rename-table [& args] (generic :alter-table args))
|
||||
(defn create-table [& args] (generic :create-table args))
|
||||
(defn with-columns [& args] (generic :with-columns args))
|
||||
(defn create-view [& args] (generic-1 :create-view args))
|
||||
(defn drop-table [& args] (generic :drop-table args))
|
||||
(defn nest [& args] (generic :nest args))
|
||||
(defn with [& args] (generic :with args))
|
||||
(defn with-recursive [& args] (generic :with-recursive args))
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@
|
|||
:cljs [cljs.test :refer-macros [deftest is testing]])
|
||||
[honey.sql :as sql]
|
||||
[honey.sql.helpers
|
||||
:refer [columns create-view
|
||||
cross-join do-update-set from full-join
|
||||
:refer [add-column add-index alter-table columns create-table create-view
|
||||
cross-join do-update-set drop-column drop-index drop-table from full-join
|
||||
group-by having insert-into
|
||||
join left-join limit offset on-conflict order-by
|
||||
over partition-by
|
||||
returning right-join
|
||||
select select-distinct values where window with]]))
|
||||
rename-column rename-table returning right-join
|
||||
select select-distinct values where window with with-columns]]))
|
||||
|
||||
(deftest test-select
|
||||
(let [m1 (-> (with [:cte (-> (select :*)
|
||||
|
|
@ -331,13 +331,38 @@
|
|||
" MAX(salary) OVER () AS MaxSalary"
|
||||
" FROM employee")])))
|
||||
|
||||
(deftest issue-293-ddl
|
||||
(deftest issue-293-basic-ddl
|
||||
(is (= (sql/format {:create-view :metro :select [:*] :from [:cities] :where [:= :metroflag "y"]})
|
||||
["CREATE VIEW metro AS SELECT * FROM cities WHERE metroflag = ?" "y"]))
|
||||
(is (= (sql/format {:create-table :films
|
||||
:with-columns [[:id :int :unsigned :auto-increment]
|
||||
[:name [:varchar 50] [:not nil]]]})
|
||||
["CREATE TABLE films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
||||
(is (= (sql/format (-> (create-view :metro)
|
||||
(select :*)
|
||||
(from :cities)
|
||||
(where [:= :metroflag "y"])))
|
||||
["CREATE VIEW metro AS SELECT * FROM cities WHERE metroflag = ?" "y"]))
|
||||
(is (= (sql/format (-> (create-table :films)
|
||||
(with-columns
|
||||
[:id :int :unsigned :auto-increment]
|
||||
[:name [:varchar 50] [:not nil]])))
|
||||
["CREATE TABLE films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
||||
(is (= (sql/format (-> (create-table :films :if-not-exists)
|
||||
(with-columns
|
||||
[:id :int :unsigned :auto-increment]
|
||||
[:name [:varchar 50] [:not nil]])))
|
||||
["CREATE TABLE IF NOT EXISTS films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
||||
(is (= (sql/format (-> {:create-table :films
|
||||
:with-columns
|
||||
[[:id :int :unsigned :auto-increment]
|
||||
[:name [:varchar 50] [:not nil]]]}))
|
||||
["CREATE TABLE films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
||||
(is (= (sql/format (-> {:create-table [:films :if-not-exists]
|
||||
:with-columns
|
||||
[[:id :int :unsigned :auto-increment]
|
||||
[:name [:varchar 50] [:not nil]]]}))
|
||||
["CREATE TABLE IF NOT EXISTS films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
||||
(is (= (sql/format {:drop-table :foo})
|
||||
["DROP TABLE foo"]))
|
||||
(is (= (sql/format {:drop-table [:if-exists :foo]})
|
||||
|
|
@ -347,8 +372,25 @@
|
|||
(is (= (sql/format {:drop-table [:foo :bar]})
|
||||
["DROP TABLE foo, bar"]))
|
||||
(is (= (sql/format {:drop-table [:if-exists :foo :bar]})
|
||||
["DROP TABLE IF EXISTS foo, bar"]))
|
||||
(is (= (sql/format (drop-table :foo))
|
||||
["DROP TABLE foo"]))
|
||||
(is (= (sql/format (drop-table :if-exists :foo))
|
||||
["DROP TABLE IF EXISTS foo"]))
|
||||
(is (= (sql/format (drop-table :foo :bar))
|
||||
["DROP TABLE foo, bar"]))
|
||||
(is (= (sql/format (drop-table :if-exists :foo :bar))
|
||||
["DROP TABLE IF EXISTS foo, bar"])))
|
||||
|
||||
(deftest issue-293-alter-table
|
||||
(is (= (sql/format (-> (alter-table :fruit)
|
||||
(add-column :id :int [:not nil])))
|
||||
["ALTER TABLE fruit ADD COLUMN id INT NOT NULL"]))
|
||||
(is (= (sql/format (alter-table :fruit
|
||||
(add-column :id :int [:not nil])
|
||||
(drop-column :ident)))
|
||||
["ALTER TABLE fruit ADD COLUMN id INT NOT NULL, DROP COLUMN ident"])))
|
||||
|
||||
(deftest issue-293-insert-into-data
|
||||
;; insert into as (and other tests) based on :insert-into
|
||||
;; examples in the clause reference docs:
|
||||
|
|
|
|||
Loading…
Reference in a new issue