korma.mysql
count
(count _query_ v)On MySQL, when an argument for COUNT() is a '*', 3 | it must be a simple '*', instead of 'fieldname.*'.
├── .travis.yml ├── .gitignore ├── test └── korma │ └── test │ ├── sql │ └── utils.clj │ ├── integration │ ├── delete.clj │ ├── driver_properties.clj │ ├── mysql.clj │ ├── per_query_database.clj │ ├── update.clj │ ├── with_db.clj │ ├── one_to_one.clj │ ├── one_to_many.clj │ └── helpers.clj │ └── db.clj ├── src └── korma │ ├── sql │ ├── utils.clj │ ├── fns.clj │ └── engine.clj │ ├── db.clj │ └── core.clj ├── project.clj ├── doc ├── korma.mysql.html ├── js │ └── page_effects.js ├── korma.config.html ├── index.html ├── css │ └── default.css ├── korma.db.html └── korma.core.html ├── README.md └── HISTORY.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: clojure 2 | script: lein run-tests 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | .lein-failures 6 | .lein-deps-sum 7 | .lein-repl-history 8 | .nrepl-port 9 | *.swp 10 | /target/ 11 | *.iml 12 | /.idea 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /test/korma/test/sql/utils.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.sql.utils 2 | (:use [clojure.test :only [deftest is testing]] 3 | [korma.sql.utils :only [left-assoc]])) 4 | 5 | (deftest test-left-assoc 6 | (testing "left-assoc with empty list" 7 | (is (= "" (left-assoc [])))) 8 | (testing "left-assoc with one item" 9 | (is (= "1" (left-assoc (list 1))))) 10 | (testing "left-assoc with multiple items" 11 | (is (= "(((1)2)3)4" (left-assoc (list 1 2 3 4)))))) 12 | -------------------------------------------------------------------------------- /test/korma/test/integration/delete.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.delete 2 | (:refer-clojure :exclude [update]) 3 | (:use clojure.test 4 | korma.core 5 | korma.db 6 | [korma.test.integration.helpers :only [populate address]])) 7 | 8 | (defdb mem-db (h2 {:db "mem:delete"})) 9 | 10 | (use-fixtures :once (fn [f] 11 | (default-connection mem-db) 12 | (populate 10) 13 | (f))) 14 | 15 | (deftest delete-returns-count-of-deleted-rows 16 | (testing "Deleting one row" 17 | (is (= 1 (delete address (where {:id 1}))))) 18 | (testing "Deleting multiple rows" 19 | (is (= 3 (delete address (where {:id [< 5]}))))) 20 | (testing "No rows are deleted" 21 | (is (= 0 (delete address (where {:id -1})))))) 22 | -------------------------------------------------------------------------------- /test/korma/test/integration/driver_properties.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.driver-properties 2 | (:refer-clojure :exclude [update]) 3 | (:use clojure.test 4 | korma.db 5 | korma.core 6 | [korma.test.integration.helpers :only [reset-schema]])) 7 | 8 | (defdb mem-db (h2 {:db "mem:driver_properties"})) 9 | (defdb mem-db-no-literals-pooled (h2 {:db "mem:driver_properties" :ALLOW_LITERALS "NONE"})) 10 | (defdb mem-db-no-literals-without-pool (h2 {:db "mem:driver_properties" :ALLOW_LITERALS "NONE" :make-pool? false})) 11 | 12 | (deftest driver-properties-can-be-set-in-db-spec 13 | (with-db mem-db 14 | (reset-schema)) 15 | (testing "Using connection pool" 16 | (is (thrown-with-msg? org.h2.jdbc.JdbcSQLException 17 | #"Literals of this kind are not allowed" 18 | (with-db mem-db-no-literals-pooled 19 | (exec-raw "select * from \"users\" where id = 1"))))) 20 | (testing "Without connection pool" 21 | (is (thrown-with-msg? org.h2.jdbc.JdbcSQLException 22 | #"Literals of this kind are not allowed" 23 | (with-db mem-db-no-literals-without-pool 24 | (exec-raw "select * from \"users\" where id = 1")))))) 25 | -------------------------------------------------------------------------------- /test/korma/test/integration/mysql.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.mysql 2 | (:refer-clojure :exclude [update]) 3 | (:require [clojure.java.jdbc :as jdbc]) 4 | (:use clojure.test 5 | korma.core 6 | korma.db)) 7 | 8 | (defdb live-db-mysql (mysql {:db "korma" :user "root"})) 9 | (defentity users-live-mysql (database live-db-mysql)) 10 | 11 | (def mysql-uri 12 | {:connection-uri "jdbc:mysql://localhost/?user=root"}) 13 | 14 | (defn- setup-korma-db [] 15 | (jdbc/db-do-commands mysql-uri 16 | ["CREATE DATABASE IF NOT EXISTS korma;" 17 | "USE korma;" 18 | "CREATE TABLE IF NOT EXISTS `users-live-mysql` (name varchar(200));"])) 19 | 20 | (defn- clean-korma-db [] 21 | (jdbc/db-do-commands mysql-uri "DROP DATABASE korma;")) 22 | 23 | (use-fixtures :each (fn [f] 24 | (default-connection live-db-mysql) 25 | (setup-korma-db) 26 | (f) 27 | (clean-korma-db))) 28 | 29 | (deftest test-nested-transactions-work 30 | (transaction 31 | (insert users-live-mysql (values {:name "thiago"})) 32 | (transaction 33 | (update users-live-mysql (set-fields {:name "THIAGO"}) (where {:name "thiago"}))))) 34 | 35 | (deftest mysql-count 36 | (insert users-live-mysql (values {:name "thiago"})) 37 | (is (= [{:cnt 1}] 38 | (select users-live-mysql (aggregate (count :*) :cnt))))) 39 | -------------------------------------------------------------------------------- /src/korma/sql/utils.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} 2 | korma.sql.utils 3 | (:require [clojure.string :as string])) 4 | 5 | ;;***************************************************** 6 | ;; map-types 7 | ;;***************************************************** 8 | 9 | (defn generated [s] 10 | {::generated s}) 11 | 12 | (defn sub-query [s] 13 | {::sub s}) 14 | 15 | (defn pred [p args] 16 | {::pred p ::args args}) 17 | 18 | (defn func [f args] 19 | {::func f ::args args}) 20 | 21 | (defn func? [m] 22 | (::func m)) 23 | 24 | (defn pred? [m] 25 | (::pred m)) 26 | 27 | (defn args? [m] 28 | (::args m)) 29 | 30 | (defn sub-query? [m] 31 | (::sub m)) 32 | 33 | (defn generated? [m] 34 | (::generated m)) 35 | 36 | (defn special-map? [m] 37 | (boolean (some #(% m) [func? pred? sub-query? generated?]))) 38 | 39 | ;;***************************************************** 40 | ;; str-utils 41 | ;;***************************************************** 42 | 43 | (defn comma-separated [vs] 44 | (string/join ", " vs)) 45 | 46 | (defn wrap [v] 47 | (str "(" v ")")) 48 | 49 | (defn left-assoc [vs] 50 | (loop [ret "" [v & vs] vs] 51 | (cond 52 | (nil? v) ret 53 | (nil? vs) (str ret v) 54 | :else (recur (wrap (str ret v)) vs)))) 55 | 56 | ;;***************************************************** 57 | ;; collection-utils 58 | ;;***************************************************** 59 | 60 | (defn vconcat [v1 v2] 61 | (vec (concat v1 v2))) 62 | -------------------------------------------------------------------------------- /test/korma/test/integration/per_query_database.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.per-query-database 2 | (:use clojure.test 3 | [korma.db :only [h2 with-db create-db default-connection]] 4 | [korma.core :only [defentity has-many exec-raw insert values select with database]])) 5 | 6 | (def mem-db (create-db (h2 {:db "mem:query_database"}))) 7 | 8 | (defentity email) 9 | 10 | (defentity user 11 | (has-many email)) 12 | 13 | (def schema 14 | ["drop table if exists \"user\";" 15 | "drop table if exists \"email\";" 16 | "create table \"user\" (\"id\" integer primary key, 17 | \"name\" varchar(100));" 18 | "create table \"email\" (\"id\" integer primary key, 19 | \"user_id\" integer, 20 | \"email_address\" varchar(100), 21 | foreign key (\"user_id\") references \"user\"(\"id\"));"]) 22 | 23 | (defn- setup-db [] 24 | (with-db mem-db 25 | (dorun (map exec-raw schema)) 26 | (insert user (values {:id 1 :name "Chris"})) 27 | (insert email (values {:id 1 :user_id 1 :email_address "chris@email.com"})))) 28 | 29 | (use-fixtures :once (fn [f] 30 | (default-connection nil) 31 | (setup-db) 32 | (f))) 33 | 34 | (deftest use-database-from-parent-when-fetching-children 35 | (is (= [{:id 1 :name "Chris" :email [{:id 1 :user_id 1 :email_address "chris@email.com"}]}] 36 | (select user 37 | (database mem-db) 38 | (with email))))) 39 | -------------------------------------------------------------------------------- /test/korma/test/integration/update.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.update 2 | (:refer-clojure :exclude [update]) 3 | (:require [clojure.string]) 4 | (:use clojure.test 5 | [korma.db :only [defdb h2 default-connection]] 6 | [korma.core :only [defentity table transform exec-raw insert values update update* set-fields where]])) 7 | 8 | (defdb mem-db (h2 {:db "mem:update"})) 9 | 10 | (defentity user) 11 | 12 | (defentity user-with-transform 13 | (table :user) 14 | (transform #(update-in % [:name] clojure.string/capitalize))) 15 | 16 | (def schema 17 | ["drop table if exists \"user\";" 18 | "create table \"user\" (\"id\" integer primary key, 19 | \"name\" varchar(100), 20 | \"active\" boolean default false);"]) 21 | 22 | (defn- setup-db [] 23 | (dorun (map exec-raw schema)) 24 | (insert user (values [{:id 1 :name "James"} 25 | {:id 2 :name "John"} 26 | {:id 3 :name "Robert"}]))) 27 | 28 | (use-fixtures :once (fn [f] 29 | (default-connection mem-db) 30 | (setup-db) 31 | (f))) 32 | 33 | (deftest update-returns-count-of-updated-rows 34 | (testing "Updating one row" 35 | (is (= 1 (update user (set-fields {:active true}) (where {:id 1}))))) 36 | (testing "Updating multiple rows" 37 | (is (= 3 (update user (set-fields {:active true}))))) 38 | (testing "No rows are updated" 39 | (is (= 0 (update user (set-fields {:active true}) (where {:id [> 10]}))))) 40 | (testing "Entity with transform fn" 41 | (is (= 1 (update user-with-transform (set-fields {:active true}) (where {:id 1})))))) 42 | -------------------------------------------------------------------------------- /src/korma/sql/fns.clj: -------------------------------------------------------------------------------- 1 | (ns korma.sql.fns 2 | (:require [korma.db :as db] 3 | [korma.sql.engine :as eng] 4 | [korma.sql.utils :as utils]) 5 | (:use [korma.sql.engine :only [infix group-with sql-func trinary wrapper]])) 6 | 7 | ;;***************************************************** 8 | ;; Predicates 9 | ;;***************************************************** 10 | 11 | 12 | (defn pred-and [& args] 13 | (apply eng/pred-and args)) 14 | 15 | (defn pred-or [& args] (group-with " OR " args)) 16 | (defn pred-not [v] (wrapper "NOT" v)) 17 | 18 | (defn pred-in [k v] (infix k "IN" v)) 19 | (defn pred-not-in [k v] (infix k "NOT IN" v)) 20 | (defn pred-> [k v] (infix k ">" v)) 21 | (defn pred-< [k v] (infix k "<" v)) 22 | (defn pred->= [k v] (infix k ">=" v)) 23 | (defn pred-<= [k v] (infix k "<=" v)) 24 | (defn pred-like [k v] (infix k "LIKE" v)) 25 | (defn pred-ilike [k v] (infix k "ILIKE" v)) 26 | 27 | (defn pred-exists [v] (wrapper "EXISTS" v)) 28 | 29 | (defn pred-between [k [v1 v2]] 30 | (trinary k "BETWEEN" v1 "AND" v2)) 31 | 32 | (def pred-= eng/pred-=) 33 | (defn pred-not= [k v] (cond 34 | (and k v) (infix k "<>" v) 35 | k (infix k "IS NOT" v) 36 | v (infix v "IS NOT" k))) 37 | 38 | ;;***************************************************** 39 | ;; Aggregates 40 | ;;***************************************************** 41 | 42 | (defn agg-count [_query_ v] 43 | (if (= "*" (name v)) 44 | (sql-func "COUNT" (utils/generated "*")) 45 | (sql-func "COUNT" v))) 46 | 47 | (defn agg-sum [_query_ v] (sql-func "SUM" v)) 48 | (defn agg-avg [_query_ v] (sql-func "AVG" v)) 49 | (defn agg-stdev [_query_ v] (sql-func "STDEV" v)) 50 | (defn agg-min [_query_ v] (sql-func "MIN" v)) 51 | (defn agg-max [_query_ v] (sql-func "MAX" v)) 52 | (defn agg-first [_query_ v] (sql-func "FIRST" v)) 53 | (defn agg-last [_query_ v] (sql-func "LAST" v)) 54 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject korma "0.5.0-RC1" 2 | :description "Tasty SQL for Clojure" 3 | :url "http://github.com/korma/Korma" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :mailing-list {:name "Korma Google Group" 7 | :subscribe "https://groups.google.com/group/sqlkorma"} 8 | :codox {:exclude [korma.sql.engine 9 | korma.sql.fns 10 | korma.sql.utils] 11 | :src-dir-uri "https://github.com/korma/Korma/blob/master/" 12 | :src-linenum-anchor-prefix "L"} 13 | 14 | :dependencies [[org.clojure/clojure "1.8.0"] 15 | [com.mchange/c3p0 "0.9.5.2"] 16 | [org.clojure/java.jdbc "0.6.1"]] 17 | 18 | :min-lein-version "2.0.0" 19 | 20 | :profiles {:dev {:dependencies [[gui-diff "0.6.6"] 21 | [postgresql "9.3-1102.jdbc41"] 22 | [slamhound "1.5.5"] 23 | [criterium "0.4.3"]] 24 | :plugins [[codox "0.8.12"] 25 | [jonase/eastwood "0.2.1"] 26 | [lein-localrepo "0.5.3"]]} 27 | :test {:dependencies [[mysql/mysql-connector-java "5.1.35"] 28 | [com.h2database/h2 "1.4.187"] 29 | [criterium "0.4.3"]]} 30 | :1.4 {:dependencies [[org.clojure/clojure "1.4.0"]]} 31 | :1.5 {:dependencies [[org.clojure/clojure "1.5.1"]]} 32 | :1.6 {:dependencies [[org.clojure/clojure "1.6.0"]]} 33 | :1.7 {:dependencies [[org.clojure/clojure "1.7.0"]]} 34 | :1.8 {:dependencies [[org.clojure/clojure "1.8.0"]]} 35 | :1.9 {:dependencies [[org.clojure/clojure "1.9.0-alpha13"]]}} 36 | :aliases {"run-tests" ["with-profile" "1.4:1.5:1.6:1.7:1.8:1.9" "test"] 37 | "slamhound" ["run" "-m" "slam.hound"]} 38 | :jvm-opts ["-Dline.separator=\n"]) 39 | -------------------------------------------------------------------------------- /test/korma/test/integration/with_db.clj: -------------------------------------------------------------------------------- 1 | (ns korma.test.integration.with-db 2 | (:refer-clojure :exclude [update]) 3 | (:use clojure.test 4 | korma.db 5 | korma.core 6 | korma.test.integration.helpers) 7 | (:import [java.util.concurrent CountDownLatch])) 8 | 9 | (defn mem-db [] 10 | (create-db (h2 {:db "mem:with_db_test"}))) 11 | 12 | (defn mem-db-2 [] 13 | (create-db (h2 {:db "mem:with_db_test_2"}))) 14 | 15 | (deftest with-db-success 16 | (testing "with-db with setting *current-db* in with-db binding" 17 | (with-db (mem-db) (populate 2)) 18 | (is (> (count (:address (first (with-db (mem-db) 19 | (select user (with address))))))) 1))) 20 | 21 | (defn delete-some-rows-from-db [] 22 | (delete address)) 23 | 24 | (deftest thread-safe-with-db 25 | (testing "with-db using binding" 26 | (let [db1 (mem-db) 27 | db2 (mem-db-2)] 28 | ;; let's create some records in the first db 29 | (with-db db1 (populate 2)) 30 | 31 | ;; let's create some records in the second db 32 | (with-db db2 (populate 2)) 33 | 34 | (default-connection db1) 35 | 36 | ;; we will create 2 threads and execute them at the same time 37 | ;; using a CountDownLatch to synchronize their execution 38 | (let [latch (CountDownLatch. 2) 39 | t1 (future (with-db db2 40 | (.countDown latch) 41 | (.await latch) 42 | (delete-some-rows-from-db))) 43 | t2 (future (with-db db1 44 | (.countDown latch) 45 | (.await latch) 46 | (delete-some-rows-from-db)))] 47 | @t1 48 | @t2 49 | (.await latch)) 50 | 51 | (default-connection nil) 52 | 53 | (let [addresses-mem-db-1 (with-db db1 54 | (select address)) 55 | addresses-mem-db-2 (with-db db2 56 | (select address))] 57 | (is (= (count addresses-mem-db-1) 0)) 58 | (is (= (count addresses-mem-db-2) 0)))))) 59 | -------------------------------------------------------------------------------- /doc/korma.mysql.html: -------------------------------------------------------------------------------- 1 | 2 |
(count _query_ v)On MySQL, when an argument for COUNT() is a '*', 3 | it must be a simple '*', instead of 'fieldname.*'.
(set-delimiters & cs)Set the global default for field delimiters in connections. Delimiters can 3 | either be a string or a vector of the start and end: 4 | 5 | (set-delimiters "`") 6 | (set-delimiters ["[" "]"])
(set-naming strategy)Set the naming strategy to use. The strategy should be a map with transforms
7 | to be applied to field names (:fields) and the keys for generated maps (:keys)
8 | e.g:
9 |
10 | (set-naming {:keys string/lower-case :fields string/upper-case})Tasty SQL for Clojure
Core querying and entity functions
Public variables and functions:
Functions for creating and managing database specifications. 3 |
(connection-pool {:keys [connection-uri subprotocol subname classname excess-timeout idle-timeout initial-pool-size minimum-pool-size maximum-pool-size test-connection-query idle-connection-test-period test-connection-on-checkin test-connection-on-checkout], :or {maximum-pool-size 15, idle-timeout (* 3 60 60), excess-timeout (* 30 60), idle-connection-test-period 0, test-connection-query nil, test-connection-on-checkin false, test-connection-on-checkout false, initial-pool-size 3, minimum-pool-size 3}, :as spec})Create a connection pool for the given database spec. 4 |
(create-db spec)Create a db connection object manually instead of using defdb. This is often 5 | useful for creating connections dynamically, and probably should be followed 6 | up with: 7 | 8 | (default-connection my-new-conn) 9 | 10 | If the spec includes `:make-pool? true` makes a connection pool from the spec.
(default-connection conn)Set the database connection that Korma should use by default when no 11 | alternative is specified.
(defdb db-name spec)Define a database specification. The last evaluated defdb will be used by 12 | default for all queries where no database is specified by the entity.
(delay-pool spec)Return a delay for creating a connection pool for the given spec. 13 |
(firebird {:keys [host port db], :or {host "localhost", port 3050, db ""}, :as opts})Create a database specification for a FirebirdSQL database. Opts should include 14 | keys for :db, :user, :password. You can also optionally set host, port and make-pool?
(get-connection db)Get a connection from the potentially delayed connection object. 15 |
(h2 {:keys [db], :or {db "h2.db"}, :as opts})Create a database specification for a h2 database. Opts should include a key 16 | for :db which is the path to the database file.
(is-rollback?)Returns true if the current transaction will be rolled back 17 |
(msaccess {:keys [db], :or {db ""}, :as opts})Create a database specification for a Microsoft Access database. Opts 18 | should include keys for :db and optionally :make-pool?.
(mssql {:keys [user password db host port], :or {user "dbuser", password "dbpassword", db "", host "localhost", port 1433}, :as opts})Create a database specification for a mssql database. Opts should include keys 19 | for :db, :user, and :password. You can also optionally set host and port.
(mysql {:keys [host port db], :or {host "localhost", port 3306, db ""}, :as opts})Create a database specification for a mysql database. Opts should include keys 20 | for :db, :user, and :password. You can also optionally set host and port. 21 | Delimiters are automatically set to "`".
(odbc {:keys [dsn], :or {dsn ""}, :as opts})Create a database specification for an ODBC DSN. Opts 22 | should include keys for :dsn and optionally :make-pool?.
(oracle {:keys [host port], :or {host "localhost", port 1521}, :as opts})Create a database specification for an Oracle database. Opts should include keys 23 | for :user and :password. You can also optionally set host and port.
(postgres {:keys [host port db], :or {host "localhost", port 5432, db ""}, :as opts})Create a database specification for a postgres database. Opts should include 24 | keys for :db, :user, and :password. You can also optionally set host and 25 | port.
(sqlite3 {:keys [db], :or {db "sqlite.db"}, :as opts})Create a database specification for a SQLite3 database. Opts should include a 27 | key for :db which is the path to the database file.
(transaction body)(transaction options & body)Execute all queries within the body in a single transaction. 28 | Optionally takes as a first argument a map to specify the :isolation and :read-only? properties of the transaction.
(vertica {:keys [host port db], :or {host "localhost", port 5433, db ""}, :as opts})Create a database specification for a vertica database. Opts should include keys 29 | for :db, :user, and :password. You can also optionally set host and port. 30 | Delimiters are automatically set to "`".
(with-db db & body)Execute all queries within the body using the given db spec 31 |
Core querying and entity functions 3 |
(aggregate query agg alias & [group-by])Use a SQL aggregator function, aliasing the results, and optionally grouping by 5 | a field: 6 | 7 | (select users 8 | (aggregate (count :*) :cnt :status)) 9 | 10 | Aggregates available: count, sum, avg, min, max, first, last
(belongs-to ent sub-ent)(belongs-to ent sub-ent opts)Add a belongs-to relationship for the given entity. It is assumed that the foreign key 12 | is on the current entity with the format sub-ent-table_id: email.user_id = user.id. 13 | Can optionally pass a map with a :fk key collection or use the helper `fk` function 14 | to explicitly set the foreign key. 15 | 16 | (belongs-to users email) 17 | (belongs-to users email (fk :userId))
(create-entity table)Create an entity representing a table in a database. 18 |
(defentity ent & body)Define an entity representing a table in the database, applying any modifications in 20 | the body.
(delete ent & body)Creates a delete query, applies any modifying functions in the body and then
21 | executes it. `ent` is either a string or an entity created by defentity.
22 | Returns number of deleted rows as provided by the JDBC driver.
23 |
24 | ex: (delete user
25 | (where {:id 7}))(delete* ent)Create an empty delete query. Ent can either be an entity defined by defentity, 26 | or a string of the table name
(dry-run & body)Wrap around a set of queries to print to the console all SQL that would 27 | be run and return dummy values instead of executing them.
(entity-fields ent & fields)Set the fields to be retrieved in all select queries for the 28 | entity.
(exec-raw conn? & [sql with-results?])Execute a raw SQL string, supplying whether results should be returned. `sql` 30 | can either be a string or a vector of the sql string and its params. You can 31 | also optionally provide the connection to execute against as the first 32 | parameter. 33 | 34 | (exec-raw ["SELECT * FROM users WHERE age > ?" [5]] :results)
(fields query & vs)Set the fields to be selected in a query. Fields can either be a keyword 35 | or a vector of two keywords [field alias]: 36 | 37 | (fields query :name [:firstname :first])
(has-many ent sub-ent)(has-many ent sub-ent opts)Add a has-many relation for the given entity. It is assumed that the foreign key 41 | is on the sub-entity with the format table_id: user.id = email.user_id 42 | Can optionally pass a map with a :fk key collection or use the helper `fk` function 43 | to explicitly set the foreign key. 44 | 45 | (has-many users email) 46 | (has-many users email (fk :emailID))
(has-one ent sub-ent)(has-one ent sub-ent opts)Add a has-one relationship for the given entity. It is assumed that the foreign key 47 | is on the sub-entity with the format table_id: user.id = address.user_id 48 | Can optionally pass a map with a :fk key collection or use the helper `fk` function 49 | to explicitly set the foreign key. 50 | 51 | (has-one users address) 52 | (has-one users address (fk :userId))
(having query form)Add a having clause to the query, expressing the clause in clojure expressions
53 | with keywords used to reference fields.
54 | e.g. (having query (or (= :hits 1) (> :hits 5)))
55 |
56 | Available predicates: and, or, =, not=, <, >, <=, >=, in, like, not, between
57 |
58 | Having can also take a map at any point and will create a clause that compares
59 | keys to values. The value can be a vector with one of the above predicate
60 | functions describing how the key is related to the value:
61 | (having query {:name [like "chris"})
62 |
63 | Having only works if you have an aggregation, using it without one will cause
64 | an error.(having* query clause)Add a having clause to the query. Clause can be either a map or a string, and 65 | will be AND'ed to the other clauses.
(insert ent & body)Creates an insert query, applies any modifying functions in the body
66 | and then executes it. `ent` is either a string or an entity created by
67 | defentity. The return value is the last inserted item, but its
68 | representation is dependent on the database driver used
69 | (e.g. postgresql returns the full row as a hash,
70 | MySQL returns a {:generated_key <ID>} hash,
71 | and MSSQL returns a {:generated_keys <ID>} hash).
72 |
73 | ex: (insert user
74 | (values [{:name "chris"} {:name "john"}]))(insert* ent)Create an empty insert query. Ent can either be an entity defined by defentity, 75 | or a string of the table name
(intersect & body)Creates an intersect query, applies any modifying functions in the body and then
76 | executes it.
77 |
78 | ex: (intersect
79 | (queries (subselect user
80 | (where {:id 7}))
81 | (subselect user-backup
82 | (where {:id 8})))
83 | (order :name))(join query ent)(join query type-or-table ent-or-clause)(join query type table clause)Add a join clause to a select query, specifying an entity defined by defentity, or the table name to 85 | join and the predicate to join on. If the entity relationship uses a join 86 | table then two clauses will be added. Otherwise, only one clause 87 | will be added. 88 | 89 | (join query addresses) 90 | (join query :right addresses) 91 | (join query addresses (= :addres.users_id :users.id)) 92 | (join query :right addresses (= :address.users_id :users.id))
(many-to-many ent sub-ent join-table)(many-to-many ent sub-ent join-table fk1 fk2)(many-to-many ent sub-ent join-table opts)Add a many-to-many relation for the given entity. It is assumed that a join 95 | table is used to implement the relationship and that the foreign keys are in 96 | the join table with the format table_id: 97 | user.id = user_email.user_id 98 | user_email.email_id = email.id 99 | Can optionally pass a map with :lfk and :rfk key collections or use the 100 | helper `lfk`, `rfk` functions to explicitly set the join keys. 101 | 102 | (many-to-many email :user_email) 103 | (many-to-many email :user_email (lfk :user_id) 104 | (rfk :email_id))
(modifier query & modifiers)Add a modifer to the beginning of a query: 105 | 106 | (select orders 107 | (modifier "DISTINCT"))
(order query field dir)(order query field)Add an ORDER BY clause to a select, union, union-all, or intersect query. 109 | field should be a keyword of the field name, dir is ASC by default. 110 | 111 | (order query :created :asc)
(post-query query post)Add a function representing a query that should be executed for each result 113 | in a select. This is done lazily over the result set.
(prepare ent func)Add a function to be applied to records/values going into the database 114 |
(queries query & queries)Adds a group of queries to a union, union-all or intersect 115 |
(query-only & body)Wrap around a set of queries to force them to return their query objects. 116 |
(raw s)Embed a raw string of SQL in a query. This is used when Korma doesn't 117 | provide some specific functionality you're looking for: 118 | 119 | (select users 120 | (fields (raw "PERIOD(NOW(), NOW())")))
(select ent & body)Creates a select query, applies any modifying functions in the body and then
122 | executes it. `ent` is either a string or an entity created by defentity.
123 |
124 | ex: (select user
125 | (fields :name :email)
126 | (where {:id 2}))(select* ent)Create a select query with fields provided in Ent. If fields are not provided, 127 | create an empty select query. Ent can either be an entity defined by defentity, 128 | or a string of the table name
(set-fields query fields-map)Set the fields and values for an update query. 129 |
(sql-only & body)Wrap around a set of queries so that instead of executing, each will return a 130 | string of the SQL that would be used.
(sqlfn func & params)Call an arbitrary SQL function by providing func as a symbol or keyword 131 | and its params
(sqlfn* fn-name & params)Call an arbitrary SQL function by providing the name of the function 132 | and its params
(subselect & parts)Create a subselect clause to be used in queries. This works exactly like
133 | (select ...) execept it will wrap the query in ( .. ) and make sure it can be
134 | used in any current query:
135 |
136 | (select users
137 | (where {:id [in (subselect users2 (fields :id))]}))(table ent t & [alias])Set the name of the table and an optional alias to be used for the entity. 138 | By default the table is the name of entity's symbol.
(transform ent func)Add a function to be applied to results coming from the database 139 |
(union & body)Creates a union query, applies any modifying functions in the body and then
140 | executes it.
141 |
142 | ex: (union
143 | (queries (subselect user
144 | (where {:id 7}))
145 | (subselect user-backup
146 | (where {:id 7})))
147 | (order :name))(union-all & body)Creates a union-all query, applies any modifying functions in the body and then
149 | executes it.
150 |
151 | ex: (union-all
152 | (queries (subselect user
153 | (where {:id 7}))
154 | (subselect user-backup
155 | (where {:id 7})))
156 | (order :name))(update ent & body)Creates an update query, applies any modifying functions in the body and then
158 | executes it. `ent` is either a string or an entity created by defentity.
159 | Returns number of updated rows as provided by the JDBC driver.
160 |
161 | ex: (update user
162 | (set-fields {:name "chris"})
163 | (where {:id 4}))(update* ent)Create an empty update query. Ent can either be an entity defined by defentity, 164 | or a string of the table name.
(values query values)Add records to an insert clause. values can either be a vector of maps or a
165 | single map.
166 |
167 | (values query [{:name "john"} {:name "ed"}])(where query form)Add a where clause to the query, expressing the clause in clojure expressions
168 | with keywords used to reference fields.
169 | e.g. (where query (or (= :hits 1) (> :hits 5)))
170 |
171 | Available predicates: and, or, =, not=, <, >, <=, >=, in, like, not, between
172 |
173 | Where can also take a map at any point and will create a clause that compares keys
174 | to values. The value can be a vector with one of the above predicate functions
175 | describing how the key is related to the value:
176 | (where query {:name [like "chris"]})(where* query clause)Add a where clause to the query. Clause can be either a map or a string, and 177 | will be AND'ed to the other clauses.
(with query ent & body)Add a related entity to the given select query. If the entity has a relationship
178 | type of :belongs-to or :has-one, the requested fields will be returned directly in
179 | the result map. If the entity is a :has-many, a second query will be executed lazily
180 | and a key of the entity name will be assoc'd with a vector of the results.
181 |
182 | (defentity email (entity-fields :email))
183 | (defentity user (has-many email))
184 | (select user
185 | (with email) => [{:name "chris" :email [{email: "c@c.com"}]} ...
186 |
187 | With can also take a body that will further refine the relation:
188 | (select user
189 | (with address
190 | (with state)
191 | (fields :address.city :state.state)
192 | (where {:address.zip x})))(with-batch query ent & body)Add a related entity. This behaves like `with`, except that, for has-many 193 | relationships, it runs a single query to get relations of all fetched rows. 194 | This is faster than regular `with` but it doesn't support many of the 195 | additional options (order, limit, offset, group, having)