diff --git a/CHANGELOG.md b/CHANGELOG.md index f390aee..52415ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * 2.3.next in progress * Address [#430](https://github.com/seancorfield/honeysql/issues/430) by treating `:'` as introducing a name that should be treating literally and not formatted as a SQL entity (which respects quoting, dot-splitting, etc); this effectively expands the "escape hatch" introduced via [#352](https://github.com/seancorfield/honeysql/issues/352) in 2.2.868. _Note that the function context behavior formats as a SQL entity, rather than the usual SQL "keyword", whereas this new context is a literal transcription rather than as a SQL entity!_ * Address [#427](https://github.com/seancorfield/honeysql/issues/427) by adding `set-options!`. + * Address [#415](https://github.com/seancorfield/honeysql/issues/415) by supporting multiple column names in `DROP COLUMN`. * 2.3.928 -- 2022-09-04 * Address [#425](https://github.com/seancorfield/honeysql/issues/425) by clarifying that `INTERVAL` as special syntax may be MySQL-specific and PostgreSQL uses difference syntax (because `INTERVAL` is a data type there). diff --git a/doc/clause-reference.md b/doc/clause-reference.md index 4289a3a..686bdfb 100644 --- a/doc/clause-reference.md +++ b/doc/clause-reference.md @@ -85,7 +85,8 @@ user=> (sql/format {:alter-table [:fruit As can be seen above, `:add-column` and `:alter-column` both accept a column description (as a sequence of simple -expressions); `:drop-column` accepts a single column name, +expressions); `:drop-column` accepts one or more column names +optionally prefixed by `:if-exists`, and `:rename-column` accepts a sequence with two column names: the "from" and the "to" names. diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 3d2746b..95ef85e 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -981,6 +981,32 @@ (into [(str/join sqls)] params)) [s])) +(defn- destructure-drop-columns [tables] + (let [params + (if (sequential? tables) + tables + [tables]) + _ (when-not (every? ident? params) + (throw (ex-info "DROP COLUMNS expects just column names" + {:tables tables})))] + (loop [if-exists false coll params sqls []] + (if (seq coll) + (if (#{:if-exists 'if-exists} (first coll)) + (recur true (rest coll) sqls) + (recur false (rest coll) + (conj sqls (cond->> (format-entity (first coll)) + if-exists + (str (sql-kw :if-exists) " "))))) + (if if-exists + (throw (ex-info (str "DROP COLUMNS: missing column name after IF EXISTS") + {:tables tables})) + sqls))))) + +(defn- format-drop-columns + [k params] + (let [tables (destructure-drop-columns params)] + [(str/join ", " (mapv #(str (sql-kw k) " " %) tables))])) + (defn- check-where "Given a formatter function, performs a pre-flight check that there is a non-empty where clause if at least basic checking is enabled." @@ -1009,7 +1035,7 @@ and removed." (atom {:alter-table #'format-alter-table :add-column #'format-add-item - :drop-column #'format-drop-items + :drop-column #'format-drop-columns :alter-column (fn [k spec] (format-add-item (if (mysql?) :modify-column k) diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index a26e263..0dd99a0 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -172,12 +172,15 @@ (generic :add-column col-elems)) (defn drop-column - "Takes a single column name (use with `alter-table`). + "Takes one or more column names (use with `alter-table`). - (alter-table :foo (drop-column :bar))" + Accepts an `IF EXISTS` flag (keyword or symbol) before + any column names. + + (alter-table :foo (drop-column :bar :if-exists :quux))" {:arglists '([col])} - [& args] - (generic-1 :drop-column args)) + [& col-elems] + (generic :drop-column col-elems)) (defn alter-column "Like add-column, accepts any number of SQL elements diff --git a/test/honey/sql/helpers_test.cljc b/test/honey/sql/helpers_test.cljc index c0d0654..37bf7f0 100644 --- a/test/honey/sql/helpers_test.cljc +++ b/test/honey/sql/helpers_test.cljc @@ -633,8 +633,13 @@ ["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"]))) + (drop-column :ident) + (drop-column :if-exists :another))) + ["ALTER TABLE fruit ADD COLUMN id INT NOT NULL, DROP COLUMN ident, DROP COLUMN IF EXISTS another"])) + (is (= (sql/format (alter-table :fruit + (drop-column :a :b :if-exists :c :d) + (drop-column :if-exists :e))) + ["ALTER TABLE fruit DROP COLUMN a, DROP COLUMN b, DROP COLUMN IF EXISTS c, DROP COLUMN d, DROP COLUMN IF EXISTS e"]))) (deftest issue-293-insert-into-data ;; insert into as (and other tests) based on :insert-into