summaryrefslogtreecommitdiff
path: root/src/clojure_sql
diff options
context:
space:
mode:
authorCarlo Zancanaro <carlo@clearboxsystems.com.au>2013-11-12 16:58:44 +1100
committerCarlo Zancanaro <carlo@clearboxsystems.com.au>2013-11-12 16:58:44 +1100
commitfe513564989d9151a79d5494f2958ae190c20d02 (patch)
treee2a802702a1fb19f4dfe7220bc9f09a3710f239b /src/clojure_sql
parent1685984030a9d1cd588196d9e2cf648e57034f72 (diff)
Change the query executor model: now it's query local and the interface is managed by a protocol.
Diffstat (limited to 'src/clojure_sql')
-rw-r--r--src/clojure_sql/compiler.clj2
-rw-r--r--src/clojure_sql/core.clj39
-rw-r--r--src/clojure_sql/dsl.clj18
-rw-r--r--src/clojure_sql/jdbc.clj37
-rw-r--r--src/clojure_sql/query.clj24
5 files changed, 69 insertions, 51 deletions
diff --git a/src/clojure_sql/compiler.clj b/src/clojure_sql/compiler.clj
index 87899f9..50c14af 100644
--- a/src/clojure_sql/compiler.clj
+++ b/src/clojure_sql/compiler.clj
@@ -254,7 +254,7 @@
-(defn compile-insert [db {:keys [fields tables joins]} & records]
+(defn compile-insert [db {:keys [fields tables joins]} records]
(assert (= (count tables) 1) "Cannot insert into a multiple-table query")
(let [fields-order (map key fields)
wrap #(str "INSERT INTO "
diff --git a/src/clojure_sql/core.clj b/src/clojure_sql/core.clj
index 2a9e77f..415c7f4 100644
--- a/src/clojure_sql/core.clj
+++ b/src/clojure_sql/core.clj
@@ -22,40 +22,27 @@
select
group
sort take drop
- union intersection)
-
-
-(def ^:private ^:dynamic *database-type* nil)
-(defn set-database-type! [new-type]
- (alter-var-root #'*database-type* (constantly new-type))
- nil)
-
-(def ^:private ^:dynamic *query-executor* (comp second vector))
-(defn set-query-executor! [exec-fn]
- (alter-var-root #'*query-executor* (constantly exec-fn))
- nil)
-
-
+ union intersection
+ set-default-query-executor!)
(defmethod print-method clojure_sql.query.Query [query writer]
(binding [*out* writer]
- (pr (c/compile-select *database-type* query))))
+ (pr (c/compile-select nil query))))
(defn run-query
"Run a select query. Return value is determined by query executor."
[query]
- (assert *query-executor* "Cannot execute a query without a query executor")
- (*query-executor* :select (c/compile-select *database-type* query)))
+ (assert (:executor query) "Cannot execute a query without a query executor")
+ (q/query (:executor query) query))
(defn insert!
"Insert a number of records into a table, setting each column to the
corresponding value from the record. Return value is determined by
query executor."
[query & records]
- (assert *query-executor* "Cannot execute a query without a query executor")
- (let [compiled (apply c/compile-insert *database-type* query records)]
- (*query-executor* :insert compiled)))
+ (assert (:executor query) "Cannot execute a query without a query executor")
+ (q/insert! (:executor query) query records))
(defn update!
"Update everything which would have been selected by the query,
@@ -67,16 +54,12 @@
with `project` before calling `update!`."
[query partial-record]
- (assert *query-executor* "Cannot execute a query without a query executor")
- (let [compiled (c/compile-update *database-type* query partial-record)]
- (*query-executor* :update compiled)))
+ (assert (:executor query) "Cannot execute a query without a query executor")
+ (q/update! (:executor query) query partial-record))
(defn delete!
"Delete everything which would have been selected by the
query. Return value is determined by query executor."
[query]
- (assert *query-executor* "Cannot execute a query without a query executor")
- (let [compiled (c/compile-delete *database-type* query)]
- (*query-executor* :delete compiled)))
-
-(q/set-query-deref-behaviour! run-query)
+ (assert (:executor query) "Cannot execute a query without a query executor")
+ (q/delete! (:executor query) query))
diff --git a/src/clojure_sql/dsl.clj b/src/clojure_sql/dsl.clj
index 413b9c0..2a22eea 100644
--- a/src/clojure_sql/dsl.clj
+++ b/src/clojure_sql/dsl.clj
@@ -85,14 +85,21 @@
(keyword? expression) (resolve-field tables aliases expression)
:else expression))
+(def ^:dynamic *default-executor* nil)
+(defn set-default-query-executor! [executor]
+ (alter-var-root #'*default-executor* (constantly executor)))
+(defmacro with-default-query-executor [executor & body]
+ `(binding [*default-executor* ~executor] ~@body))
+
(defn table
"Create a query on a database table. If `table` is itself a query it
will be wrapped, otherwise `table` will be used as the table name."
- [table]
+ [table & [executor]]
(q/map->Query (let [table-name (if (u/named? table) (name table) "table")
table-keyword (keyword (gensym table-name))]
{:tables {table-keyword table}
- :joins [table-keyword]})))
+ :joins [table-keyword]
+ :executor (or executor *default-executor*)})))
(defn ^:private into-map-duplicate-error [coll error-fn]
(reduce (fn [acc [k v]]
@@ -229,6 +236,7 @@
outer join is considered a LEFT outer join. To achieve a right outer
join reverse the query arguments."
[left right & {:keys [on type]}]
+ (assert (= (:executor left) (:executor right)) "Cannot join queries with different executors.")
(let [left (make-join-subquery left)
right (rename-all-tables (make-join-subquery right))
merged-tables (merge (:tables left) (:tables right))
@@ -387,7 +395,8 @@
(defn ^:private union-compatible? [& queries]
(and (every? (comp seq keys :fields) queries)
- (apply = (map (comp set keys :fields) queries))))
+ (apply = (map (comp set keys :fields) queries))
+ (apply = (map :executor queries))))
(defn union
"Combine the results of two queries.
@@ -407,4 +416,5 @@
{:pre [(apply union-compatible? queries)]}
(convert-to-subquery (q/map->Query {:set-operation :intersect
:queries queries
- :fields (zipmap (keys (:fields (first queries))) (repeat nil))})))
+ :fields (zipmap (keys (:fields (first queries))) (repeat nil))
+ :executor (:executor (first queries))})))
diff --git a/src/clojure_sql/jdbc.clj b/src/clojure_sql/jdbc.clj
index 44ed55e..5a3a55d 100644
--- a/src/clojure_sql/jdbc.clj
+++ b/src/clojure_sql/jdbc.clj
@@ -1,6 +1,8 @@
(ns clojure-sql.jdbc
(:require [clojure.java.jdbc :as jdbc]
- [clojure-sql.core :refer [set-query-executor!]]
+ [clojure-sql.query :refer [QueryExecutor fn->QueryExecutor]]
+ [clojure-sql.compiler :as compiler]
+ ;;[clojure-sql.core :refer [set-query-executor!]]
[clojure.string :as string]))
(defn ^:private dotted-to-nested-map-one [obj]
@@ -16,13 +18,26 @@
(defn ^:private dotted-to-nested-maps [objs]
(mapv dotted-to-nested-map-one objs))
-(defn use-jdbc! [connection-string]
- (set-query-executor! (fn [type query]
- (jdbc/with-connection connection-string
- (case type
- :select (jdbc/with-query-results results query
- (dotted-to-nested-maps results))
- :insert (jdbc/do-prepared-return-keys (first query) (next query))
- :update (jdbc/do-prepared-return-keys (first query) (next query))
- :delete (first (jdbc/do-prepared (first query) (next query)))
- (assert false (str "Unknown query type: " type)))))))
+(defn jdbc-executor [connection-string]
+ (let [[_ db-type] (re-find #"^jdbc:([^:]+)" connection-string)]
+ (reify QueryExecutor
+ (query [_ query]
+ (let [compiled (compiler/compile-select db-type query)]
+ (jdbc/with-connection connection-string
+ (jdbc/with-query-results results compiled
+ (dotted-to-nested-maps results)))))
+
+ (insert! [_ query records]
+ (let [compiled (compiler/compile-insert db-type query records)]
+ (jdbc/with-connection connection-string
+ (jdbc/do-prepared-return-keys (first query) (next query)))))
+
+ (update! [_ query partial-record]
+ (let [compiled (compiler/compile-update db-type query partial-record)]
+ (jdbc/with-connection connection-string
+ (jdbc/do-prepared-return-keys (first query) (next query)))))
+
+ (delete! [_ query]
+ (let [compiled (compiler/compile-delete db-type query)]
+ (jdbc/with-connection connection-string
+ (first (jdbc/do-prepared (first query) (next query)))))))))
diff --git a/src/clojure_sql/query.clj b/src/clojure_sql/query.clj
index 218ae7a..113e748 100644
--- a/src/clojure_sql/query.clj
+++ b/src/clojure_sql/query.clj
@@ -1,13 +1,23 @@
(ns clojure-sql.query)
-(def ^:private ^:dynamic *query-deref-behaviour* identity)
-(defn set-query-deref-behaviour! [f]
- (alter-var-root #'*query-deref-behaviour* (constantly f))
- nil)
+(defprotocol QueryExecutor
+ (query [_ query]
+ "Retrieve information from the database.")
+ (insert! [_ query records]
+ "Insert a number of records into a table, setting each column to the corresponding value in its record.")
+ (update! [_ query partial-record]
+ "Update every row which this query would select by setting each field to the corresponding value in partial-record.")
+ (delete! [_ query]
+ "Delete every row which this query would select."))
-(defrecord ^:private Query []
+(defn fn->QueryExecutor [f]
+ (reify QueryExecutor
+ (query [_ query] ())))
+
+(defrecord ^:private Query [executor]
clojure.lang.IDeref
- (deref [this] (*query-deref-behaviour* this)))
+ (deref [this]
+ (assert executor "Cannot deref a query without a query executor")
+ (query executor this)))
(def query? (partial instance? Query))
-