20

I'm working with an existing rails app, using postgresql. Its schema.rb file has id: :serial for many, but not all, tables:

create_table "foos", id: :serial, force: :cascade do |t|

When I run rails db:migrate:reset, id: :serial is removed. We are all on the same version of postgres, but different OSes. I haven't exhaustively tested the behavior between machines, but I think there is a difference between machines.

The rails version is the same as it was when the project started.

The project did start with sqlite3. When I switch to that and regenerate the file, same behavior.

What could cause this option to be removed in my environment?

here's some code that is probably relevant:

update

  • I just tried rails db:migrate:reset on colleague's machines, and I was wrong! their environments also remove id: :serial.
  • I looked closer at recent migrations from a colleague, and the most recent one did not create id: :serial in schema.rb either.
John Bachir
  • 22,495
  • 29
  • 154
  • 227
  • what version is that? seems like `id: :serial` is a postgres + rails 5.1 addition. – engineersmnky Feb 08 '19 at 20:07
  • version 9.6. all my experiments were with rails 5.1. in some environments, id: :serial is added. in others, it is not. – John Bachir Feb 11 '19 at 15:15
  • I need more information. What do you mean by `id: :serial` is removed? Does the table not have a primary key anymore? What does the table definition (`\d foos`) look like before and after. Please also post your `schema.rb` and `structure.sql` files (whichever exist). – Old Pro Feb 14 '19 at 03:08
  • @OldPro in the question, that line is from my schema.rb file. there is one for each table. that's how the file is in my project, generated by other devs. when i regenerate the file in my environment (vagrant, ubuntu xenial, no notable changes to config), all instances of `, id: :serial` are removed. – John Bachir Feb 14 '19 at 04:45

2 Answers2

21

When you run rails db:migrate:reset as opposed to rails db:reset, the database schema is not loaded from schema.rb but is instead reconstructed from all of your migrations. In migrations and schema files you do not need to specify an id field, one is provided by default. However, starting with Rails 5.1, the default size of the id field was increased from INT to BIGINT for MySQL and from SERIAL to BIGSERIAL for PostgreSQL. So probably there is some interaction between your migrations, schema.rb, and the actual schema in the database that is causing the id field to be treated as default (and omitted) in some cases and be explicitly specified in others, just due to the change in the default size. It's hard to guess at the source of the problem without seeing all the relevant files.

Old Pro
  • 24,624
  • 7
  • 58
  • 106
12

The answer is simply rails 5.0 vs 5.1 migrations. I had previously thought that the project started in 5.1, so I didin't test this. But then I dug deeper and discovered it started in 5.0, and experimentation shows that's the answer.

5.0, no id specified

class SerialIdTest < ActiveRecord::Migration[5.0]
  def change
    create_table "test" do |t|
      t.integer "foo_id"
      t.string "foo_role"
    end
  end
end
create_table "test", id: :serial, force: :cascade do |t|
  t.integer "foo_id"
  t.string "foo_role"
end
# \d test
                                   Table "public.test"
      Column      |       Type        |                       Modifiers                       
------------------+-------------------+-------------------------------------------------------
 id               | integer           | not null default nextval('test_id_seq'::regclass)
 foo_id   | integer           | 
 foo_role | character varying | 
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

5.1, no id specified

class SerialIdTest < ActiveRecord::Migration[5.1]
  def change
    create_table "test" do |t|
      t.integer "foo_id"
      t.string "foo_role"
    end
  end
end
create_table "test", force: :cascade do |t|
  t.integer "foo_id"
  t.string "foo_role"
end
# \d test
                                   Table "public.test"
      Column      |       Type        |                       Modifiers                       
------------------+-------------------+-------------------------------------------------------
 id               | bigint            | not null default nextval('test_id_seq'::regclass)
 foo_id   | integer           | 
 foo_role | character varying | 
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

5.1, id serial specified

class SerialIdTest < ActiveRecord::Migration[5.1]
  def change
    create_table "test", id: :serial do |t|
      t.integer "foo_id"
      t.string "foo_role"
    end
  end
end
create_table "test", id: :serial, force: :cascade do |t|
  t.integer "foo_id"
  t.string "foo_role"
end
# \d test
                                   Table "public.test"
      Column      |       Type        |                       Modifiers                       
------------------+-------------------+-------------------------------------------------------
 id               | integer           | not null default nextval('test_id_seq'::regclass)
 foo_id   | integer           | 
 foo_role | character varying | 
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)
John Bachir
  • 22,495
  • 29
  • 154
  • 227