diff --git a/CHANGELOG.md b/CHANGELOG.md index 458adaa..418296c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changes +* 2.0.next in progress + * Tentative fix for #315 by expanding `:in` handling to deal with `nil` values. + * 2.0.0-beta1 (for testing; 2021-04-09) * Since Alpha 3, more documentation has been written and existing documentation clarified (addressing #300, #309, #313, #314). * Fix #319 by ensuring `register-clause!` is idempotent. diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index 6d99710..12b951e 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -921,17 +921,34 @@ x)) (defn- format-in [in [x y]] - (let [[sql-x & params-x] (format-expr x {:nested true}) - [sql-y & params-y] (format-expr y {:nested true}) + (let [nil-check (and (sequential? y) (not (ident? (first y)))) + y' (if nil-check (remove nil? y) y) + [sql-x & params-x] (format-expr x {:nested true}) + [sql-y & params-y] (format-expr y' {:nested true}) values (unwrap (first params-y) {})] (if (and (= "?" sql-y) (= 1 (count params-y)) (coll? values)) - (let [sql (str "(" (str/join ", " (repeat (count values) "?")) ")")] - (-> [(str sql-x " " (sql-kw in) " " sql)] + (let [values' (remove nil? values) + sql (str "(" (str/join ", " (repeat (count values') "?")) ")") + in (str sql-x " " (sql-kw in) " " sql)] + (-> [(if (not= (count values) (count values')) + (if (zero? (count values')) + (str sql-x " IS NULL") + (str "(" in " OR " sql-x " IS NULL)")) + (if (zero? (count values)) + "FALSE" + in))] (into params-x) - (into values))) - (-> [(str sql-x " " (sql-kw in) " " sql-y)] - (into params-x) - (into params-y))))) + (into values'))) + (let [in (str sql-x " " (sql-kw in) " " sql-y)] + (-> [(if (and nil-check (not= (count y) (count y'))) + (if (zero? (count y')) + (str sql-x " IS NULL") + (str "(" in " OR " sql-x " IS NULL)")) + (if (zero? (count y)) + "FALSE" + in))] + (into params-x) + (into params-y)))))) (defn- function-0 [k xs] [(str (sql-kw k) diff --git a/test/honey/sql/helpers_test.cljc b/test/honey/sql/helpers_test.cljc index 8daa0eb..b69599f 100644 --- a/test/honey/sql/helpers_test.cljc +++ b/test/honey/sql/helpers_test.cljc @@ -274,6 +274,39 @@ :from [:customers] :where [:in :id values]}))) (is (= ["SELECT * FROM customers WHERE id IN (?)" 1] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:params {:ids values}}))))) + (testing (str "with just a nil value from a " (name cname)) + (let [values (conj coll nil)] + (is (= ["SELECT * FROM customers WHERE id IS NULL"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE id IS NULL"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:params {:ids values}}))))) + (testing (str "with nil values from a " (name cname)) + (let [values (conj coll 1 nil)] + (is (= ["SELECT * FROM customers WHERE (id IN (?) OR id IS NULL)" 1] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE (id IN (?) OR id IS NULL)" 1] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:params {:ids values}}))))) + (testing (str "with no values from a " (name cname)) + (let [values coll] + (is (= ["SELECT * FROM customers WHERE FALSE"] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE FALSE"] (sql/format {:select [:*] :from [:customers] :where [:in :id :?ids]} @@ -285,6 +318,16 @@ :from [:customers] :where [:in :id values]}))) (is (= ["SELECT * FROM customers WHERE id IN (?, ?)" 1 2] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id :?ids]} + {:params {:ids values}})))) + (let [values [1 nil 2]] + (is (= ["SELECT * FROM customers WHERE (id IN (?, ?) OR id IS NULL)" 1 2] + (sql/format {:select [:*] + :from [:customers] + :where [:in :id values]}))) + (is (= ["SELECT * FROM customers WHERE (id IN (?, ?) OR id IS NULL)" 1 2] (sql/format {:select [:*] :from [:customers] :where [:in :id :?ids]}