Rails 6
adds an if_not_exists
option to create_table
option of creating a table if it does not exist.
The default value of if_not_exists
is false
Before Rails 6 could we do this? Absolutely, but the way it will be written is longer.
Before rails 6 we used ActiveRecord :: Base.connection.table_exists?
to check if a table exists when create_table
1. Rails 5.2
Create a users table in Rails 5.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | >> class CreateUsers < ActiveRecord::Migration[6.0] >> def change >> create_table :users do |t| >> t.string :name, index: { unique: true } >> >> t.timestamps >> end >> end >> end >> CreateUsers.new.change -- create_table(:users) CREATE TABLE "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL) |
Now recreate the users table with the option if_not_exists
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | >> class CreateUsers < ActiveRecord::Migration[6.0] >> def change >> create_table :users, if_not_exists: true do |t| >> t.string :name, index: { unique: true } >> >> t.timestamps >> end >> end >> end >> CreateUsers.new.change -- create_table(:users, {:if_not_exists=>true}) CREATE TABLE "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL) => Traceback (most recent call last): 2: from (irb):121 1: from (irb):114:in 'change' ActiveRecord::StatementInvalid (PG::DuplicateTable: ERROR: relation "users" already exists) : CREATE TABLE "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL) |
We can see that rails 5.2 ignores the if_not_exists
option, and tries to recreate the table
Now try ActiveRecord :: Base.connection.table_exists?
with rails 5.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | >> class CreateUsers < ActiveRecord::Migration[5.2] >> def change >> unless ActiveRecord::Base.connection.table_exists?('users') >> create_table :users do |t| >> t.string :name >> >> t.timestamps >> end >> end >> end >> end >> CreateUsers.new.change => nil |
Based on the above results we can see that create_table: :users
have not been run because ActiveRecord::Base.connection.table_exists?('users')
returns true.
2. Rails 6
Now create users table in rails 6 with if_not_exists
= true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | >> class CreateUsers < ActiveRecord::Migration[6.0] >> def change >> create_table :users, if_not_exists: true do |t| >> t.string :name, index: { unique: true } >> >> t.timestamps >> end >> end >> end >> CreateUsers.new.change -- create_table(:users, {:if_not_exists=>true}) CREATE TABLE IF NOT EXISTS "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp(6) NOT NULL, "updated_at" timestamp(6) NOT NULL) => #<PG::Result:0x00007fc4614fef48 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0> >> CreateUsers.new.change -- create_table(:users, {:if_not_exists=>true}) CREATE TABLE IF NOT EXISTS "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp(6) NOT NULL, "updated_at" timestamp(6) NOT NULL) => #<PG::Result:0x00007fc46513fde0 status=PGRES_COMMAND_OK ntuples=0 nfields=0 cmd_tuples=0> |
Look at the example above. We can see no exceptions are made when we create the second user
Now recreate the users table with the option if_not_exists
= false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | >> class CreateUsers < ActiveRecord::Migration[6.0] >> def change >> create_table :users, if_not_exists: false do |t| >> t.string :name, index: { unique: true } >> >> t.timestamps >> end >> end >> end >> CreateUsers.new.change -- create_table(:users, {:if_not_exists=>false}) CREATE TABLE "users" ("id" bigserial primary key, "name" character varying, "created_at" timestamp(6) NOT NULL, "updated_at" timestamp(6) NOT NULL) => Traceback (most recent call last): 2: from (irb):23 1: from (irb):15:in `change' ActiveRecord::StatementInvalid (PG::DuplicateTable: ERROR: relation "users" already exists ) |
An exception is fired because the if_not_exists
= false option
This is a related pull request
Reference source https://blog.bigbinary.com/2019/05/22/rails-6-adds-if_not_exists-option-to-create_table.html https://api.rubyonrails.org/v5.2/classes/ ActiveRecord / ConnectionAdapters / SchemaStatements.html # method-i-table_exists-3F