Fixes #292 by supporting offset/fetch

This commit is contained in:
Sean Corfield 2021-03-13 12:36:25 -08:00
parent 6b070df52c
commit 82ee465820
6 changed files with 43 additions and 20 deletions

View file

@ -8,7 +8,7 @@
* Fix #301 by adding support for `CREATE`/`DROP`/`REFRESH` on `MATERIALIZED VIEW`. * Fix #301 by adding support for `CREATE`/`DROP`/`REFRESH` on `MATERIALIZED VIEW`.
* Add tests to confirm #299 does not affect v2. * Add tests to confirm #299 does not affect v2.
* Confirm the whole of the [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) is implemented out-of-the-box (#293). * Confirm the whole of the [nilenso/honeysql-postgres](https://github.com/nilenso/honeysql-postgres) is implemented out-of-the-box (#293).
* Add support for `SELECT TOP` (#292). * Fix #292 by adding support for `SELECT TOP` and `OFFSET`/`FETCH`.
* Reconcile `where` behavior with recent 1.0 changes (porting #283 to v2). * Reconcile `where` behavior with recent 1.0 changes (porting #283 to v2).
* Fix #280 by adding `:escape` as special syntax for regular expression patterns. * Fix #280 by adding `:escape` as special syntax for regular expression patterns.
* Fix #277 by adding `:join-by`/`join-by` so that you can have multiple `JOIN`'s in a specific order. * Fix #277 by adding `:join-by`/`join-by` so that you can have multiple `JOIN`'s in a specific order.

View file

@ -570,18 +570,27 @@ user=> (sql/format {:select [:*] :from :table
["SELECT * FROM table ORDER BY status ASC, YEAR(created_date) ASC"] ["SELECT * FROM table ORDER BY status ASC, YEAR(created_date) ASC"]
``` ```
## limit, offset (MySQL) ## limit, offset, fetch
Both `:limit` and `:offset` expect a single SQL expression: Some databases, including MySQL, support `:limit` and `:offset`
for paginated queries, other databases support `:offset` and
`fetch` for that (which is ANSI-compliant and should be
preferred if your database supports it). All three expect a
single SQL expression:
```clojure ```clojure
user=> (sql/format {:select [:id :name] user=> (sql/format {:select [:id :name]
:from [:table] :from [:table]
:limit 20 :offset 20}) :limit 10 :offset 20})
["SELECT id, name FROM table LIMIT ? OFFSET ?" 20 20] ["SELECT id, name FROM table LIMIT ? OFFSET ?" 10 20]
user=> (sql/format {:select [:id :name]
:from [:table]
:offset 20 :fetch 10})
["SELECT id, name FROM table OFFSET ? FETCH ? ONLY" 20 10]
``` ```
> Note: In the prerelease, these MySQL-specific clauses are in the default dialect but these will be moved to the `:mysql` dialect. All three are available in all dialects for HoneySQL so it
is up to you to choose the correct pair for your database.
## for ## for
@ -627,8 +636,6 @@ expected to be just one of those three mentioned above).
The syntax accepted for MySQL's `:lock` is exactly the The syntax accepted for MySQL's `:lock` is exactly the
same as the `:for` clause above. same as the `:for` clause above.
> Note: In the prerelease, this MySQL-specific clauses is in the default dialect but this will be moved to the `:mysql` dialect.
## values ## values
`:values` accepts either a sequence of hash maps representing `:values` accepts either a sequence of hash maps representing

View file

@ -74,7 +74,7 @@ Other `honeysql.core` functions that no longer exist include: `build`, `qualify`
You can now select a non-ANSI dialect of SQL using the new `honey.sql/set-dialect!` function (which sets a default dialect for all `format` operations) or by passing the new `:dialect` option to the `format` function. `:ansi` is the default dialect (which will mostly incorporate PostgreSQL usage over time). Other dialects supported are `:mysql` (which has a different quoting strategy and uses a different ranking for the `:set` clause), `:oracle` (which is essentially the `:ansi` dialect but will control other things over time), and `:sqlserver` (which is essentially the `:ansi` dialect but with a different quoting strategy). Other dialects and changes may be added over time. You can now select a non-ANSI dialect of SQL using the new `honey.sql/set-dialect!` function (which sets a default dialect for all `format` operations) or by passing the new `:dialect` option to the `format` function. `:ansi` is the default dialect (which will mostly incorporate PostgreSQL usage over time). Other dialects supported are `:mysql` (which has a different quoting strategy and uses a different ranking for the `:set` clause), `:oracle` (which is essentially the `:ansi` dialect but will control other things over time), and `:sqlserver` (which is essentially the `:ansi` dialect but with a different quoting strategy). Other dialects and changes may be added over time.
> Note: `:limit` and `:offset` are currently in the default `:ansi` dialect even though they are MySQL-specific. This will change as the dialects are fleshed out. I plan to add `:top` for `:sqlserver` and `:offset` / `:fetch` for `:ansi`, at which point `:limit` / `:offset` will become MySQL-only. > Note: in general, all clauses are available in all dialects in HoneySQL unless the syntax of the clauses conflict between dialects (currently, no such clauses exist). The `:mysql` dialect is the only one so far that changes the priority ordering of a few clauses.
## Option Changes ## Option Changes

View file

@ -259,8 +259,7 @@ The `:sqlserver` dialect uses `[`..`]` and the `:mysql` dialect uses
```..```. In addition, the `:oracle` dialect disables `AS` in aliases. ```..```. In addition, the `:oracle` dialect disables `AS` in aliases.
Currently, the only dialect that has substantive differences from Currently, the only dialect that has substantive differences from
the others is `:mysql` which has a `:lock` clause (that is very the others is `:mysql` for which the `:set` clause
similar to the ANSI `:for` clause) and for which the `:set` clause
has a different precedence than ANSI SQL. has a different precedence than ANSI SQL.
You can change the dialect globally using the `set-dialect!` function, You can change the dialect globally using the `set-dialect!` function,

View file

@ -53,7 +53,7 @@
:cross-join :cross-join
:where :group-by :having :where :group-by :having
:window :partition-by :window :partition-by
:order-by :limit :offset :for :values :order-by :limit :offset :fetch :for :lock :values
:on-conflict :on-constraint :do-nothing :do-update-set :on-duplicate-key-update :on-conflict :on-constraint :do-nothing :do-update-set :on-duplicate-key-update
:returning :returning
:with-data]) :with-data])
@ -79,14 +79,9 @@
:sqlserver {:quote #(str \[ % \])} :sqlserver {:quote #(str \[ % \])}
:mysql {:quote #(str \` % \`) :mysql {:quote #(str \` % \`)
:clause-order-fn (fn [order] :clause-order-fn (fn [order]
;; :lock is like :for
(swap! clause-format assoc :lock
(get @clause-format :for))
;; MySQL :set has different priority ;; MySQL :set has different priority
;; and :lock is between :for and :values
(-> (filterv (complement #{:set}) order) (-> (filterv (complement #{:set}) order)
(add-clause-before :set :where) (add-clause-before :set :where)))}
(add-clause-before :lock :values)))}
:oracle {:quote #(str \" % \") :as false}}) :oracle {:quote #(str \" % \") :as false}})
; should become defonce ; should become defonce
@ -778,7 +773,9 @@
:order-by #'format-order-by :order-by #'format-order-by
:limit #'format-on-expr :limit #'format-on-expr
:offset #'format-on-expr :offset #'format-on-expr
:fetch #'format-on-expr
:for #'format-lock-strength :for #'format-lock-strength
:lock #'format-lock-strength
:values #'format-values :values #'format-values
:on-conflict #'format-on-conflict :on-conflict #'format-on-conflict
:on-constraint #'format-selector :on-constraint #'format-selector

View file

@ -527,13 +527,18 @@
(limit 40) (limit 40)
Produces: LIMIT ? Produces: LIMIT ?
Parameters: 40" Parameters: 40
The two-argument syntax is not supported: use `offset`
instead:
`LIMIT 20,10` is equivalent to `LIMIT 10 OFFSET 20`"
{:arglists '([limit])} {:arglists '([limit])}
[& args] [& args]
(generic-1 :limit args)) (generic-1 :limit args))
(defn offset (defn offset
"Specific to MySQL, accepts a single SQL expression: "Accepts a single SQL expression:
(offset 10) (offset 10)
@ -543,10 +548,25 @@
[& args] [& args]
(generic-1 :offset args)) (generic-1 :offset args))
(defn fetch
"Accepts a single SQL expression:
(fetch 10)
Produces: FETCH ? ONLY
Parameters: 10"
{:arglists '([offset])}
[& args]
(generic-1 :offset args))
(defn for (defn for
[& args] [& args]
(generic-1 :for args)) (generic-1 :for args))
(defn lock
[& args]
(generic-1 :lock args))
(defn values (defn values
"Accepts a single argument: a collection of row values. "Accepts a single argument: a collection of row values.
Each row value can be either a sequence of column values Each row value can be either a sequence of column values