Fixes #52 by using US-locale lower-case function

This commit is contained in:
Sean Corfield 2019-08-08 17:01:23 -07:00
parent 175904bcf6
commit 87e44ae6a6
4 changed files with 33 additions and 19 deletions

View file

@ -6,7 +6,7 @@ Only accretive/fixative changes will be made from now on.
The following changes have been committed to the **master** branch since the 1.0.5 release:
* None.
* Fix #52 by replacing `clojure.string/lower-case` with a US-locale function to avoid breakage in locales such as Turkish.
## Stable Builds

View file

@ -15,7 +15,7 @@ The default builder for rows and result sets creates qualified keywords that mat
The reason behind the default is to a) be a simple transform, b) produce qualified keys in keeping with Clojure's direction (with `clojure.spec` etc), and c) not mess with the data. `as-arrays` is (slightly) faster than `as-maps` since it produces less data (vectors of values instead of vectors of hash maps), but the `lower` options will be slightly slower since they include (conditional) logic to convert strings to lower-case. The `unqualified` options may be slightly faster than their qualified equivalents but make no attempt to keep column names unique if your SQL joins across multiple tables.
In addition, the following generic builders can take `:label-fn` and `:qualifier-fn` options to control how the label and qualified are processed. The `lower` variants above are implemented in terms of these, passing `clojure.string/lower-case` for both of those options.
In addition, the following generic builders can take `:label-fn` and `:qualifier-fn` options to control how the label and qualified are processed. The `lower` variants above are implemented in terms of these, passing a `lower-case` function for both of those options.
* `as-modified-maps` -- table-qualified keywords,
* `as-unqualified-modified-maps` -- simple keywords,

View file

@ -3,9 +3,9 @@
(ns next.jdbc.optional
"Builders that treat NULL SQL values as 'optional' and omit the
corresponding keys from the Clojure hash maps for the rows."
(:require [clojure.string :as str]
[next.jdbc.result-set :as rs])
(:import (java.sql ResultSet)))
(:require [next.jdbc.result-set :as rs])
(:import (java.sql ResultSet)
(java.util Locale)))
(set! *warn-on-reflection* true)
@ -69,18 +69,25 @@
cols (rs/get-unqualified-modified-column-names rsmeta opts)]
(->MapResultSetOptionalBuilder rs rsmeta cols)))
(defn- lower-case
"Converts a string to lower case in the US locale to avoid problems in
locales where the lower case version of a character is not a valid SQL
entity name (e.g., Turkish)."
[^String s]
(.toLowerCase s (Locale/US)))
(defn as-lower-maps
"Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder`
that produces bare vectors of hash map rows, with lower-case keys and nil
columns omitted."
[rs opts]
(as-modified-maps rs (assoc opts
:qualifier-fn str/lower-case
:label-fn str/lower-case)))
:qualifier-fn lower-case
:label-fn lower-case)))
(defn as-unqualified-lower-maps
"Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder`
that produces bare vectors of hash map rows, with simple, lower-case keys
and nil columns omitted."
[rs opts]
(as-unqualified-modified-maps rs (assoc opts :label-fn str/lower-case)))
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))

View file

@ -12,12 +12,12 @@
Also provides the default implemenations for `Executable` and
the default `datafy`/`nav` behavior for rows from a result set."
(:require [clojure.core.protocols :as core-p]
[clojure.string :as str]
[next.jdbc.prepare :as prepare]
[next.jdbc.protocols :as p])
(:import (java.sql PreparedStatement
ResultSet ResultSetMetaData
SQLException)))
SQLException)
(java.util Locale)))
(set! *warn-on-reflection* true)
@ -60,19 +60,26 @@
(mapv (fn [^Integer i] (keyword ((:label-fn opts) (.getColumnLabel rsmeta i))))
(range 1 (inc (.getColumnCount rsmeta)))))
(defn- lower-case
"Converts a string to lower case in the US locale to avoid problems in
locales where the lower case version of a character is not a valid SQL
entity name (e.g., Turkish)."
[^String s]
(.toLowerCase s (Locale/US)))
(defn get-lower-column-names
"Given `ResultSetMetaData`, return a vector of lower-case column names, each
qualified by the table from which it came."
[rsmeta opts]
(get-modified-column-names rsmeta (assoc opts
:qualifier-fn str/lower-case
:label-fn str/lower-case)))
:qualifier-fn lower-case
:label-fn lower-case)))
(defn get-unqualified-lower-column-names
"Given `ResultSetMetaData`, return a vector of unqualified column names."
[rsmeta opts]
(get-unqualified-modified-column-names rsmeta
(assoc opts :label-fn str/lower-case)))
(assoc opts :label-fn lower-case)))
(defprotocol ReadableColumn
"Protocol for reading objects from the `java.sql.ResultSet`. Default
@ -184,14 +191,14 @@
that produces bare vectors of hash map rows, with lower-case keys."
[rs opts]
(as-modified-maps rs (assoc opts
:qualifier-fn str/lower-case
:label-fn str/lower-case)))
:qualifier-fn lower-case
:label-fn lower-case)))
(defn as-unqualified-lower-maps
"Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder`
that produces bare vectors of hash map rows, with simple, lower-case keys."
[rs opts]
(as-unqualified-modified-maps rs (assoc opts :label-fn str/lower-case)))
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))
(defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
RowBuilder
@ -254,15 +261,15 @@
row values."
[rs opts]
(as-modified-arrays rs (assoc opts
:qualifier-fn str/lower-case
:label-fn str/lower-case)))
:qualifier-fn lower-case
:label-fn lower-case)))
(defn as-unqualified-lower-arrays
"Given a `ResultSet` and options, return a `RowBuilder` / `ResultSetBuilder`
that produces a vector of simple, lower-case column names followed by
vectors of row values."
[rs opts]
(as-unqualified-modified-arrays rs (assoc opts :label-fn str/lower-case)))
(as-unqualified-modified-arrays rs (assoc opts :label-fn lower-case)))
(declare navize-row)