Fixes #291 by ensuring consistent values

For uneven vectors of values, pad with NULLs.

For uneven maps of values, use full set of keys across all of them.
This commit is contained in:
Sean Corfield 2021-02-01 10:44:07 -08:00
parent 3439bb6c48
commit 7aab640e30
2 changed files with 32 additions and 7 deletions

View file

@ -315,17 +315,29 @@
(defn- format-values [k xs]
(cond (sequential? (first xs))
;; [[1 2 3] [4 5 6]]
(let [[sqls params]
(let [n-1 (map count xs)
;; issue #291: ensure all value sequences are the same length
xs' (if (apply = n-1)
xs
(let [n-n (apply max n-1)]
(map (fn [x] (take n-n (concat x (repeat nil)))) xs)))
[sqls params]
(reduce (fn [[sql params] [sqls' params']]
[(conj sql (str "(" (str/join ", " sqls') ")"))
(into params params')])
[[] []]
(map #'format-expr-list xs))]
(map #'format-expr-list xs'))]
(into [(str (sql-kw k) " " (str/join ", " sqls))] params))
(map? (first xs))
;; [{:a 1 :b 2 :c 3}]
(let [cols (keys (first xs))
(let [cols-1 (keys (first xs))
;; issue #291: check for all keys in all maps but still
;; use the keys from the first map if they match so that
;; users can rely on the key ordering if they want to,
;; e.g., see test that uses array-map for the first row
cols-n (into #{} (mapcat keys) xs)
cols (if (= (set cols-1) cols-n) cols-1 cols-n)
[sqls params]
(reduce (fn [[sql params] [sqls' params']]
[(conj sql (str "(" (str/join ", " sqls') ")"))
@ -347,6 +359,10 @@
(throw (ex-info ":values expects sequences or maps"
{:first (first xs)}))))
(comment
(into #{} (mapcat keys) [{:a 1 :b 2} {:b 3 :c 4}])
,)
(defn- format-set-exprs [k xs]
(let [[sqls params]
(reduce-kv (fn [[sql params] v e]

View file

@ -102,14 +102,14 @@
["WITH query AS (SELECT foo FROM bar)"]))
(is (= (format {:with-recursive [[:query {:select [:foo] :from [:bar]}]]})
["WITH RECURSIVE query AS (SELECT foo FROM bar)"]))
(is (= (format {:with [[[:static {:columns [:a :b :c]}] {:values [[1 2 3] [4 5 6]]}]]})
["WITH static (a, b, c) AS (VALUES (?, ?, ?), (?, ?, ?))" 1 2 3 4 5 6]))
(is (= (format {:with [[[:static {:columns [:a :b :c]}] {:values [[1 2 3] [4 5]]}]]})
["WITH static (a, b, c) AS (VALUES (?, ?, ?), (?, ?, NULL))" 1 2 3 4 5]))
(is (= (format
{:with [[[:static {:columns [:a :b :c]}]
{:values [[1 2 3] [4 5 6]]}]]
{:values [[1 2] [4 5 6]]}]]
:select [:*]
:from [:static]})
["WITH static (a, b, c) AS (VALUES (?, ?, ?), (?, ?, ?)) SELECT * FROM static" 1 2 3 4 5 6])))
["WITH static (a, b, c) AS (VALUES (?, ?, NULL), (?, ?, ?)) SELECT * FROM static" 1 2 4 5 6])))
(deftest insert-into
(is (= (format {:insert-into :foo})
@ -134,6 +134,15 @@
{:namespace-as-table? true})
["INSERT INTO foo (id) VALUES (?)" 2])))
(deftest insert-into-uneven-maps
;; we can't rely on ordering when the set of keys differs between maps:
(let [res (format {:insert-into :foo :values [{:id 1} {:id 2, :bar "quux"}]})]
(is (or (= res ["INSERT INTO foo (id, bar) VALUES (?, NULL), (?, ?)" 1 2 "quux"])
(= res ["INSERT INTO foo (bar, id) VALUES (NULL, ?), (?, ?)" 1 "quux" 2]))))
(let [res (format {:insert-into :foo :values [{:id 1, :bar "quux"} {:id 2}]})]
(is (or (= res ["INSERT INTO foo (id, bar) VALUES (?, ?), (?, NULL)" 1 "quux" 2])
(= res ["INSERT INTO foo (bar, id) VALUES (?, ?), (NULL, ?)" "quux" 1 2])))))
(deftest exists-test
;; EXISTS should never have been implemented as SQL syntax: it's an operator!
#_(is (= (format {:exists {:select [:a] :from [:foo]}})