document :cache option
Also try to clear up confusion about function syntax while I'm adding examples!
This commit is contained in:
parent
18adeb1048
commit
e3de2a621d
4 changed files with 69 additions and 4 deletions
36
README.md
36
README.md
|
|
@ -39,6 +39,12 @@ HoneySQL 1.x will continue to get critical security fixes but otherwise should b
|
|||
|
||||
## Usage
|
||||
|
||||
This section includes a number of usage examples but does not dive deep into the
|
||||
way the data structure acts as a DSL that can specify SQL statements (as hash maps)
|
||||
and SQL expressions and function calls (as vectors). It is recommended that you read the
|
||||
[**Getting Started**](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started)
|
||||
section of the documentation before trying to use HoneySQL to build your own queries!
|
||||
|
||||
From Clojure:
|
||||
<!-- {:test-doc-blocks/reader-cond :clj} -->
|
||||
```clojure
|
||||
|
|
@ -512,10 +518,20 @@ There are also helpers for each of those:
|
|||
=> ["SELECT * FROM foo UNION SELECT * FROM bar"]
|
||||
```
|
||||
|
||||
|
||||
### Functions
|
||||
|
||||
Keywords that begin with `%` are interpreted as SQL function calls:
|
||||
Function calls (and expressions with operators) can be specified as
|
||||
vectors where the first element is either a keyword or a symbol:
|
||||
|
||||
```clojure
|
||||
(-> (select :*) (from :foo)
|
||||
(where [:> :date_created [:date_add [:now] [:interval 24 :hours]]])
|
||||
(sql/format))
|
||||
=> ["SELECT * FROM foo WHERE date_created > DATE_ADD(NOW(), INTERVAL ? HOURS)" 24]
|
||||
```
|
||||
|
||||
A shorthand syntax also exists for simple function calls:
|
||||
keywords that begin with `%` are interpreted as SQL function calls:
|
||||
|
||||
```clojure
|
||||
(-> (select :%count.*) (from :foo) sql/format)
|
||||
|
|
@ -535,11 +551,27 @@ regular function calls in a select:
|
|||
=> ["SELECT COUNT(*) FROM foo"]
|
||||
```
|
||||
```clojure
|
||||
(-> (select [:%count.*]) (from :foo) sql/format)
|
||||
=> ["SELECT COUNT(*) FROM foo"]
|
||||
;; or even:
|
||||
(-> (select :%count.*) (from :foo) sql/format)
|
||||
=> ["SELECT COUNT(*) FROM foo"]
|
||||
```
|
||||
```clojure
|
||||
(-> (select [[:max :id]]) (from :foo) sql/format)
|
||||
=> ["SELECT MAX(id) FROM foo"]
|
||||
;; the pure data DSL requires an extra level of brackets:
|
||||
(-> {:select [[[:max :id]]], :from [:foo]} sql/format)
|
||||
=> ["SELECT MAX(id) FROM foo"]
|
||||
;; the shorthand makes this simpler:
|
||||
(-> {:select [[:%max.id]], :from [:foo]} sql/format)
|
||||
=> ["SELECT MAX(id) FROM foo"]
|
||||
;; or even:
|
||||
(-> {:select [:%max.id], :from [:foo]} sql/format)
|
||||
=> ["SELECT MAX(id) FROM foo"]
|
||||
;; or even:
|
||||
(-> {:select :%max.id, :from :foo} sql/format)
|
||||
=> ["SELECT MAX(id) FROM foo"]
|
||||
```
|
||||
|
||||
### Bindable parameters
|
||||
|
|
|
|||
|
|
@ -122,6 +122,32 @@ are two possible approaches:
|
|||
1. Use named parameters (e.g., `[:param :myval]`) instead of having the values directly in the DSL structure and then pass `{:params {:myval some-json}}` as part of the options in the call to `format`, or
|
||||
2. Use `[:lift ..]` wrapped around any structured values which tells HoneySQL not to interpret the vector or hash map value as a DSL: `[:lift some-json]`.
|
||||
|
||||
## Caching
|
||||
|
||||
As of 2.2.next, `format` can cache the SQL and parameters produced from the data structure so that it does not need to be computed on every call. This functionality is available only in Clojure and depends on [`org.clojure/core.cache`](https://github.com/clojure/core.cache) being on your classpath. If you are repeatedly building the same complex SQL statements over and over again, this can be a good way to provide a performance boost but there are some caveats.
|
||||
|
||||
* You need `core.cache` as a dependency: `org.clojure/core.cache {:mvn/version "1.0.225"}` was the latest as of January 20th, 2022,
|
||||
* You need to create one or more caches yourself, from the various factory functions in the [`clojure.core.cache.wrapped` namespace](http://clojure.github.io/core.cache/#clojure.core.cache.wrapped),
|
||||
* You should use named parameters in your SQL DSL data structure, e.g., `:?foo` or `'?foo`, and pass the actual parameter values via the `:params` option to `format`.
|
||||
|
||||
You can then pass the (atom containing the) cache to `format` using the `:cache` option. The call to `format` then looks in that cache for a match for the data structure passed in, i.e., the entire data structure is used as a key into the cache, including any literal parameter values. If the cache contains a match, the corresponding vector of a SQL string and parameters is used, otherwise the data structure is parsed as usual and the SQL string (and parameters) generated from it (and stored in the cache for the next call). Finally, named parameters in the vector are replaced by their values from the `:params` option.
|
||||
|
||||
The code that _builds_ the DSL data structure will be run in all cases, so any conditional logic and helper function calls will still happen, since that is how the data structure is created and then passed to `format`. If you want to avoid overhead, you'd need to take steps to build the data structure separately and store it somewhere for reuse in the call to `format`.
|
||||
|
||||
Since the data structure is used as the key into the cache, literal parameter values will lead to different keys:
|
||||
|
||||
<!-- :test-doc-blocks/skip -->
|
||||
```clojure
|
||||
;; these are two different cache entries:
|
||||
(sql/format {:select :* :from :table :where [:= :id 1]} {:cache my-cache})
|
||||
(sql/format {:select :* :from :table :where [:= :id 2]} {:cache my-cache})
|
||||
;; these are the same cache entry:
|
||||
(sql/format {:select :* :from :table :where [:= :id :?id]} {:cache my-cache :params {:id 1}})
|
||||
(sql/format {:select :* :from :table :where [:= :id :?id]} {:cache my-cache :params {:id 2}})
|
||||
```
|
||||
|
||||
Since HoneySQL accepts any of the `clojure.core.cache.wrapped` caches and runs every data structure through the provided `:cache`, it's up to you to ensure that your cache is appropriate for that usage: a "basic" cache will keep every entry until the cache is explicitly emptied; a TTL cache will keep each entry for a specific period of time; and so on.
|
||||
|
||||
## Other Sections Will Be Added!
|
||||
|
||||
As questions arise about the use of HoneySQL 2.x, I will add new sections here.
|
||||
|
|
|
|||
|
|
@ -349,6 +349,9 @@ HoneySQL supports quite a few [PostgreSQL extensions](postgresql.md).
|
|||
|
||||
In addition to the `:quoted` and `:dialect` options described above,
|
||||
`format` also accepts `:checking`, `:inline`, and `:params`.
|
||||
As of 2.2.next, `format` accepts a `:cache` option -- see the
|
||||
[**Caching** section of the **General Reference**](https://cljdoc.org/d/com.github.seancorfield/honeysql/CURRENT/doc/getting-started/general-reference#caching)
|
||||
for details.
|
||||
|
||||
The `:params` option was mentioned above and is used to specify
|
||||
the values of named parameters in the DSL.
|
||||
|
|
|
|||
|
|
@ -1362,8 +1362,12 @@
|
|||
dialect)
|
||||
|
||||
(def through-opts
|
||||
"Resolves to core.cache.wrapped/lookup-or-miss when it's available, or to a throwing function otherwise.
|
||||
In CLJS it always resolves to a throwing function."
|
||||
"If org.clojure/core.cache is available, resolves to a function that
|
||||
calls core.cache.wrapped/lookup-or-miss, otherwise to a function that
|
||||
throws an exception.
|
||||
|
||||
In ClojureScript, a resolves to a function that throws an exception
|
||||
because core.cache relies on JVM machinery and is Clojure-only."
|
||||
#?(:clj (try (require 'clojure.core.cache.wrapped)
|
||||
(let [lookup-or-miss (deref (resolve 'clojure.core.cache.wrapped/lookup-or-miss))]
|
||||
(fn [_opts cache data f]
|
||||
|
|
|
|||
Loading…
Reference in a new issue