Initial commit.
This commit is contained in:
parent
4e5ee99d36
commit
abc65cec79
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/target
|
||||||
|
/classes
|
||||||
|
/checkouts
|
||||||
|
pom.xml
|
||||||
|
pom.xml.asc
|
||||||
|
*.jar
|
||||||
|
*.class
|
||||||
|
/.lein-*
|
||||||
|
/.nrepl-port
|
||||||
|
.hgignore
|
||||||
|
.hg/
|
||||||
|
.idea
|
||||||
|
*.iml
|
13
project.clj
13
project.clj
|
@ -1,6 +1,11 @@
|
||||||
(defproject views-honeysql "0.1.0-SNAPSHOT"
|
(defproject views/honeysql "0.1.0-SNAPSHOT"
|
||||||
:description "FIXME: write description"
|
:description "FIXME: write description"
|
||||||
:url "http://example.com/FIXME"
|
:url "http://example.com/FIXME"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "MIT License"
|
||||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
:url "http://opensource.org/licenses/MIT"}
|
||||||
:dependencies [[org.clojure/clojure "1.6.0"]])
|
:dependencies [[views "1.4.0-SNAPSHOT"]
|
||||||
|
[honeysql "0.5.2"]
|
||||||
|
[org.clojure/java.jdbc "0.3.6"]
|
||||||
|
[org.clojure/tools.logging "0.3.1"]]
|
||||||
|
:profiles {:dev {:dependencies
|
||||||
|
[[org.clojure/clojure "1.6.0"]]}})
|
||||||
|
|
45
src/views/honeysql/core.clj
Normal file
45
src/views/honeysql/core.clj
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
(ns views.honeysql.core
|
||||||
|
(:require
|
||||||
|
[views.core :refer [hint]]
|
||||||
|
[views.honeysql.util :refer [query-tables]]
|
||||||
|
[honeysql.core :as hsql]
|
||||||
|
[clojure.tools.logging :refer [error]]
|
||||||
|
[clojure.java.jdbc :as j]))
|
||||||
|
|
||||||
|
(def send-hints! (atom (fn [hints] (error "send-hints! not configured"))))
|
||||||
|
|
||||||
|
(defmacro with-view-transaction
|
||||||
|
"Like with-db-transaction, but sends view hints at end of transaction."
|
||||||
|
[binding & forms]
|
||||||
|
(let [tvar (first binding), db (second binding), args (drop 2 binding)]
|
||||||
|
`(if (:hints ~db) ;; check if we are in a nested transaction
|
||||||
|
(let [~tvar ~db] ~@forms)
|
||||||
|
(let [hints# (atom [])
|
||||||
|
result# (j/with-db-transaction [t# ~db ~@args]
|
||||||
|
(let [~tvar (assoc ~db :hints hints#)]
|
||||||
|
~@forms))]
|
||||||
|
(@send-hints! @hints#)
|
||||||
|
result#))))
|
||||||
|
|
||||||
|
(defn execute-honeysql!
|
||||||
|
"Always return keys for inserts."
|
||||||
|
[db hsql-map]
|
||||||
|
(if-let [table (:insert-into hsql-map)]
|
||||||
|
(if (vector? table)
|
||||||
|
(j/execute! db (hsql/format hsql-map))
|
||||||
|
(apply j/insert! db table (:values hsql-map)))
|
||||||
|
(j/execute! db (hsql/format hsql-map))))
|
||||||
|
|
||||||
|
(defn vexec!
|
||||||
|
"Used to perform arbitrary insert/update/delete actions on the database,
|
||||||
|
while ensuring that view hints are sent to the view system.
|
||||||
|
Arguments are:
|
||||||
|
- db: a clojure.java.jdbc database with fid field
|
||||||
|
- action-map: the HoneySQL map for the insert/update/delete action"
|
||||||
|
[db action-map]
|
||||||
|
(let [results (execute-honeysql! db action-map)
|
||||||
|
hsql-hint (hint :views/honeysql (query-tables action-map))]
|
||||||
|
(if-let [hints (:hints db)]
|
||||||
|
(swap! hints conj hsql-hint)
|
||||||
|
(send-hints! [hsql-hint]))
|
||||||
|
results))
|
78
src/views/honeysql/util.clj
Normal file
78
src/views/honeysql/util.clj
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
(ns views.honeysql.util
|
||||||
|
(:require
|
||||||
|
[honeysql.core :as hsql]
|
||||||
|
[honeysql.helpers :as hh]
|
||||||
|
[clojure.string :refer [split]]))
|
||||||
|
;; The following is used for full refresh views where we can have CTEs and
|
||||||
|
;; subselects in play.
|
||||||
|
|
||||||
|
(declare query-tables)
|
||||||
|
|
||||||
|
(defn- first-leaf
|
||||||
|
"Retrieves the first leaf in a collection of collections
|
||||||
|
|
||||||
|
(first-leaf :table) -> :table
|
||||||
|
(first-leaf [[:table] [& values]]) -> :table"
|
||||||
|
[v]
|
||||||
|
(if (coll? v) (recur (first v)) v))
|
||||||
|
|
||||||
|
(defn cte-tables
|
||||||
|
[query]
|
||||||
|
(mapcat #(query-tables (second %)) (:with query)))
|
||||||
|
|
||||||
|
(defn isolate-tables
|
||||||
|
"Isolates tables from table definitions in from and join clauses."
|
||||||
|
[c]
|
||||||
|
(if (keyword? c) [c] (let [v (first c)] (if (map? v) (query-tables v) [v]))))
|
||||||
|
|
||||||
|
(defn from-tables
|
||||||
|
[query]
|
||||||
|
(mapcat isolate-tables (:from query)))
|
||||||
|
|
||||||
|
(defn every-second
|
||||||
|
[coll]
|
||||||
|
(map first (partition 2 coll)))
|
||||||
|
|
||||||
|
(defn join-tables
|
||||||
|
[query k]
|
||||||
|
(mapcat isolate-tables (every-second (k query))))
|
||||||
|
|
||||||
|
(defn collect-maps
|
||||||
|
[wc]
|
||||||
|
(cond
|
||||||
|
(coll? wc) (let [maps (filterv map? wc)
|
||||||
|
colls (filter #(and (coll? %) (not (map? %))) wc)]
|
||||||
|
(into maps (mapcat collect-maps colls)))
|
||||||
|
(map? wc) [wc]
|
||||||
|
:else []))
|
||||||
|
|
||||||
|
(defn where-tables
|
||||||
|
"This search for subqueries in the where clause."
|
||||||
|
[query]
|
||||||
|
(mapcat query-tables (collect-maps (:where query))))
|
||||||
|
|
||||||
|
(defn insert-tables
|
||||||
|
[query]
|
||||||
|
(some->> query :insert-into first-leaf vector))
|
||||||
|
|
||||||
|
(defn update-tables
|
||||||
|
[query]
|
||||||
|
(if-let [v (:update query)] [v] []))
|
||||||
|
|
||||||
|
(defn delete-tables
|
||||||
|
[query]
|
||||||
|
(if-let [v (:delete-from query)] [v] []))
|
||||||
|
|
||||||
|
(defn query-tables
|
||||||
|
"Return all the tables in an sql statement."
|
||||||
|
[query]
|
||||||
|
(set (concat
|
||||||
|
(cte-tables query)
|
||||||
|
(from-tables query)
|
||||||
|
(join-tables query :join)
|
||||||
|
(join-tables query :left-join)
|
||||||
|
(join-tables query :right-join)
|
||||||
|
(where-tables query)
|
||||||
|
(insert-tables query)
|
||||||
|
(update-tables query)
|
||||||
|
(delete-tables query))))
|
27
src/views/honeysql/view.clj
Normal file
27
src/views/honeysql/view.clj
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
(ns views.honeysql.view
|
||||||
|
(:require
|
||||||
|
[views.protocols :refer :all]
|
||||||
|
[views.honeysql.util :refer [query-tables]]
|
||||||
|
[honeysql.core :as hsql]
|
||||||
|
[clojure.set :refer [intersection]]
|
||||||
|
[clojure.java.jdbc :as j]
|
||||||
|
[clojure.tools.logging :refer [warn]]))
|
||||||
|
|
||||||
|
(defrecord HSQLView [id db query-fn post-fn]
|
||||||
|
IView
|
||||||
|
(id [_] id)
|
||||||
|
(data [_ namespace parameters]
|
||||||
|
(let [start (System/currentTimeMillis)
|
||||||
|
data (j/query db (hsql/format (apply query-fn parameters)) :row-fn post-fn)
|
||||||
|
time (- (System/currentTimeMillis) start)]
|
||||||
|
(when (>= time 1000) (warn id "took" time "msecs"))
|
||||||
|
data))
|
||||||
|
(relevant? [_ namespace parameters hints]
|
||||||
|
(let [tables (query-tables (apply query-fn parameters))
|
||||||
|
nhints (filter #(= :views/honeysql (:namespace %)) hints)]
|
||||||
|
(boolean (some #(not-empty (intersection (:hint %) tables)) nhints)))))
|
||||||
|
|
||||||
|
(defn view
|
||||||
|
"Creates a Honey SQL view that uses a jdbc database configuration"
|
||||||
|
([id db hsql-fn post-fn] (HSQLView. id db hsql-fn post-fn))
|
||||||
|
([id db hsql-fn] (view id db hsql-fn identity)))
|
|
@ -1,6 +0,0 @@
|
||||||
(ns views-honeysql.core)
|
|
||||||
|
|
||||||
(defn foo
|
|
||||||
"I don't do a whole lot."
|
|
||||||
[x]
|
|
||||||
(println x "Hello, World!"))
|
|
Loading…
Reference in a new issue