Add compatibility with honeysql (#476)
This commit is contained in:
parent
6bd3dc3d9f
commit
be39ee7e27
6 changed files with 622 additions and 2 deletions
2
sci
2
sci
|
|
@ -1 +1 @@
|
|||
Subproject commit 42243de76af11810ab45703a414d86d61e8d94df
|
||||
Subproject commit 5d958bf28319ef34bacb533282b1fdd2fe440df3
|
||||
24
script/lib_tests/honeysql_test
Executable file
24
script/lib_tests/honeysql_test
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
|
||||
BB_CMD="./bb"
|
||||
else
|
||||
BB_CMD="lein bb"
|
||||
fi
|
||||
|
||||
export BABASHKA_CLASSPATH
|
||||
BABASHKA_CLASSPATH=$(clojure -Sdeps '{:deps {honeysql {:mvn/version "1.0.444"}}}' -Spath)
|
||||
|
||||
$BB_CMD -cp "$BABASHKA_CLASSPATH:test-resources/lib_tests" -e "
|
||||
(require '[honeysql.core-test])
|
||||
(require '[honeysql.format-test])
|
||||
(require '[clojure.test :as t])
|
||||
|
||||
(let [{:keys [:test :pass :fail :error]}
|
||||
(t/run-tests 'honeysql.core-test 'honeysql.format-test)]
|
||||
(when-not (pos? test)
|
||||
(System/exit 1))
|
||||
(System/exit (+ fail error)))
|
||||
"
|
||||
|
|
@ -27,3 +27,4 @@ script/lib_tests/cljc_java_time_test
|
|||
script/lib_tests/camel_snake_kebab_test
|
||||
script/lib_tests/aero_test
|
||||
script/lib_tests/clojure_data_generators_test
|
||||
script/lib_tests/honeysql_test
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@
|
|||
java.util.Base64$Decoder
|
||||
java.util.Base64$Encoder
|
||||
java.util.Date
|
||||
java.util.Locale
|
||||
java.util.Map
|
||||
java.util.MissingResourceException
|
||||
java.util.Properties
|
||||
|
|
@ -227,12 +228,14 @@
|
|||
clojure.lang.IEditableCollection
|
||||
clojure.lang.IMapEntry
|
||||
clojure.lang.IPersistentMap
|
||||
clojure.lang.IPersistentSet
|
||||
clojure.lang.IPersistentVector
|
||||
clojure.lang.IRecord
|
||||
clojure.lang.ISeq
|
||||
clojure.lang.Named
|
||||
clojure.lang.Keyword
|
||||
clojure.lang.Symbol]
|
||||
clojure.lang.Symbol
|
||||
clojure.lang.Sequential]
|
||||
:custom ~custom-map})
|
||||
|
||||
(defmacro gen-class-map []
|
||||
|
|
|
|||
270
test-resources/lib_tests/honeysql/core_test.cljc
Normal file
270
test-resources/lib_tests/honeysql/core_test.cljc
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
(ns honeysql.core-test
|
||||
(:refer-clojure :exclude [format update])
|
||||
(:require [#?@(:clj [clojure.test :refer]
|
||||
:cljs [cljs.test :refer-macros]) [deftest testing is]]
|
||||
[honeysql.core :as sql]
|
||||
[honeysql.helpers :refer [select modifiers from join left-join
|
||||
right-join full-join cross-join
|
||||
where group having
|
||||
order-by limit offset values columns
|
||||
insert-into with merge-where]]
|
||||
honeysql.format-test))
|
||||
|
||||
;; TODO: more tests
|
||||
|
||||
(deftest test-select
|
||||
(let [m1 (-> (with [:cte (-> (select :*)
|
||||
(from :example)
|
||||
(where [:= :example-column 0]))])
|
||||
(select :f.* :b.baz :c.quux [:b.bla :bla-bla]
|
||||
:%now (sql/raw "@x := 10"))
|
||||
;;(un-select :c.quux)
|
||||
(modifiers :distinct)
|
||||
(from [:foo :f] [:baz :b])
|
||||
(join :draq [:= :f.b :draq.x])
|
||||
(left-join [:clod :c] [:= :f.a :c.d])
|
||||
(right-join :bock [:= :bock.z :c.e])
|
||||
(full-join :beck [:= :beck.x :c.y])
|
||||
(where [:or
|
||||
[:and [:= :f.a "bort"] [:not= :b.baz :?param1]]
|
||||
[:< 1 2 3]
|
||||
[:in :f.e [1 (sql/param :param2) 3]]
|
||||
[:between :f.e 10 20]])
|
||||
;;(merge-where [:not= nil :b.bla])
|
||||
(group :f.a)
|
||||
(having [:< 0 :f.e])
|
||||
(order-by [:b.baz :desc] :c.quux [:f.a :nulls-first])
|
||||
(limit 50)
|
||||
(offset 10))
|
||||
m2 {:with [[:cte {:select [:*]
|
||||
:from [:example]
|
||||
:where [:= :example-column 0]}]]
|
||||
:select [:f.* :b.baz :c.quux [:b.bla :bla-bla]
|
||||
:%now (sql/raw "@x := 10")]
|
||||
;;:un-select :c.quux
|
||||
:modifiers :distinct
|
||||
:from [[:foo :f] [:baz :b]]
|
||||
:join [:draq [:= :f.b :draq.x]]
|
||||
:left-join [[:clod :c] [:= :f.a :c.d]]
|
||||
:right-join [:bock [:= :bock.z :c.e]]
|
||||
:full-join [:beck [:= :beck.x :c.y]]
|
||||
:where [:or
|
||||
[:and [:= :f.a "bort"] [:not= :b.baz :?param1]]
|
||||
[:< 1 2 3]
|
||||
[:in :f.e [1 (sql/param :param2) 3]]
|
||||
[:between :f.e 10 20]]
|
||||
;;:merge-where [:not= nil :b.bla]
|
||||
:group-by :f.a
|
||||
:having [:< 0 :f.e]
|
||||
:order-by [[:b.baz :desc] :c.quux [:f.a :nulls-first]]
|
||||
:limit 50
|
||||
:offset 10}
|
||||
m3 (sql/build m2)
|
||||
m4 (apply sql/build (apply concat m2))]
|
||||
(testing "Various construction methods are consistent"
|
||||
(is (= m1 m3 m4)))
|
||||
(testing "SQL data formats correctly"
|
||||
(is (= ["WITH cte AS (SELECT * FROM example WHERE example_column = ?) SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10 FROM foo f, baz b INNER JOIN draq ON f.b = draq.x LEFT JOIN clod c ON f.a = c.d RIGHT JOIN bock ON bock.z = c.e FULL JOIN beck ON beck.x = c.y WHERE ((f.a = ? AND b.baz <> ?) OR (? < ? AND ? < ?) OR (f.e in (?, ?, ?)) OR f.e BETWEEN ? AND ?) GROUP BY f.a HAVING ? < f.e ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST LIMIT ? OFFSET ? "
|
||||
0 "bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
|
||||
(sql/format m1 {:param1 "gabba" :param2 2}))))
|
||||
#?(:clj (testing "SQL data prints and reads correctly"
|
||||
(is (= m1 (read-string (pr-str m1))))))
|
||||
(testing "SQL data formats correctly with alternate param naming"
|
||||
(is (= (sql/format m1 :params {:param1 "gabba" :param2 2} :parameterizer :postgresql)
|
||||
["WITH cte AS (SELECT * FROM example WHERE example_column = $1) SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10 FROM foo f, baz b INNER JOIN draq ON f.b = draq.x LEFT JOIN clod c ON f.a = c.d RIGHT JOIN bock ON bock.z = c.e FULL JOIN beck ON beck.x = c.y WHERE ((f.a = $2 AND b.baz <> $3) OR ($4 < $5 AND $6 < $7) OR (f.e in ($8, $9, $10)) OR f.e BETWEEN $11 AND $12) GROUP BY f.a HAVING $13 < f.e ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST LIMIT $14 OFFSET $15 "
|
||||
0 "bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10])))
|
||||
(testing "Locking"
|
||||
(is (= ["WITH cte AS (SELECT * FROM example WHERE example_column = ?) SELECT DISTINCT f.*, b.baz, c.quux, b.bla AS bla_bla, now(), @x := 10 FROM foo f, baz b INNER JOIN draq ON f.b = draq.x LEFT JOIN clod c ON f.a = c.d RIGHT JOIN bock ON bock.z = c.e FULL JOIN beck ON beck.x = c.y WHERE ((f.a = ? AND b.baz <> ?) OR (? < ? AND ? < ?) OR (f.e in (?, ?, ?)) OR f.e BETWEEN ? AND ?) GROUP BY f.a HAVING ? < f.e ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST LIMIT ? OFFSET ? FOR UPDATE "
|
||||
0 "bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
|
||||
(sql/format (assoc m1 :lock {:mode :update})
|
||||
{:param1 "gabba" :param2 2}))))))
|
||||
|
||||
(deftest test-cast
|
||||
(is (= ["SELECT foo, CAST(bar AS integer)"]
|
||||
(sql/format {:select [:foo (sql/call :cast :bar :integer)]})))
|
||||
(is (= ["SELECT foo, CAST(bar AS integer)"]
|
||||
(sql/format {:select [:foo (sql/call :cast :bar 'integer)]}))))
|
||||
|
||||
(deftest test-value
|
||||
(is (= ["INSERT INTO foo (bar) VALUES (?)" {:baz "my-val"}]
|
||||
(->
|
||||
(insert-into :foo)
|
||||
(columns :bar)
|
||||
(values [[(honeysql.format/value {:baz "my-val"})]])
|
||||
sql/format)))
|
||||
(is (= ["INSERT INTO foo (a, b, c) VALUES (?, ?, ?), (?, ?, ?)"
|
||||
"a" "b" "c" "a" "b" "c"]
|
||||
(-> (insert-into :foo)
|
||||
(values [(array-map :a "a" :b "b" :c "c")
|
||||
(hash-map :a "a" :b "b" :c "c")])
|
||||
sql/format))))
|
||||
|
||||
(deftest test-operators
|
||||
(testing "="
|
||||
(testing "with nil"
|
||||
(is (= ["SELECT * FROM customers WHERE name IS NULL"]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:= :name nil]})))
|
||||
(is (= ["SELECT * FROM customers WHERE name = ?" nil]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:= :name :?name]}
|
||||
{:name nil})))))
|
||||
(testing "in"
|
||||
(doseq [[cname coll] [[:vector []] [:set #{}] [:list '()]]]
|
||||
(testing (str "with values from a " (name cname))
|
||||
(let [values (conj coll 1)]
|
||||
(is (= ["SELECT * FROM customers WHERE (id in (?))" 1]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id values]})))
|
||||
(is (= ["SELECT * FROM customers WHERE (id in (?))" 1]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id :?ids]}
|
||||
{:ids values}))))))
|
||||
(testing "with more than one integer"
|
||||
(let [values [1 2]]
|
||||
(is (= ["SELECT * FROM customers WHERE (id in (?, ?))" 1 2]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id values]})))
|
||||
(is (= ["SELECT * FROM customers WHERE (id in (?, ?))" 1 2]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id :?ids]}
|
||||
{:ids values})))))
|
||||
(testing "with more than one string"
|
||||
(let [values ["1" "2"]]
|
||||
(is (= ["SELECT * FROM customers WHERE (id in (?, ?))" "1" "2"]
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id values]})
|
||||
(sql/format {:select [:*]
|
||||
:from [:customers]
|
||||
:where [:in :id :?ids]}
|
||||
{:ids values})))))))
|
||||
|
||||
(deftest test-case
|
||||
(is (= ["SELECT CASE WHEN foo < ? THEN ? WHEN (foo > ? AND (foo mod ?) = ?) THEN (foo / ?) ELSE ? END FROM bar"
|
||||
0 -1 0 2 0 2 0]
|
||||
(sql/format
|
||||
{:select [(sql/call
|
||||
:case
|
||||
[:< :foo 0] -1
|
||||
[:and [:> :foo 0] [:= (sql/call :mod :foo 2) 0]] (sql/call :/ :foo 2)
|
||||
:else 0)]
|
||||
:from [:bar]})))
|
||||
(let [param1 1
|
||||
param2 2
|
||||
param3 "three"]
|
||||
(is (= ["SELECT CASE WHEN foo = ? THEN ? WHEN foo = bar THEN ? WHEN bar = ? THEN (bar * ?) ELSE ? END FROM baz"
|
||||
param1 0 param2 0 param3 "param4"]
|
||||
(sql/format
|
||||
{:select [(sql/call
|
||||
:case
|
||||
[:= :foo :?param1] 0
|
||||
[:= :foo :bar] (sql/param :param2)
|
||||
[:= :bar 0] (sql/call :* :bar :?param3)
|
||||
:else "param4")]
|
||||
:from [:baz]}
|
||||
{:param1 param1
|
||||
:param2 param2
|
||||
:param3 param3})))))
|
||||
|
||||
(deftest test-raw
|
||||
(is (= ["SELECT 1 + 1 FROM foo"]
|
||||
(-> (select (sql/raw "1 + 1"))
|
||||
(from :foo)
|
||||
sql/format))))
|
||||
|
||||
(deftest test-call
|
||||
(is (= ["SELECT min(?) FROM ?" "time" "table"]
|
||||
(-> (select (sql/call :min "time"))
|
||||
(from "table")
|
||||
sql/format))))
|
||||
|
||||
(deftest join-test
|
||||
(testing "nil join"
|
||||
(is (= ["SELECT * FROM foo INNER JOIN x ON foo.id = x.id INNER JOIN y"]
|
||||
(-> (select :*)
|
||||
(from :foo)
|
||||
(join :x [:= :foo.id :x.id] :y nil)
|
||||
sql/format)))))
|
||||
|
||||
(deftest join-using-test
|
||||
(testing "nil join"
|
||||
(is (= ["SELECT * FROM foo INNER JOIN x USING (id) INNER JOIN y USING (foo, bar)"]
|
||||
(-> (select :*)
|
||||
(from :foo)
|
||||
(join :x [:using :id] :y [:using :foo :bar])
|
||||
sql/format)))))
|
||||
|
||||
(deftest inline-test
|
||||
(is (= ["SELECT * FROM foo WHERE id = 5"]
|
||||
(-> (select :*)
|
||||
(from :foo)
|
||||
(where [:= :id (sql/inline 5)])
|
||||
sql/format)))
|
||||
;; testing for = NULL always fails in SQL -- this test is just to show
|
||||
;; that an #inline nil should render as NULL (so make sure you only use
|
||||
;; it in contexts where a literal NULL is acceptable!)
|
||||
(is (= ["SELECT * FROM foo WHERE id = NULL"]
|
||||
(-> (select :*)
|
||||
(from :foo)
|
||||
(where [:= :id (sql/inline nil)])
|
||||
sql/format))))
|
||||
|
||||
(deftest merge-where-no-params-test
|
||||
(testing "merge-where called with just the map as parameter - see #228"
|
||||
(let [sqlmap (-> (select :*)
|
||||
(from :table)
|
||||
(where [:= :foo :bar]))]
|
||||
(is (= ["SELECT * FROM table WHERE foo = bar"]
|
||||
(sql/format (apply merge-where sqlmap [])))))))
|
||||
|
||||
(deftest merge-where-test
|
||||
(is (= ["SELECT * FROM table WHERE (foo = bar AND quuz = xyzzy)"]
|
||||
(-> (select :*)
|
||||
(from :table)
|
||||
(where [:= :foo :bar] [:= :quuz :xyzzy])
|
||||
sql/format)))
|
||||
(is (= ["SELECT * FROM table WHERE (foo = bar AND quuz = xyzzy)"]
|
||||
(-> (select :*)
|
||||
(from :table)
|
||||
(where [:= :foo :bar])
|
||||
(merge-where [:= :quuz :xyzzy])
|
||||
sql/format))))
|
||||
|
||||
(deftest where-nil-params-test
|
||||
(testing "where called with nil parameters - see #246"
|
||||
(is (= ["SELECT * FROM table WHERE (foo = bar AND quuz = xyzzy)"]
|
||||
(-> (select :*)
|
||||
(from :table)
|
||||
(where nil [:= :foo :bar] nil [:= :quuz :xyzzy] nil)
|
||||
sql/format)))
|
||||
(is (= ["SELECT * FROM table"]
|
||||
(-> (select :*)
|
||||
(from :table)
|
||||
(where)
|
||||
sql/format)))
|
||||
(is (= ["SELECT * FROM table"]
|
||||
(-> (select :*)
|
||||
(from :table)
|
||||
(where nil nil nil nil)
|
||||
sql/format)))))
|
||||
|
||||
(deftest cross-join-test
|
||||
(is (= ["SELECT * FROM foo CROSS JOIN bar"]
|
||||
(-> (select :*)
|
||||
(from :foo)
|
||||
(cross-join :bar)
|
||||
sql/format)))
|
||||
(is (= ["SELECT * FROM foo f CROSS JOIN bar b"]
|
||||
(-> (select :*)
|
||||
(from [:foo :f])
|
||||
(cross-join [:bar :b])
|
||||
sql/format))))
|
||||
|
||||
#?(:cljs (cljs.test/run-all-tests))
|
||||
322
test-resources/lib_tests/honeysql/format_test.cljc
Normal file
322
test-resources/lib_tests/honeysql/format_test.cljc
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
(ns honeysql.format-test
|
||||
(:refer-clojure :exclude [format])
|
||||
(:require [#?@(:clj [clojure.test :refer]
|
||||
:cljs [cljs.test :refer-macros]) [deftest testing is are]]
|
||||
honeysql.core
|
||||
[honeysql.types :as sql]
|
||||
[honeysql.format :refer
|
||||
[*allow-dashed-names?* *allow-namespaced-names?*
|
||||
*namespace-as-table?*
|
||||
quote-identifier format-clause format
|
||||
parameterize]]))
|
||||
|
||||
(deftest test-quote
|
||||
(are
|
||||
[qx res]
|
||||
(= (apply quote-identifier "foo.bar.baz" qx) res)
|
||||
[] "foo.bar.baz"
|
||||
[:style :mysql] "`foo`.`bar`.`baz`"
|
||||
[:style :mysql :split false] "`foo.bar.baz`")
|
||||
(are
|
||||
[x res]
|
||||
(= (quote-identifier x) res)
|
||||
3 "3"
|
||||
'foo "foo"
|
||||
:foo-bar "foo_bar")
|
||||
(is (= (quote-identifier "*" :style :ansi) "*"))
|
||||
(is (= (quote-identifier "foo\"bar" :style :ansi) "\"foo\"\"bar\""))
|
||||
(is (= (quote-identifier "foo\"bar" :style :oracle) "\"foo\"\"bar\""))
|
||||
(is (= (quote-identifier "foo`bar" :style :mysql) "`foo``bar`"))
|
||||
(is (= (quote-identifier "foo]bar" :style :sqlserver) "[foo]]bar]")))
|
||||
|
||||
(deftest test-dashed-quote
|
||||
(binding [*allow-dashed-names?* true]
|
||||
(is (= (quote-identifier :foo-bar) "foo-bar"))
|
||||
(is (= (quote-identifier :foo-bar :style :ansi) "\"foo-bar\""))
|
||||
(is (= (quote-identifier :foo-bar.moo-bar :style :ansi)
|
||||
"\"foo-bar\".\"moo-bar\""))))
|
||||
|
||||
(deftest test-namespaced-identifier
|
||||
(is (= (quote-identifier :foo/bar) "bar"))
|
||||
(is (= (quote-identifier :foo/bar :style :ansi) "\"bar\""))
|
||||
(binding [*namespace-as-table?* true]
|
||||
(is (= (quote-identifier :foo/bar) "foo.bar"))
|
||||
(is (= (quote-identifier :foo/bar :style :ansi) "\"foo\".\"bar\""))
|
||||
(is (= (quote-identifier :foo/bar :style :ansi :split false) "\"foo.bar\"")))
|
||||
(binding [*allow-namespaced-names?* true]
|
||||
(is (= (quote-identifier :foo/bar) "foo/bar"))
|
||||
(is (= (quote-identifier :foo/bar :style :ansi) "\"foo/bar\""))))
|
||||
|
||||
(deftest alias-splitting
|
||||
(is (= ["SELECT `aa`.`c` AS `a.c`, `bb`.`c` AS `b.c`, `cc`.`c` AS `c.c`"]
|
||||
(format {:select [[:aa.c "a.c"]
|
||||
[:bb.c :b.c]
|
||||
[:cc.c 'c.c]]}
|
||||
:quoting :mysql))
|
||||
"aliases containing \".\" are quoted as necessary but not split"))
|
||||
|
||||
(deftest values-alias
|
||||
(is (= ["SELECT vals.a FROM (VALUES (?, ?, ?)) vals (a, b, c)" 1 2 3]
|
||||
(format {:select [:vals.a]
|
||||
:from [[{:values [[1 2 3]]} [:vals {:columns [:a :b :c]}]]]}))))
|
||||
(deftest test-cte
|
||||
(is (= (format-clause
|
||||
(first {:with [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
||||
"WITH query AS SELECT foo FROM bar"))
|
||||
(is (= (format-clause
|
||||
(first {:with-recursive [[:query {:select [:foo] :from [:bar]}]]}) nil)
|
||||
"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 6]]}]]
|
||||
:select [:*]
|
||||
:from [:static]})
|
||||
["WITH static (a, b, c) AS (VALUES (?, ?, ?), (?, ?, ?)) SELECT * FROM static" 1 2 3 4 5 6])))
|
||||
|
||||
(deftest insert-into
|
||||
(is (= (format-clause (first {:insert-into :foo}) nil)
|
||||
"INSERT INTO foo"))
|
||||
(is (= (format-clause (first {:insert-into [:foo {:select [:bar] :from [:baz]}]}) nil)
|
||||
"INSERT INTO foo SELECT bar FROM baz"))
|
||||
(is (= (format-clause (first {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]}) nil)
|
||||
"INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz"))
|
||||
(is (= (format {:insert-into [[:foo [:a :b :c]] {:select [:d :e :f] :from [:baz]}]})
|
||||
["INSERT INTO foo (a, b, c) SELECT d, e, f FROM baz"])))
|
||||
|
||||
(deftest insert-into-namespaced
|
||||
;; un-namespaced: works as expected:
|
||||
(is (= (format {:insert-into :foo :values [{:foo/id 1}]})
|
||||
["INSERT INTO foo (id) VALUES (?)" 1]))
|
||||
(is (= (format {:insert-into :foo :columns [:foo/id] :values [[2]]})
|
||||
["INSERT INTO foo (id) VALUES (?)" 2]))
|
||||
(is (= (format {:insert-into :foo :values [{:foo/id 1}]}
|
||||
:namespace-as-table? true)
|
||||
["INSERT INTO foo (id) VALUES (?)" 1]))
|
||||
(is (= (format {:insert-into :foo :columns [:foo/id] :values [[2]]}
|
||||
:namespace-as-table? true)
|
||||
["INSERT INTO foo (id) VALUES (?)" 2])))
|
||||
|
||||
(deftest exists-test
|
||||
(is (= (format {:exists {:select [:a] :from [:foo]}})
|
||||
["EXISTS (SELECT a FROM foo)"]))
|
||||
(is (= (format {:select [:id]
|
||||
:from [:foo]
|
||||
:where [:exists {:select [1]
|
||||
:from [:bar]
|
||||
:where :deleted}]})
|
||||
["SELECT id FROM foo WHERE EXISTS (SELECT ? FROM bar WHERE deleted)" 1])))
|
||||
|
||||
(deftest array-test
|
||||
(is (= (format {:insert-into :foo
|
||||
:columns [:baz]
|
||||
:values [[(sql/array [1 2 3 4])]]})
|
||||
["INSERT INTO foo (baz) VALUES (ARRAY[?, ?, ?, ?])" 1 2 3 4]))
|
||||
(is (= (format {:insert-into :foo
|
||||
:columns [:baz]
|
||||
:values [[(sql/array ["one" "two" "three"])]]})
|
||||
["INSERT INTO foo (baz) VALUES (ARRAY[?, ?, ?])" "one" "two" "three"])))
|
||||
|
||||
(deftest union-test
|
||||
;; UNION and INTERSECT subexpressions should not be parenthesized.
|
||||
;; If you need to add more complex expressions, use a subquery like this:
|
||||
;; SELECT foo FROM bar1
|
||||
;; UNION
|
||||
;; SELECT foo FROM (SELECT foo FROM bar2 ORDER BY baz LIMIT 2)
|
||||
;; ORDER BY foo ASC
|
||||
(is (= (format {:union [{:select [:foo] :from [:bar1]}
|
||||
{:select [:foo] :from [:bar2]}]})
|
||||
["SELECT foo FROM bar1 UNION SELECT foo FROM bar2"])))
|
||||
|
||||
(deftest union-all-test
|
||||
(is (= (format {:union-all [{:select [:foo] :from [:bar1]}
|
||||
{:select [:foo] :from [:bar2]}]})
|
||||
["SELECT foo FROM bar1 UNION ALL SELECT foo FROM bar2"])))
|
||||
|
||||
(deftest intersect-test
|
||||
(is (= (format {:intersect [{:select [:foo] :from [:bar1]}
|
||||
{: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
|
||||
{:union
|
||||
[{:select [:amount :id :created_on]
|
||||
:from [:transactions]}
|
||||
{:select [:amount :id :created_on]
|
||||
:from [{:select [:amount :id :created_on]
|
||||
:from [:other_transactions]
|
||||
:order-by [[:amount :desc]]
|
||||
:limit 5}]}]
|
||||
:order-by [[:amount :asc]]})
|
||||
["SELECT amount, id, created_on FROM transactions UNION SELECT amount, id, created_on FROM (SELECT amount, id, created_on FROM other_transactions ORDER BY amount DESC LIMIT ?) ORDER BY amount ASC" 5]))))
|
||||
|
||||
(deftest compare-expressions-test
|
||||
(testing "Sequences should be fns when in value/comparison spots"
|
||||
(is (= ["SELECT foo FROM bar WHERE (col1 mod ?) = (col2 + ?)" 4 4]
|
||||
(format {:select [:foo]
|
||||
:from [:bar]
|
||||
:where [:= [:mod :col1 4] [:+ :col2 4]]}))))
|
||||
|
||||
(testing "Value context only applies to sequences in value/comparison spots"
|
||||
(let [sub {:select [:%sum.amount]
|
||||
:from [:bar]
|
||||
:where [:in :id ["id-1" "id-2"]]}]
|
||||
(is (= ["SELECT total FROM foo WHERE (SELECT sum(amount) FROM bar WHERE (id in (?, ?))) = total" "id-1" "id-2"]
|
||||
(format {:select [:total]
|
||||
:from [:foo]
|
||||
:where [:= sub :total]})))
|
||||
(is (= ["WITH t AS (SELECT sum(amount) FROM bar WHERE (id in (?, ?))) SELECT total FROM foo WHERE total = t" "id-1" "id-2"]
|
||||
(format {:with [[:t sub]]
|
||||
:select [:total]
|
||||
:from [:foo]
|
||||
:where [:= :total :t]}))))))
|
||||
|
||||
(deftest union-with-cte
|
||||
(is (= (format {:union [{:select [:foo] :from [:bar1]}
|
||||
{:select [:foo] :from [:bar2]}]
|
||||
:with [[[:bar {:columns [:spam :eggs]}]
|
||||
{:values [[1 2] [3 4] [5 6]]}]]})
|
||||
["WITH bar (spam, eggs) AS (VALUES (?, ?), (?, ?), (?, ?)) SELECT foo FROM bar1 UNION SELECT foo FROM bar2" 1 2 3 4 5 6])))
|
||||
|
||||
|
||||
(deftest union-all-with-cte
|
||||
(is (= (format {:union-all [{:select [:foo] :from [:bar1]}
|
||||
{:select [:foo] :from [:bar2]}]
|
||||
:with [[[:bar {:columns [:spam :eggs]}]
|
||||
{:values [[1 2] [3 4] [5 6]]}]]})
|
||||
["WITH bar (spam, eggs) AS (VALUES (?, ?), (?, ?), (?, ?)) SELECT foo FROM bar1 UNION ALL SELECT foo FROM bar2" 1 2 3 4 5 6])))
|
||||
|
||||
(deftest parameterizer-none
|
||||
(testing "array parameter"
|
||||
(is (= (format {:insert-into :foo
|
||||
:columns [:baz]
|
||||
:values [[(sql/array [1 2 3 4])]]}
|
||||
:parameterizer :none)
|
||||
["INSERT INTO foo (baz) VALUES (ARRAY[1, 2, 3, 4])"])))
|
||||
|
||||
(testing "union complex values"
|
||||
(is (= (format {:union [{:select [:foo] :from [:bar1]}
|
||||
{:select [:foo] :from [:bar2]}]
|
||||
:with [[[:bar {:columns [:spam :eggs]}]
|
||||
{:values [[1 2] [3 4] [5 6]]}]]}
|
||||
:parameterizer :none)
|
||||
["WITH bar (spam, eggs) AS (VALUES (1, 2), (3, 4), (5, 6)) SELECT foo FROM bar1 UNION SELECT foo FROM bar2"]))))
|
||||
|
||||
(deftest where-and
|
||||
(testing "should ignore a nil predicate"
|
||||
(is (= (format {:where [:and [:= :foo "foo"] [:= :bar "bar"] nil]}
|
||||
:parameterizer :postgresql)
|
||||
["WHERE (foo = $1 AND bar = $2)" "foo" "bar"]))))
|
||||
|
||||
|
||||
(defmethod parameterize :single-quote [_ value pname] (str \' value \'))
|
||||
(defmethod parameterize :mysql-fill [_ value pname] "?")
|
||||
|
||||
(deftest customized-parameterizer
|
||||
(testing "should fill param with single quote"
|
||||
(is (= (format {:where [:and [:= :foo "foo"] [:= :bar "bar"] nil]}
|
||||
:parameterizer :single-quote)
|
||||
["WHERE (foo = 'foo' AND bar = 'bar')" "foo" "bar"])))
|
||||
(testing "should fill param with ?"
|
||||
(is (= (format {:where [:and [:= :foo "foo"] [:= :bar "bar"] nil]}
|
||||
:parameterizer :mysql-fill)
|
||||
["WHERE (foo = ? AND bar = ?)" "foo" "bar"]))))
|
||||
|
||||
|
||||
(deftest set-before-from ; issue 235
|
||||
(is (=
|
||||
["UPDATE `films` `f` SET `kind` = `c`.`test` FROM (SELECT `b`.`test` FROM `bar` `b` WHERE `b`.`id` = ?) `c` WHERE `f`.`kind` = ?" 1 "drama"]
|
||||
(->
|
||||
{:update [:films :f]
|
||||
:set0 {:kind :c.test}
|
||||
:from [[{:select [:b.test]
|
||||
:from [[:bar :b]]
|
||||
:where [:= :b.id 1]} :c]]
|
||||
:where [:= :f.kind "drama"]}
|
||||
(format :quoting :mysql)))))
|
||||
|
||||
(deftest set-after-join
|
||||
(is (=
|
||||
["UPDATE `foo` INNER JOIN `bar` ON `bar`.`id` = `foo`.`bar_id` SET `a` = ? WHERE `bar`.`b` = ?" 1 42]
|
||||
(->
|
||||
{:update :foo
|
||||
:join [:bar [:= :bar.id :foo.bar_id]]
|
||||
:set {:a 1}
|
||||
:where [:= :bar.b 42]}
|
||||
(format :quoting :mysql))))
|
||||
(is (=
|
||||
["UPDATE `foo` INNER JOIN `bar` ON `bar`.`id` = `foo`.`bar_id` SET `a` = ? WHERE `bar`.`b` = ?" 1 42]
|
||||
(->
|
||||
{:update :foo
|
||||
:join [:bar [:= :bar.id :foo.bar_id]]
|
||||
:set1 {:a 1}
|
||||
:where [:= :bar.b 42]}
|
||||
(format :quoting :mysql)))))
|
||||
|
||||
(deftest delete-from-test
|
||||
(is (= ["DELETE FROM `foo` WHERE `foo`.`id` = ?" 42]
|
||||
(-> {:delete-from :foo
|
||||
:where [:= :foo.id 42]}
|
||||
(format :quoting :mysql)))))
|
||||
|
||||
(deftest delete-test
|
||||
(is (= ["DELETE `t1`, `t2` FROM `table1` `t1` INNER JOIN `table2` `t2` ON `t1`.`fk` = `t2`.`id` WHERE `t1`.`bar` = ?" 42]
|
||||
(-> {:delete [:t1 :t2]
|
||||
:from [[:table1 :t1]]
|
||||
:join [[:table2 :t2] [:= :t1.fk :t2.id]]
|
||||
:where [:= :t1.bar 42]}
|
||||
(format :quoting :mysql)))))
|
||||
|
||||
(deftest truncate-test
|
||||
(is (= ["TRUNCATE `foo`"]
|
||||
(-> {:truncate :foo}
|
||||
(format :quoting :mysql)))))
|
||||
|
||||
(deftest inlined-values-are-stringified-correctly
|
||||
(is (= ["SELECT foo, bar, NULL"]
|
||||
(format {:select [(honeysql.core/inline "foo")
|
||||
(honeysql.core/inline :bar)
|
||||
(honeysql.core/inline nil)]}))))
|
||||
|
||||
;; Make sure if Locale is Turkish we're not generating queries like İNNER JOIN (dot over the I) because
|
||||
;; `string/upper-case` is converting things to upper-case using the default Locale. Generated query should be the same
|
||||
;; regardless of system Locale. See #236
|
||||
#?(:clj
|
||||
(deftest statements-generated-correctly-with-turkish-locale
|
||||
(let [format-with-locale (fn [^String language-tag]
|
||||
(let [original-locale (java.util.Locale/getDefault)]
|
||||
(try
|
||||
(java.util.Locale/setDefault (java.util.Locale/forLanguageTag language-tag))
|
||||
(format {:select [:t2.name]
|
||||
:from [[:table1 :t1]]
|
||||
:join [[:table2 :t2] [:= :t1.fk :t2.id]]
|
||||
:where [:= :t1.id 1]})
|
||||
(finally
|
||||
(java.util.Locale/setDefault original-locale)))))]
|
||||
(is (= (format-with-locale "en")
|
||||
(format-with-locale "tr"))))))
|
||||
|
||||
(deftest join-on-true-253
|
||||
;; used to work on honeysql 0.9.2; broke in 0.9.3
|
||||
(is (= ["SELECT foo FROM bar INNER JOIN table t ON TRUE"]
|
||||
(format {:select [:foo]
|
||||
:from [:bar]
|
||||
:join [[:table :t] true]}))))
|
||||
|
||||
(deftest cross-join-test
|
||||
(is (= ["SELECT * FROM foo CROSS JOIN bar"]
|
||||
(format {:select [:*]
|
||||
:from [:foo]
|
||||
:cross-join [:bar]})))
|
||||
(is (= ["SELECT * FROM foo f CROSS JOIN bar b"]
|
||||
(format {:select [:*]
|
||||
:from [[:foo :f]]
|
||||
:cross-join [[:bar :b]]}))))
|
||||
Loading…
Reference in a new issue