From f7d71bc3e4075227b143687ba34ca40e4d11556f Mon Sep 17 00:00:00 2001 From: Alex Nelson Date: Thu, 17 Nov 2016 14:03:04 -0800 Subject: [PATCH] Upgraded jdbc --- project.clj | 22 ++-- src/lobos/analyzer.clj | 48 ++++----- src/lobos/ast.clj | 2 +- src/lobos/backends/h2.clj | 10 +- src/lobos/backends/sqlite.clj | 13 ++- src/lobos/compiler.clj | 2 +- src/lobos/connectivity.clj | 164 ++++++++++++++++-------------- src/lobos/connectivity/jdbc_2.clj | 10 +- src/lobos/connectivity/jdbc_3.clj | 12 +-- src/lobos/core.clj | 13 ++- src/lobos/internal.clj | 56 +++++----- src/lobos/metadata.clj | 47 +++++---- src/lobos/migration.clj | 51 ++++++---- src/lobos/schema.clj | 10 +- src/lobos/utils.clj | 8 +- test/lobos/test.clj | 18 ++-- test/lobos/test/connectivity.clj | 42 +------- test/lobos/test/integration.clj | 40 ++++---- test/lobos/test/migration.clj | 35 ++++--- test/lobos/test/system.clj | 63 +++++++----- 20 files changed, 345 insertions(+), 321 deletions(-) diff --git a/project.clj b/project.clj index 665628b..5bf0b74 100644 --- a/project.clj +++ b/project.clj @@ -1,21 +1,17 @@ (defproject lobos "1.0.0-beta3" - :description - "A library to create and manipulate SQL database schemas." + :description "A library to create and manipulate SQL database schemas." :url "http://budu.github.com/lobos/" :license {:name "Eclipse Public License"} - :dependencies [[org.clojure/clojure "1.4.0"] - [org.clojure/java.jdbc "0.3.3"] - [org.clojure/tools.macro "0.1.2"]] - :profiles {:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]} - :1.5 {:dependencies [[org.clojure/clojure "1.5.0"]]} - :dev + :dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/java.jdbc "0.6.1"] + [org.clojure/tools.macro "0.1.5"]] + :profiles {:dev {:dependencies - [[lein-clojars "0.7.0"] - [lein-marginalia "0.6.1"] + [[lein-clojars "0.9.1"] + [lein-marginalia "0.9.0"] [lein-multi "1.1.0"] [cljss "0.1.1"] - [hiccup "0.3.1"] - [com.h2database/h2 "1.3.160"]]}} - :aliases {"all" ["with-profile" "dev:dev,1.3"]} + [hiccup "1.0.5"] + [com.h2database/h2 "1.4.193"]]}} :jar-exclusions [#"www.clj" #"config.clj" #"migrations.clj"] :min-lein-version "2.0.0") diff --git a/src/lobos/analyzer.clj b/src/lobos/analyzer.clj index 9aded0c..ed20488 100644 --- a/src/lobos/analyzer.clj +++ b/src/lobos/analyzer.clj @@ -9,12 +9,12 @@ (ns lobos.analyzer "Analyze a database's meta-data to contruct an abstract schema." (:refer-clojure :exclude [defonce replace]) - (:require (lobos [connectivity :as conn] - [schema :as schema])) - (:use (clojure [string :only [replace]]) - lobos.internal - lobos.metadata - lobos.utils) + (:require [clojure.string :as string :refer [replace]] + (lobos ;; [connectivity :as conn] + [internal :as internal :refer :all] + [metadata :as metadata] ;; :refer :all] + [schema :as schema] + [utils :refer :all])) (:import (java.sql DatabaseMetaData) (lobos.schema Column DataType @@ -43,7 +43,7 @@ (fn [dispatch-val & args] (if (vector? dispatch-val) dispatch-val - [(as-keyword (.getDatabaseProductName (db-meta))) + [(as-keyword (.getDatabaseProductName (metadata/db-meta))) dispatch-val])) :hierarchy db-hierarchy) @@ -71,7 +71,7 @@ (defmethod analyze [::standard UniqueConstraint] [_ sname tname cname index-meta] - (let [pkeys (primary-keys sname tname)] + (let [pkeys (metadata/primary-keys sname tname)] (UniqueConstraint. (keyword cname) (if (pkeys (keyword cname)) @@ -112,14 +112,14 @@ [_ sname tname] (concat (map (fn [[cname meta]] (analyze UniqueConstraint sname tname cname meta)) - (indexes-meta sname tname #(let [nu (:non_unique %)] - (or (false? nu) (= nu 0))))) + (metadata/indexes-meta sname tname #(let [nu (:non_unique %)] + (or (false? nu) (= nu 0))))) (map (fn [[cname meta]] (analyze ForeignKeyConstraint cname meta)) - (references-meta sname tname)))) + (metadata/references-meta sname tname)))) (defmethod analyze [::standard Index] [_ sname tname iname index-meta] - (let [pkeys (primary-keys sname tname)] + (let [pkeys (metadata/primary-keys sname tname)] (Index. (keyword iname) tname @@ -131,7 +131,7 @@ (defmethod analyze [::standard :indexes] [_ sname tname] (map (fn [[iname meta]] (analyze Index sname tname iname meta)) - (indexes-meta sname tname))) + (metadata/indexes-meta sname tname))) (defn analyze-data-type-args "Returns a vector containing the data type arguments for the given @@ -172,7 +172,7 @@ (schema/table* tname (into {} (map #(let [c (analyze Column %)] [(:cname c) c]) - (columns-meta sname tname))) + (metadata/columns-meta sname tname))) (into {} (map #(vector (:cname %) %) (analyze :constraints sname tname))) (into {} (map #(vector (:iname %) %) @@ -180,21 +180,23 @@ (defmethod analyze [::standard Schema] [_ sname] - (apply schema/schema sname {:db-spec (db-meta-spec)} + (apply schema/schema sname {:db-spec (metadata/db-meta-spec)} (map #(analyze Table sname %) - (tables sname)))) + (metadata/tables sname)))) (defn analyze-schema [& args] {:arglists '([connection-info? sname?])} - (let [[db-spec sname _] (optional-cnx-and-sname args)] - (with-db-meta db-spec - (autorequire-backend db-spec) + (let [[db-spec sname _] (internal/optional-cnx-and-sname args)] + (metadata/with-db-meta db-spec + (internal/autorequire-backend db-spec) (let [sname (or (keyword sname) - (default-schema) - (first _))] - (if-let [schemas (schemas)] + (metadata/default-schema) + )] + (if-let [schemas (metadata/schemas)] (when (or (nil? sname) ((set schemas) sname)) (analyze Schema sname)) - (analyze Schema sname)))))) + (analyze Schema sname)) + + )))) diff --git a/src/lobos/ast.clj b/src/lobos/ast.clj index 7e88d2c..5d33e88 100644 --- a/src/lobos/ast.clj +++ b/src/lobos/ast.clj @@ -8,7 +8,7 @@ (ns lobos.ast "Abstract SQL syntax tree for the DDL part of the language." - (:use clojure.template) + (:require [clojure.template :refer :all]) (:import (java.io Writer))) ;; ----------------------------------------------------------------------------- diff --git a/src/lobos/backends/h2.clj b/src/lobos/backends/h2.clj index 3408b38..14e0aba 100644 --- a/src/lobos/backends/h2.clj +++ b/src/lobos/backends/h2.clj @@ -9,9 +9,13 @@ (ns lobos.backends.h2 "Compiler implementation for H2." (:refer-clojure :exclude [compile defonce]) - (:require (lobos [schema :as schema])) - (:use (clojure [string :only [split]]) - (lobos analyzer compiler connectivity internal metadata utils)) + (:require [clojure.string :refer [split]] + (lobos [analyzer :refer :all] + [compiler :refer :all] + [internal :refer :all] + [metadata :refer :all] + [schema :as schema] + [utils :refer :all])) (:import (lobos.ast AlterRenameAction AutoIncClause CreateSchemaStatement diff --git a/src/lobos/backends/sqlite.clj b/src/lobos/backends/sqlite.clj index 9429fb5..aae0b54 100644 --- a/src/lobos/backends/sqlite.clj +++ b/src/lobos/backends/sqlite.clj @@ -9,8 +9,12 @@ (ns lobos.backends.sqlite "Compiler implementation for SQLite." (:refer-clojure :exclude [compile defonce]) - (:require (lobos [schema :as schema])) - (:use (lobos analyzer compiler connectivity internal metadata utils)) + (:require (lobos [analyzer :refer :all] + [compiler :refer :all] + [schema :as schema] + [internal :refer :all] + [metadata :refer :all] + [utils :refer :all])) (:import (lobos.ast AlterTableStatement AutoIncClause CreateSchemaStatement @@ -149,9 +153,8 @@ #(schema/build-drop-statement % :cascade db-spec) #(schema/table %) :name) - (with-connection db-spec - (raw-query - (format "select name from sqlite_master where type <> 'index';"))))) + (raw-query + (format "select name from sqlite_master where type <> 'index';")))) (defmethod compile [:sqlite DropStatement] [statement] diff --git a/src/lobos/compiler.clj b/src/lobos/compiler.clj index 12ea669..a688472 100644 --- a/src/lobos/compiler.clj +++ b/src/lobos/compiler.clj @@ -126,7 +126,7 @@ (let [{:keys [db-spec name qualifiers]} identifier] (join* \. (->> (concat qualifiers [name]) (filter identity) - (map #(when % (as-str \" % \"))))))) + (map #(when % (as-str \" % \" ))))))) (defmethod compile [::standard FunctionExpression] [function] diff --git a/src/lobos/connectivity.clj b/src/lobos/connectivity.clj index c08a14a..9dfc94a 100644 --- a/src/lobos/connectivity.clj +++ b/src/lobos/connectivity.clj @@ -9,41 +9,50 @@ (ns lobos.connectivity "A set of connectivity functions." (:refer-clojure :exclude [defonce]) - (:use lobos.utils)) - -(try - (require 'lobos.connectivity.jdbc-1) - (catch Exception e - (try - (require 'lobos.connectivity.jdbc-2) - (catch Exception e - (do - (ns-unalias 'lobos.connectivity 'sqlint) - (require 'lobos.connectivity.jdbc-3)))))) + (:require [clojure.string :as string] + [clojure.java.jdbc :as jdbc] + [lobos.schema :as schema] + [lobos.utils :refer :all])) + +;; (try +;; (require 'lobos.connectivity.jdbc-1) +;; (catch Exception e +;; (try +;; (require 'lobos.connectivity.jdbc-2) +;; (catch Exception e +;; (do +;; (ns-unalias 'lobos.connectivity 'sqlint) +;; (require 'lobos.connectivity.jdbc-3)))))) ;; ----------------------------------------------------------------------------- - ;; ## Globals - (defonce global-connections (atom {}) "This atom contains a map of all opened global connections.") ;; ----------------------------------------------------------------------------- - ;; ## Helpers +(def find-connection jdbc/db-find-connection) +(def connection jdbc/get-connection) -(def find-connection sqlint/find-connection*) +(defonce db-spec-atom (atom nil)) +(defn jdbc-db-spec [] @db-spec-atom) -(def connection sqlint/connection*) - -(defn jdbc-db-spec [] (:db-spec sqlint/*db*)) +(defn db-spec? [m] + (and (map? m) + (not (string/blank? (:classname m))) + (not (string/blank? (:subprotocol m))) + (not (string/blank? (:subname m))))) (defn get-db-spec "Returns the associated db-spec or itself. *For internal use*." [& [connection-info]] (let [connection-info (or connection-info :default-connection)] - (or (jdbc-db-spec) + (or (when (schema/schema? connection-info) + (get-in connection-info [:options :db-spec])) + (when (db-spec? connection-info) + connection-info) + (jdbc-db-spec) (if (keyword? connection-info) (-> @global-connections connection-info :db-spec) connection-info)))) @@ -53,7 +62,7 @@ namespace. Dissociates the `:schema` key to prevent conflict." [db-spec] (let [db-spec (dissoc db-spec :schema)] - (sqlint/get-connection db-spec))) + (jdbc/get-connection db-spec))) (defn connection? "Checks if the given argument is a named connection or a db-spec. As a @@ -63,7 +72,6 @@ (map? cnx))) ;; ----------------------------------------------------------------------------- - ;; ## Global Connections (defn close-global @@ -86,6 +94,7 @@ (defn- open-global* [connection-name db-spec] (let [cnx (*get-cnx* db-spec)] + (swap! db-spec-atom db-spec) (when-let [ac (-> db-spec :auto-commit)] (.setAutoCommit cnx ac)) (swap! global-connections assoc @@ -120,78 +129,81 @@ ;; ## With Connections -(defn with-named-connection +(defn- with-named-connection "Evaluates func in the context of a named global connection to a database." [connection-name func] (io! (if-let [cnx (@global-connections connection-name)] - (binding [sqlint/*db* - (assoc sqlint/*db* - :connection (:connection cnx) - :level 0 - :rollback (atom false) - :db-spec (:db-spec cnx))] - (func)) + ;; (binding [sqlint/*db* + ;; (assoc sqlint/*db* + ;; :connection (:connection cnx) + ;; :level 0 + ;; :rollback (atom false) + ;; :db-spec (:db-spec cnx))] + ;; (func)) + nil (throw (Exception. (format "No such global connection currently open: %s, only got %s" connection-name (vec (keys @global-connections)))))))) -(defn with-spec-connection +(defn- with-spec-connection "Evaluates func in the context of a new connection to a database then closes the connection." [db-spec func] - (with-open [cnx (*get-cnx* db-spec)] - (binding [sqlint/*db* (assoc sqlint/*db* - :connection cnx - :level 0 - :rollback (atom false) - :db-spec db-spec)] - (when-let [ac (-> db-spec :auto-commit)] - (.setAutoCommit cnx ac)) - (func)))) - -(defmacro with-connection - "Evaluates body in the context of a new connection or a named global - connection to a database then closes the connection if it's a new - one. The connection-info parameter can be a keyword denoting a global - connection or a map containing values for one of the following - parameter sets: - - * Factory: - * `:factory` (required) a function of one argument, a map of params - * (others) (optional) passed to the factory function in a map - - * DriverManager: - * `:classname` (required) a String, the jdbc driver class name - * `:subprotocol` (required) a String, the jdbc subprotocol - * `:subname` (required) a String, the jdbc subname - * (others) (optional) passed to the driver as properties. - - * DataSource: - * `:datasource` (required) a javax.sql.DataSource - * `:username` (optional) a String - * `:password` (optional) a String, required if :username is supplied - - * JNDI: - * `:name` (required) a String or javax.naming.Name - * `:environment` (optional) a java.util.Map - - * Options (for ClojureQL): - * `:auto-commit` (optional) a Boolean - * `:fetch-size` (optional) an integer" - [connection-info & body] - `(let [connection-info# (or ~connection-info :default-connection)] - ((if (keyword? connection-info#) - with-named-connection - with-spec-connection) connection-info# (fn [] ~@body)))) + (jdbc/db-do-commands db-spec (func))) + ;; (with-open [cnx (*get-cnx* db-spec)] + ;; (binding [sqlint/*db* (assoc sqlint/*db* + ;; :connection cnx + ;; :level 0 + ;; :rollback (atom false) + ;; :db-spec db-spec)] + ;; (when-let [ac (-> db-spec :auto-commit)] + ;; (.setAutoCommit cnx ac)) + ;; (func)))) + ;; nil + ;; )) + +;; (defmacro with-connection +;; "Evaluates body in the context of a new connection or a named global +;; connection to a database then closes the connection if it's a new +;; one. The connection-info parameter can be a keyword denoting a global +;; connection or a map containing values for one of the following +;; parameter sets: + +;; * Factory: +;; * `:factory` (required) a function of one argument, a map of params +;; * (others) (optional) passed to the factory function in a map + +;; * DriverManager: +;; * `:classname` (required) a String, the jdbc driver class name +;; * `:subprotocol` (required) a String, the jdbc subprotocol +;; * `:subname` (required) a String, the jdbc subname +;; * (others) (optional) passed to the driver as properties. + +;; * DataSource: +;; * `:datasource` (required) a javax.sql.DataSource +;; * `:username` (optional) a String +;; * `:password` (optional) a String, required if :username is supplied + +;; * JNDI: +;; * `:name` (required) a String or javax.naming.Name +;; * `:environment` (optional) a java.util.Map + +;; * Options (for ClojureQL): +;; * `:auto-commit` (optional) a Boolean +;; * `:fetch-size` (optional) an integer" +;; [connection-info & body] +;; `(let [connection-info# (or ~connection-info :default-connection)] +;; ((if (keyword? connection-info#) +;; with-named-connection +;; with-spec-connection) connection-info# (fn [] ~@body)))) (defn default-connection "Returns the default connection if it exists." [] (try - (with-named-connection :default-connection - connection) + (:connection (:default-connection @global-connections)) (catch Exception _ nil))) diff --git a/src/lobos/connectivity/jdbc_2.clj b/src/lobos/connectivity/jdbc_2.clj index 5384e83..5122f9a 100644 --- a/src/lobos/connectivity/jdbc_2.clj +++ b/src/lobos/connectivity/jdbc_2.clj @@ -10,8 +10,8 @@ (:refer-clojure :exclude [defonce]) (:require [clojure.java.jdbc :as sqlint])) -(in-ns 'clojure.java.jdbc) -(let [old *db*] (def ^{:private false :dynamic true} *db* old)) -(let [old get-connection] (def get-connection (fn [& args] (apply old args)))) -(def find-connection* find-connection) -(def connection* connection) +;; (in-ns 'clojure.java.jdbc) +;; (let [old *db*] (def ^{:private false :dynamic true} *db* old)) +;; (let [old get-connection] (def get-connection (fn [& args] (apply old args)))) +;; (def find-connection* find-connection) +;; (def connection* connection) diff --git a/src/lobos/connectivity/jdbc_3.clj b/src/lobos/connectivity/jdbc_3.clj index db57526..b42381c 100644 --- a/src/lobos/connectivity/jdbc_3.clj +++ b/src/lobos/connectivity/jdbc_3.clj @@ -8,10 +8,10 @@ (ns lobos.connectivity (:refer-clojure :exclude [defonce]) - (:require [clojure.java.jdbc.deprecated :as sqlint])) + (:require [clojure.java.jdbc :as sqlint])) -(in-ns 'clojure.java.jdbc.deprecated) -(let [old *db*] (def ^{:private false :dynamic true} *db* old)) -(let [old get-connection] (def get-connection (fn [& args] (apply old args)))) -(def find-connection* find-connection) -(def connection* connection) +;; (in-ns 'clojure.java.jdbc) +;; (let [old *db*] (def ^{:private false :dynamic true} *db* old)) +;; (let [old get-connection] (def get-connection (fn [& args] (apply old args)))) +;; (def find-connection* find-connection) +;; (def connection* connection) diff --git a/src/lobos/core.clj b/src/lobos/core.clj index f609232..dd28076 100644 --- a/src/lobos/core.clj +++ b/src/lobos/core.clj @@ -19,17 +19,16 @@ * [Lobos wiki](https://github.com/budu/lobos/wiki)" {:author "Nicolas Buduroi"} (:refer-clojure :exclude [alter defonce drop]) - (:require (lobos [compiler :as compiler] + (:require [clojure.tools.macro :refer [name-with-attributes]] + [clojure.pprint :refer [pprint]] + (lobos [compiler :as compiler] [connectivity :as conn] + [internal :refer :all] [migration :as mig] - [schema :as schema])) - (:use (clojure.tools [macro :only [name-with-attributes]]) - (clojure [pprint :only [pprint]]) - lobos.internal - lobos.utils)) + [schema :as schema] + [utils :refer :all]))) ;; ----------------------------------------------------------------------------- - ;; ## Helpers (defmacro without-migration [& body] diff --git a/src/lobos/internal.clj b/src/lobos/internal.clj index ad10e69..a56dd45 100755 --- a/src/lobos/internal.clj +++ b/src/lobos/internal.clj @@ -8,7 +8,8 @@ (ns lobos.internal (:refer-clojure :exclude [defonce]) - (:require (lobos [compiler :as compiler] + (:require [clojure.java.jdbc :as jdbc] + (lobos [compiler :as compiler] [connectivity :as conn] [metadata :as metadata] [schema :as schema])) @@ -38,11 +39,11 @@ (defn- execute* "Execute the given SQL string or sequence of strings. Prints them if the `debug-level` is set to `:sql`." - [sql] + [db-conn sql] (doseq [sql-string (if (seq? sql) sql [sql])] - (when (= :sql @debug-level) (println sql-string)) - (with-open [stmt (.createStatement (conn/connection))] - (.execute stmt sql-string)))) + (when (= :sql @debug-level) + (println "execute* sql-string = " (pr-str sql-string))) + (jdbc/db-do-commands db-conn sql-string))) (defn execute "Executes the given statement(s) using the specified connection @@ -55,14 +56,13 @@ [statements]) db-spec (conn/get-db-spec connection-info) mode (compiler/compile (compiler/mode db-spec))] - (conn/with-connection connection-info - (autorequire-backend connection-info) - (when mode (execute* mode)) - (doseq [statement statements] - (let [sql (if (string? statement) - statement - (compiler/compile statement))] - (when sql (execute* sql)))))) nil) + (autorequire-backend connection-info) + (when mode (execute* mode)) + (doseq [statement statements] + (let [sql (if (string? statement) + statement + (compiler/compile statement))] + (when sql (execute* connection-info sql)))))) ;; ----------------------------------------------------------------------------- @@ -75,7 +75,8 @@ (conn/connection? %))) args) args* args schema (when (schema/schema? cnx-or-schema) cnx-or-schema) - cnx (or (conn/find-connection) + cnx (or (when-let [db-spec (and (seq args) (first args))] + (conn/find-connection db-spec)) (-> schema :options :db-spec) (when-not schema cnx-or-schema) :default-connection) @@ -86,9 +87,11 @@ (defn optional-cnx-and-sname [args] (let [[db-spec schema args] (optional-cnx-or-schema args) - [sname args] (conn/with-connection db-spec - (optional #(or (nil? %) - ((set (metadata/schemas)) %)) args)) + [sname args] (jdbc/with-db-connection [db-conn db-spec] + (binding [metadata/*db-meta* (.getMetaData (:connection db-conn))] + (optional #(or (nil? %) + ((set (metadata/schemas)) %)) + args))) sname (or sname (:name schema))] [db-spec sname args])) @@ -97,14 +100,13 @@ ;; ## DML Helpers (defn raw-query [sql-string] - (with-open [stmt (.createStatement (conn/connection))] - (let [resultset (.executeQuery stmt sql-string)] - (when resultset - (doall (resultset-seq resultset)))))) + {:pre [(string? sql-string)]} + (jdbc/query (conn/get-db-spec) + [sql-string])) (defn raw-update [sql-string] - (with-open [stmt (.createStatement (conn/connection))] - (.executeUpdate stmt sql-string))) + (jdbc/execute! (conn/get-db-spec) + [sql-string])) (defn sql-from-where [db-spec stmt sname tname where-expr] (do @@ -120,13 +122,13 @@ (schema/build-definition where-expr db-spec))))))) (defmacro query [db-spec sname tname & [conditions]] - `(raw-query + `(jdbc/query ~db-spec (sql-from-where ~db-spec "select *" ~sname ~tname (when-not ~(nil? conditions) (schema/expression ~conditions))))) (defmacro delete [db-spec sname tname & [conditions]] - `(raw-update - (sql-from-where ~db-spec "delete" ~sname ~tname + `(let [foo# (sql-from-where ~db-spec "delete" ~sname ~tname (when-not ~(nil? conditions) - (schema/expression ~conditions))))) + (schema/expression ~conditions)))] + (raw-update foo#))) diff --git a/src/lobos/metadata.clj b/src/lobos/metadata.clj index 96813b8..baf4e03 100644 --- a/src/lobos/metadata.clj +++ b/src/lobos/metadata.clj @@ -8,7 +8,8 @@ (ns lobos.metadata "Helpers to query the database's meta-data." - (:require (lobos [compiler :as compiler] + (:require [clojure.java.jdbc :as jdbc] + (lobos [compiler :as compiler] [connectivity :as conn] [schema :as schema])) (:import (java.sql DatabaseMetaData))) @@ -23,12 +24,13 @@ (defn db-meta "Returns the binded DatabaseMetaData object found in *db-meta* or get one from the default connection if not available." + ^java.sql.DatabaseMetaData [] - (or (when (conn/connection) - (.getMetaData (conn/connection))) - *db-meta* - (conn/with-connection :default-connection - (.getMetaData (conn/connection))))) + (or *db-meta* + (when-let [connection (conn/default-connection)] + (.getMetaData (if (map? connection) + (:connection connection) + connection))))) (defn db-meta-spec [] @@ -39,12 +41,12 @@ (defmacro with-db-meta "Evaluates body in the context of a new connection or a named global connection to a database then closes the connection while binding its - DatabaseMetaData object to *db-meta*." - [connection-info & body] - `(if ~connection-info - (conn/with-connection ~connection-info - (binding [*db-meta-spec* (conn/get-db-spec ~connection-info) - *db-meta* (.getMetaData (conn/connection))] + `DatabaseMetaData` object to `*db-meta*`." + [db-spec & body] + `(if ~db-spec + (jdbc/with-db-connection [db-conn# ~db-spec] + (binding [*db-meta-spec* ~db-spec + *db-meta* (.getMetaData (:connection db-conn#))] ~@body)) (do ~@body))) @@ -77,20 +79,29 @@ ;; ----------------------------------------------------------------------------- ;; ## Database Objects +(defn- getter-db-meta [getter-kw] + (when-let [method (get {:catalogs (memfn getCatalogs) + :schemas (memfn getSchemas)} getter-kw)] + (let [field (get {:catalogs :table_cat + :schemas :table_schem} getter-kw)] + (->> (db-meta) + method + resultset-seq + doall + (map (comp keyword field)))))) (defn catalogs "Returns a list of catalog names as keywords." [] - (map #(-> % :table_cat keyword) - (doall (resultset-seq (.getCatalogs (db-meta)))))) + (getter-db-meta :catalogs)) (defn schemas "Returns a list of schema names as keywords." [] - (cond (supports-schemas) - (map #(-> % :table_schem keyword) - (doall (resultset-seq (.getSchemas (db-meta))))) - (supports-catalogs) (catalogs))) + (cond + (supports-schemas) (getter-db-meta :schemas) + (supports-catalogs) (catalogs) + :else nil)) (defn default-schema [] (when (supports-schemas) diff --git a/src/lobos/migration.clj b/src/lobos/migration.clj index bc98c72..ada3578 100755 --- a/src/lobos/migration.clj +++ b/src/lobos/migration.clj @@ -9,17 +9,17 @@ (ns lobos.migration "Migrations support." (:refer-clojure :exclude [complement defonce replace]) - (:require (clojure.java.jdbc [deprecated :as sql]) + (:require [clojure.pprint :refer :all] + [clojure.string :as string] + [clojure.tools.macro :refer [name-with-attributes]] + [clojure.walk :refer [postwalk]] + [clojure.java.io :refer [file writer]] + [clojure.java.jdbc :as sql] (lobos [analyzer :as analyzer] [compiler :as compiler] - [connectivity :as conn] - [schema :as schema])) - (:use (clojure [walk :only [postwalk]]) - (clojure.java [io :only [file writer]]) - (clojure.tools [macro :only [name-with-attributes]]) - (clojure pprint) - lobos.internal - lobos.utils) + [internal :as internal] + [schema :as schema] + [utils :refer :all])) (:import (java.sql Timestamp) (java.util Date))) @@ -193,7 +193,7 @@ (defn create-migrations-table [db-spec sname] - (autorequire-backend db-spec) + (internal/autorequire-backend db-spec) (when-not (-> (analyzer/analyze-schema db-spec sname) :tables *migrations-table*) @@ -201,23 +201,31 @@ (schema/varchar :name 255)) db-spec (assoc db-spec :schema sname) create-stmt (schema/build-create-statement action db-spec)] - (execute create-stmt db-spec)))) + (internal/execute create-stmt db-spec)))) (defn insert-migrations [db-spec sname & names] - (when-not (empty? names) - (sql/with-connection db-spec - (apply - sql/insert-rows - (compiler/as-identifier db-spec *migrations-table* sname) - (map (comp vector str) names))))) + (when (seq names) + (sql/insert-multi! + db-spec + (compiler/as-identifier db-spec *migrations-table* sname) + (mapv (fn [n] {:name (str n)}) names)))) (defn delete-migrations [db-spec sname & names] (when-not (empty? names) - (conn/with-connection db-spec - (delete db-spec sname *migrations-table* - (in :name (vec (map str names))))))) + (let [table-name (compiler/as-identifier db-spec + *migrations-table* + sname) + params (mapv str names)] + (sql/execute! db-spec + [(str "DELETE FROM " + table-name + " WHERE name IN (" + (string/join "," (repeat (count names) "?")) + ")") + (vec params)] + {:multi? true})))) ;; ### Commands Helpers @@ -230,8 +238,7 @@ (defn query-migrations-table [db-spec sname] - (conn/with-connection db-spec - (map :name (query db-spec sname *migrations-table*)))) + (map :name (internal/query db-spec sname *migrations-table*))) (defn pending-migrations [db-spec sname] (exclude (query-migrations-table db-spec diff --git a/src/lobos/schema.clj b/src/lobos/schema.clj index 1c1e4af..d713414 100644 --- a/src/lobos/schema.clj +++ b/src/lobos/schema.clj @@ -25,11 +25,11 @@ definitons and the typed data definitions." (:refer-clojure :exclude [defonce replace bigint boolean char double float time]) - (:require (lobos [ast :as ast])) - (:use (clojure [walk :only [postwalk]] - [set :only [union]] - [string :only [replace]]) - lobos.utils)) + (:require [clojure.walk :refer [postwalk]] + [clojure.set :refer [union]] + [clojure.string :refer [replace]] + (lobos [ast :as ast] + [utils :refer :all]))) (ast/import-all) diff --git a/src/lobos/utils.clj b/src/lobos/utils.clj index 15e2e19..39d6a71 100644 --- a/src/lobos/utils.clj +++ b/src/lobos/utils.clj @@ -9,11 +9,9 @@ (ns lobos.utils "Helpers used in unrelated namespaces." (:refer-clojure :exclude [defonce replace]) - (:use (clojure [string :only [lower-case - replace - upper-case]] - [walk :only [postwalk]]) - (clojure.java [io :only [file]])) + (:require [clojure.string :refer [lower-case replace upper-case]] + [clojure.walk :refer [postwalk]] + [clojure.java.io :refer [file]]) (:import java.io.File)) (defn join [separator & coll] diff --git a/test/lobos/test.clj b/test/lobos/test.clj index 02f44f5..c47e730 100644 --- a/test/lobos/test.clj +++ b/test/lobos/test.clj @@ -16,11 +16,15 @@ ;;;; DB connection specifications +;; Need to include the ";DB_CLOSE_DELAY=-1", otherwise the content of +;; the database is lost whenever the last connection is closed. See +;; http://makble.com/using-h2-in-memory-database-in-clojure (def h2-spec {:classname "org.h2.Driver" :subprotocol "h2" - :subname "./lobos.h2" - :unsafe true}) + :subname "./lobos.h2;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=FALSE" + :unsafe true + }) (def mysql-spec {:classname "com.mysql.jdbc.Driver" @@ -105,11 +109,12 @@ (defmacro with-schema [[var-name sname] & body] `(let [db-spec# (get-db-spec *db*)] (try - (let [~var-name (schema ~sname {:db-spec db-spec#})] + (let [^lobos.schema.Schema ~var-name (schema ~sname {:db-spec db-spec#})] (create db-spec# ~var-name) ~@body) (finally (try (drop db-spec# (schema ~sname) :cascade) - (catch Exception _#)))))) + (catch Throwable e# (println (.getMessage e#))))) + ))) (defmacro inspect-schema [& keys] `(-> (analyze-schema *db* :lobos) ~@keys)) @@ -123,9 +128,10 @@ (defn close-global-connections [] (doseq [db (available-global-cnx)] - (close-global db))) + (close-global db)) + ) -;;;; Fixtures +;;;; Fixtures (def tmp-files-ext '(db sqlite3)) diff --git a/test/lobos/test/connectivity.clj b/test/lobos/test/connectivity.clj index caac345..3daeb51 100644 --- a/test/lobos/test/connectivity.clj +++ b/test/lobos/test/connectivity.clj @@ -7,8 +7,8 @@ ;; You must not remove this notice, or any other, from this software. (ns lobos.test.connectivity - (:use clojure.test - lobos.connectivity)) + (:require [clojure.test :refer :all] + [lobos.connectivity :refer :all])) (def ^{:dynamic true} *cnx*) @@ -47,40 +47,6 @@ (close-global :foo)) "Closing an inexistant global connection")) -(deftest test-with-named-connection - (is (thrown? Exception - (with-named-connection :test - #(identity))) - "With inexistant named connection") - (open-global :test {}) - (is (= (with-named-connection :test - #(connection)) - *cnx*) - "With existant named connection") - (close-global :test)) - -(deftest test-with-spec-connection - (is (= (with-spec-connection {} - #(connection)) - *cnx*) - "With connection specification")) - -(deftest test-with-connection - (is (= (with-connection {} - (connection)) - *cnx*) - "With connection specification") - (is (thrown? Exception - (with-connection :test - nil)) - "With inexistant named connection") - (open-global :test {}) - (is (= (with-connection :test - (connection)) - *cnx*) - "With existant named connection") - (close-global :test)) - (deftest test-default-connection (open-global {}) (is (= (default-connection) @@ -89,9 +55,9 @@ (close-global)) (deftest test-get-db-spec - (is (= (get-db-spec {}) {}) + (is (= {} (get-db-spec {})) "Get db-spec from db-spec") (open-global {}) - (is (= (get-db-spec) {}) + (is (= {} (get-db-spec)) "Get db-spec from global connection") (close-global)) diff --git a/test/lobos/test/integration.clj b/test/lobos/test/integration.clj index f0be5aa..189de11 100644 --- a/test/lobos/test/integration.clj +++ b/test/lobos/test/integration.clj @@ -9,9 +9,20 @@ (ns lobos.test.integration (:refer-clojure :exclude [alter compile defonce drop bigint boolean char double float time]) - (:use clojure.test - (lobos analyzer connectivity core metadata schema test utils) - (lobos.backends h2 mysql postgresql sqlite sqlserver)) + (:require [clojure.test :refer :all] + [clojure.java.jdbc :as jdbc] + (lobos [analyzer :refer :all] + [connectivity :refer :all] + [core :refer :all] + [metadata :refer :all] + [schema :refer :all] + [test :refer :all] + [utils :refer :all]) + (lobos.backends [h2 :refer :all] + [mysql :refer :all] + [postgresql :refer :all] + [sqlite :refer :all] + [sqlserver :refer :all])) (:import (lobos.schema ForeignKeyConstraint UniqueConstraint))) @@ -19,22 +30,11 @@ (use-fixtures :once remove-tmp-files-fixture - open-global-connections-fixture) + open-global-connections-fixture + ) ;;;; Tests -(deftest test-action-connectivity - (let [test-action - #(with-schema [lobos :lobos] - ;; cannot rely on creating just a schema as some dbms ignore that action - (create lobos (table :foo (integer :bar))) - (= (-> :lobos analyze-schema :tables :foo) - (table :foo (integer :bar))))] - (is (thrown? Exception (test-action)) - "An exception should have been thrown because there are no connection") - (is (with-connection h2-spec (test-action)) - "No exception should have been thrown when executing an action"))) - (def-db-test test-create-and-drop-schema (when (with-db-meta (get-db-spec *db*) (or (supports-schemas) @@ -54,9 +54,12 @@ (is (= (inspect-schema) lobos) "A schema named 'lobos' should have been created") (create lobos (table :foo (integer :bar))) + (drop *db* lobos :cascade) (is (not= (inspect-schema :sname) :lobos) - "A schema named 'lobos' should have been dropped")))) + "A schema named 'lobos' should have been dropped") + ) + )) (def-db-test test-create-and-drop-table (with-schema [lobos :lobos] @@ -66,7 +69,8 @@ "A table named 'foo' should have been created") (drop lobos (table :foo)) (is (nil? (inspect-schema :tables :foo)) - "A table named 'foo' should have been dropped"))) + "A table named 'foo' should have been dropped") + )) (def-db-test test-create-and-drop-index (with-schema [lobos :lobos] diff --git a/test/lobos/test/migration.clj b/test/lobos/test/migration.clj index 207febc..11f4654 100644 --- a/test/lobos/test/migration.clj +++ b/test/lobos/test/migration.clj @@ -8,13 +8,14 @@ (ns lobos.test.migration (:refer-clojure :exclude [complement create alter drop]) - (:require (lobos [connectivity :as conn] - [schema :as schema])) - (:use (clojure.java [io :only [delete-file]]) - clojure.test - (lobos [internal :only [query]] - migration - test))) + (:require [clojure.java.io :refer [delete-file]] + [clojure.java.jdbc :as jdbc] + [clojure.test :refer :all] + (lobos [connectivity :as conn] + [internal :refer [query]] + [migration :refer :all] + [schema :as schema] + [test :refer :all]))) ;;;; Fixtures @@ -27,7 +28,8 @@ (use-fixtures :once remove-tmp-files-fixture - with-config-file-global-connection-fixture) + with-config-file-global-connection-fixture + ) ;;;; Tests @@ -135,10 +137,9 @@ (defmacro with-migrations-table [& body] `(binding [*db* h2-spec] - (conn/with-connection *db* - (with-schema [~'lobos :lobos] - (create-migrations-table *db* :lobos) - ~@body)))) + (with-schema [~'lobos :lobos] + (create-migrations-table *db* :lobos) + ~@body))) (deftest test-create-migrations-table (with-migrations-table @@ -165,7 +166,8 @@ "Should delete a migration entry named 'foo'") (delete-migrations *db* :lobos 'bar 'baz) (is (empty? (query *db* :lobos :lobos_migrations)) - "Should delete all migration entries"))) + "Should delete all migration entries") + )) (deftest test-record (delete-file *stash-file* true) @@ -213,7 +215,7 @@ (delete-file (migrations-file) true) (doseq [n [:foo :bar :baz]] (generate-migration* *db* :lobos (symbol (name n)) nil - `[(~'create (~'table ~n (~'integer ~n)))])) + `[(~'create ~*db* (~'table ~n (~'integer ~n)))])) (delete-migrations *db* :lobos 'foo 'bar 'baz) (is (= (pending-migrations *db* :lobos) (list "foo" "bar" "baz")) "Should return a list containing 'foo' 'bar' and 'baz'") @@ -228,12 +230,15 @@ "Should return a list containing 'bar'") (do-migrations *db* :lobos :down '[foo baz] true) (is (= (pending-migrations *db* :lobos) (list "foo" "bar" "baz")) - "Should return a list containing 'foo' 'bar' and 'baz'"))) + "Should return a list containing 'foo' 'bar' and 'baz'") + )) (deftest test-generate-migration* (with-migrations-table + (println "test.migration/test-generate-migration* start") (delete-file (migrations-file) true) (generate-migration* *db* :lobos 'foo nil []) + (println "test.migration/test-generate-migration* finished generating-migration* :lobos") (is (empty? (query-migrations-table *db* :lobos)) "Should return an empty list") (generate-migration* *db* :lobos 'bar nil '[(println "up")]) diff --git a/test/lobos/test/system.clj b/test/lobos/test/system.clj index ecb0448..d7f7401 100644 --- a/test/lobos/test/system.clj +++ b/test/lobos/test/system.clj @@ -8,7 +8,7 @@ (ns lobos.test.system (:refer-clojure :exclude [compile conj! disj! distinct drop sort take]) - (:require (clojure.java.jdbc [deprecated :as sql]) + (:require [clojure.java.jdbc :as sql] (lobos [compiler :as compiler] [connectivity :as conn])) (:use clojure.test @@ -58,37 +58,46 @@ (def-db-test test-check-constraint (when-not (= *db* :mysql) - (sql/with-connection (conn/get-db-spec *db*) - (is (thrown? Exception - (sql/insert-records (table :users) - {(identifier :name) "x"})) - "An exception should have been thrown because of a check constraint") - (is (sql/insert-records (table :users) - {(identifier :name) "foo"}) - "The insert statement should not throw an exception")))) + (is (thrown? Exception + (sql/insert! + (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "x"})) + "An exception should have been thrown because of a check constraint") + (is (sql/insert! + (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "foo"}) + "The insert statement should not throw an exception"))) (def-db-test test-unique-constraint - (sql/with-connection (conn/get-db-spec *db*) - (sql/insert-records (table :users) {(identifier :name) "foo"}) - (is (thrown? Exception - (sql/insert-records (table :users) - {(identifier :name) "foo"})) + (sql/insert! (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "foo"}) + (is (thrown? Exception + (sql/insert! (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "foo"})) "An exception should have been thrown because of an unique constraint") - (is (sql/insert-records (table :users) - {(identifier :name) "bar"}) - "The insert statement should not throw an exception"))) + (is (sql/insert! (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "bar"}) + "The insert statement should not throw an exception")) ;;; Using hardcoded id is a bad idea! (def-db-test test-foreign-key-constraint (when-not (= *db* :sqlite) - (sql/with-connection (conn/get-db-spec *db*) - (sql/insert-records (table :users) {(identifier :name) "foo"}) - (is (thrown? Exception - (sql/insert-records (table :posts) - {(identifier :title) "foo" - (identifier :user_id) 2})) - "An exception should have been thrown because of a foreign key constraint") - (is (sql/insert-records (table :posts) + (sql/insert! (conn/get-db-spec *db*) + (table :users) + {(identifier :name) "foo"}) + (is (thrown? Exception + (sql/insert! (conn/get-db-spec *db*) + (table :posts) {(identifier :title) "foo" - (identifier :user_id) 1}) - "The insert statement should not throw an exception")))) + (identifier :user_id) 2})) + "An exception should have been thrown because of a foreign key constraint") + (is (sql/insert! (conn/get-db-spec *db*) + (table :posts) + {(identifier :title) "foo" + (identifier :user_id) 1}) + "The insert statement should not throw an exception")))