New table extraction code for full views.

This commit is contained in:
Alexander K. Hudek 2015-01-09 21:43:30 -05:00
parent 9f47500451
commit e89800e099
5 changed files with 102 additions and 17 deletions

View file

@ -1,4 +1,4 @@
(defproject views "0.5.0"
(defproject views "0.5.1"
:description "You underestimate the power of the SQL side"
:url "https://github.com/diligenceengine/views"

View file

@ -38,9 +38,8 @@
(defn have-overlapping-tables?
"Takes two Honeysql hash-maps, one for action, one for view, and returns
boolean value representing whether or not their set of tables intersect."
[action view]
(->> [action view]
(map (comp set #(map first %) vh/extract-tables))
(apply intersection)
seq
boolean))
[action view refresh?]
(let [a (set (map first (vh/extract-tables action)))]
(if refresh?
(boolean (seq (intersection a (vh/query-tables view))))
(boolean (seq (intersection a (set (map first (vh/extract-tables view)))))))))

View file

@ -310,7 +310,7 @@
as the original :view-sig the deltas apply to."
[persistence namespace schema db all-views action templates]
(j/with-db-transaction [t db :isolation :serializable]
(let [filtered-views (filterv #(vc/have-overlapping-tables? action (:view %)) all-views)
(let [filtered-views (filterv #(vc/have-overlapping-tables? action (:view %) (:refresh-only? %)) all-views)
{full-refresh-views true normal-views nil} (group-by :refresh-only? filtered-views)
need-deltas (map #(generate-view-delta-map % action) normal-views)
table (-> action vh/extract-tables ffirst)

View file

@ -15,13 +15,13 @@
(defn process-complex-clause
[tables clause]
(reduce
#(if (coll? %2)
#(if (coll? %2)
(if (some pred-ops [(first %2)])
%1
(conj %1 %2))
(conj %1 [%2]))
tables
clause))
tables
clause))
(defn extract-tables*
[tables clause]
@ -31,12 +31,6 @@
(conj tables [clause]))
tables))
(defn with-op
"Takes a collection of things and returns either an nary op of them, or
the item in the collection if there is only one."
[op coll]
(if (> (count coll) 1) (into [op] coll) (first coll)))
(defn extract-tables
"Extracts a set of table vector from a HoneySQL spec hash-map.
Each vector either contains a single table keyword, or the
@ -44,6 +38,74 @@
([hh-spec] (extract-tables hh-spec table-clauses))
([hh-spec clauses] (reduce #(extract-tables* %1 (%2 hh-spec)) #{} clauses)))
;; The following is used for full refresh views where we can have CTEs and
;; subselects in play.
(declare query-tables)
(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
[coll]
(let[maps (filterv map? coll)
colls (filter #(and (coll? %) (not (map? %))) coll)]
(into maps (mapcat collect-maps colls))))
(defn where-tables
"This search for subqueries in the where clause."
[query]
(mapcat query-tables (collect-maps (:where query))))
(defn insert-tables
[query]
(if-let [v (:insert-into query)] [v] []))
(defn update-tables
[query]
(if-let [v (:update query)] [v] []))
(defn delete-tables
[query]
(if-let [v (:delete-from query)] [v] []))
(defn query-tables
[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))))
(defn with-op
"Takes a collection of things and returns either an nary op of them, or
the item in the collection if there is only one."
[op coll]
(if (> (count coll) 1) (into [op] coll) (first coll)))
(defn find-table-aliases
"Returns the table alias for the supplied table."
[action-table tables]

View file

@ -34,6 +34,30 @@
(is (= (vh/extract-tables join-with-alias-test) #{[:foo] [:bar :b]}))
(is (= (vh/extract-tables join-and-from-with-alias-test) #{[:foo :f] [:bar :b]})))
(def cte-test
{:with [[:a {:select [:*] :from [:bar]}]]
:select [:*] :from [:foo]})
(def from-subselect-test
{:select [:*] :from [[{:select [:*] :from [:foo]} :a]]})
(def where-subselect-test
{:select [:*] :from [:foo] :where [:in :a {:select [:*] :from [:bar]}]})
(def nested-where-subselect-test
{:select [:*] :from [:foo] :where [:and [:in :a {:select [:*] :from [:bar]}] [:in :a {:select [:*] :from [:baz]}]]})
(deftest extracts-tables-from-full-refresh-specs
(is (= (vh/query-tables simple-test) #{:foo}))
(is (= (vh/query-tables insert-test) #{:foo}))
(is (= (vh/query-tables join-test) #{:foo :bar}))
(is (= (vh/query-tables join-with-alias-test) #{:foo :bar}))
(is (= (vh/query-tables join-and-from-with-alias-test) #{:foo :bar}))
(is (= (vh/query-tables cte-test) #{:foo :bar}))
(is (= (vh/query-tables from-subselect-test) #{:foo}))
(is (= (vh/query-tables where-subselect-test) #{:foo :bar}))
(is (= (vh/query-tables nested-where-subselect-test) #{:foo :bar :baz})))
;; Do we really need to test the new version?
(deftest merges-where-clauses
(is (= (vh/merge-where-clauses [:= :foo 1] [:= :bar 2])