From b18120a55b1341386b2bd9c65116279b0d87abc7 Mon Sep 17 00:00:00 2001
From: Nelson Morris <nmorris@nelsonmorris.net>
Date: Mon, 13 Feb 2012 23:17:53 -0600
Subject: [PATCH] Add sqlite3 support

---
 .gitignore                                      |    3 +-
 pom.xml                                         |    6 ++++
 src/main/clojure/clojure/java/jdbc/internal.clj |   26 ++++++++++------
 src/test/clojure/clojure/java/test_jdbc.clj     |   35 ++++++++++++++++++----
 4 files changed, 52 insertions(+), 18 deletions(-)

diff --git a/.gitignore b/.gitignore
index 4122369..2ea6c8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,9 @@
 .settings
 bin
 classes
+clojure_test_sqlite3
 clojure_test_derby
 clojure_test_hsqldb.*
 derby.log
 target
-settings.xml
\ No newline at end of file
+settings.xml
diff --git a/pom.xml b/pom.xml
index fb9f082..0027a3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,12 @@
   		<scope>test</scope>
   	</dependency>
     <dependency>
+      <groupId>org.xerial</groupId>
+      <artifactId>sqlite-jdbc</artifactId>
+      <version>3.7.2</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>postgresql</groupId>
       <artifactId>postgresql</artifactId>
       <version>8.4-702.jdbc4</version>
diff --git a/src/main/clojure/clojure/java/jdbc/internal.clj b/src/main/clojure/clojure/java/jdbc/internal.clj
index 7ab7ed3..639a530 100644
--- a/src/main/clojure/clojure/java/jdbc/internal.clj
+++ b/src/main/clojure/clojure/java/jdbc/internal.clj
@@ -335,11 +335,17 @@
   (with-open [^PreparedStatement stmt (prepare-statement* (connection*) sql :return-keys true)]
     (set-parameters stmt param-group)
     (transaction* (fn [] (let [counts (.executeUpdate stmt)]
-                           (try
-                             (first (resultset-seq* (.getGeneratedKeys stmt)))
-                             (catch Exception _
-                               ;; assume generated keys is unsupported and return counts instead: 
-                               counts)))))))
+                          (try
+                            (let [rs (.getGeneratedKeys stmt)
+                                  result (first (resultset-seq* rs))]
+                              ;; close is required for sqlite3
+                              ;; transaction support
+                              ;; see http://www.zentus.com/sqlitejdbc/usage.html
+                              (.close rs)
+                              result)
+                            (catch Exception e
+                              ;; assume generated keys is unsupported and return counts instead: 
+                              counts)))))))
 
 (defn do-prepared*
   "Executes an (optionally parameterized) SQL prepared statement on the
@@ -349,11 +355,11 @@
   [sql & param-groups]
   (with-open [^PreparedStatement stmt (prepare-statement* (connection*) sql)]
     (if (empty? param-groups)
-      (.addBatch stmt)
-      (doseq [param-group param-groups]
-        (set-parameters stmt param-group)
-        (.addBatch stmt)))
-    (transaction* (fn [] (seq (.executeBatch stmt))))))
+      (transaction* (fn [] [(.executeUpdate stmt)]))
+      (do (doseq [param-group param-groups]
+            (set-parameters stmt param-group)
+            (.addBatch stmt))
+          (transaction* (fn [] (seq (.executeBatch stmt))))))))
 
 (defn with-query-results*
   "Executes a query, then evaluates func passing in a seq of the results as
diff --git a/src/test/clojure/clojure/java/test_jdbc.clj b/src/test/clojure/clojure/java/test_jdbc.clj
index ff99ebf..f92f892 100644
--- a/src/test/clojure/clojure/java/test_jdbc.clj
+++ b/src/test/clojure/clojure/java/test_jdbc.clj
@@ -52,6 +52,9 @@
                   :user "clojure_test"
                   :password "clojure_test"})
 
+(def sqlite3-db {:subprotocol "sqlite"
+                 :subname "clojure_test_sqlite3"})
+
 ;; To test against the stringified DB connection settings:
 (def mysql-str-db
   "mysql://clojure_test:clojure_test@localhost:3306/clojure_test")
@@ -174,7 +177,9 @@
           "postgresql" (is (= 2 (count r)))
           "mysql" (is (= '({:generated_key 1} {:generated_key 2}) r))
           "hsqldb" (is (= '(1 1) r))
-          "derby" (is (= '({:1 nil} {:1 nil}) r))))
+          "derby" (is (= '({:1 nil} {:1 nil}) r))
+          "sqlite" (is (= (list {(keyword "last_insert_rowid()") 1}
+                                {(keyword "last_insert_rowid()") 2}) r))))
       (is (= 2 (sql/with-query-results res ["SELECT * FROM fruit"] (count res))))
       (is (= "Pomegranate" (sql/with-query-results res ["SELECT * FROM fruit WHERE cost = ?" 585] (:name (first res))))))))
 
@@ -242,12 +247,14 @@
       (create-test-table :fruit db)
       (try
         (sql/transaction
-          (sql/insert-values
-            :fruit
-            [:name :appearance]
-            ["Grape" "yummy"]
-            ["Pear" "bruised"]
-            ["Apple" "strange" "whoops"]))
+         (sql/insert-values
+          :fruit
+          [:name :appearance]
+          ["Grape" "yummy"]
+          ["Pear" "bruised"]
+          ["Apple" "strange" "whoops"])
+         ;; sqlite3 does not error on a group that is too large
+         (throw (java.sql.SQLException.)))
         (catch java.sql.SQLException _
           (is (= 0 (sql/with-query-results res ["SELECT * FROM fruit"] (count res))))))
       (is (= 0 (sql/with-query-results res ["SELECT * FROM fruit"] (count res)))))))
@@ -272,6 +279,20 @@
           (is (= 0 (sql/with-query-results res ["SELECT * FROM fruit"] (count res))))))
       (is (= 0 (sql/with-query-results res ["SELECT * FROM fruit"] (count res)))))))
 
+(deftest test-transactions-with-possible-generated-keys-result-set
+  (doseq [db (test-specs)]
+    (sql/with-connection db
+      (create-test-table :fruit db)
+      (try
+        (sql/transaction
+         (sql/set-rollback-only)
+         (sql/insert-values
+          :fruit
+          [:name :appearance]
+          ["Grape" "yummy"])
+         (is (= 1 (sql/with-query-results res ["SELECT * FROM fruit"] (count res))))))
+      (is (= 0 (sql/with-query-results res ["SELECT * FROM fruit"] (count res)))))))
+
 (deftest test-metadata
   (doseq [db (test-specs)]
     (let [metadata (sql/with-connection
-- 
1.7.4.1

