Skip to content
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

Clean associative fix #80

Merged
2 commits merged into from Mar 13, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 40 additions & 21 deletions src/clojureql/core.clj
Expand Up @@ -226,26 +226,45 @@
(outer-join this table2 nil join-on))

(outer-join [this table2 type join-on]
(if (requires-subselect? table2)
(assoc this
:tcols (into (or tcols [])
(rename-subselects (:tname table2)
(-> table2 :grouped-by first)))
:joins (conj (or joins [])
{:data [table2 join-on]
:type (if (keyword? type) :outer :join)
:position type}))
(assoc this
:tcols (if-let [t2cols (seq (:tcols table2))]
(apply conj (or tcols [])
(map #(add-tname (:tname table2) %)
(if (coll? t2cols)
t2cols [t2cols])))
tcols)
:joins (conj (or joins [])
{:data [(to-tablename (:tname table2)) join-on]
:type (if (keyword? type) :outer :join)
:position type}))))
(let [sort-joins (fn sort-joins [joins]
(let [to-tbl-name (fn to-tbl-name [{[table-name join-on] :data :as join}]
(->> join-on :cols
(map #(-> % name (.replaceAll "\\..*" "")))
(filter #(not= % table-name))
first))
to-graph-el (fn to-graph-el [m {[table-name join-on] :data :as join}]
(let [required-table (to-tbl-name join)]
(assoc m table-name required-table)))
map-of-joins (reduce #(let [{[table-name join-on] :data :as join} %2
k table-name]
(assoc %1 k (conj (%1 k) join))) {} joins)
edges (reduce to-graph-el {} joins)
set-of-root-nodes (clojure.set/difference (into #{} (vals edges)) (into #{} (keys edges)))
add-deps (fn add-deps [tbl]
(into [(map-of-joins tbl)] (map add-deps (filter #(= tbl (edges %)) (keys edges)))))
sorted-joins (filter #(not (nil? %)) (flatten (map add-deps set-of-root-nodes)))]
sorted-joins))
j (into (or joins []) (-> table2 :joins (or [])))]
(if (requires-subselect? table2)
(assoc this
:tcols (into (or tcols [])
(rename-subselects (:tname table2)
(-> table2 :grouped-by first)))
:joins (sort-joins (conj j
{:data [table2 join-on]
:type (if (keyword? type) :outer :join)
:position type})))
(assoc this
:tcols (if-let [t2cols (seq (:tcols table2))]
(apply conj (or tcols [])
(map #(add-tname (:tname table2) %)
(if (coll? t2cols)
t2cols [t2cols])))
tcols)
:joins (sort-joins (conj j
{:data [(to-tablename (:tname table2)) join-on]
:type (if (keyword? type) :outer :join)
:position type}))))))

(modify [this new-modifiers]
(assoc this :modifiers
Expand Down Expand Up @@ -448,4 +467,4 @@
(if (coll? kw)
(map (first results) kw)
(kw (first results)))
(throw (Exception. "Multiple items in resultsetseq, keyword lookup not possible"))))))
(throw (Exception. "Multiple items in resultsetseq, keyword lookup not possible"))))))
84 changes: 84 additions & 0 deletions test/clojureql/test/core.clj
Expand Up @@ -252,6 +252,90 @@
"ON (countries.id = spots_subselect.country_id) "
"WHERE (regions_subselect.country_id = spots_subselect.country_id)")))

(testing "joins are associative"
(let [ta (join (table :t1) (table :t2) :id)
tb (join (table :t3) ta :id)] ;; swapping argument order of "ta" and "(table :t3)" works
(are [x y] (= (-> x (compile nil) interpolate-sql (.replaceAll "SELECT .* FROM" "SELECT * FROM")) y)
tb
"SELECT * FROM t3 JOIN t1 USING(id) JOIN t2 USING(id)"))
(let [ta (-> (table :t1)
(join (table :t2) (where (= :t1.a :t2.a)))
(join (table :t6) (where (= :t6.e :t2.e))))
tb (-> (table :t3)
(join (table :t4) (where (= :t3.b :t4.b)))
(join (table :t5) (where (= :t5.d :t4.d))))
qu (join ta tb (where (= :t3.c :t2.c)))]
(are [x y] (= (-> x (compile nil) interpolate-sql (.replaceAll "SELECT .* FROM" "SELECT * FROM")) y)
qu
(str "SELECT * FROM t1 "
"JOIN t2 ON (t1.a = t2.a) "
"JOIN t6 ON (t6.e = t2.e) "
"JOIN t3 ON (t3.c = t2.c) "
"JOIN t4 ON (t3.b = t4.b) "
"JOIN t5 ON (t5.d = t4.d)")))
(let [product-variants-table (project (table :product_variants)
[[:id :as :product_variant_id]
[:product_id :as :product_variant_product_id]
[:product_code :as :product_variant_product_code]
[:status :as :product_variant_status]
[:price_id :as :product_variant_price_id]])
products-table (project (table :products)
[[:id :as :product_id]
[:name :as :product_name]
[:description :as :product_description]
[:manufacturer_id :as :product_manufacturer_id]])
product-variant-skus-table (project (table :product_variant_skus)
[[:id :as :product_variant_sku_id]
[:product_variant_id :as :product_variant_sku_product_variant_id]
[:sku_id :as :product_variant_sku_sku_id]
[:quantity :as :product_variant_sku_quantity]])
orders-table (project (table :orders)
[[:id :as :order_id]
[:customer_id :as :order_customer_id]
[:customer_ref :as :order_customer_ref]
[:created :as :order_created]
[:status :as :order_status]
[:created_by :as :order_created_by]
[:source_id :as :order_source_id]
[:updated :as :order_updated]
[:cancellation_reason_id :as :order_cancellation_reason_id]
[:expirable :as :order_expirable]
[:shipping_method_id :as :order_shipping_method_id]])
order-lines-table (project (table :order_lines)
[[:id :as :order_line_id]
[:order_id :as :order_line_order_id]
[:product_variant_id :as :order_line_product_variant_id]
[:quantity :as :order_line_quantity]
[:status :as :order_line_status]
[:updated :as :order_line_updated]
[:price_id :as :order_line_price_id]
[:shippable_estimate :as :order_line_shippable_estimate]])
orders-with-lines-query (-> orders-table
(join order-lines-table (where (= :orders.id :order_lines.order_id))))
sku-table (project (table :skus) [[:id :as :sku_id]
[:stock_code :as :sku_stock_code]
[:barcode :as :sku_barcode]
[:reorder_quantity :as :sku_reorder_quantity]
[:minimum_level :as :sku_minimum_level]])
products-with-skus-query (-> product-variants-table
(join products-table (where (= :products.id :product_variants.product_id)))
(join product-variant-skus-table (where (= :product_variants.id :product_variant_skus.product_variant_id)))
(join sku-table (where (= :skus.id :product_variant_skus.sku_id))))
orders-with-skus-query (-> orders-with-lines-query
(join products-with-skus-query
(where (= :order_lines.product_variant_id :product_variants.id))))
open-orders-with-skus-query (-> orders-with-skus-query
(select (where (= :orders.status 1))))]
(are [x y] (= (-> x (compile nil) interpolate-sql (.replaceAll "SELECT .* FROM" "SELECT * FROM")) y)
open-orders-with-skus-query
(str "SELECT * FROM orders "
"JOIN order_lines ON (orders.id = order_lines.order_id) "
"JOIN product_variants ON (order_lines.product_variant_id = product_variants.id) "
"JOIN product_variant_skus ON (product_variants.id = product_variant_skus.product_variant_id) "
"JOIN skus ON (skus.id = product_variant_skus.sku_id) "
"JOIN products ON (products.id = product_variants.product_id) "
"WHERE (orders.status = 1)"))))

(testing "update-in!"
(expect [update-or-insert-vals (has-args [:users ["(id = ?)" 1] {:name "Bob"}])
find-connection (returns true)]
Expand Down