As described in the title, I have a migration in my Rails app which creates a table. We have a Postgres database. When I run the migration in development, it fails:
ActiveRecord::StatementInvalid: PG::Error: ERROR: current transaction is aborted, commands ignored until end of transaction block
: CREATE TABLE "feedback_questions" ("id" serial primary key, "survey_id" integer, "question_text" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
The migration is long, but the table creation is the first thing that happens. It looks like this:
def up
create_table :feedback_questions do |t|
t.integer :survey_id
t.text :question_text
t.timestamps
end
# ...
end
It looks to me like this isn't an issue with the migration, but with the database (the error is coming from PG). The error message is similar to this question, but it's not saying anything about what's wrong - just that there's an error. (Also, the table doesn't exist yet; I've checked.)
How can I get more data to debug this?
The error message was a red herring. The issue was not with the table creation; the issue was with the format of the migration itself. I was defining a few models in the migration which used tables created in the #up method. When rake tried to start the migration, it looked for tables for those rows, and flipped when it couldn't find them. The create statement just happened to be the first command not run after the error.
Related
Been working on this particular issue for a while and thought it was time to ask for help. I've had a look at the following links but no luck
PG undefinedtable error relation users does not exist
https://github.com/rails/rails/issues/3279
And my issue is somewhat similar to this https://github.com/rails/rails/issues/6470 but not exactly.
I have a class called type_tables which is the base class for the STI here is the schema for STI
create_table "type_tables", force: :cascade do |t|
t.string "title"
t.string "desc"
t.string "type"
t.string "uom"
t.index ["title"], name: "index_type_tables_on_title"
t.index ["type"], name: "index_type_tables_on_type"
end
Now, this type table is only a base class in my project and other types such as device_type, certificate_type etc inherit from it.
The model for type_table
class TypeTable < ApplicationRecord
validates :title, presence: true, uniqueness: true
end
The other device_type model
class DeviceType < TypeTable
has_many :certificates
#this is where the error originates
GARMIN_ID = find_by(title: "Garmin").id
#when i use some constant value rails successfully executes the
db:create task in my rake file
GARMIN_ID = 22
end
Now, here's the interesting bit, it only shows this behaviour the first time i,e when there is no existing table in my Postgres database. When I successfully create and migrate my application schema and when the tables are present this line will work everytime.
GARMIN_ID = find_by(title: "Garmin").id
My idea is that since it tries to find the column in the type_tables relation and the relation doesn't exist it throws a no relation exists error. Although the error you see on the console is
2018-08-07 02:24:49.281 UTC [69] FATAL: database "myappdb" does not
exist
mlcot_api | /usr/local/bundle/gems/activerecord-5.1.3/lib/active_record/connection_adapters/postgresql_adapter.rb:699:in
`rescue in connect': FATAL: database "myappdb" does not exist
(ActiveRecord::NoDatabaseError)
When I create the db manually using
docker-compose run api rake db:create
And then run docker-compose up I get
ERROR: relation "type_tables" does not exist at character 566
/usr/local/bundle/gems/activerecord- `async_exec': PG::UndefinedTable:
ERROR: relation "type_tables" does not exist
(ActiveRecord::StatementInvalid)
WHERE a.attrelid = '"type_tables"'::regclass
NOTE: I know the work around and have my app working but I have a dependancy on a particular branch which has all of the commented code
required to build my db for the first time which I want to remove.
Any help appreciated and thanks in advance.
It's a very bad idea to access your database while loading model definitions. Don't do that.
Instead, consider using a class method that only retrieves the value the first time it's needed:
def self.garmin_id
#garmin_id ||= find_by(title: "Garmin").id
end
Personally I'd strongly discourage even that much persistence -- better to spend one query retrieving it fresh for each request that needs it -- but that's more a matter of design judgement.
So I have a few connected problems here. So I have begun to add topic back into my app. However when I try to create a new post, I get "Topic must exist". I am not sure what is missing for this to go through specifically.
I do know that I need to somehow redo
AddTopicReferenceToBlogs migration because right now my blogs table looks like this:
create_table "blogs", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.integer "status", default: 0
t.index ["slug"], name: "index_blogs_on_slug", unique: true
end
The t.integer "topic_id" is missing.
I know I cannot rerun AddTopicReferenceToBlogs because I will get an identical.
If I try to do a rake db:migrate:down VERSION=
I get this error:
== 20170725215733 AddTopicReferenceToBlogs: reverting =========================
-- remove_reference(:blogs, :topic, {:foreign_key=>true})
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:
Table 'blogs' has no foreign key for {:to_table=>"topics", :column=>"topic_id"}
By the way my Topic.rb file looks like this:
class Topic < ApplicationRecord
validates_presence_of :title
end
There is no quick answer to your problem, but I think what will help you is to understand that you CAN redo AddTopicReferenceToBlogs, you are just going to have to give it a different name.
This documentation speaks to that:
Rails migrations with the same name
Try something like:
rails g migration add_reference_of_topic_to_blogs topic:references
That should work assuming that you haven't screwed up some other migrations in the process as well. Rails migrations can get pretty hairy if you are not sure of what you are doing.
You may still have a problem if this doesn't execute correctly in your dev environment.
rails db:drop db:create db:migrate db:seed
If you just added a new migration to fix the old one, this full database reset may fail because you now have duplicate migrations trying to add_reference :blogs, :topic, .... This will be a problem for your teammates and when you deploy to production.
Assumptions:
You generated an empty migration named AddTopicReferenceToBlogs and ran it with rails db:migrate (or rake db:migrate).
When you checked your schema.rb afterwards, the foreign key you expected to be there (topic_id), wasn't.
You edited the empty migration to add a line that used add_reference and ran rails db:migrate again.
The schema still didn't contain blogs.topic_id.
You tried to rollback the migration with rake db:migrate:down VERSION=??? and it failed (... because you were trying to rollback a migration that never actually ran. Rails can't remove a reference it never added to begin with).
Answer:
Don't add a duplicate migration that does the same thing, but with a different class name.
Comment out the add_reference line in the bad migration AddTopicReferenceToBlogs.
Rollback the empty migration (undo the bad thing you did).
Reactivate the add_reference line.
rake db:migrate again, this time correctly adding the foreign key you expected to schema.rb.
In the future, use this command to generate a pre-filled-in migration for these types of associations.
rails g migration AddTopicToBlog topic:belongs_to
Alternatives:
rails g migration AddTopicToBlogs topic:belongs_to
rails g migration AddTopicToBlog topic:references
rails g migration AddYouCanSayWhateverYouWantHereToBlog topic:references
I have created a new migration in order to add a new Paperclip attachment to a current model already owning a Paperclip attachment.
Though made an error and defined the data type as attachment instead of add_attachment :
class AddDetailsToProfilepics < ActiveRecord::Migration[5.0]
def change
add_column :profilepics, :finalimage, :attachment
end
end
Then i noticed no extra column was created by paperclip (*_file_name *_content_type) and realise I made a mistake.
Though now no Rollback is possible as it triggers an error. I created a new migration (to remove this column with wrong data type) and got a similar error:
undefined method 'to_sym' for nil:NilClass
When looking at shema.rb the data type is not entered:
...
t.integer "y"
t.integer "width"
t.integer "height"
t. "finalimage"
t.index ["professionnel_id"], name: "index_profilepics_on_professionnel_id"
I tried to change it to string and re-run the new "remove_column" migration but same error.
Ok I found solution. Kill both development.sqlite3 and schema.rb. Delete reference to the wrong column in the last migration. Do a rails db:migrate
Everything will be reconstructed.
The DB is empty though
I'm pushing a working app (developed it locally) to a Heroku environment. When trying to run db:migrate on the production environment, the migrations fail because the postgreSQL seems to be doing some sanity checks that my sqlite on localhost wasn't doing, for instance:
I created a migration for a post model and already defined a relationship to a (at that point) non existing comment model. Postgre is complaining that the table comment does not exist. (solved this by rearranging the migrations, bad I know)
I use a sort of STI trick in my model in that the models Exercise, Video & Lesson all store their data in the same table (steps). Exercise has many Questions but when trying to create the table, Postgre is complaining about a missing table exercises.
Is there a way to fix the second error or is there a general way to prevent rails from the sanity checks (does the related table exist)?
EDIT:
Ok, so the relevant migration is this one:
class CreateQuestions < ActiveRecord::Migration
def change
create_table :questions do |t|
t.string :title
t.references :exercise, index: true, foreign_key: true
t.text :content
t.timestamps null: false
end
end
end
The problem with this is that there's no table exercises, because that one is named steps. So the referential integrity is breached for PostgreSQL resulting in the following error:
== 20151208132820 CreateQuestions: migrating ==================================
-- create_table(:questions)
(14.1ms) CREATE TABLE "questions" ("id" serial primary key, "title" character varying, "exercise_id" integer, "content" text, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL)
(5.2ms) CREATE INDEX "index_questions_on_exercise_id" ON "questions" ("exercise_id")
(9.3ms) ALTER TABLE "questions" ADD CONSTRAINT "fk_rails_5ba13b3a6e"
FOREIGN KEY ("exercise_id")
REFERENCES "exercises" ("id")
PG::UndefinedTable: ERROR: relation "exercises" does not exist
: ALTER TABLE "questions" ADD CONSTRAINT "fk_rails_5ba13b3a6e"
FOREIGN KEY ("exercise_id")
REFERENCES "exercises" ("id")
The foreign key constraint isn't needed. I would always recommend using the same database on your localhost as you will on your production server.
Remove the foreign key, deploy and the migration will work :)
I do think that the problem is coming from the foreign_key constraint, you can just remove it and check again.
A better solution is to mimic the production environment locally and make it work before pushing up.
I was looking for a way to drop a table in Rails and start fresh and came across this answer: Rails DB Migration - How To Drop a Table?
However, when I ran drop_table :examples I got the following error:
-bash: drop_table: command not found
Here's my create_examples migrate file:
def self.down
drop_table :examples
end
Can anyone help guide me into fixing this and give me insight on what I'm doing wrong? I need to fix it because this particular migrate is preventing from doing a rake db:migrate, generating the following error:
== CreateExamples: migrating ====================================================
-- create_table(:examples)
rake aborted!
An error has occurred, this and all later migrations canceled:
SQLite3::SQLException: table "examples" already exists: CREATE TABLE "examples" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "content" varchar(255), "user_id" integer, "created_at" datetime, "updated_at" datetime)
Thanks! (And if I need to provide more code let me know.)
You should be dropping your old table right before creating the new version:
def self.up
drop_table :examples
create_table :examples do |t|
#...
end
end
And since you can't really reverse that drop_table, you might want to raise an exception in the rollback:
def self.down
raise ActiveRecord::IrreversibleMigration
end
Or maybe you just want to keep your current self.down.