Ruby on Rails: db:schema:load fails due foreign key - ruby-on-rails

okay,
I've been trying to wrap my head around this problem for a while, and I simply cannot come up with an explanation why this behavior is happening.
I have a project that's running Ruby 2.7, Ruby on Rails 5.2.8.1.
On a new system, I'm trying to set up the database using a MySQL 5.7 database, and every time I run bundle exec rake db:create db:schema:load it fails because of a Foreign Key error. And the weird part is that it tries to create the Foreign Key before the actual tables, which is what doesn't make any sense to me as the add_foreign_key statement is the last statement in the schema.rb.
Simplified schema.rb
ActiveRecord::Schema.define(version: 20211230212616) do
create_table "categories", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" do |t|
t.integer "category_id", null: false
t.string "subtitle"
t.text "page_content"
t.text "pinned_styles"
t.text "unpinned_styles"
t.text "updated_by"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category_id"], name: "index_categories_on_category_id", unique: true
end
create_table "faqs", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" do |t|
t.text "question"
t.text "answer"
t.bigint "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["category_id"], name: "index_faqs_on_category_id"
end
add_foreign_key "faqs", "categories"
end
Error Message
bundle exec db:drop db:create db:schema:load
-- create_table("categories", {:force=>:cascade, :options=>"ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"})
rake aborted!
ActiveRecord::StatementInvalid: Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails: DROP TABLE IF EXISTS `categories` CASCADE
/Users/olivar/RubymineProjects/catalog/db/schema.rb:15:in `block in <main>'
/Users/olivar/RubymineProjects/catalog/db/schema.rb:13:in `<main>'
/Users/olivar/.rbenv/versions/2.7.2/bin/bundle:25:in `load'
/Users/olivar/.rbenv/versions/2.7.2/bin/bundle:25:in `<main>'
Caused by:
Mysql2::Error: Cannot delete or update a parent row: a foreign key constraint fails
/Users/olivar/RubymineProjects/catalog/db/schema.rb:15:in `block in <main>'
/Users/olivar/RubymineProjects/catalog/db/schema.rb:13:in `<main>'
/Users/olivar/.rbenv/versions/2.7.2/bin/bundle:25:in `load'
/Users/olivar/.rbenv/versions/2.7.2/bin/bundle:25:in `<main>'
Tasks: TOP => db:schema:load
Yet, every time the command fails because it creates the categories table, then it tries the foreign key, without the faqs table existing (or switches the tables).
Can someone explain this behavior?

Related

Heroku run rails db:migrate showing PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists

When i try to run heroku run rails db:migrate, i get the following error
Running rails db:migrate on ⬢ sharley... up, run.6479 (Free)
I, [2021-01-03T23:13:45.240708 #4] INFO -- : Migrating to AddTimestampToBlogs (20201225171213)
== 20201225171213 AddTimestampToBlogs: migrating ==============================
-- add_timestamps(:blogs, {:null=>true})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `exec'
/app/bin/rails:4:in `<main>'
Caused by:
PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `exec'
/app/bin/rails:4:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
This is my schema file
Please, any help will be appreciated. Ive searched all over but to no avail.
ActiveRecord::Schema.define(version: 2020_12_29_225613) do
create_table "blogs", force: :cascade do |t|
t.string "name"
t.string "title"
t.string "content"
t.datetime "created_at", precenter code hereision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
t.index ["user_id"], name: "index_blogs_on_user_id"
end
create_table "comments", force: :cascade do |t|
t.string "name"
t.text "body"
t.integer "blog_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "post_id"
t.integer "user_id"
t.index ["blog_id"], name: "index_comments_on_blog_id"
end
create_table "users", force: :cascade 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.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "name"
t.integer "user_id"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "comments", "blogs"
end
This is my add timestamps to blog file
class AddTimestampsToBlogs < ActiveRecord::Migration[6.1]
def change
add_timestamps :blogs, null: true
# backfill existing record with created_at and updated_at
# values making clear that the records are faked
long_ago = DateTime.new(2020, 12, 29)
Blog.update_all(created_at: long_ago, updated_at: long_ago)
# change not null constraints
change_column_null :blogs, :created_at, false
change_column_null :blogs, :updated_at, false
end
end
Is there anything else needed?
You could add add_timestamps(:blogs, {:null=>true}) unless column_exists?(:blogs, :created_at) && column_exists?(:blogs, :updated_at). It looks like someone deployed a migration to fix that before or something and now you're trying as well. With the conditional, it will only run the add_timestamps migration method if there are no created or updated at columns
UPDATE
In the migration add the conditional. With the conditional, it will only run that step if the timestamp columns don't exist. The issue is that you or someone on your team has run the migration on heroku before then possibly rolled back locally and then redployed. The db still has that change, hence the error you're receiving. So if you add the conditional, it will add the time stamp columns only if they already don't exist on that table
def change
add_timestamps :blogs, null: true unless column_exists??(:blogs, :created_at) && column_exists?(:blogs, :updated_at)
# backfill existing record with created_at and updated_at
# values making clear that the records are faked
long_ago = DateTime.new(2020, 12, 29)
Blog.update_all(created_at: long_ago, updated_at: long_ago)
# change not null constraints
change_column_null :blogs, :created_at, false
change_column_null :blogs, :updated_at, false
end

PG::UndefinedColumn: ERROR: column "oauth_expires_in" of relation "users" does not exist (in Rails 5)

I'm getting the following error trying to remove a column I have just created.
PG::UndefinedColumn: ERROR: column "oauth_expires_in" of relation
"users" does not exist
Here my first migration file.
20170709013540_add_oauth_expires_in_to_users.rb
class AddOauthExpiresInToUsers < ActiveRecord::Migration[5.0]
def change
add_column :users, :oauth_expires_in, :integer
end
end
Which created consequently this new column in my table "users" in schema.rb
create_table "users", force: :cascade 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.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.string "oauth_token"
t.string "oauth_secret"
t.string "photo"
t.boolean "admin", default: false, null: false
t.integer "oauth_expires_in"
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
end
Then I created a new migration file to remove the column I created. See below:
20170709111539_remove_oauth_expires_in_from_users.rb
class RemoveOauthExpiresInFromUsers < ActiveRecord::Migration[5.0]
def change
remove_column :users, :oauth_expires_in, :integer
end
end
And when I run the migration I get the following error:
== 20170709111539 RemoveOauthExpiresInFromUsers: migrating ====================
-- remove_column(:users, :oauth_expires_in, :integer)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedColumn: ERROR: column "oauth_expires_in" of relation "users" does not exist
: ALTER TABLE "users" DROP "oauth_expires_in"
/Users/davidverbustel/code/davidverbustel/barky/db/migrate/20170709111539_remove_oauth_exp ires_in_from_users.rb:3:in `change'
./bin/rails:4:in `require'
./bin/rails:4:in `<main>'
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "oauth_expires_in" of relation "users" does not exist
: ALTER TABLE "users" DROP "oauth_expires_in"
/Users/davidverbustel/code/davidverbustel/barky/db/migrate/20170709111539_remove_oauth_exp ires_in_from_users.rb:3:in `change'
./bin/rails:4:in `require'
./bin/rails:4:in `<main>'
PG::UndefinedColumn: ERROR: column "oauth_expires_in" of relation "users" does not exist
/Users/davidverbustel/code/davidverbustel/barky/db/migrate/20170709111539_remove_oauth_exp ires_in_from_users.rb:3:in `change'
./bin/rails:4:in `require'
./bin/rails:4:in `<main>'
Tasks: TOP => db:migrate
I have been scratching my head for a while but cannot spot the mistake?!.
Any idea would be much appreciated.
Thanks!

heroku rake migrate db error relation "post" doesn't exist

So.. im trying to migrate my db to heroku for the first time, doing:
heroku rake db:migrate
but im getting this horrible error that i cant resolve !
** Invoke db:migrate (first_time)
** Invoke environment (first_time)
** Execute environment
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:migrate
ActiveRecord::SchemaMigration Load (1.8ms) SELECT "schema_migrations".* FROM "schema_migrations"
Migrating to CreateComments (20150528110329)
(1.6ms) BEGIN
== 20150528110329 CreateComments: migrating ===================================
-- create_table(:comments)
(9.4ms) CREATE TABLE "comments" ("id" serial primary key, "name" character varying, "body" character varying, "post_id" integer, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
(3.8ms) CREATE INDEX "index_comments_on_post_id" ON "comments" ("post_id")
(6.4ms) ALTER TABLE "comments" ADD CONSTRAINT "fk_rails_2fd19c0db7"
FOREIGN KEY ("post_id")
REFERENCES "posts" ("id")
PG::UndefinedTable: ERROR: relation "posts" does not exist
: ALTER TABLE "comments" ADD CONSTRAINT "fk_rails_2fd19c0db7"
FOREIGN KEY ("post_id")
REFERENCES "posts" ("id")
(1.6ms) ROLLBACK
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
what i can understand is that its trying to reference from a table that doesnt exist yet.. but I dont know how to create my post table first...
schema.rb>
ActiveRecord::Schema.define(version: 20150622053408) do
create_table "comments", force: :cascade do |t|
t.string "name"
t.string "body"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "comments", ["post_id"], name: "index_comments_on_post_id"
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "subtitle"
t.text "content"
t.string "img"
t.string "category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
end
I tried switching in every way possible my code but with no results.
Hope that someone could help me out... thanks for everything!
I'm of the opinion that somewhere along the line one of your migrations were edited (which would be the problem if not properly rolledback first etc) and is now causing a major problem. And since you need to keep the data and can't do a full db:rollback I would say there is maybe one option I can think of unless someone else has a better idea. I never recommend editing migration files but I've had to do it in the past in certain circumstances. Do the following and make sure you keep in mind that any changes to the db can't be undone with a simple
git checkout .
I would create a new migration file by hand, not by using the rails generator and give it a timestamp that is a couple seconds before the timestamp of the Comments table migration file. I would then create the Posts table in that migration and edit the other existing migration that created the Posts table to just add it's columns. You must then go into your database (Not your rails console but your database which is probably Postgres) and insert this migration into the "schema_migrations" table. This schema_migrations table is automagically setup by rails whenever you generate a migration and run db:migrate. You'll notice the entries in this schema_migrations table are listed by "Version" where the version is the timestamp of the migration in your migration folder. You must then add the migration file timestamp to your schema_migrations table and it should be good to go.
UPDATE: IF you don't care about losing the data you can rollback all migrations, edit your migrations files and then re-run them.
bundle exec rake db:migrate:status #Look at the first version id and copy it.
bundle exec rake db:rollback VERSION=theVersionNumberYouCopiedHere
Then re check the migration statuses with the first command and make sure all migration statuses are listed as "down". This will have dropped all tables etc. Then go into your migration files and remove the referencing of your posts table from the Comments table that was giving you the issues. Then re-run all migrations again. Then add your posts_id reference to the comments table as follows by generating and running the following migration:
rails g migration AddPostToComments post:references
Looking in that migration file you should see something like this:
class AddPostToCommentss < ActiveRecord::Migration
def change
add_reference :comments, :post, index: true
add_foreign_key :comments, :posts
end
end
Now run bundle exec rake db:migrate and you should be good to go. Commit all the changes to git and then push to heroku!

Ruby On Rails with MemSQL. Table `schema_migrations` must either have a PRIMARY or SHARD key

I am currently using Rails (4.2.0) with MySQL.
I want to setup MemSQL with my Rails application.
I followed the tutorial to get MemSQL working on OSX inside a docker container. It works fine.
I have set up the MemSQL IP inside my database.yml.
I successfully ran rake db:create.
However when running rake db:migrate, I get the following error:
> rake db:migrate
rake aborted!
ActiveRecord::StatementInvalid: Mysql2::Error: Distributed tables must either have a PRIMARY or SHARD key. Visit http://docs.memsql.com/4.0/concepts/porting-apps/ for more information: CREATE TABLE `schema_migrations` (`version` varchar(255) NOT NULL) ENGINE=InnoDB
Mysql2::Error: Distributed tables must either have a PRIMARY or SHARD key. Visit http://docs.memsql.com/4.0/concepts/porting-apps/ for more information
I guess I could, somehow, edit the db/schema.rb file and try to alter the schema_migrations table. But id doesn't seems to be a good solution as it will be regenerated each time I update my tables.
What should I do to get over with this issue ?
EDIT
This error occurs with a brand new rails project.
I did the following:
create a new rails project
add mysql2 gem
configure database.yml to memsql ip
add devise gem
add user table with devise
rake db:create
rake db:migrate (result of this command below)
Content of schema.rb
ActiveRecord::Schema.define(version: 20150729143850) do
create_table "users", force: :cascade 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.datetime "created_at", null: false
t.datetime "updated_at", null: false
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
EDIT II
This questions is more for Rails Developer than MemSQL expert.
- MemSQL NEED a primary or shard key for each table it handles.
- However Rails does not define one for its schema_migrations table.
This issue, brings me the following question:
How to tweak Rails to have it define a Primary or Shard key for its schema_migrations table ?
Memsql is a distributed database. This probably means it would require some form of sharding (horizontal partitioning - logical of course!) implemented. By default, Memsql uses the primary key as the shard key if not explicitly specified. It seems that in your case, there are tables that are missing a primary key and shard keys have not been specified. Hence you would have to either set primary keys for those tables or explicitly choose a shard key column. See the Memsql documentation for more details.

Rails database migration fails with "duplicate column name: email" when adding devise's generated User table

I am in the process of installing devise. I followed all the required steps and ended here:
$ rails generate devise User
$ rake db:migrate
When I run rake db:migrate I get the following error:
$ rake db:migrate
== 20140618020442 AddDeviseToUsers: migrating =================================
-- change_table(:users)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: duplicate column name: email: ALTER TABLE "users" ADD "em
ail" varchar(255) DEFAULT '' NOT NULLc:/appname/db/migrate/20140618020442_add_d
evise_to_users.rb:5:in `block in up'
c:/appname/db/migrate/20140618020442_add_devise_to_users.rb:3:in `up'
c:in `migrate'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
My project has the following database schema. Some of the devise migration appears to be successful, but the migration was not completed.
ActiveRecord::Schema.define(version: 20140618020442) do
create_table "listings", force: true do |t|
t.string "name"
t.text "description"
t.decimal "price"
t.datetime "created_at"
t.datetime "updated_at"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_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.datetime "created_at"
t.datetime "updated_at"
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
What is the problem and how do I fix it?
So the problem is you already have the users table inside your database. You need to either drop that table and create a new one if it's not working or just continue using that table.
It's not a good idea to drop your database but since you are making a new app(as you creating a users table) so in your case it'll be better to drop that database and recreate it(to make sure everything works). Try these commands in sequence:
rake db:drop
rake db:create
rake db:migrate
It should work fine and you need not do rails generate devise User as you already have your users migration file
Update:
If you don't want to drop your database then you can create a new migration file and delete your users table there.
rails generate migration DropUsersTable
after that edit your migration file
class DropUsersTable < ActiveRecord::Migration
def change
drop_table :users
end
end
and then do rake db:migrate
Solution:
1. Put all the migrations in migration folder in new archived_migrations folder.
2. db:schema:load
3. Run rake db:migrate

Resources