MichaelBlume's first pass at ClojureScript support.
This commit is contained in:
parent
5603e9df9f
commit
7c8cdf241d
6 changed files with 149 additions and 100 deletions
|
|
@ -3,15 +3,15 @@
|
|||
(:require [honeysql.format :as format]
|
||||
[honeysql.types :as types]
|
||||
[honeysql.helpers :refer [build-clause]]
|
||||
[honeysql.util :refer [defalias]]
|
||||
#?(:clj [honeysql.util :refer [defalias]])
|
||||
[clojure.string :as string]))
|
||||
|
||||
(defalias call types/call)
|
||||
(defalias raw types/raw)
|
||||
(defalias param types/param)
|
||||
(defalias format format/format)
|
||||
(defalias format-predicate format/format-predicate)
|
||||
(defalias quote-identifier format/quote-identifier)
|
||||
(#?(:clj defalias :cljs def) call types/call)
|
||||
(#?(:clj defalias :cljs def) raw types/raw)
|
||||
(#?(:clj defalias :cljs def) param types/param)
|
||||
(#?(:clj defalias :cljs def) format format/format)
|
||||
(#?(:clj defalias :cljs def) format-predicate format/format-predicate)
|
||||
(#?(:clj defalias :cljs def) quote-identifier format/quote-identifier)
|
||||
|
||||
(defn qualify
|
||||
"Takes one or more keyword or string qualifers and name. Returns
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
(ns honeysql.format
|
||||
(:refer-clojure :exclude [format])
|
||||
(:require [honeysql.types :refer [call raw param param-name]]
|
||||
(:require [honeysql.types :refer [call raw param param-name
|
||||
#?@(:cljs [SqlCall SqlRaw SqlParam SqlArray])]]
|
||||
[clojure.string :as string])
|
||||
(:import [honeysql.types SqlCall SqlRaw SqlParam SqlArray]))
|
||||
#?(:clj (:import [honeysql.types SqlCall SqlRaw SqlParam SqlArray])))
|
||||
|
||||
;;(set! *warn-on-reflection* true)
|
||||
|
||||
|
|
@ -203,7 +204,10 @@
|
|||
|
||||
(defn sort-clauses [clauses]
|
||||
(let [m @clause-store]
|
||||
(sort-by #(m % Long/MAX_VALUE) clauses)))
|
||||
(sort-by
|
||||
(fn [c]
|
||||
(m c #?(:clj Long/MAX_VALUE :cljs js/Number.MAX_VALUE)))
|
||||
clauses)))
|
||||
|
||||
(defn format
|
||||
"Takes a SQL map and optional input parameters and returns a vector
|
||||
|
|
@ -243,11 +247,22 @@
|
|||
(defprotocol Parameterizable
|
||||
(to-params [value pname]))
|
||||
|
||||
(defn to-params-seq [s pname]
|
||||
(paren-wrap (comma-join (mapv #(to-params % pname) s))))
|
||||
|
||||
(defn to-params-default [value pname]
|
||||
(swap! *params* conj value)
|
||||
(swap! *param-names* conj pname)
|
||||
(*parameterizer*))
|
||||
|
||||
(extend-protocol Parameterizable
|
||||
clojure.lang.Sequential
|
||||
(to-params [value pname]
|
||||
(paren-wrap (comma-join (mapv #(to-params % pname) value))))
|
||||
clojure.lang.IPersistentSet
|
||||
#?@(:clj
|
||||
[clojure.lang.Sequential
|
||||
|
||||
(to-params [value pname]
|
||||
(to-params-seq value pname))])
|
||||
#?(:clj clojure.lang.IPersistentSet
|
||||
:cljs cljs.core/PersistentHashSet)
|
||||
(to-params [value pname]
|
||||
(to-params (seq value) pname))
|
||||
nil
|
||||
|
|
@ -255,11 +270,14 @@
|
|||
(swap! *params* conj value)
|
||||
(swap! *param-names* conj pname)
|
||||
(*parameterizer*))
|
||||
java.lang.Object
|
||||
#?(:clj Object :cljs default)
|
||||
(to-params [value pname]
|
||||
(swap! *params* conj value)
|
||||
(swap! *param-names* conj pname)
|
||||
(*parameterizer*)))
|
||||
#?(:clj
|
||||
(to-params-default value pname)
|
||||
:cljs
|
||||
(if (sequential? value)
|
||||
(to-params-seq value pname)
|
||||
(to-params-default value pname)))))
|
||||
|
||||
(defn add-param [pname pval]
|
||||
(to-params pval pname))
|
||||
|
|
@ -279,8 +297,34 @@
|
|||
|
||||
(declare -format-clause)
|
||||
|
||||
(defn map->sql [m]
|
||||
(let [clause-ops (sort-clauses (keys m))
|
||||
sql-str (binding [*subquery?* true
|
||||
*fn-context?* false]
|
||||
(space-join
|
||||
(map (comp #(-format-clause % m) #(find m %))
|
||||
clause-ops)))]
|
||||
(if *subquery?*
|
||||
(paren-wrap sql-str)
|
||||
sql-str)))
|
||||
|
||||
(defn seq->sql [x]
|
||||
(if *fn-context?*
|
||||
;; list argument in fn call
|
||||
(paren-wrap (comma-join (map to-sql x)))
|
||||
;; alias
|
||||
(str (to-sql (first x))
|
||||
; Omit AS in FROM, JOIN, etc. - Oracle doesn't allow it
|
||||
(if (= :select *clause*)
|
||||
" AS "
|
||||
" ")
|
||||
(if (string? (second x))
|
||||
(quote-identifier (second x))
|
||||
(to-sql (second x))))))
|
||||
|
||||
(extend-protocol ToSql
|
||||
clojure.lang.Keyword
|
||||
#?(:clj clojure.lang.Keyword
|
||||
:cljs cljs.core/Keyword)
|
||||
(to-sql [x]
|
||||
(let [s (name x)]
|
||||
(case (.charAt s 0)
|
||||
|
|
@ -288,25 +332,15 @@
|
|||
(to-sql (apply call (map keyword call-args))))
|
||||
\? (to-sql (param (keyword (subs s 1))))
|
||||
(quote-identifier x))))
|
||||
clojure.lang.Symbol
|
||||
#?(:clj clojure.lang.Symbol
|
||||
:cljs cljs.core/Symbol)
|
||||
(to-sql [x] (quote-identifier x))
|
||||
java.lang.Boolean
|
||||
#?(:clj java.lang.Boolean :cljs boolean)
|
||||
(to-sql [x]
|
||||
(if x "TRUE" "FALSE"))
|
||||
clojure.lang.Sequential
|
||||
(to-sql [x]
|
||||
(if *fn-context?*
|
||||
;; list argument in fn call
|
||||
(paren-wrap (comma-join (map to-sql x)))
|
||||
;; alias
|
||||
(str (to-sql (first x))
|
||||
; Omit AS in FROM, JOIN, etc. - Oracle doesn't allow it
|
||||
(if (= :select *clause*)
|
||||
" AS "
|
||||
" ")
|
||||
(if (string? (second x))
|
||||
(quote-identifier (second x))
|
||||
(to-sql (second x))))))
|
||||
#?@(:clj
|
||||
[clojure.lang.Sequential
|
||||
(to-sql [x] (seq->sql x))])
|
||||
SqlCall
|
||||
(to-sql [x]
|
||||
(binding [*fn-context?* true]
|
||||
|
|
@ -315,18 +349,12 @@
|
|||
(apply fn-handler fn-name (.-args x)))))
|
||||
SqlRaw
|
||||
(to-sql [x] (.-s x))
|
||||
clojure.lang.IPersistentMap
|
||||
#?(:clj clojure.lang.IPersistentMap
|
||||
:cljs cljs.core/PersistentArrayMap)
|
||||
(to-sql [x]
|
||||
(let [clause-ops (sort-clauses (keys x))
|
||||
sql-str (binding [*subquery?* true
|
||||
*fn-context?* false]
|
||||
(space-join
|
||||
(map (comp #(-format-clause % x) #(find x %))
|
||||
clause-ops)))]
|
||||
(if *subquery?*
|
||||
(paren-wrap sql-str)
|
||||
sql-str)))
|
||||
clojure.lang.IPersistentSet
|
||||
(map->sql x))
|
||||
#?(:clj clojure.lang.IPersistentSet
|
||||
:cljs cljs.core/PersistentHashSet)
|
||||
(to-sql [x]
|
||||
(to-sql (seq x)))
|
||||
nil
|
||||
|
|
@ -342,9 +370,15 @@
|
|||
SqlArray
|
||||
(to-sql [x]
|
||||
(str "ARRAY[" (comma-join (map to-sql (.-values x))) "]"))
|
||||
Object
|
||||
#?(:clj Object :cljs default)
|
||||
(to-sql [x]
|
||||
(add-anon-param x)))
|
||||
#?(:clj (add-anon-param x)
|
||||
:cljs (if (sequential? x)
|
||||
(seq->sql x)
|
||||
(add-anon-param x))))
|
||||
#?@(:cljs
|
||||
[cljs.core/PersistentHashMap
|
||||
(to-sql [x] (map->sql x))]))
|
||||
|
||||
(defn sqlable? [x]
|
||||
(satisfies? ToSql x))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
(ns honeysql.helpers
|
||||
(:refer-clojure :exclude [update]))
|
||||
(:refer-clojure :exclude [update])
|
||||
#?(:cljs (:require-macros [honeysql.helpers :refer [defhelper]])))
|
||||
|
||||
(defmulti build-clause (fn [name & args]
|
||||
name))
|
||||
|
|
@ -12,22 +13,23 @@
|
|||
(map? m)
|
||||
(not (instance? clojure.lang.IRecord m))))
|
||||
|
||||
(defmacro defhelper [helper arglist & more]
|
||||
(let [kw (keyword (name helper))]
|
||||
`(do
|
||||
(defmethod build-clause ~kw ~(into ['_] arglist) ~@more)
|
||||
(doto (defn ~helper [& args#]
|
||||
(let [[m# args#] (if (plain-map? (first args#))
|
||||
[(first args#) (rest args#)]
|
||||
[{} args#])]
|
||||
(build-clause ~kw m# args#)))
|
||||
;; maintain the original arglist instead of getting
|
||||
;; ([& args__6880__auto__])
|
||||
(alter-meta!
|
||||
assoc
|
||||
:arglists
|
||||
'(~(into [] (rest arglist))
|
||||
~(into [(first arglist)] (rest arglist))))))))
|
||||
#?(:clj
|
||||
(defmacro defhelper [helper arglist & more]
|
||||
(let [kw (keyword (name helper))]
|
||||
`(do
|
||||
(defmethod build-clause ~kw ~(into ['_] arglist) ~@more)
|
||||
(doto (defn ~helper [& args#]
|
||||
(let [[m# args#] (if (plain-map? (first args#))
|
||||
[(first args#) (rest args#)]
|
||||
[{} args#])]
|
||||
(build-clause ~kw m# args#)))
|
||||
;; maintain the original arglist instead of getting
|
||||
;; ([& args__6880__auto__])
|
||||
(alter-meta!
|
||||
assoc
|
||||
:arglists
|
||||
'(~(into [] (rest arglist))
|
||||
~(into [(first arglist)] (rest arglist)))))))))
|
||||
|
||||
(defn collify [x]
|
||||
(if (coll? x) x [x]))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
(ns honeysql.types)
|
||||
(ns honeysql.types
|
||||
(:refer-clojure :exclude [array]))
|
||||
|
||||
(defrecord SqlCall [name args])
|
||||
|
||||
|
|
@ -9,13 +10,7 @@
|
|||
|
||||
(defn read-sql-call [form]
|
||||
;; late bind so that we get new class on REPL reset
|
||||
(apply (resolve `call) form))
|
||||
|
||||
(defmethod print-method SqlCall [^SqlCall o ^java.io.Writer w]
|
||||
(.write w (str "#sql/call " (pr-str (into [(.-name o)] (.-args o))))))
|
||||
|
||||
(defmethod print-dup SqlCall [o w]
|
||||
(print-method o w))
|
||||
(apply #?(:clj (resolve `call) :cljs call) form))
|
||||
|
||||
;;;;
|
||||
|
||||
|
|
@ -28,13 +23,7 @@
|
|||
|
||||
(defn read-sql-raw [form]
|
||||
;; late bind, as above
|
||||
((resolve `raw) form))
|
||||
|
||||
(defmethod print-method SqlRaw [^SqlRaw o ^java.io.Writer w]
|
||||
(.write w (str "#sql/raw " (pr-str (.-s o)))))
|
||||
|
||||
(defmethod print-dup SqlRaw [o w]
|
||||
(print-method o w))
|
||||
(#?(:clj (resolve `raw) :cljs raw) form))
|
||||
|
||||
;;;;
|
||||
|
||||
|
|
@ -50,13 +39,7 @@
|
|||
|
||||
(defn read-sql-param [form]
|
||||
;; late bind, as above
|
||||
((resolve `param) form))
|
||||
|
||||
(defmethod print-method SqlParam [^SqlParam o ^java.io.Writer w]
|
||||
(.write w (str "#sql/param " (pr-str (.-name o)))))
|
||||
|
||||
(defmethod print-dup SqlParam [o w]
|
||||
(print-method o w))
|
||||
(#?(:clj (resolve `param) :cljs param) form))
|
||||
|
||||
;;;;
|
||||
|
||||
|
|
@ -72,10 +55,30 @@
|
|||
|
||||
(defn read-sql-array [form]
|
||||
;; late bind, as above
|
||||
((resolve `array) form))
|
||||
(#?(:clj (resolve `array) :cljs array) form))
|
||||
|
||||
(defmethod print-method SqlArray [^SqlArray a ^java.io.Writer w]
|
||||
(.write w (str "#sql/array " (pr-str (.-values a)))))
|
||||
#?(:clj
|
||||
(do
|
||||
(defmethod print-method SqlCall [^SqlCall o ^java.io.Writer w]
|
||||
(.write w (str "#sql/call " (pr-str (into [(.-name o)] (.-args o))))))
|
||||
|
||||
(defmethod print-dup SqlArray [a w]
|
||||
(print-method a w))
|
||||
(defmethod print-dup SqlCall [o w]
|
||||
(print-method o w))
|
||||
|
||||
(defmethod print-method SqlRaw [^SqlRaw o ^java.io.Writer w]
|
||||
(.write w (str "#sql/raw " (pr-str (.s o)))))
|
||||
|
||||
(defmethod print-dup SqlRaw [o w]
|
||||
(print-method o w))
|
||||
|
||||
(defmethod print-method SqlParam [^SqlParam o ^java.io.Writer w]
|
||||
(.write w (str "#sql/param " (pr-str (.name o)))))
|
||||
|
||||
(defmethod print-dup SqlParam [o w]
|
||||
(print-method o w))
|
||||
|
||||
(defmethod print-method SqlArray [^SqlArray a ^java.io.Writer w]
|
||||
(.write w (str "#sql/array " (pr-str (.values a)))))
|
||||
|
||||
(defmethod print-dup SqlArray [a w]
|
||||
(print-method a w))))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
(ns honeysql.core-test
|
||||
(:refer-clojure :exclude [format update])
|
||||
(:require [clojure.test :refer [deftest testing is]]
|
||||
(:require [#?@(:clj [clojure.test :refer]
|
||||
:cljs [cljs.test :refer-macros]) [deftest testing is]]
|
||||
[honeysql.core :as sql]
|
||||
[honeysql.helpers :refer :all]))
|
||||
[honeysql.helpers :refer [select modifiers from join left-join
|
||||
right-join full-join where group having
|
||||
order-by limit offset values columns
|
||||
insert-into]]
|
||||
honeysql.format-test))
|
||||
|
||||
;; TODO: more tests
|
||||
|
||||
|
|
@ -55,8 +60,8 @@
|
|||
(is (= ["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 ? "
|
||||
"bort" "gabba" 1 2 2 3 1 2 3 10 20 0 50 10]
|
||||
(sql/format m1 {:param1 "gabba" :param2 2}))))
|
||||
(testing "SQL data prints and reads correctly"
|
||||
(is (= m1 (read-string (pr-str m1)))))
|
||||
#?(: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)
|
||||
["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 = $1 AND b.baz <> $2) OR ($3 < $4 AND $5 < $6) OR (f.e in ($7, $8, $9)) OR f.e BETWEEN $10 AND $11) GROUP BY f.a HAVING $12 < f.e ORDER BY b.baz DESC, c.quux, f.a NULLS FIRST LIMIT $13 OFFSET $14 "
|
||||
|
|
@ -174,3 +179,5 @@
|
|||
(from :foo)
|
||||
(join :x [:= :foo.id :x.id] :y nil)
|
||||
sql/format)))))
|
||||
|
||||
#?(:cljs (cljs.test/run-all-tests))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
(ns honeysql.format-test
|
||||
(:refer-clojure :exclude [format])
|
||||
(:require [clojure.test :refer [deftest testing is are]]
|
||||
[honeysql.format :refer :all]))
|
||||
(:require [#?@(:clj [clojure.test :refer]
|
||||
:cljs [cljs.test :refer-macros]) [deftest testing is are]]
|
||||
[honeysql.types :as sql]
|
||||
[honeysql.format :refer
|
||||
[*allow-dashed-names?* quote-identifier format-clause format]]))
|
||||
|
||||
(deftest test-quote
|
||||
(are
|
||||
|
|
@ -66,11 +69,11 @@
|
|||
(deftest array-test
|
||||
(is (= (format {:insert-into :foo
|
||||
:columns [:baz]
|
||||
:values [[#sql/array [1 2 3 4]]]})
|
||||
: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"]]]})
|
||||
:values [[(sql/array ["one" "two" "three"])]]})
|
||||
["INSERT INTO foo (baz) VALUES (ARRAY[?, ?, ?])" "one" "two" "three"])))
|
||||
|
||||
(deftest union-test
|
||||
|
|
|
|||
Loading…
Reference in a new issue