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` 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` 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` 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` accepts a single view name:
|
||||
|
|
@ -66,6 +131,14 @@ user=> (sql/format {:drop-table [:foo :bar]})
|
|||
|
||||
## 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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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]
|
||||
```
|
||||
|
||||
## default
|
||||
|
||||
Takes no arguments and produces the SQL keyword `DEFAULT`.
|
||||
|
||||
_[I expect this to be expanded for PostgreSQL]_
|
||||
|
||||
## inline
|
||||
|
||||
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"]]]]]})
|
||||
;;=> ["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
|
||||
"The (default) order for known clauses. Can have items added and removed."
|
||||
[;; 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
|
||||
:create-table :with-columns :create-view :drop-table
|
||||
;; then SQL clauses in priority order:
|
||||
|
|
@ -469,11 +469,12 @@
|
|||
(format-set-exprs k x)))
|
||||
|
||||
(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))
|
||||
(binding [*inline* true]
|
||||
(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)
|
||||
|
|
@ -500,16 +501,16 @@
|
|||
(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))
|
||||
(binding [*inline* true]
|
||||
(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))))))
|
||||
(str/join " " (let [[id & spec] (map #'format-simple-expr xs)]
|
||||
(cons id (map upper-case spec)))))
|
||||
|
||||
(defn- format-table-columns [k xs]
|
||||
[(str "(\n "
|
||||
|
|
@ -540,8 +541,10 @@
|
|||
(atom {:alter-table #'format-alter-table
|
||||
:add-column #'format-add-item
|
||||
:drop-column #'format-selector
|
||||
:modify-column #'format-add-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
|
||||
:rename-table #'format-rename-item
|
||||
:create-table #'format-create-table
|
||||
|
|
@ -676,9 +679,46 @@
|
|||
(into params-x)
|
||||
(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
|
||||
(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]]
|
||||
(let [[sqls params] (format-expr-list arr)]
|
||||
(into [(str "ARRAY[" (str/join ", " sqls) "]")] params)))
|
||||
|
|
@ -720,9 +760,6 @@
|
|||
(fn [_ [& args]]
|
||||
(let [[sqls params] (format-expr-list args)]
|
||||
(into [(str "(" (str/join ", " sqls) ")")] params)))
|
||||
:default
|
||||
(fn [_ []]
|
||||
["DEFAULT"])
|
||||
:inline
|
||||
(fn [_ [x]]
|
||||
(if (sequential? x)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
(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 modify-column [& args] (generic :modify-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))
|
||||
|
|
|
|||
Loading…
Reference in a new issue