Initial commit.

This commit is contained in:
Alexander K. Hudek 2015-04-21 01:27:02 -04:00
parent 4e5ee99d36
commit abc65cec79
6 changed files with 172 additions and 10 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
/target
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
.hgignore
.hg/
.idea
*.iml

View file

@ -1,6 +1,11 @@
(defproject views-honeysql "0.1.0-SNAPSHOT"
(defproject views/honeysql "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.6.0"]])
:license {:name "MIT License"
:url "http://opensource.org/licenses/MIT"}
: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"]]}})

View 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))

View 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))))

View 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)))

View file

@ -1,6 +0,0 @@
(ns views-honeysql.core)
(defn foo
"I don't do a whole lot."
[x]
(println x "Hello, World!"))