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
|
Except as noted, these clauses apply to all the SQL
|
||||||
dialects that HoneySQL supports.
|
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
|
||||||
|
|
||||||
|
`: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
|
||||||
|
|
||||||
`:drop-table` can accept a single table name or a sequence of
|
`: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"]
|
["DROP TABLE foo, bar"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## rename-table
|
||||||
|
|
||||||
## nest
|
## nest
|
||||||
|
|
||||||
This is pseudo-syntax that lets you wrap a substatement
|
This is pseudo-syntax that lets you wrap a substatement
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,9 @@
|
||||||
(def ^:private default-clause-order
|
(def ^:private default-clause-order
|
||||||
"The (default) order for known clauses. Can have items added and removed."
|
"The (default) order for known clauses. Can have items added and removed."
|
||||||
[;; DDL comes first (these don't really have a precedence):
|
[;; 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:
|
;; then SQL clauses in priority order:
|
||||||
:nest :with :with-recursive :intersect :union :union-all :except :except-all
|
:nest :with :with-recursive :intersect :union :union-all :except :except-all
|
||||||
:select :select-distinct :insert-into :update :delete :delete-from :truncate
|
:select :select-distinct :insert-into :update :delete :delete-from :truncate
|
||||||
|
|
@ -466,7 +468,19 @@
|
||||||
[(str (sql-kw k) " " e " = EXCLUDED." e)])
|
[(str (sql-kw k) " " e " = EXCLUDED." e)])
|
||||||
(format-set-exprs k x)))
|
(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]
|
(defn- format-create-table [k table]
|
||||||
(let [[table if-not-exists] (if (sequential? table) table [table])]
|
(let [[table if-not-exists] (if (sequential? table) table [table])]
|
||||||
|
|
@ -485,21 +499,28 @@
|
||||||
(when if-exists (str (sql-kw :if-exists) " "))
|
(when if-exists (str (sql-kw :if-exists) " "))
|
||||||
(str/join ", " (map #'format-entity tables)))]))
|
(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]
|
(defn- format-table-columns [k xs]
|
||||||
(let [simple-expr (fn [e]
|
[(str "(\n "
|
||||||
(let [[x & y] (format-expr e)]
|
(str/join ",\n " (map #'format-single-column xs))
|
||||||
(when (seq y)
|
"\n)")])
|
||||||
(throw (ex-info "column elements must be simple expressions"
|
|
||||||
{:expr e :params y})))
|
(defn- format-add-item [k spec]
|
||||||
x))]
|
[(str (sql-kw k) " " (format-single-column spec))])
|
||||||
(binding [*inline* true]
|
|
||||||
[(str "(\n "
|
(defn- format-rename-item [k [x y]]
|
||||||
(str/join ",\n "
|
[(str (sql-kw k) " " (format-entity x) " TO " (format-entity y))])
|
||||||
(map #(str/join " "
|
|
||||||
(let [[id & spec] (map simple-expr %)]
|
|
||||||
(cons id (map upper-case spec))))
|
|
||||||
xs))
|
|
||||||
"\n)")])))
|
|
||||||
|
|
||||||
(def ^:private base-clause-order
|
(def ^:private base-clause-order
|
||||||
"The (base) order for known clauses. Can have items added and removed.
|
"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
|
"The (default) behavior for each known clause. Can also have items added
|
||||||
and removed."
|
and removed."
|
||||||
(atom {:alter-table #'format-alter-table
|
(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
|
:create-table #'format-create-table
|
||||||
:with-columns #'format-table-columns
|
:with-columns #'format-table-columns
|
||||||
:create-view #'format-create-view
|
:create-view #'format-create-view
|
||||||
|
|
|
||||||
|
|
@ -43,11 +43,17 @@
|
||||||
(assoc data k arg)
|
(assoc data k arg)
|
||||||
(assoc {} k data)))
|
(assoc {} k data)))
|
||||||
|
|
||||||
(defn alter-table [& args] (generic :nest args))
|
(defn alter-table [& args] (generic :alter-table args))
|
||||||
(defn create-table [& args] (generic :nest args))
|
(defn add-column [& args] (generic :add-column args))
|
||||||
(defn with-columns [& args] (generic :nest args))
|
(defn drop-column [& args] (generic-1 :drop-column args))
|
||||||
(defn create-view [& args] (generic :nest args))
|
(defn rename-column [& args] (generic :rename-column args))
|
||||||
(defn drop-table [& args] (generic :nest 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 nest [& args] (generic :nest args))
|
||||||
(defn with [& args] (generic :with args))
|
(defn with [& args] (generic :with args))
|
||||||
(defn with-recursive [& args] (generic :with-recursive args))
|
(defn with-recursive [& args] (generic :with-recursive args))
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
:cljs [cljs.test :refer-macros [deftest is testing]])
|
:cljs [cljs.test :refer-macros [deftest is testing]])
|
||||||
[honey.sql :as sql]
|
[honey.sql :as sql]
|
||||||
[honey.sql.helpers
|
[honey.sql.helpers
|
||||||
:refer [columns create-view
|
:refer [add-column add-index alter-table columns create-table create-view
|
||||||
cross-join do-update-set from full-join
|
cross-join do-update-set drop-column drop-index drop-table from full-join
|
||||||
group-by having insert-into
|
group-by having insert-into
|
||||||
join left-join limit offset on-conflict order-by
|
join left-join limit offset on-conflict order-by
|
||||||
over partition-by
|
over partition-by
|
||||||
returning right-join
|
rename-column rename-table returning right-join
|
||||||
select select-distinct values where window with]]))
|
select select-distinct values where window with with-columns]]))
|
||||||
|
|
||||||
(deftest test-select
|
(deftest test-select
|
||||||
(let [m1 (-> (with [:cte (-> (select :*)
|
(let [m1 (-> (with [:cte (-> (select :*)
|
||||||
|
|
@ -331,13 +331,38 @@
|
||||||
" MAX(salary) OVER () AS MaxSalary"
|
" MAX(salary) OVER () AS MaxSalary"
|
||||||
" FROM employee")])))
|
" FROM employee")])))
|
||||||
|
|
||||||
(deftest issue-293-ddl
|
(deftest issue-293-basic-ddl
|
||||||
(is (= (sql/format {:create-view :metro :select [:*] :from [:cities] :where [:= :metroflag "y"]})
|
(is (= (sql/format {:create-view :metro :select [:*] :from [:cities] :where [:= :metroflag "y"]})
|
||||||
["CREATE VIEW metro AS SELECT * FROM cities WHERE metroflag = ?" "y"]))
|
["CREATE VIEW metro AS SELECT * FROM cities WHERE metroflag = ?" "y"]))
|
||||||
(is (= (sql/format {:create-table :films
|
(is (= (sql/format {:create-table :films
|
||||||
:with-columns [[:id :int :unsigned :auto-increment]
|
:with-columns [[:id :int :unsigned :auto-increment]
|
||||||
[:name [:varchar 50] [:not nil]]]})
|
[:name [:varchar 50] [:not nil]]]})
|
||||||
["CREATE TABLE films (\n id INT UNSIGNED AUTO_INCREMENT,\n name VARCHAR(50) NOT NULL\n)"]))
|
["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})
|
(is (= (sql/format {:drop-table :foo})
|
||||||
["DROP TABLE foo"]))
|
["DROP TABLE foo"]))
|
||||||
(is (= (sql/format {:drop-table [:if-exists :foo]})
|
(is (= (sql/format {:drop-table [:if-exists :foo]})
|
||||||
|
|
@ -347,8 +372,25 @@
|
||||||
(is (= (sql/format {:drop-table [:foo :bar]})
|
(is (= (sql/format {:drop-table [:foo :bar]})
|
||||||
["DROP TABLE foo, bar"]))
|
["DROP TABLE foo, bar"]))
|
||||||
(is (= (sql/format {:drop-table [:if-exists :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"])))
|
["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
|
(deftest issue-293-insert-into-data
|
||||||
;; insert into as (and other tests) based on :insert-into
|
;; insert into as (and other tests) based on :insert-into
|
||||||
;; examples in the clause reference docs:
|
;; examples in the clause reference docs:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue