diff --git a/src/honey/sql.cljc b/src/honey/sql.cljc index afc8389..bd8cf9c 100644 --- a/src/honey/sql.cljc +++ b/src/honey/sql.cljc @@ -209,6 +209,9 @@ {:symbol x :failure (str t)}))))) +(defn- ensure-sequential [xs] + (if (sequential? xs) xs [xs])) + (defn format-entity "Given a simple SQL entity (a keyword or symbol -- or string), return the equivalent SQL fragment (as a string -- no parameters). @@ -549,7 +552,7 @@ (-> [sql'] (into params) (into params')))) (defn- format-select-into [k xs] - (let [[v e] (if (sequential? xs) xs [xs]) + (let [[v e] (ensure-sequential xs) [sql & params] (when e (format-expr e))] (into [(str (sql-kw k) " " (format-entity v) (when sql @@ -708,11 +711,12 @@ [])) (defn- format-group-by [k xs] - (let [[sqls params] (format-expr-list xs)] + (let [[sqls params] (format-expr-list (ensure-sequential xs))] (into [(str (sql-kw k) " " (str/join ", " sqls))] params))) (defn- format-order-by [k xs] - (let [dirs (map #(when (sequential? %) (second %)) xs) + (let [xs (ensure-sequential xs) + dirs (map #(when (sequential? %) (second %)) xs) [sqls params] (format-expr-list (map #(if (sequential? %) (first %) %) xs))] (into [(str (sql-kw k) " " @@ -722,7 +726,7 @@ dirs)))] params))) (defn- format-lock-strength [k xs] - (let [[strength tables nowait] (if (sequential? xs) xs [xs])] + (let [[strength tables nowait] (ensure-sequential xs)] [(str (sql-kw k) " " (sql-kw strength) (when tables (str @@ -946,7 +950,7 @@ (format-ddl-options opts context))))) (defn- format-truncate [k xs] - (let [[table & options] (if (sequential? xs) xs [xs]) + (let [[table & options] (ensure-sequential xs) [pre table ine options] (destructure-ddl-item [table options] "truncate")] (when (seq pre) (throw (ex-info "TRUNCATE syntax error" {:unexpected pre}))) (when (seq ine) (throw (ex-info "TRUNCATE syntax error" {:unexpected ine}))) diff --git a/src/honey/sql/helpers.cljc b/src/honey/sql/helpers.cljc index d25bd19..9a2b8b1 100644 --- a/src/honey/sql/helpers.cljc +++ b/src/honey/sql/helpers.cljc @@ -51,7 +51,11 @@ ;; implementation helpers: (defn- default-merge [current args] - (c/into (vec current) args)) + (let [current (cond + (nil? current) [] + (sequential? current) (vec current) + :else [current])] + (c/into current args))) (defn- sym->kw "Given a symbol, produce a keyword, retaining the namespace diff --git a/test/honey/sql/helpers_test.cljc b/test/honey/sql/helpers_test.cljc index be159ba..b1b4a75 100644 --- a/test/honey/sql/helpers_test.cljc +++ b/test/honey/sql/helpers_test.cljc @@ -144,14 +144,14 @@ (deftest select-top-tests (testing "Basic TOP syntax" (is (= ["SELECT TOP(?) foo FROM bar ORDER BY quux ASC" 10] - (sql/format {:select-top [10 :foo] :from :bar :order-by [:quux]}))) + (sql/format {:select-top [10 :foo] :from :bar :order-by :quux}))) (is (= ["SELECT TOP(?) foo FROM bar ORDER BY quux ASC" 10] (sql/format (-> (select-top 10 :foo) (from :bar) (order-by :quux)))))) (testing "Expanded TOP syntax" (is (= ["SELECT TOP(?) PERCENT WITH TIES foo, baz FROM bar ORDER BY quux ASC" 10] - (sql/format {:select-top [[10 :percent :with-ties] :foo :baz] :from :bar :order-by [:quux]}))) + (sql/format {:select-top [[10 :percent :with-ties] :foo :baz] :from :bar :order-by :quux}))) (is (= ["SELECT TOP(?) PERCENT WITH TIES foo, baz FROM bar ORDER BY quux ASC" 10] (sql/format (-> (select-top [10 :percent :with-ties] :foo :baz) (from :bar) @@ -865,7 +865,7 @@ [[:filter ; two pairs -- alias is on last pair [:avg :x [:order-by :y [:a :desc]]] {:where [:< :i 10]} [:sum :q] {:where [:= :x nil]}] :b] - [[:within-group [:foo :y] {:order-by [:x]}]]]}) + [[:within-group [:foo :y] {:order-by :x}]]]}) [(str "SELECT COUNT(*) FILTER (WHERE i > ?) AS a," " AVG(x, y ORDER BY a DESC) FILTER (WHERE i < ?)," " SUM(q) FILTER (WHERE x IS NULL) AS b," diff --git a/test/honey/sql/postgres_test.cljc b/test/honey/sql/postgres_test.cljc index 03af583..9aad005 100644 --- a/test/honey/sql/postgres_test.cljc +++ b/test/honey/sql/postgres_test.cljc @@ -255,6 +255,12 @@ (sql/format))))) (deftest over-test + (testing "simple window statement" + (is (= ["SELECT AVG(salary) OVER w FROM employee WINDOW w AS (PARTITION BY department ORDER BY salary ASC)"] + (sql/format {:select [[[:over [[:avg :salary] :w]]]] + :from :employee + :window [:w {:partition-by :department + :order-by :salary}]})))) (testing "window function over on select statemt" (is (= ["SELECT id, AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) AS Average, MAX(salary) OVER w AS MaxSalary FROM employee WINDOW w AS (PARTITION BY department)"] ;; honeysql treats over as a function: diff --git a/test/honey/sql_test.cljc b/test/honey/sql_test.cljc index 14b21fb..0c69d79 100644 --- a/test/honey/sql_test.cljc +++ b/test/honey/sql_test.cljc @@ -83,10 +83,14 @@ (sut/format {:select [:*] :from [:table] :where (sut/map= {:id 1})} {:quoted true}))) (is (= ["SELECT \"t\".* FROM \"table\" AS \"t\" WHERE \"id\" = ?" 1] (sut/format {:select [:t.*] :from [[:table :t]] :where [:= :id 1]} {:quoted true}))) + (is (= ["SELECT * FROM \"table\" GROUP BY \"foo\""] + (sut/format {:select [:*] :from [:table] :group-by :foo} {:quoted true}))) (is (= ["SELECT * FROM \"table\" GROUP BY \"foo\", \"bar\""] (sut/format {:select [:*] :from [:table] :group-by [:foo :bar]} {:quoted true}))) (is (= ["SELECT * FROM \"table\" GROUP BY DATE(\"bar\")"] (sut/format {:select [:*] :from [:table] :group-by [[:date :bar]]} {:quoted true}))) + (is (= ["SELECT * FROM \"table\" ORDER BY \"foo\" ASC"] + (sut/format {:select [:*] :from [:table] :order-by :foo} {:quoted true}))) (is (= ["SELECT * FROM \"table\" ORDER BY \"foo\" DESC, \"bar\" ASC"] (sut/format {:select [:*] :from [:table] :order-by [[:foo :desc] :bar]} {:quoted true}))) (is (= ["SELECT * FROM \"table\" ORDER BY DATE(\"expiry\") DESC, \"bar\" ASC"]