Alex Wayne
Alex Wayne

Reputation: 187014

Data migration problems when you explicitly set ID's (Rails + Postgres)

I'm writing a script for a big migration and have come across a major issue.

# Import users
user_data.each do |data|
  u = User.new
  u.id = data.id
  u.email = data.email
  # more user attributes set...
  u.save!
end

# required to prevent Postgres from trying to use now taken user ids
ActiveRecord::Base.connection.execute "ALTER SEQUENCE users_id_seq RESTART WITH #{User.last.id+1};"

So first we read user data from a data source, and set it's id manually. We need to preserve ids since we are migrating associated data as well.

Then later on, we need to create more users conditionally from the data of an associated object.

# Create a user for this email if no user with this email exists.
if data.email
  user = User.find_by_email(data.email)
  if user
    o.user = user
  else
    o.user = User.create!(
      first_name: 'Unknown',
      last_name:  'Unknown',
      email:      data.email,
      password:   generate_temp_password
    )
  end
end

This fails at User.create! with:

Validation failed: Email has already been taken (ActiveRecord::RecordInvalid)

I've debugged this a bit and can see that User.where(email: data.email).first is nil right before this error is thrown. I suspect this has something to with setting ids beyond the current auto increment value, somehow causing the new records to be invisible in my queries, but visible to Postgres own validations.

So how can a user with a specific email not be present, but still trigger DB validation errors?

Upvotes: 1

Views: 317

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187014

Apparently, Devise downcases email addresses. And the offending email had some caps in it. So it missed a case sensitive check, and then failed as a dupe when case insensitive.

Devise outsmarted me it seems.

Upvotes: 1

Related Questions