From 97c92368423960f1e5d1b729c97d88f21aff6a30 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 7 Mar 2021 17:07:38 -0800 Subject: [PATCH] Fixes #307 by adding data DSL examples I've added a pure data DSL version of nearly all the helper function examples. I've added a few examples of the data DSL with symbols instead of keywords as well. --- README.md | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a3158fb..65d1a95 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ Column names can be provided as keywords or symbols (but not strings -- HoneySQL ```clojure (sql/format sqlmap) => ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"] +;; sqlmap as symbols instead of keywords: +(-> '{select (a, b, c) from (foo) where (= f.a "baz")} + (sql/format)) +=> ["SELECT a, b, c FROM foo WHERE f.a = ?" "baz"] ``` HoneySQL is a relatively "pure" library, it does not manage your sql connection @@ -82,16 +86,25 @@ _[In HoneySQL 1.x, this was the behavior when `:namespace-as-table? true` was sp :where [:= :foo/a "baz"]}) (sql/format q-sqlmap) => ["SELECT foo.a, foo.b, foo.c FROM foo WHERE foo.a = ?" "baz"] +;; this also works with symbols instead of keywords: +(-> '{select (foo/a, foo/b, foo/c) + from (foo) + where (= foo/a "baz")} + (sql/format)) +=> ["SELECT foo.a, foo.b, foo.c FROM foo WHERE foo.a = ?" "baz"] ``` ### Vanilla SQL clause helpers -There are also functions for each clause type in the `honey.sql.helpers` namespace: +For every single SQL clause supported by HoneySQL (as keywords or symbols +in the data structure that is the DSL), there is also a corresponding +function in the `honey.sql.helpers` namespace: ```clojure (-> (select :a :b :c) (from :foo) (where [:= :f.a "baz"])) +=> {:select [:a :b :c] :from [:foo] :where [:= :f.a "baz"]} ``` Order doesn't matter (for independent clauses): @@ -145,6 +158,11 @@ In particular, note that `(select [:a :b])` means `SELECT a AS b` rather than `SELECT a, b` -- helpers like `select` are generally variadic and do not take a collection of column names. +The examples in this README use a mixture of data structures and the helper +functions interchangably. For any example using the helpers, you could evaluate +it (without the call to `sql/format`) to see what the equivalent data structure +would be. + ### Inserts Inserts are supported in two patterns. @@ -165,6 +183,19 @@ INSERT INTO properties VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) " "Jon" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56] +;; or as pure data DSL: +(-> {:insert-into [:properties] + :columns [:name :surname :age] + :values [["Jon" "Smith" 34] + ["Andrew" "Cooper" 12] + ["Jane" "Daniels" 56]]} + (sql/format {:pretty true})) +=> [" +INSERT INTO properties +(name, surname, age) +VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) +" +"Jon" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56] ``` If the rows are of unequal lengths, they will be padded with `NULL` values to make them consistent. @@ -184,6 +215,19 @@ INSERT INTO properties "John" "Smith" 34 "Andrew" "Cooper" 12 "Jane" "Daniels" 56] +;; or as pure data DSL: +(-> {:insert-into [:properties] + :values [{:name "John", :surname "Smith", :age 34} + {:name "Andrew", :surname "Cooper", :age 12} + {:name "Jane", :surname "Daniels", :age 56}]} + (sql/format {:pretty true})) +=> [" +INSERT INTO properties +(name, surname, age) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?) +" +"John" "Smith" 34 +"Andrew" "Cooper" 12 +"Jane" "Daniels" 56] ``` The set of columns used in the insert will be the union of all column names from all @@ -209,13 +253,34 @@ INSERT INTO user_profile_to_role " 12345 "user"] +;; or as pure data DSL: +(let [user-id 12345 + role-name "user"] + (-> {:insert-into [:user_profile_to_role] + :values [{:user_profile_id 12345, + :role_id {:select [:id], + :from [:role], + :where [:= :name "user"]}}]} + (sql/format {:pretty true}))) +=> [" +INSERT INTO user_profile_to_role +(user_profile_id, role_id) VALUES (?, (SELECT id FROM role WHERE name = ?)) +" +12345 +"user"] ``` ```clojure (-> (select :*) (from :foo) (where [:in :foo.a (-> (select :a) (from :bar))]) - sql/format) + (sql/format)) +=> ["SELECT * FROM foo WHERE foo.a IN (SELECT a FROM bar)"] +;; or as pure data DSL: +(-> {:select [:*], + :from [:foo], + :where [:in :foo.a {:select [:a], :from [:bar]}]} + (sql/format)) => ["SELECT * FROM foo WHERE foo.a IN (SELECT a FROM bar)"] ``` @@ -236,6 +301,18 @@ INSERT INTO comp_table VALUES (?, (?, ?)), (?, (?, ?)) " "small" 1 "inch" "large" 10 "feet"] +;; or as pure data DSL: +(-> {:insert-into [:comp_table], + :columns [:name :comp_column], + :values [["small" [:composite 1 "inch"]] + ["large" [:composite 10 "feet"]]]} + (sql/format {:pretty true})) +=> [" +INSERT INTO comp_table +(name, comp_column) +VALUES (?, (?, ?)), (?, (?, ?)) +" +"small" 1 "inch" "large" 10 "feet"] ``` ### Updates @@ -256,6 +333,19 @@ WHERE kind = ? "dramatic" 1 "drama"] +;; or as pure data DSL: +(-> {:update :films, + :set {:kind "dramatic", :watched [:+ :watched 1]}, + :where [:= :kind "drama"]} + (sql/format {:pretty true})) +=> [" +UPDATE films +SET kind = ?, watched = watched + ? +WHERE kind = ? +" +"dramatic" +1 +"drama"] ``` If you are trying to build a compound update statement (with `from` or `join`), @@ -274,6 +364,11 @@ Deletes look as you would expect: (where [:<> :kind "musical"]) (sql/format)) => ["DELETE FROM films WHERE kind <> ?" "musical"] +;; or as pure data DSL: +(-> {:delete-from [:films], + :where [:<> :kind "musical"]} + (sql/format)) +=> ["DELETE FROM films WHERE kind <> ?" "musical"] ``` If your database supports it, you can also delete from multiple tables: @@ -291,6 +386,19 @@ INNER JOIN directors ON films.director_id = directors.id WHERE kind <> ? " "musical"] +;; or pure data DSL: +(-> {:delete [:films :directors], + :from [:films], + :join [:directors [:= :films.director_id :directors.id]], + :where [:<> :kind "musical"]} + (sql/format {:pretty true})) +=> [" +DELETE films, directors +FROM films +INNER JOIN directors ON films.director_id = directors.id +WHERE kind <> ? +" +"musical"] ``` If you want to delete everything from a table, you can use `truncate`: @@ -299,6 +407,10 @@ If you want to delete everything from a table, you can use `truncate`: (-> (truncate :films) (sql/format)) => ["TRUNCATE films"] +;; or as pure data DSL: +(-> {:truncate :films} + (sql/format)) +=> ["TRUNCATE films"] ``` ### Set operations @@ -344,6 +456,9 @@ regular function calls in a select: ```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"] ``` ### Bindable parameters @@ -356,6 +471,10 @@ Keywords that begin with `?` are interpreted as bindable parameters: (where [:= :a :?baz]) (sql/format {:params {:baz "BAZ"}})) => ["SELECT id FROM foo WHERE a = ?" "BAZ"] +;; or as pure data DSL: +(-> {:select [:id], :from [:foo], :where [:= :a :?baz]} + (sql/format {:params {:baz "BAZ"}})) +=> ["SELECT id FROM foo WHERE a = ?" "BAZ"] ``` ### Miscellaneous