From c2a18bfea6b100a74f4de1ce98b65667fb50af41 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 22 Jul 2020 16:23:39 -0700 Subject: [PATCH] Address #134 by documenting builder-adapter workaround for SQLite --- doc/tips-and-tricks.md | 20 ++++++++++++++++++++ test/next/jdbc_test.clj | 20 +++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/doc/tips-and-tricks.md b/doc/tips-and-tricks.md index 3ece69b..9cbd79f 100644 --- a/doc/tips-and-tricks.md +++ b/doc/tips-and-tricks.md @@ -351,4 +351,24 @@ And those columns are nicely transformed into Clojure data when querying: If you're unsure whether you want to use json or jsonb, use jsonb. +## SQLite + +SQLite supports both `bool` and `bit` column types but, unlike pretty much every other database out there, it yields `0` or `1` as the column value instead of `false` or `true`. This means that with SQLite alone, you can't just rely on `bool` or `bit` columns being treated as truthy/falsey values in Clojure. + +You can work around this using a builder that handles reading the column directly as a `Boolean`: + +```clojure +(jdbc/execute! ds ["select * from some_table"] + {:builder-fn (rs/builder-adapter + rs/as-maps + (fn [builder ^ResultSet rs ^Integer i] + (let [rsm ^ResultSetMetaData (:rsmeta builder)] + (rs/read-column-by-index + (if (#{"BIT" "BOOL"} (.getColumnTypeName rsm i)) + (.getBoolean rs i) + (.getObject rs i)) + rsm + i))))}) +``` + [<: Friendly SQL Functions](/doc/friendly-sql-functions.md) | [Result Set Builders :>](/doc/result-set-builders.md) diff --git a/test/next/jdbc_test.clj b/test/next/jdbc_test.clj index eb1e951..0944de3 100644 --- a/test/next/jdbc_test.clj +++ b/test/next/jdbc_test.clj @@ -15,7 +15,7 @@ [next.jdbc.result-set :as rs] [next.jdbc.specs :as specs] [next.jdbc.types :as types]) - (:import (java.sql ResultSet))) + (:import (java.sql ResultSet ResultSetMetaData Types))) (set! *warn-on-reflection* true) @@ -336,6 +336,24 @@ VALUES ('Pear', 'green', 49, 47) (is (every? number? (map (column :BTEST/IS_IT) data))) (is (every? boolean? (map (column :BTEST/IS_IT) data)))) (if (or (sqlite?) (derby?)) + (is (every? number? (map (column :BTEST/TWIDDLE) data))) + (is (every? boolean? (map (column :BTEST/TWIDDLE) data))))) + (let [data (jdbc/execute! (ds) ["select * from btest"] + (cond-> (default-options) + (sqlite?) + (assoc :builder-fn + (rs/builder-adapter + rs/as-maps + (fn [builder ^ResultSet rs ^Integer i] + (let [rsm ^ResultSetMetaData (:rsmeta builder)] + (rs/read-column-by-index + (if (#{"BIT" "BOOL"} (.getColumnTypeName rsm i)) + (.getBoolean rs i) + (.getObject rs i)) + rsm + i)))))))] + (is (every? boolean? (map (column :BTEST/IS_IT) data))) + (if (derby?) (is (every? number? (map (column :BTEST/TWIDDLE) data))) (is (every? boolean? (map (column :BTEST/TWIDDLE) data))))))