summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md16
-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
6 files changed, 84 insertions, 52 deletions
diff --git a/README.md b/README.md
index 97f1afb..650c9fe 100644
--- a/README.md
+++ b/README.md
@@ -229,7 +229,21 @@ be the return value of the associated query function call (`deref`,
(q/group [:age] {:name :name}))
;=> Exception! Expr is not a function application - could not possible be an aggregate
-* **Breaking:** Remove `having`, use `select` instead now
+* **Breaking:** remove `having`, use `select` instead now
+
+* **Breaking:** make query executors query local, rather than
+ global. This changed two things in particular:
+
+ * `table` now takes a second, optional, argument representing a
+ query executor
+
+ * `set-query-executor!` is no longer present, use
+ `set-default-query-executor!` instead (which will be used
+ whenever `table`'s optional argument is omitted)
+
+ As a result of this, the `use-jdbc!` function has been
+ removed. To connect to a database via jdbc use this:
+ `(set-default-query-executor (clojure-sql.jdbc/jdbc-executor "connection-string"))`
* `sort` can now sort on arbitrary expressions, not just fields
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))
-