diff --git a/jdbc/src/ragtime/jdbc.clj b/jdbc/src/ragtime/jdbc.clj index 4a2f59f..529ec97 100644 --- a/jdbc/src/ragtime/jdbc.clj +++ b/jdbc/src/ragtime/jdbc.clj @@ -35,32 +35,44 @@ (str (:table_schem metadata) "." (:table_name metadata)) (:table_name metadata)))) -(defn- table-exists? [db-spec ^String table-name] +(defn- table-exists-via-metadata-scan? [db-spec ^String table-name] (some (partial metadata-matches-table? table-name) (get-table-metadata db-spec))) -(defn- ensure-migrations-table-exists [db-spec migrations-table] - (when-not (table-exists? db-spec migrations-table) +(defn- table-exists-via-provided-sql? [db-spec sql] + (pos? (count (sql/query db-spec [sql])))) + +(defn- table-exists? [datasource table-name migrations-table-exists-sql] + (if migrations-table-exists-sql + (table-exists-via-provided-sql? datasource migrations-table-exists-sql) + (table-exists-via-metadata-scan? datasource table-name))) + +(defn- ensure-migrations-table-exists + [db-spec migrations-table migrations-table-exists-sql] + (when-not (table-exists? db-spec migrations-table migrations-table-exists-sql) (sql/execute! db-spec [(migrations-table-ddl migrations-table)]))) (defn- format-datetime [^Date dt] (-> (SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss.SSS") (.format dt))) -(defrecord SqlDatabase [db-spec migrations-table] +(defrecord SqlDatabase [db-spec migrations-table migrations-table-exists-sql] p/DataStore (add-migration-id [_ id] - (ensure-migrations-table-exists db-spec migrations-table) + (ensure-migrations-table-exists db-spec migrations-table + migrations-table-exists-sql) (sql/insert! db-spec migrations-table {:id (str id) :created_at (format-datetime (Date.))})) (remove-migration-id [_ id] - (ensure-migrations-table-exists db-spec migrations-table) + (ensure-migrations-table-exists db-spec migrations-table + migrations-table-exists-sql) (sql/delete! db-spec migrations-table ["id = ?" id])) (applied-migration-ids [_] - (ensure-migrations-table-exists db-spec migrations-table) + (ensure-migrations-table-exists db-spec migrations-table + migrations-table-exists-sql) (sql/query db-spec [(str "SELECT id FROM " migrations-table " ORDER BY created_at")] {:row-fn :id}))) @@ -70,11 +82,17 @@ The following options are allowed: :migrations-table - the name of the table to store the applied migrations - (defaults to ragtime_migrations)" + (defaults to ragtime_migrations) + + :migrations-table-exists-sql - SQL string to check if migrations table + exists. If not supplied, database metadata is + used instead, which can be slow for large DBs." ([db-spec] (sql-database db-spec {})) ([db-spec options] - (->SqlDatabase db-spec (:migrations-table options "ragtime_migrations")))) + (->SqlDatabase db-spec + (:migrations-table options "ragtime_migrations") + (:migrations-table-exists-sql options)))) (defn- execute-sql! [db-spec statements transaction?] (doseq [s statements] diff --git a/jdbc/test/ragtime/jdbc_test.clj b/jdbc/test/ragtime/jdbc_test.clj index b373210..32a76bc 100644 --- a/jdbc/test/ragtime/jdbc_test.clj +++ b/jdbc/test/ragtime/jdbc_test.clj @@ -36,6 +36,20 @@ (is (= ["20" "21"] (sql/query (:db-spec db) ["SELECT * FROM myschema.migrations"] {:row-fn :id}))))) +(deftest test-migrations-table-exists-sql + (let [migrations-table-exists-sql (str "SELECT * FROM INFORMATION_SCHEMA.TABLES" + " WHERE TABLE_CATALOG='TESTDB'" + " AND TABLE_NAME='RAGTIME_MIGRATIONS'") + db (jdbc/sql-database db-spec + {:migrations-table-exists-sql migrations-table-exists-sql})] + (p/add-migration-id db "12") + (is (= ["12"] + (sql/query (:db-spec db) ["SELECT * FROM ragtime_migrations"] {:row-fn :id}))) + + (p/add-migration-id db "13") + (is (= ["12" "13"] + (sql/query (:db-spec db) ["SELECT * FROM ragtime_migrations"] {:row-fn :id}))))) + (defn table-names [db] (set (sql/query (:db-spec db) ["SHOW TABLES"] {:row-fn :table_name}))) diff --git a/next-jdbc/src/ragtime/next_jdbc.clj b/next-jdbc/src/ragtime/next_jdbc.clj index 5ab6b66..b2053cc 100644 --- a/next-jdbc/src/ragtime/next_jdbc.clj +++ b/next-jdbc/src/ragtime/next_jdbc.clj @@ -60,14 +60,24 @@ (str table_schem "." table_name) table_name))) -(defn- table-exists? [datasource ^String table-name] +(defn- table-exists-via-metadata-scan? [datasource ^String table-name] (let [{:keys [tables quote]} (get-db-metadata datasource) table-name-unquoted (unquote-table-name quote table-name)] (some (partial metadata-matches-table? table-name-unquoted) tables))) -(defn- ensure-migrations-table-exists [datasource migrations-table] - (when-not (table-exists? datasource migrations-table) +(defn- table-exists-via-provided-sql? [datasource sql] + (pos? (count (jdbc/execute! datasource [sql])))) + +(defn- table-exists? [datasource table-name migrations-table-exists-sql] + (if migrations-table-exists-sql + (table-exists-via-provided-sql? datasource migrations-table-exists-sql) + (table-exists-via-metadata-scan? datasource table-name))) + +(defn- ensure-migrations-table-exists + [datasource migrations-table migrations-table-exists-sql] + (when-not (table-exists? datasource migrations-table + migrations-table-exists-sql) (let [sql (str "create table " migrations-table " (id varchar(255), created_at varchar(32))")] (jdbc/execute! datasource [sql])))) @@ -76,20 +86,23 @@ (-> (SimpleDateFormat. "yyyy-MM-dd'T'HH:mm:ss.SSS") (.format dt))) -(defrecord SqlDatabase [datasource migrations-table] +(defrecord SqlDatabase [datasource migrations-table migrations-table-exists-sql] p/DataStore (add-migration-id [_ id] - (ensure-migrations-table-exists datasource migrations-table) + (ensure-migrations-table-exists datasource migrations-table + migrations-table-exists-sql) (sql/insert! datasource migrations-table {:id (str id) :created_at (format-datetime (Date.))})) (remove-migration-id [_ id] - (ensure-migrations-table-exists datasource migrations-table) + (ensure-migrations-table-exists datasource migrations-table + migrations-table-exists-sql) (sql/delete! datasource migrations-table ["id = ?" id])) (applied-migration-ids [_] - (ensure-migrations-table-exists datasource migrations-table) + (ensure-migrations-table-exists datasource migrations-table + migrations-table-exists-sql) (let [sql [(str "SELECT id FROM " migrations-table " ORDER BY created_at")]] (->> (sql/query datasource sql {:builder-fn rs/as-unqualified-lower-maps}) (map :id))))) @@ -101,11 +114,17 @@ :migrations-table - the name of the table to store the applied migrations (defaults to ragtime_migrations). You must include the schema name if your DB supports that and you are not - using the default one (ex.: myschema.migrations)" + using the default one (ex.: myschema.migrations) + + :migrations-table-exists-sql - SQL string to check if migrations table + exists. If not supplied, database metadata is + used instead, which can be slow for large DBs." ([datasource] (sql-database datasource {})) ([datasource options] - (->SqlDatabase datasource (:migrations-table options "ragtime_migrations")))) + (->SqlDatabase datasource + (:migrations-table options "ragtime_migrations") + (:migrations-table-exists-sql options)))) (defn- execute-sql! [datasource statements transaction?] (if transaction? diff --git a/next-jdbc/test/ragtime/next_jdbc_test.clj b/next-jdbc/test/ragtime/next_jdbc_test.clj index 3c790a3..8ee369b 100644 --- a/next-jdbc/test/ragtime/next_jdbc_test.clj +++ b/next-jdbc/test/ragtime/next_jdbc_test.clj @@ -53,6 +53,24 @@ (n.j/execute! (:datasource db)) (map :MIGRATIONS/ID)))))) +(deftest test-migrations-table-exists-sql + (let [migrations-table-exists-sql (str "SELECT * FROM INFORMATION_SCHEMA.TABLES" + " WHERE TABLE_CATALOG='TESTDB'" + " AND TABLE_NAME='RAGTIME_MIGRATIONS'") + db (jdbc/sql-database datasource + {:migrations-table-exists-sql migrations-table-exists-sql})] + (p/add-migration-id db "12") + (is (= ["12"] + (->> ["SELECT * FROM ragtime_migrations"] + (sql/query (:datasource db)) + (map :RAGTIME_MIGRATIONS/ID)))) + + (p/add-migration-id db "13") + (is (= ["12" "13"] + (->> ["SELECT * FROM ragtime_migrations"] + (sql/query (:datasource db)) + (map :RAGTIME_MIGRATIONS/ID)))))) + (defn table-names [db] (set (map :TABLES/TABLE_NAME (sql/query (:datasource db) ["SHOW TABLES"]))))