Skip to content

Add support for explain #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@ The `ilike` and `not-ilike` operators can be used to query data using a pattern
0.25 0.50 0.75]
```

### explain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add this section to the index upstairs.

`EXPLAIN` is used to show the execution plan of a statement

``` clojure
(-> (select :*)
(from :products)
(explain)
(sql/format))
=> ["EXPLAIN SELECT * FROM PRODUCTS"]

(-> (select :*)
(from :products)
(explain {:analzye true :verbose true})
(sql/format))
=> ["EXPLAIN ANALZYE VERBOSE SELECT * FROM PRODUCTS"]

(-> (select :*)
(from :products)
(explain {:format :json})
(sql/format))
=> ["EXPLAIN (FORMAT JSON) SELECT * FROM PRODUCTS"]
```

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we kill these 2 extra lines? 2 lines should be enough.



### SQL functions

The following are the SQL functions added in `honeysql-postgres`
Expand Down
34 changes: 33 additions & 1 deletion src/honeysql_postgres/format.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@

(def ^:private postgres-clause-priorities
"Determines the order that clauses will be placed within generated SQL"
(merge {:with 30
(merge {:explain 20
:with 30
:with-recursive 40
:except 45
:except-all 45
Expand Down Expand Up @@ -297,3 +298,34 @@
(-> extension-name
util/get-first
sqlf/to-sql)))

(def ^:private explain-params->string
{:costs "COSTS"
:settings "SETTINGS"
:buffers "BUFFERS"
:wal "WAL"
:timing "TIMING"
:summary "SUMMARY"})

(def ^:private explain-format->string
{:text "TEXT"
:xml "XML"
:json "JSON"
:yaml "YAML"})

(defn- ->explain-boolean-param-string [param value]
(when-let [param-string (explain-params->string param)]
(sqlf/paren-wrap
(sqlf/space-join [param-string (if (true? value) "TRUE" "FALSE")]))))

(defmethod format-clause :explain [[_ [params]] _]
(if (empty? params)
"EXPLAIN"
(sqlf/space-join
(remove nil?
(concat ["EXPLAIN"
(when (:analyze params) "ANALYZE")
(when (:verbose params) "VERBOSE")
(when-let [format-type (:format params)]
(sqlf/paren-wrap (str "FORMAT" " " (explain-format->string format-type))))]
(map (partial apply ->explain-boolean-param-string) params))))))
3 changes: 3 additions & 0 deletions src/honeysql_postgres/helpers.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,6 @@

(defhelper drop-extension [m extension-name]
(assoc m :drop-extension (sqlh/collify extension-name)))

(defhelper explain [m args]
(assoc m :explain args))
67 changes: 66 additions & 1 deletion test/honeysql_postgres/postgres_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
drop-column
drop-extension
drop-table
explain
filter
insert-into-as
on-conflict
Expand Down Expand Up @@ -354,6 +355,7 @@
(within-group [(sql/call :percentile_disc (hsql-types/array [0.25 0.5 0.75])) (order-by :s.i) :alias])
(from (sql/raw "generate_series(1,10) AS s(i)"))
(sql/format)))))

(deftest create-extension-test
(testing "create extension"
(is (= ["CREATE EXTENSION \"uuid-ossp\""]
Expand All @@ -367,8 +369,71 @@
:quoting :ansi))))))

(deftest drop-extension-test
(testing "create extension"
(testing "drop extension"
(is (= ["DROP EXTENSION \"uuid-ossp\""]
(-> (drop-extension :uuid-ossp)
(sql/format :allow-dashed-names? true
:quoting :ansi))))))

(deftest explain-test
(let [query (-> (select :*)
(from :products))]
(testing "EXPLAIN without any arguments"
(is (= ["EXPLAIN SELECT * FROM products"]
(-> query (explain) (sql/format)))))

(testing "Explain with analyze"
(is (= ["EXPLAIN ANALYZE SELECT * FROM products"]
(-> query (explain {:analyze true}) (sql/format)))))

(testing "Explain with analyze verbose"
(is (= ["EXPLAIN ANALYZE VERBOSE SELECT * FROM products"]
(-> query (explain {:analyze true :verbose true}) (sql/format))))
(is (= ["EXPLAIN ANALYZE VERBOSE SELECT * FROM products"]
(-> query (explain {:verbose true :analyze true}) (sql/format)))))

(testing "Explain with costs"
(is (= ["EXPLAIN (COSTS TRUE) SELECT * FROM products"]
(-> query (explain {:costs true}) (sql/format))))
(is (= ["EXPLAIN (COSTS FALSE) SELECT * FROM products"]
(-> query (explain {:costs false}) (sql/format)))))

(testing "Explain with settings"
(is (= ["EXPLAIN (SETTINGS TRUE) SELECT * FROM products"]
(-> query (explain {:settings true}) (sql/format))))
(is (= ["EXPLAIN (SETTINGS FALSE) SELECT * FROM products"]
(-> query (explain {:settings false}) (sql/format)))))

(testing "Explain with buffers"
(is (= ["EXPLAIN (BUFFERS TRUE) SELECT * FROM products"]
(-> query (explain {:buffers true}) (sql/format))))
(is (= ["EXPLAIN (BUFFERS FALSE) SELECT * FROM products"]
(-> query (explain {:buffers false}) (sql/format)))))

(testing "Explain with wal"
(is (= ["EXPLAIN (WAL TRUE) SELECT * FROM products"]
(-> query (explain {:wal true}) (sql/format))))
(is (= ["EXPLAIN (WAL FALSE) SELECT * FROM products"]
(-> query (explain {:wal false}) (sql/format)))))

(testing "Explain with timing"
(is (= ["EXPLAIN (TIMING TRUE) SELECT * FROM products"]
(-> query (explain {:timing true}) (sql/format))))
(is (= ["EXPLAIN (TIMING FALSE) SELECT * FROM products"]
(-> query (explain {:timing false}) (sql/format)))))

(testing "Explain with summary"
(is (= ["EXPLAIN (SUMMARY TRUE) SELECT * FROM products"]
(-> query (explain {:summary true}) (sql/format))))
(is (= ["EXPLAIN (SUMMARY FALSE) SELECT * FROM products"]
(-> query (explain {:summary false}) (sql/format)))))

(testing "Explain with format"
(is (= ["EXPLAIN (FORMAT TEXT) SELECT * FROM products"]
(-> query (explain {:format :text}) (sql/format))))
(is (= ["EXPLAIN (FORMAT XML) SELECT * FROM products"]
(-> query (explain {:format :xml}) (sql/format))))
(is (= ["EXPLAIN (FORMAT JSON) SELECT * FROM products"]
(-> query (explain {:format :json}) (sql/format))))
(is (= ["EXPLAIN (FORMAT YAML) SELECT * FROM products"]
(-> query (explain {:format :yaml}) (sql/format)))))))