From 8d21c6b0c6d31c203a3acd5b960562864781fd7c Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Sun, 27 Nov 2022 16:57:10 +0100 Subject: [PATCH 1/3] Remove array_agg to support cardinality :many in MS SQL MS SQL does not support arrays. This patch removes array_agg and moves aggregation into Clojure code. --- .../rad/database_adapters/sql/query.clj | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj b/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj index 139c72a..09b458d 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj +++ b/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj @@ -30,6 +30,19 @@ (boolean? v) v :else (str "'" v "'"))) +(defn group-join-results + "Takes result of join query in format [{:c0 a, :c1 b}] and transforms it into + format [{:c0 a, :c1 [b ...]}]" + [result] + (->> result + (reduce (fn [acc {:keys [c0 c1]}] + (update acc c0 #(conj! (or % (transient [])) c1))) + {}) + (reduce-kv (fn [acc c0 c1ts] + (conj! acc {:c0 c0, :c1 (persistent! c1ts)})) + (transient [])) + persistent!)) + (>defn to-many-join-column-query [{::attr/keys [key->attribute] :as env} {::attr/keys [target cardinality identities qualified-key] :as attr} ids] [any? ::attr/attribute coll? => (? (s/tuple string? ::attr/attributes))] @@ -46,7 +59,7 @@ table (table-name key->attribute target-attr) ; address target-id-column (column-name target-attr) ; id id-list (str/join "," (map q ids))] - [(format "SELECT %1$s.%2$s AS c0, array_agg(%3$s.%4$s) AS c1 FROM %1$s LEFT JOIN %3$s ON %1$s.%2$s = %3$s.%5$s WHERE %1$s.%2$s IN (%6$s) GROUP BY %1$s.%2$s" + [(format "SELECT %1$s.%2$s AS c0, %3$s.%4$s AS c1 FROM %1$s LEFT JOIN %3$s ON %1$s.%2$s = %3$s.%5$s WHERE %1$s.%2$s IN (%6$s)" rev-target-table rev-target-column table target-id-column column id-list) [reverse-target-attr attr]] (throw (ex-info "Cannot create to-many reference column." {:k qualified-key})))))) @@ -153,7 +166,8 @@ results-by-id (reduce (fn [result [join-query join-attributes]] (let [join-rows (log/spy :debug (sql/query datasource [join-query] {:builder-fn row-builder})) - join-eql-results (log/spy :debug (sql-results->edn-results join-rows join-attributes)) + join-rows-groups (log/spy :debug (group-join-results join-rows)) + join-eql-results (log/spy :debug (sql-results->edn-results join-rows-groups join-attributes)) join-result-by-id (log/spy :debug (enc/keys-by id-key join-eql-results))] (deep-merge result join-result-by-id))) base-result-map-by-id From 876902c440acedcd2201d2a45a0b313fd4902f7b Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Thu, 8 Dec 2022 22:54:41 +0100 Subject: [PATCH 2/3] Replace LEFT JOIN by JOIN Imagine you have Account and Address one-to-many relation. You filter on Account.ID and left-join Address to Account. Then not-only you filter by Address.AccountId but moreover Account must exist. Now is LEFT JOIN necessary? It would return result [Account.ID, NULL] if no Address exist. But these results are filtered out in later stages of query post-processing, because when there is no Address, adapter will return {::account/id ID} and not {::account/id ID, ::account/addresses []}. LEFT JOIN is not necessary. --- src/main/com/fulcrologic/rad/database_adapters/sql/query.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj b/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj index 09b458d..3513ca5 100644 --- a/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj +++ b/src/main/com/fulcrologic/rad/database_adapters/sql/query.clj @@ -59,7 +59,7 @@ table (table-name key->attribute target-attr) ; address target-id-column (column-name target-attr) ; id id-list (str/join "," (map q ids))] - [(format "SELECT %1$s.%2$s AS c0, %3$s.%4$s AS c1 FROM %1$s LEFT JOIN %3$s ON %1$s.%2$s = %3$s.%5$s WHERE %1$s.%2$s IN (%6$s)" + [(format "SELECT %1$s.%2$s AS c0, %3$s.%4$s AS c1 FROM %1$s JOIN %3$s ON %1$s.%2$s = %3$s.%5$s WHERE %1$s.%2$s IN (%6$s)" rev-target-table rev-target-column table target-id-column column id-list) [reverse-target-attr attr]] (throw (ex-info "Cannot create to-many reference column." {:k qualified-key})))))) From 2184e92e9422d5324dc8c05134b73d6f5a39c733 Mon Sep 17 00:00:00 2001 From: Daniel Skarda Date: Thu, 8 Dec 2022 23:03:11 +0100 Subject: [PATCH 3/3] Fix test for generated SELECT --- .../com/fulcrologic/rad/database_adapters/sql/query_test.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/com/fulcrologic/rad/database_adapters/sql/query_test.clj b/src/test/com/fulcrologic/rad/database_adapters/sql/query_test.clj index f716a0e..63219d6 100644 --- a/src/test/com/fulcrologic/rad/database_adapters/sql/query_test.clj +++ b/src/test/com/fulcrologic/rad/database_adapters/sql/query_test.clj @@ -34,7 +34,7 @@ [1 2 3])] (assertions "Generates the correct SQL" - actual => "SELECT accounts.id AS c0, array_agg(addresses.id) AS c1 FROM accounts LEFT JOIN addresses ON accounts.id = addresses.accounts_addresses_accounts_id WHERE accounts.id IN (1,2,3) GROUP BY accounts.id" + actual => "SELECT accounts.id AS c0, addresses.id AS c1 FROM accounts JOIN addresses ON accounts.id = addresses.accounts_addresses_accounts_id WHERE accounts.id IN (1,2,3)" "Returns the attributes in the order they appear in the query" attrs => [attrs/account-id attrs/account-addresses])))