Index: app/models/user.rb =================================================================== --- app/models/user.rb (revision 46) +++ app/models/user.rb (revision 50) @@ -96,7 +96,13 @@ def self.try_to_login(login, password) # Make sure no one can sign in with an empty password return nil if password.to_s.empty? - user = find(:first, :conditions => ["login=?", login]) + # This does a case-insensitive lookup: + # - Uses the index (avoids table walks) if the user exists (maybe causes table walks on MySQL for non-existant users?) + # - Does not depend on the database adapter + # (MySQL will always match on the first, as it does not support function indexes; + # PostgreSQL will match on first or second, both times with index (standard or the one created by the 20100203232731 migration); + # SQLite should always be small enough for table walks) + user = find(:first, :conditions => ["login=?", login]) || find(:first, :conditions => ["LOWER(login)=LOWER(?)", login]) if user # user is already in local database return nil if !user.active? Index: db/migrate/20100203232731_add_case_insensitive_user_index.rb =================================================================== --- db/migrate/20100203232731_add_case_insensitive_user_index.rb (revision 0) +++ db/migrate/20100203232731_add_case_insensitive_user_index.rb (revision 50) @@ -0,0 +1,15 @@ +class AddCaseInsensitiveUserIndex < ActiveRecord::Migration + def self.up + # Migrates PostgreSQL databases only + # MySQL compares case-insensitive by default + if ActiveRecord::Base.connection.adapter_name =~ /postgresql/i + execute "CREATE INDEX index_users_on_lower_login ON users (LOWER(login));" + end + end + + def self.down + if ActiveRecord::Base.connection.adapter_name =~ /postgresql/i + execute "DROP INDEX index_users_on_lower_login;" + end + end +end