Migration to create table raises Mysql2::Error: Table doesn't exist - mysql

I wrote a migration with the following:
class CreateTableSomeTable < ActiveRecord::Migration[5.1]
def change
create_table :some_tables do |t|
t.references :user, foreign_key: true
t.references :author, references: :user, foreign_key: true
t.text :summary
end
end
end
It is a basic migration that is creating a database table. However: when I run rails db:migrate a very odd error message aborts the migration:
Mysql2::Error: Table 'my_database.some_tables' doesn't exist: SHOW FULL FIELDS FROM 'some_tables'
It is as if the error is saying it can't create the table because the table does exist, which doesn't make sense.
Things I have looked at and tried:
reviewed the database.yml which seems fine. Nothing has changed, and I have recently run other migrations just fine (though no migrations that created database tables)
ran bundle to ensure all gems were installed
deleted the schema.rb file, recreated the database with data from another copy, and I ran rake db:schema:dump to recreate the schema.rb file. I attempted to run the migration again and still got the same error.
I am using rails 5.1.1 as well as mysql2 0.4.6
Any tips on how I can get the migration to run?

I got a similar error when trying to create a new model that has a reference to an existing model that was created before migrating to Rails 5.1.
Although the error message was not very clear about that, in my case it turned out that the problem was data type mismatch between the primary key of the old model and the foreign key of the new model (MySQL does not permit that). It was so because since Rails 5.1 the default data type of all the primary and foreign keys is bigint, but for the old model the primary key type was still integer.
I solved this by converting all the primary and foreign keys of the current models to bigint, so I can use the Rails new defaults and forget about it.
A workaround could also be specifying integer type for the new foreign keys so that they match the primary keys type of the old models. Something like the following:
class CreateUserImages < ActiveRecord::Migration[5.1]
def change
create_table :user_images do |t|
t.references :user, type: :integer, foreign_key: true
t.string :url
end
end
end

The big issue with the ActiveRecord migration 5.1 is that now the id are expected to be BIGINT instead of INT, so when you adding a column referring another table created before rails 5.1 it expect the column type to be BIGINT but instead is just an INT, hence the error.
The best solution is just modify your migration and change the type of the column to int.
class CreateTableSomeTable < ActiveRecord::Migration[5.1]
def change
create_table :some_tables do |t|
t.references :user, foreign_key: true, type: :int
t.references :author, references: :user, foreign_key: true
t.text :summary
end
end
that should work.

I figured out a work around, but it is still very puzzling to me.
The error message in the log file was not exactly pointing to the issue. For some reason, it might be rails 5.1.1 or it might be mysql2 0.4.6, but it doesn't like using references within the create_table block for some reason. Very odd because it has worked for me in the past.
So I changed the migration from this:
class CreateTableSomeTable < ActiveRecord::Migration[5.1]
def change
create_table :some_tables do |t|
t.references :user, foreign_key: true
t.references :author, references: :user, foreign_key: true
t.text :summary
end
end
end
To this:
class CreateTableSomeTable < ActiveRecord::Migration[5.1]
def change
create_table :some_tables do |t|
t.integer :user_id
t.integer :author_id
t.text :summary
end
end
end
And it worked.
It is very odd because references works just fine with sqlite3 (I tested this by generating a dummy app, ran a scaffold command with a references column, and ran rails db:migrate and it all worked).

This drove me nuts, I think I was seeing a different reason for this than what others suggested. In my case it happened because my migration file names didn't exactly match the migration class therein. For example, I had a migration file named 20171205232654_bonus.rb but inside the class was declared as class CreateBonus < ActiveRecord::Migration[5.1]. Once I changed the file name to 20171205232654_create_bonus.rb everything worked.
This might have something to do with the fact that I've been creating migrations only, not full scaffolds, and maybe I did something wrong. I really don't know how I wound up with that mismatch.

Related

Rails change primary id to 64 bit bigint

I am using rails and the mysql2 adapter. I want to change all primary ids and foreign keys to be 64 bit integers instead of the default 32 bit as they are right now for my production database.
Is this possible on the fly or do I have to drop the database, change the structure and import the data again?
If there is a way to do it without dropping the database, even if it's a hack, it would be great to know.
Rails 5.1 already added a bigint type for migrations, you can do this:
change_column :users, :id, :bigint
Source:
http://www.mccartie.com/2016/12/05/rails-5.1.html
While ActiveRecord does not support this, you are able to do it using execute
class UpdateUserIdLimit < ActiveRecord::Migration
def up
# PostgreSQL
execute('ALTER TABLE users ALTER COLUMN id SET DATA TYPE BIGINT')
# MySQL
execute('ALTER TABLE users MODIFY COLUMN id BIGINT(8) NOT NULL AUTO_INCREMENT')
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
For new tables you should be able to simply do
def change
create_table :users, id: false do |t|
t.int :id, limit: 8, primary_key: true
t.string :first_name
t.string :last_name
end
end
Also starting with Rails 5.1 primary keys will be BIGINT by default.

Getting a binary limit from a migration to the schema in Rails

I have a migration for a model with a binary field meant to store a file that can be bigger than 10Mb:
class CreateNewModel < ActiveRecord::Migration
def change
create_table :new_model do |t|
...
t.binary :data, limit: 16777216
...
end
end
end
With the limit information the migration can create a longblob object in a MySQL or MariaDB database, as seen in How do you get Rails to use the LONGBLOB column in mysql?.
The migration seems to work fine on a MariaDB database: data has longblob type. However loading directly from the schema gives data a blob type instead of longblob, this means the rake db:setup command is no longer usable as the schema doesn't reflect the database I want.
This seems pretty evident when one looks at the db/schema.rb file:
create_table "new_model", force: :cascade do |t|
...
t.binary "data"
...
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
There is no limit info and as such loading the schema can only lead to blobs and not longblobs.
Why doesn't the limit information get written in the schema?
I don't want to change the schema manually as it would mean have to redo the change at each migration (as they regenerate the schema file). What other solutions do I have, is there a way to force the limit field from the migration to the schema?
I've tried using the shorthands described in https://github.com/rails/rails/pull/21688 but it doesn't seem to exist in Rails 4.2.6.

Redmine Not Recognizing Migration File to Change DB

Essentially I am trying to add a new column to an existing Redmine database table through a plugin.
To my knowledge, the main command for migrating to the database through a plugin is:
rake redmine:plugins:migrate
yet when I run that command, on my local machine it says that it is migrating all the plugins i have installed, however it is not updating the database. Are there any checks I can do to make sure that redmine is recognizing which database to migrate to?
For example, in one of my plugins I simple want to add a new field :foo of type :datetime to the users table with the following migration file:
class AddColumnIssuePosition < ActiveRecord::Migration
def self.up
change_table :user do |t|
t.column :foo, :datetime
end
end
def self.down
change_table :user do |t|
t.remove :foo
end
end
end
I run the migration rake command and it says that it has migrated the plugin, however it doesnt give any details on the new column addition "foo" to the "users" table...
Migrating user_foo (User Foo Plugin)...
It just spouts out that line and it ends there. No changes found on the users table.
Forgive me as I am somewhat new to rails.
Change your migration to look like below:
class AddColumnIssuePosition < ActiveRecord::Migration
def change
add_column :users, :foo, :datetime
end
end

Does a Rails foreign_key have to be an integer?

We have some imported data from a 3rd party which provides non-integer unique ids.
In Rails 2.2.2 we were able to use :foreign_key on our has_many relationships with a non-integer column and it worked.
But we are now upgrading to Rails 2.3.8 and it seems to force the foreign_key to an integer. Has anyone found a way to get this working?
According to this answer, the procedure has to be completed using execute:
create_table :employees, {:id => false} do |t|
t.string :emp_id
t.string :first_name
t.string :last_name
end
execute "ALTER TABLE employees ADD PRIMARY KEY (emp_id);"
And as Sean McCleary mentioned, your ActiveRecord model should set the primary key using set_primary_key:
class Employee < ActiveRecord::Base
set_primary_key :emp_id
...
end

Drop mysql tables in Rails

How do I drop selected tables in Rails? I want to drop all tables from a database with a given prefix. PHPMyAdmin would be very useful at this point.
Thanks
You could install phpMyAdmin and manually delete your tables, or if you wanted to do it from within the rails framework and keep you migrations in sync, why not create a new migration that drops the tables on self.up and creates the tables on self.down.
class DropOldTablesMigration < ActiveRecord::Migration
def self.up
drop_table :prefix_table1
drop_table :prefix_table2
end
def self.down
create_table :prefix_table1 do |t|
t.column :name, :string
end
create_table :prefix_table2 do |t|
t.column :name, :string
end
end
end
EDIT:
Just to follow up, if the issue is that there are a lot of tables and you don't want to type them all, you could do something like this:
class DropOldTablesMigration < ActiveRecord::Migration
def self.up
ActiveRecord::Base.connection.tables.each do |t|
unless t.index('yourprefix_') == nil
drop_table t
end
end
end
end
Of course you would not be able to recreate the tables when you migrate down that way, but depending on what is going on in your app, that may not be a concern.
EDIT IN RESPONSE TO YOUR COMMENT:
To create a new migration, from the root of your app run the following command:
script/generate migration YourMigrationName
The migration file will be created for you in db/migrate. Add the code you want to run and save it. To run the new file enter the following at the command line
rake db:migrate