ActiveRecord validate uniqueness case sensitivity: variazioni

Eil,

mi trovo con un rdbms rompiballe che non distingue tra maiuscole e
minuscole, e che quindi mi da lo stesso risultato quando faccio

select 1 from records where column = ‘loL’
select 1 from records where column = ‘Lol’
select 1 from records where column = ‘lOl’

C’ un simpatico indice sulla colonna, e ovviamente il db mi scoppia
in mano quando provo a fare INSERT di due stringhe uguali.

Voglio validare l’unicit di un campo. Chiaramente:

validates :column, :uniqueness

apparentemente, questo mi risulta in due operazioni: un check sul db,
e un’altro in ruby land.
Ergo, il db mi risponde indifferentemente qualunque sia il case, ma
poi in ruby non funziona perch normalmente “FOO” e “foo” sono
diversi.

A me, visto che comunque serve che siano uguali indifferentemente dal
case, andrebbe bene usare

validates :column, :uniqueness => {:case_sensitive => false}

ma questo mi risulta in una

select 1 from records where LOWER(column) = LOWER(‘lol’)

che plausibilmente dovrebbe essere la stessa cosa, visto che l’indice
case insensitive, ma concretamente mi risulta nei log come una query
non indicizzata e mi crea un sacco di righe inutili.

Qualcuno ha visto una situazione simile, ed a conoscenza di una cosa
tipo

validates :column, :uniqueness => { :case_sensitive => {:db =>
:dontcare, :ruby => :ignorecase} }

?

(peraltro ora che vedo i sorgenti di AR 3.2 non c’ un check in ruby
land eppure son sicuro di avere un test che dimostra sto doppio
check)


twitter: @riffraff
blog (en, it): www.riffraff.info riffraff.blogsome.com
work: circleme.com

2012/6/18 gabriele renzi [email protected]

select 1 from records where LOWER(column) = LOWER(‘lol’)

che plausibilmente dovrebbe essere la stessa cosa, visto che l’indice
case insensitive, ma concretamente mi risulta nei log come una query
non indicizzata e mi crea un sacco di righe inutili.

Non conosco nessun modo per fare l’ignore solo in rubyland. Immagino che
il
db non ti permetta di creare un indice sul risultato della funzione
lower?
(sono quasi certo che postgresql e oracle lo permettano).

Come workaround non particolarmente bello, potresti aggiungere una
colonna
‘column_lower’ che ha dentro il dato denormalizzato dell’operazione
lower(column) e mettere l’indice su questa nuova colonna. Da quanto ho
capito ad esempio mysql deve fare un full table scan quando usi funzioni
SQL su una colonna indicizzata.

Stefano

Potresti sistemarti il dato per non far arrabbiare il db es.

before_validation :force_downcase

private
def force_downcase
self.pippo.downcase! if attribute_present?(“pippo”)
end