diff --git a/README.md b/README.md index b8ff7f29..9cc77d17 100644 --- a/README.md +++ b/README.md @@ -295,9 +295,9 @@ If you want to delete everything from a table, you can use `truncate`: => ["TRUNCATE films"] ``` -### Unions +### Set operations -Queries may be united within a :union or :union-all keyword: +Queries may be combined within a :union, :union-all, :intersect or :except keyword: ```clojure (sql/format {:union [(-> (select :*) (from :foo)) diff --git a/src/honeysql/format.cljc b/src/honeysql/format.cljc index 53eed4d9..b1339f82 100644 --- a/src/honeysql/format.cljc +++ b/src/honeysql/format.cljc @@ -226,6 +226,7 @@ :intersect 35 :union 40 :union-all 45 + :except 47 :select 50 :insert-into 60 :update 70 @@ -686,6 +687,10 @@ (binding [*subquery?* false] (string/join " INTERSECT " (map to-sql maps)))) +(defmethod format-clause :except [[_ maps] _] + (binding [*subquery?* false] + (string/join " EXCEPT " (map to-sql maps)))) + (defmethod fn-handler "case" [_ & clauses] (str "CASE " (space-join diff --git a/src/honeysql/helpers.cljc b/src/honeysql/helpers.cljc index 01c2d0d0..6a54dbe2 100644 --- a/src/honeysql/helpers.cljc +++ b/src/honeysql/helpers.cljc @@ -345,3 +345,6 @@ (defmethod build-clause :intersect [_ m maps] (assoc m :intersect maps)) + +(defmethod build-clause :except [_ m maps] + (assoc m :except maps)) diff --git a/test/honeysql/format_test.cljc b/test/honeysql/format_test.cljc index fa2d8eb3..cf8258cd 100644 --- a/test/honeysql/format_test.cljc +++ b/test/honeysql/format_test.cljc @@ -126,6 +126,11 @@ {:select [:foo] :from [:bar2]}]}) ["SELECT foo FROM bar1 INTERSECT SELECT foo FROM bar2"]))) +(deftest except-test + (is (= (format {:except [{:select [:foo] :from [:bar1]} + {:select [:foo] :from [:bar2]}]}) + ["SELECT foo FROM bar1 EXCEPT SELECT foo FROM bar2"]))) + (deftest inner-parts-test (testing "The correct way to apply ORDER BY to various parts of a UNION" (is (= (format