summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarlo Zancanaro <carlo@clearboxsystems.com.au>2013-06-21 17:53:21 +1000
committerCarlo Zancanaro <carlo@clearboxsystems.com.au>2013-06-21 17:53:21 +1000
commit1975c39d905d32b071fc6488cf3b22c6bf227489 (patch)
tree28a8635903dff01f07b57b6d94e8c557fd54abf0 /src
parente02f9fde1e5e0d4097d15ec11141f3998dd11308 (diff)
Docstrings, re-export from core better (with metadata)
Diffstat (limited to 'src')
-rw-r--r--src/clojure_sql/core.clj29
-rw-r--r--src/clojure_sql/dsl.clj105
2 files changed, 110 insertions, 24 deletions
diff --git a/src/clojure_sql/core.clj b/src/clojure_sql/core.clj
index 03f992b..81c9c13 100644
--- a/src/clojure_sql/core.clj
+++ b/src/clojure_sql/core.clj
@@ -7,6 +7,23 @@
[clojure-sql.util :as u]
[clojure.walk]))
+
+(defmacro pull [ns & vlist]
+ `(do ~@(for [i vlist
+ :let [sym (symbol (name ns) (name i))]]
+ `(def ~(with-meta i
+ (u/map-vals (fn [x] `(quote ~x)) (meta (resolve sym))))
+ ~sym))
+ nil))
+
+(pull clojure-sql.dsl
+ table join
+ project rename
+ select
+ group having
+ sort take drop)
+
+
(def ^:private ^:dynamic *database-type* nil)
(defn set-database-type! [new-type]
(alter-var-root #'*database-type* (constantly new-type))
@@ -24,18 +41,6 @@
(pr (c/compile-select *database-type* query))))
-(def table #'d/table)
-(def project #'d/project)
-(def rename #'d/rename)
-(def join #'d/join)
-(def select #'d/select)
-(def group #'d/group)
-(def having #'d/having)
-(def sort #'d/sort)
-(def take #'d/take)
-(def drop #'d/drop)
-
-
(defn run-query [query]
(assert *query-executor* "Cannot execute a query without a query executor")
(*query-executor* :select (c/compile-select *database-type* query)))
diff --git a/src/clojure_sql/dsl.clj b/src/clojure_sql/dsl.clj
index 10b1e8f..73de216 100644
--- a/src/clojure_sql/dsl.clj
+++ b/src/clojure_sql/dsl.clj
@@ -74,7 +74,10 @@
(throw (ex-info "Cannot union queries with different fields"
{:queries queries})))
-(defn table [arg]
+(defn table
+ "Create a query on a database table. If `arg` is itself a query it
+ will be wrapped, otherwise `arg` will be used as the table name."
+ [arg]
(q/map->Query (let [alias (keyword (gensym (if (u/named? arg)
(name arg)
"table")))]
@@ -97,7 +100,15 @@
(keyword? expression) (resolve-field table aliases expression)
:else expression))
-(defn project [query fields]
+(defn project
+ "Limit a query's fields to a specified set of fields. `fields` can
+ be either a seq of allowed field names, or a map of field names to
+ aliases.
+
+ If the query is currently on a single table then any unknown fields
+ will be resolved as columns in that table. If the query is on more
+ than one table then an exception will be thrown for unknown fields."
+ [query fields]
(let [table (if (= (count (:tables query)) 1)
(-> query :tables first key))
alias-lookup (or (:fields query) {})
@@ -128,18 +139,32 @@
(missing-rename-error field field-renames query)))
(rename-with-fn query field-renames))
-(defn rename [query field-renames]
+(defn rename
+ "Rename fields in a query. All fields must already exist prior to
+ calling rename (no automatic creation like `project`).
+
+ `field-renames` can be either a function or a map. If a function is
+ provided then it will be applied to each field: the return value
+ will be used as the new alias, with a return of nil indicating no
+ change. If a map is provided then it will behave identically to a
+ function, but will throw errors if you are attempting to rename a
+ non-existent field."
+ [query field-renames]
(if (map? field-renames)
(rename-with-map query field-renames)
(rename-with-fn query field-renames)))
-(defn prefix-names-matching [pred prefix]
+(defn prefix-names-matching
+ "Higher-order helper function to use with `rename`."
+ [pred prefix]
(fn [alias]
(if (pred alias)
(keyword (str prefix (name alias)))
alias)))
-(defn prefix-names [prefix]
+(defn prefix-names
+ "Higher-order helper function to use with `rename`."
+ [prefix]
(prefix-names-matching (constantly true) prefix))
@@ -178,7 +203,20 @@
(convert-to-subquery (remove-sort query)))))
(def ^:private valid-join-type? (comp boolean #{:cross :inner :outer :full-outer}))
-(defn join [left right & {:keys [on type]}]
+(defn join
+ "Join two queries into one query The fields of the resultant query
+ will be the union of the argument queries.
+
+ If `type` is not provided then the join type will be automatically
+ set: if the arguments have any fields in common, or if an `on` is
+ provided, then an inner join will be performed (joining on common
+ attributes if no `on` is provided), otherwise a cross join will be
+ performed.
+
+ Valid join types are :cross, :inner, :outer and :full-outer. An
+ outer join is considered a LEFT outer join. To achieve a right outer
+ join reverse the query arguments. "
+ [left right & {:keys [on type]}]
(if (= type :right)
(join right left :on on :type :left)
(let [type (if (= type :left)
@@ -230,7 +268,18 @@
:sort (seq (concat (:sort left)
(:sort right))))))))
-(defn select [query expression]
+(defn select
+ "Apply a filter to a query. The expression is an unevaluated expression
+ which is compiled by the clojure-sql compiler.
+
+ Any keywords present in the query are interpreted as field names. A
+ quote can be used to suppress evaluation of terms in the
+ expression.
+
+ Example:
+ (select query `(= :id 10)) - filter for an id of 10
+ (select query `(in :id '(1 2 3)) - filter for an id of 1, 2 or 3"
+ [query expression]
(let [table-name (if (= (count (:tables query)) 1)
(-> query :tables first key))
old-where (:where query)
@@ -238,7 +287,13 @@
new-where (combine-conjunctions old-where resolved-expression)]
(assoc query :where new-where)))
-(defn sort [query fields]
+(defn sort
+ "Apply a sort to a query.
+
+ `fields` is a sequential collection of fields to sort by. Each
+ element of fields can be either a field name, :field, or a vector of
+ field and direction, [:field :desc]."
+ [query fields]
(let [table-name (if (= (count (:tables query)) 1)
(-> query :tables first key))
fields-seq (if (sequential? fields)
@@ -251,7 +306,11 @@
[(resolve-field table-name fields-lookup (first field)) (second field)]
[(resolve-field table-name fields-lookup field) :asc])))))
-(defn group [query fields]
+(defn group
+ "Apply a grouping to a query.
+
+ `fields` is a sequential collection of fields to group by."
+ [query fields]
(let [table-name (if (= (count (:tables query)) 1)
(-> query :tables first key))
fields-seq (if (sequential? fields)
@@ -261,7 +320,17 @@
(assoc query :group (map (partial resolve-field table-name fields-lookup)
fields-seq))))
-(defn having [query expression]
+(defn having
+ "Apply a filter to the groupings of a query.
+
+ `expression` is the same as a `select`, but may only reference
+ fields by which the query is grouped (see `group`) or other fields
+ within aggregating functions (eg. count).
+
+ Example:
+ (having query `(< (sum :age) 100)) - select groups with a combined
+ age under 100"
+ [query expression]
(let [table-name (if (= (count (:tables query)) 1)
(-> query :tables first key))
old-having (:having query)
@@ -269,12 +338,24 @@
new-having (combine-conjunctions old-having resolved-expression)]
(assoc query :having new-having)))
-(defn take [query n]
+(defn take
+ "Limit the number of results of a query.
+
+ Note: take/drop will function as they do on clojure sequences. They
+ will not simply overwrite the previous take/drop value. Example:
+ (-> query (take 10) (drop 2)) = (-> query (drop 2) (take 8))"
+ [query n]
(if-let [old-take (:take query)]
(assoc query :take (min old-take n))
(assoc query :take n)))
-(defn drop [query n]
+(defn drop
+ "Exclude the first `n` results of a query.
+
+ Note: take/drop will function as they do on clojure sequences. They
+ will not simply overwrite the previous take/drop value. Example:
+ (-> query (take 10) (drop 2)) = (-> query (drop 2) (take 8))"
+ [query n]
(let [query (if-let [old-take (:take query)]
(assoc query :take (- old-take n))
query)]