Finish and document DDL
This commit is contained in:
parent
167d7cee0c
commit
41ed38ea38
4 changed files with 187 additions and 25 deletions
|
|
@ -13,8 +13,62 @@ dialects that HoneySQL supports.
|
||||||
|
|
||||||
## alter-table, add-column, drop-column, modify-column, rename-column
|
## alter-table, add-column, drop-column, modify-column, rename-column
|
||||||
|
|
||||||
|
`:alter-table` can accept either a single table name or
|
||||||
|
a sequence that begins with a table name and is followed
|
||||||
|
by clauses that manipulate columns (or indices, see below).
|
||||||
|
|
||||||
|
If a single table name is provided, a single column
|
||||||
|
(or index) operation can provided in the hash map DSL:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:add-column [:id :int [:not nil]]})
|
||||||
|
["ALTER TABLE fruit ADD COLUMN id INT NOT NULL"]
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:drop-column :ident})
|
||||||
|
["ALTER TABLE fruit DROP COLUMN ident"]
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:modify-column [:id :int :unsigned nil]})
|
||||||
|
["ALTER TABLE fruit MODIFY COLUMN id INT UNSIGNED NULL"]
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:rename-column [:look :appearance]})
|
||||||
|
["ALTER TABLE fruit RENAME COLUMN look TO appearance"]
|
||||||
|
```
|
||||||
|
|
||||||
|
If a sequence of a table name and various clauses is
|
||||||
|
provided, the generated `ALTER` statement will have
|
||||||
|
comma-separated clauses:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (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"]
|
||||||
|
```
|
||||||
|
|
||||||
|
As can be seen above, `:add-column` and `:modify-column`
|
||||||
|
both accept a column description (as a sequence of simple
|
||||||
|
expressions); `:drop-column` accepts a single column name,
|
||||||
|
and `:rename-column` accepts a sequence with two column
|
||||||
|
names: the "from" and the "to" names.
|
||||||
|
|
||||||
## add-index, drop-index
|
## add-index, drop-index
|
||||||
|
|
||||||
|
`:add-index` accepts a single (function) expression
|
||||||
|
that describes an index, and `:drop-index` accepts a
|
||||||
|
single index name:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:add-index [:index :look :appearance]})
|
||||||
|
["ALTER TABLE fruit ADD INDEX look(appearance)"]
|
||||||
|
user=> (sql/format {:alter-table :fruit
|
||||||
|
:add-index [:unique nil :color :appearance]})
|
||||||
|
["ALTER TABLE fruit ADD UNIQUE(color,appearance)"]
|
||||||
|
user=> (sql/format {:alter-table :fruit :drop-index :look})
|
||||||
|
["ALTER TABLE fruit DROP INDEX look"]
|
||||||
|
```
|
||||||
|
|
||||||
## create-table, with-columns
|
## create-table, with-columns
|
||||||
|
|
||||||
`:create-table` can accept a single table name or a pair
|
`:create-table` can accept a single table name or a pair
|
||||||
|
|
@ -38,6 +92,17 @@ user=> (sql/format {:create-table :fruit
|
||||||
)"]
|
)"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `:with-columns` clause is formatted as if `{:inline true}`
|
||||||
|
was specified so nothing is parameterized. In addition,
|
||||||
|
everything except the first element of a column description
|
||||||
|
will be uppercased (mostly to give the appearance of separating
|
||||||
|
the column name from the SQL keywords).
|
||||||
|
|
||||||
|
Various function-like expressions can be specified, as shown
|
||||||
|
in the example above, but allow things like `CHECK` for a
|
||||||
|
constraint, `FOREIGN KEY` (with a column name), `REFERENCES`
|
||||||
|
(with a pair of column names). See [special-syntax.md#clause-descriptors](Clause Descriptors in Special Syntax) for more details.
|
||||||
|
|
||||||
## create-view
|
## create-view
|
||||||
|
|
||||||
`:create-view` accepts a single view name:
|
`:create-view` accepts a single view name:
|
||||||
|
|
@ -66,6 +131,14 @@ user=> (sql/format {:drop-table [:foo :bar]})
|
||||||
|
|
||||||
## rename-table
|
## rename-table
|
||||||
|
|
||||||
|
`:rename-table` accepts a pair of the "from" table name
|
||||||
|
and the "to" table names:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
user=> (sql/format {:rename-table [:fruit :vegetable]})
|
||||||
|
["RENAME TABLE fruit TO vegetable"]
|
||||||
|
```
|
||||||
|
|
||||||
## nest
|
## nest
|
||||||
|
|
||||||
This is pseudo-syntax that lets you wrap a substatement
|
This is pseudo-syntax that lets you wrap a substatement
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ This section lists the function-like expressions that
|
||||||
HoneySQL supports out of the box which are formatted
|
HoneySQL supports out of the box which are formatted
|
||||||
as special syntactic forms.
|
as special syntactic forms.
|
||||||
|
|
||||||
|
The first group are used for SQL expressions. The second (last group) are used primarily in column definitions (as part of `:with-columns` and `:add-column` / `:modify-column`).
|
||||||
|
|
||||||
## array
|
## array
|
||||||
|
|
||||||
Accepts a single argument, which is expected to evaluate to
|
Accepts a single argument, which is expected to evaluate to
|
||||||
|
|
@ -58,12 +60,6 @@ expression (comma-separated, wrapped in parentheses):
|
||||||
;;=> ["(a, b, ?, x + ?)" "red" 1]
|
;;=> ["(a, b, ?, x + ?)" "red" 1]
|
||||||
```
|
```
|
||||||
|
|
||||||
## default
|
|
||||||
|
|
||||||
Takes no arguments and produces the SQL keyword `DEFAULT`.
|
|
||||||
|
|
||||||
_[I expect this to be expanded for PostgreSQL]_
|
|
||||||
|
|
||||||
## inline
|
## inline
|
||||||
|
|
||||||
Accepts a single argument and tries to render it as a
|
Accepts a single argument and tries to render it as a
|
||||||
|
|
@ -186,3 +182,58 @@ parameters from them:
|
||||||
(sql/format {:select [:a [[:raw ["@var := " ["foo"]]]]]})
|
(sql/format {:select [:a [[:raw ["@var := " ["foo"]]]]]})
|
||||||
;;=> ["SELECT a, @var := ?" "foo"]
|
;;=> ["SELECT a, @var := ?" "foo"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Column Descriptors
|
||||||
|
|
||||||
|
There are three types of descriptors that vary
|
||||||
|
in how they treat their first argument. All three
|
||||||
|
descriptors automatically try to inline any parameters
|
||||||
|
(and will throw an exception if they can't, since these
|
||||||
|
descriptors are meant to be used in column or index
|
||||||
|
specifications).
|
||||||
|
|
||||||
|
### foreign-key, primary-key
|
||||||
|
|
||||||
|
If no arguments are provided, these render as just SQL
|
||||||
|
keywords (uppercase):
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[:foreign-key] ;=> FOREIGN KEY
|
||||||
|
[:primary-key] ;=> PRIMARY KEY
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise, these render as regular function calls:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[:foreign-key :a] ;=> FOREIGN KEY(a)
|
||||||
|
[:primary-key :x :y] ;=> PRIMARY KEY(x,y)
|
||||||
|
```
|
||||||
|
|
||||||
|
## constraint, default, references
|
||||||
|
|
||||||
|
Although these are grouped together, they are generally
|
||||||
|
used differently. This group renders as SQL keywords if
|
||||||
|
no arguments are provided. If a single argument is
|
||||||
|
provided, this renders as a SQL keyword followed by the
|
||||||
|
argument. If two or more arguments are provided, this
|
||||||
|
renders as a SQL keyword followed by the first argument,
|
||||||
|
followed by the rest as a regular argument list:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[:default] ;=> DEFAULT
|
||||||
|
[:default 42] ;=> DEFAULT 42
|
||||||
|
[:default "str"] ;=> DEFAULT 'str'
|
||||||
|
[:constraint :name] ;=> CONSTRAINT name
|
||||||
|
[:references :foo :bar] ;=> REFERENCES foo(bar)
|
||||||
|
```
|
||||||
|
|
||||||
|
## index, unique
|
||||||
|
|
||||||
|
These behave like the group above except that if the
|
||||||
|
first argument is `nil`, it is omitted:
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
[:index :foo :bar :quux] ;=> INDEX foo(bar,quux)
|
||||||
|
[:index nil :bar :quux] ;=> INDEX(bar,quux)
|
||||||
|
[:unique :a :b] ;=> UNIQUE a(b)
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@
|
||||||
(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 :add-column :drop-column :rename-column
|
:alter-table :add-column :drop-column :modify-column :rename-column
|
||||||
:add-index :drop-index :rename-table
|
:add-index :drop-index :rename-table
|
||||||
:create-table :with-columns :create-view :drop-table
|
:create-table :with-columns :create-view :drop-table
|
||||||
;; then SQL clauses in priority order:
|
;; then SQL clauses in priority order:
|
||||||
|
|
@ -469,11 +469,12 @@
|
||||||
(format-set-exprs k x)))
|
(format-set-exprs k x)))
|
||||||
|
|
||||||
(defn- format-simple-clause [c]
|
(defn- format-simple-clause [c]
|
||||||
|
(binding [*inline* true]
|
||||||
(let [[x & y] (format-dsl c)]
|
(let [[x & y] (format-dsl c)]
|
||||||
(when (seq y)
|
(when (seq y)
|
||||||
(throw (ex-info "column/index operations must be simple clauses"
|
(throw (ex-info "column/index operations must be simple clauses"
|
||||||
{:clause c :params y})))
|
{:clause c :params y})))
|
||||||
x))
|
x)))
|
||||||
|
|
||||||
(defn- format-alter-table [k x]
|
(defn- format-alter-table [k x]
|
||||||
(if (sequential? x)
|
(if (sequential? x)
|
||||||
|
|
@ -500,16 +501,16 @@
|
||||||
(str/join ", " (map #'format-entity tables)))]))
|
(str/join ", " (map #'format-entity tables)))]))
|
||||||
|
|
||||||
(defn- format-simple-expr [e]
|
(defn- format-simple-expr [e]
|
||||||
|
(binding [*inline* true]
|
||||||
(let [[x & y] (format-expr e)]
|
(let [[x & y] (format-expr e)]
|
||||||
(when (seq y)
|
(when (seq y)
|
||||||
(throw (ex-info "column elements must be simple expressions"
|
(throw (ex-info "column elements must be simple expressions"
|
||||||
{:expr e :params y})))
|
{:expr e :params y})))
|
||||||
x))
|
x)))
|
||||||
|
|
||||||
(defn- format-single-column [xs]
|
(defn- format-single-column [xs]
|
||||||
(binding [*inline* true]
|
|
||||||
(str/join " " (let [[id & spec] (map #'format-simple-expr xs)]
|
(str/join " " (let [[id & spec] (map #'format-simple-expr xs)]
|
||||||
(cons id (map upper-case spec))))))
|
(cons id (map upper-case spec)))))
|
||||||
|
|
||||||
(defn- format-table-columns [k xs]
|
(defn- format-table-columns [k xs]
|
||||||
[(str "(\n "
|
[(str "(\n "
|
||||||
|
|
@ -540,8 +541,10 @@
|
||||||
(atom {:alter-table #'format-alter-table
|
(atom {:alter-table #'format-alter-table
|
||||||
:add-column #'format-add-item
|
:add-column #'format-add-item
|
||||||
:drop-column #'format-selector
|
:drop-column #'format-selector
|
||||||
|
:modify-column #'format-add-item
|
||||||
:rename-column #'format-rename-item
|
:rename-column #'format-rename-item
|
||||||
:add-index #'format-add-item
|
;; so :add-index works with both [:index] and [:unique]
|
||||||
|
:add-index (fn [_ x] (format-on-expr :add x))
|
||||||
:drop-index #'format-selector
|
:drop-index #'format-selector
|
||||||
:rename-table #'format-rename-item
|
:rename-table #'format-rename-item
|
||||||
:create-table #'format-create-table
|
:create-table #'format-create-table
|
||||||
|
|
@ -676,9 +679,46 @@
|
||||||
(into params-x)
|
(into params-x)
|
||||||
(into params-y)))))
|
(into params-y)))))
|
||||||
|
|
||||||
|
(defn- function-0 [k xs]
|
||||||
|
[(str (sql-kw k)
|
||||||
|
(when (seq xs)
|
||||||
|
(str "(" (str/join "," (map #'format-simple-expr xs)) ")")))])
|
||||||
|
|
||||||
|
(defn- function-1 [k xs]
|
||||||
|
[(str (sql-kw k)
|
||||||
|
(when (seq xs)
|
||||||
|
(str " " (format-simple-expr (first xs))
|
||||||
|
(when-let [args (next xs)]
|
||||||
|
(str "(" (str/join "," (map #'format-simple-expr args)) ")")))))])
|
||||||
|
|
||||||
|
(defn- function-1-opt [k xs]
|
||||||
|
[(str (sql-kw k)
|
||||||
|
(when (seq xs)
|
||||||
|
(str (when-let [e (first xs)]
|
||||||
|
(str " " (format-simple-expr e)))
|
||||||
|
(when-let [args (next xs)]
|
||||||
|
(str "(" (str/join "," (map #'format-simple-expr args)) ")")))))])
|
||||||
|
|
||||||
(def ^:private special-syntax
|
(def ^:private special-syntax
|
||||||
(atom
|
(atom
|
||||||
{:array
|
{;; these "functions" are mostly used in column
|
||||||
|
;; descriptions so they generally have one of two forms:
|
||||||
|
;; function-0 - with zero arguments, renders as a keyword,
|
||||||
|
;; otherwise renders as a function call
|
||||||
|
;; function-1 - with zero arguments, renders as a keyword,
|
||||||
|
;; with one argument, as a keyword followed by an entity,
|
||||||
|
;; otherwise renders as a keyword followed by a function
|
||||||
|
;; call using the first entity as the function
|
||||||
|
;; function-1-opt - like function-1 except if the first
|
||||||
|
;; argument is nil, it is omitted
|
||||||
|
:constraint #'function-1
|
||||||
|
:default #'function-1
|
||||||
|
:foreign-key #'function-0
|
||||||
|
:index #'function-1-opt
|
||||||
|
:primary-key #'function-0
|
||||||
|
:references #'function-1
|
||||||
|
:unique #'function-1-opt
|
||||||
|
:array
|
||||||
(fn [_ [arr]]
|
(fn [_ [arr]]
|
||||||
(let [[sqls params] (format-expr-list arr)]
|
(let [[sqls params] (format-expr-list arr)]
|
||||||
(into [(str "ARRAY[" (str/join ", " sqls) "]")] params)))
|
(into [(str "ARRAY[" (str/join ", " sqls) "]")] params)))
|
||||||
|
|
@ -720,9 +760,6 @@
|
||||||
(fn [_ [& args]]
|
(fn [_ [& args]]
|
||||||
(let [[sqls params] (format-expr-list args)]
|
(let [[sqls params] (format-expr-list args)]
|
||||||
(into [(str "(" (str/join ", " sqls) ")")] params)))
|
(into [(str "(" (str/join ", " sqls) ")")] params)))
|
||||||
:default
|
|
||||||
(fn [_ []]
|
|
||||||
["DEFAULT"])
|
|
||||||
:inline
|
:inline
|
||||||
(fn [_ [x]]
|
(fn [_ [x]]
|
||||||
(if (sequential? x)
|
(if (sequential? x)
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
(defn alter-table [& args] (generic :alter-table args))
|
(defn alter-table [& args] (generic :alter-table args))
|
||||||
(defn add-column [& args] (generic :add-column args))
|
(defn add-column [& args] (generic :add-column args))
|
||||||
(defn drop-column [& args] (generic-1 :drop-column args))
|
(defn drop-column [& args] (generic-1 :drop-column args))
|
||||||
|
(defn modify-column [& args] (generic :modify-column args))
|
||||||
(defn rename-column [& args] (generic :rename-column args))
|
(defn rename-column [& args] (generic :rename-column args))
|
||||||
(defn add-index [& args] (generic :add-index args))
|
(defn add-index [& args] (generic :add-index args))
|
||||||
(defn drop-index [& args] (generic-1 :drop-index args))
|
(defn drop-index [& args] (generic-1 :drop-index args))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue