Louis Morin
Louis Morin

Reputation: 149

SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer/

I ran the command

rails g migration AddUser_idToComments User_id:string

and then I figured out that User_id should be an integer and so I ran

rails g migration AddUser_idToComments User_id:integer --force thinking that it would overwrite the initial command.

But now, I'm getting this error:

``` louismorin$ rake db:migrate == 20140910155248 AddIndexToComments: migrating =============================== -- add_column(:comments, :Index, :string) -> 0.0069s == 20140910155248 AddIndexToComments: migrated (0.0070s) ======================

== 20140910181022 AddUserIdToComments: migrating ============================== -- add_column(:comments, :User_id, :integer) rake aborted! StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer/Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:in change' ActiveRecord::StatementInvalid: SQLite3::SQLException: duplicate column name: User_id: ALTER TABLE "comments" ADD "User_id" integer /Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:inchange' SQLite3::SQLException: duplicate column name: User_id /Users/louismorin/code/CP299/db/migrate/20140910181022_add_user_id_to_comments.rb:3:in `change' Tasks: TOP => db:migrate (See full trace by running task with --trace) ```

Here's my schema.rb file

``` ActiveRecord::Schema.define(version: 20140910155210) do

create_table "comments", force: true do |t|
  t.text     "body"
  t.integer  "post_id"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "User_Id"
end

add_index "comments", ["post_id"], name: "index_comments_on_post_id"

create_table "posts", force: true do |t|
  t.string   "title"
  t.text     "body"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.integer  "user_id"
  t.integer  "topic_id"
end

add_index "posts", ["topic_id"], name: "index_posts_on_topic_id"
add_index "posts", ["user_id"], name: "index_posts_on_user_id"

create_table "topics", force: true do |t|
  t.string   "name"
  t.boolean  "public",      default: true
  t.text     "description"
  t.datetime "created_at"
  t.datetime "updated_at"
end

create_table "users", force: true do |t|
  t.string   "email",                  default: "", null: false
  t.string   "encrypted_password",     default: "", null: false
  t.string   "reset_password_token"
  t.datetime "reset_password_sent_at"
  t.datetime "remember_created_at"
  t.integer  "sign_in_count",          default: 0,  null: false
  t.datetime "current_sign_in_at"
  t.datetime "last_sign_in_at"
  t.string   "current_sign_in_ip"
  t.string   "last_sign_in_ip"
  t.string   "confirmation_token"
  t.datetime "confirmed_at"
  t.datetime "confirmation_sent_at"
  t.string   "unconfirmed_email"
  t.string   "name"
  t.datetime "created_at"
  t.datetime "updated_at"
  t.string   "role"
  t.string   "avatar"
  t.string   "Image"
end

add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique:     true

end

```

Upvotes: 1

Views: 3213

Answers (1)

Sasha
Sasha

Reputation: 6466

Because of the error, your second migration -- changing the column type -- didn't run. If that migration was only intended to change that one column, we could then delete the file it generated and try again.

If you don't yet have any data in that column that you care about, that's pretty easy:

rails g migration ChangeTypeOfUserIdOnComments

This migration name isn't special. Might as well have been DoWhateverIWant

Then, edit the created migration's change method to something like this:

def change
  remove_column :comments, :user_id
  add_column :comments, :user_id, :integer
  add_index :comments, :user_id
end

When you then run your un-run migrations rake db:migrate, it shouldn't trip up on the one which errored (because we deleted it), and then it should run this one, which removes the column and adds it back with the correct type.

If you DO have data you want to save, the procedure is more complicated. Your change method would have to grab the current user_id's for each comment, and then assign them to the new comments when we create the new column. The below should hopefully work:

def change
  user_ids = {}
  Comment.find_each{ |c| user_ids[c.id] = c.user_id.to_i } 

  remove_column :comments, :user_id
  add_column :comments, :user_id, :integer
  add_index :comments, :user_id
  Comment.each{ |c| c.update_attribute(:user_id, user_ids[c.id])
end

Also note that the names of the migration in the command are generally all CamelCase or all snake_case. So: AddColumnUserIdToComments or add_column_user_id_to_comments. Naming as you did might cause problems.

** EDIT **

Modify a Column's Type in sqlite3

It appears that SQLite has NO good way to modify/drop a column. I'd suggest either:

Dropping and restarting a SQLITe table

Drop the table, drop the original user_id migration and the line in the new one about removing the old user_id column, and then create a new table with the new migrations. Should work fine if you don't care about your data

Switching to Postgres, which is the database Heroku uses.

It's probably a good idea (because you want your production and local databases to behave identically), but can be bug-prone to switch to.

Look here for guidance -- Change from SQLite to PostgreSQL in a fresh Rails project

Upvotes: 0

Related Questions