Addresses #293 by adding alter table stuff

And documenting more of the DDL.
This commit is contained in:
Sean Corfield 2021-02-12 17:08:37 -08:00
parent 49d8365bfb
commit 167d7cee0c
4 changed files with 138 additions and 28 deletions

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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: