Fixes #338 properly by making offset/fetch smarter
This commit is contained in:
parent
52e2a57fca
commit
50bbfef07f
4 changed files with 93 additions and 43 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -1,13 +1,11 @@
|
|||
# Changes
|
||||
|
||||
* 2.0.next (gold) in progress
|
||||
* Address #332 by improving `:cross-join` documentation.
|
||||
* Fix `fetch` helper.
|
||||
|
||||
* 2.0.0-rc4 (for testing; 2021-07-17)
|
||||
* Fix #338 by adding `ONLY` to `:fetch`.
|
||||
* 2.0.0-rc5 in progress
|
||||
* Fix #338 by producing `OFFSET n ROWS` (or `ROW` if `n` is 1) if `:fetch` is present or `:sqlserver` dialect is specified; and by producing `FETCH NEXT n ROWS ONLY` (or `ROW` is `n` is 1; or `FIRST` instead of `NEXT` if `:offset` is not present).
|
||||
* Fix #337 by switching to `clojure.test` even for ClojureScript.
|
||||
* Address #332 by improving `:cross-join` documentation.
|
||||
* Address #330 by improving exception when a non-entity is encountered where an entity is expected.
|
||||
* Fix `fetch` helper (it previously returned an `:offset` clause).
|
||||
* Fix bug in unrolling nested argument to `with-columns` helper.
|
||||
|
||||
* 2.0.0-rc3 (for testing; 2021-06-16)
|
||||
|
|
|
|||
4
pom.xml
4
pom.xml
|
|
@ -3,7 +3,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.github.seancorfield</groupId>
|
||||
<artifactId>honeysql</artifactId>
|
||||
<version>2.0.0-rc4</version>
|
||||
<version>2.0.0-rc5</version>
|
||||
<name>honeysql</name>
|
||||
<description>SQL as Clojure data structures.</description>
|
||||
<url>https://github.com/seancorfield/honeysql</url>
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
<url>https://github.com/seancorfield/honeysql</url>
|
||||
<connection>scm:git:git://github.com/seancorfield/honeysql.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com/seancorfield/honeysql.git</developerConnection>
|
||||
<tag>v2.0.0-rc4</tag>
|
||||
<tag>v2.0.0-rc5</tag>
|
||||
</scm>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
|
|
|||
|
|
@ -86,11 +86,14 @@
|
|||
(conj order clause))))
|
||||
|
||||
(def ^:private dialects
|
||||
(reduce-kv (fn [m k v]
|
||||
(assoc m k (assoc v :dialect k)))
|
||||
{}
|
||||
{:ansi {:quote #(str \" % \")}
|
||||
:sqlserver {:quote #(str \[ % \])}
|
||||
:mysql {:quote #(str \` % \`)
|
||||
:clause-order-fn #(add-clause-before % :set :where)}
|
||||
:oracle {:quote #(str \" % \") :as false}})
|
||||
:oracle {:quote #(str \" % \") :as false}}))
|
||||
|
||||
; should become defonce
|
||||
(def ^:private default-dialect (atom (:ansi dialects)))
|
||||
|
|
@ -109,9 +112,26 @@
|
|||
(def ^:private ^:dynamic *allow-suspicious-entities* false)
|
||||
;; "linting" mode (:none, :basic, :strict):
|
||||
(def ^:private ^:dynamic *checking* :none)
|
||||
;; the current DSL hash map being formatted (for contains-clause?):
|
||||
(def ^:private ^:dynamic *dsl* nil)
|
||||
|
||||
;; clause helpers
|
||||
|
||||
(defn contains-clause?
|
||||
"Returns true if the current DSL expression being formatted
|
||||
contains the specified clause (as a keyword or symbol)."
|
||||
[clause]
|
||||
(or (contains? *dsl* clause)
|
||||
(contains? *dsl*
|
||||
(if (keyword? clause)
|
||||
(symbol (name clause))
|
||||
(keyword (name clause))))))
|
||||
|
||||
(defn- sql-server?
|
||||
"Helper to detect if SQL Server is the current dialect."
|
||||
[]
|
||||
(= :sqlserver (:dialect *dialect*)))
|
||||
|
||||
;; String.toUpperCase() or `str/upper-case` for that matter converts the
|
||||
;; string to uppercase for the DEFAULT LOCALE. Normally this does what you'd
|
||||
;; expect but things like `inner join` get converted to `İNNER JOİN` (dot over
|
||||
|
|
@ -869,10 +889,18 @@
|
|||
:partition-by #'format-selects
|
||||
:order-by #'format-order-by
|
||||
:limit #'format-on-expr
|
||||
:offset #'format-on-expr
|
||||
:offset (fn [_ x]
|
||||
(if (or (contains-clause? :fetch) (sql-server?))
|
||||
(let [[sql & params] (format-on-expr :offset x)
|
||||
rows (if (and (number? x) (== 1 x)) :row :rows)]
|
||||
(into [(str sql " " (sql-kw rows))] params))
|
||||
;; format in the old style:
|
||||
(format-on-expr :offset x)))
|
||||
:fetch (fn [_ x]
|
||||
(let [[sql & params] (format-on-expr :fetch x)]
|
||||
(into [(str sql " " (sql-kw :only))] params)))
|
||||
(let [which (if (contains-clause? :offset) :fetch-next :fetch-first)
|
||||
rows (if (and (number? x) (== 1 x)) :row-only :rows-only)
|
||||
[sql & params] (format-on-expr which x)]
|
||||
(into [(str sql " " (sql-kw rows))] params)))
|
||||
:for #'format-lock-strength
|
||||
:lock #'format-lock-strength
|
||||
:values #'format-values
|
||||
|
|
@ -907,6 +935,7 @@
|
|||
This is intended to be used when writing your own formatters to
|
||||
extend the DSL supported by HoneySQL."
|
||||
[statement-map & [{:keys [aliased nested pretty]}]]
|
||||
(binding [*dsl* statement-map]
|
||||
(let [[sqls params leftover]
|
||||
(reduce (fn [[sql params leftover] k]
|
||||
(if-some [xs (if-some [xs (k leftover)]
|
||||
|
|
@ -929,7 +958,7 @@
|
|||
pretty
|
||||
(as-> s (str "\n" s "\n"))
|
||||
(and nested (not aliased))
|
||||
(as-> s (str "(" s ")")))] params))))
|
||||
(as-> s (str "(" s ")")))] params)))))
|
||||
|
||||
(def ^:private infix-aliases
|
||||
"Provided for backward compatibility with earlier HoneySQL versions."
|
||||
|
|
|
|||
|
|
@ -783,7 +783,30 @@ ORDER BY id = ? DESC
|
|||
:join [[{:select :a :from :b :where [:= :id 123]} :x] :y]
|
||||
:where [:= :id 456]})))))
|
||||
|
||||
(deftest fetch-offset-issue338
|
||||
(is (= ["SELECT foo FROM bar OFFSET ? FETCH ? ONLY" 20 10]
|
||||
(deftest fetch-offset-issue-338
|
||||
(testing "default offset (with and without limit)"
|
||||
(is (= ["SELECT foo FROM bar LIMIT ? OFFSET ?" 10 20]
|
||||
(format {:select :foo :from :bar
|
||||
:fetch 10 :offset 20}))))
|
||||
:limit 10 :offset 20})))
|
||||
(is (= ["SELECT foo FROM bar OFFSET ?" 20]
|
||||
(format {:select :foo :from :bar
|
||||
:offset 20}))))
|
||||
(testing "default offset / fetch"
|
||||
(is (= ["SELECT foo FROM bar OFFSET ? ROWS FETCH NEXT ? ROWS ONLY" 20 10]
|
||||
(format {:select :foo :from :bar
|
||||
:fetch 10 :offset 20})))
|
||||
(is (= ["SELECT foo FROM bar OFFSET ? ROW FETCH NEXT ? ROW ONLY" 1 1]
|
||||
(format {:select :foo :from :bar
|
||||
:fetch 1 :offset 1})))
|
||||
(is (= ["SELECT foo FROM bar FETCH FIRST ? ROWS ONLY" 2]
|
||||
(format {:select :foo :from :bar
|
||||
:fetch 2}))))
|
||||
(testing "SQL Server offset"
|
||||
(is (= ["SELECT [foo] FROM [bar] OFFSET ? ROWS FETCH NEXT ? ROWS ONLY" 20 10]
|
||||
(format {:select :foo :from :bar
|
||||
:fetch 10 :offset 20}
|
||||
{:dialect :sqlserver})))
|
||||
(is (= ["SELECT [foo] FROM [bar] OFFSET ? ROWS" 20]
|
||||
(format {:select :foo :from :bar
|
||||
:offset 20}
|
||||
{:dialect :sqlserver})))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue