Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Rename default sequence when table is renamed? [AR:postgres] #6874

Merged
merged 1 commit into from

4 participants

Robb Kidd Rafael Mendonça França Steve Klabnik Aaron Patterson
Robb Kidd

Does it make sense to rename the default primary key sequence when a table is renamed?

As it is, if I rename_table :foo, :bar, I end up with a table named "bar" whose id column is incremented by a sequence named "foo_id_seq". The names being out of sync complicates things when multiple DB users are used in production (e.g. one user for migrations and one user for app connections) and permissions need to be applied to tables and their associated sequences.

Attached is proposed crazy sauce which renames the primary key sequence if its name matches the default PK sequence created by the adapter.

...tive_record/connection_adapters/postgresql_adapter.rb
@@ -1208,12 +1208,19 @@ def primary_key(table)
end
# Renames a table.
+ # Also renames a table's primary key sequence if the sequence name matches the
+ # ActiveRecord default.
Rafael Mendonça França Owner

In this case you should use Active Record. We only use ActiveRecord when we refer to the ActiveRecord Ruby module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Rafael Mendonça França rafaelfranca commented on the diff
activerecord/test/cases/migration/rename_table_test.rb
@@ -67,6 +67,19 @@ def test_rename_table_with_an_index
rename_table :octopi, :test_models
end
+
+ def test_rename_table_for_postgresql_should_also_rename_default_sequence
+ skip 'not supported' unless current_adapter?(:PostgreSQLAdapter)
+
+ rename_table :test_models, :octopi
+
+ con = ActiveRecord::Base.connection
Rafael Mendonça França Owner

I would put this is a begin/ensure block.

begin
  con = ActiveRecord::Base.connection
  pk, seq = con.pk_and_sequence_for('octopi')
  assert_equal 'octopi_id_seq', seq
ensure
  rename_table :octopi, :test_models
end

Some command can fail and we will have an inconsistent database.

Certainly do not want an inconsistent database. I was following the pattern of the other tests in this file. I am happy to add an ensure to the new test method.

Possibly a DRYer solution for these tests would be a teardown method:

def teardown
  rename_table :octopi, :test_models if connection.table_exists? :octopi
  super
end

I also notice that the use of the ActiveRecord::Base.connection object is handled differently in each of the pre-existing tests. Should I not just use connection, the attr_reader defined in ActiveRecord::Migration::TestHelper?

I've got another branch with these two changes (add teardown method and just use connection) ready and tested.

Rafael Mendonça França Owner

Cool. Let's wait to merge that commits in another pull request after merge this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Rafael Mendonça França

Seems good. I did some comments inline.

I will ask for more feedback.

@tenderlove @carlosantoniodasilva

Rafael Mendonça França

Please squash the commits at this pull request. I'm waiting for more feedback but for me this will be merged

Robb Kidd

Squooshed.

Robb Kidd

Assuming the pending feedback goes well, is this something that could be backported? I imagine that I would create new PRs for this change to go into 3.2, 3.1 and 3.0.

Rafael Mendonça França

I don't think that this can be backported since it is adding behavior.

Steve Klabnik
Collaborator

@robbkidd as a note, 3.2 is the only thing getting non-security fixes, so even if it would be backported, you'd only need to do it for 3.2

I'm not sure if this is 'new behavior' or a bugfix, though.

Rafael Mendonça França rafaelfranca was assigned
Robb Kidd

@steveklabnik I'm OK with less work.

I fall on the side that this is a bugfix, but I'm also new around here.

Steve Klabnik
Collaborator

I figured you wouldn't mind :)

Aaron Patterson tenderlove commented on the diff
...tive_record/connection_adapters/postgresql_adapter.rb
((6 lines not shown))
#
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(name, new_name)
clear_cache!
execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ pk, seq = pk_and_sequence_for(new_name)
+ if seq == "#{name}_#{pk}_seq"
Aaron Patterson Owner

This test is really confusing to me. Does it actually work? Wouldn't seq contain new_name, not name?

Robb Kidd
robbkidd added a note

Nope. That's the whole point of this pull request. ALTER TABLE ... RENAME TO performed on a table in postgres does not rename any sequences that happen to be associated with that table.

For example, in the tests, the table test_models gets renamed to octopi. After the table rename, the table's ID sequencer is still named test_models_id_seq. Hence the need in postgres to rename the sequence as well.

Robb Kidd
robbkidd added a note

More followup, if I remove this logic from the rename_table method, but keep the new test checking that the sequence matches the new name (test-driven!), I see that the sequence for the octopi table is still named test_models_id_seq.

  1) Failure:
test_rename_table_for_postgresql_should_also_rename_default_sequence(ActiveRecord::Migration::RenameTableTest) [test/cases/migration/rename_table_test.rb:79]:
Expected: "octopi_id_seq"
  Actual: "test_models_id_seq"

State left in the database:

» psql activerecord_unittest
psql (9.1.4)
Type "help" for help.

activerecord_unittest=# \d octopi 
                                   Table "public.octopi"
 Column |          Type          |                        Modifiers                         
--------+------------------------+----------------------------------------------------------
 id     | integer                | not null default nextval('test_models_id_seq'::regclass)
 url    | character varying(255) | 
Indexes:
    "test_models_pkey" PRIMARY KEY, btree (id)

activerecord_unittest=# 
Aaron Patterson Owner

Ah, I got it. Thanks. Sorry, I've got a cold so my brain is running on half power. :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Aaron Patterson tenderlove merged commit d7b8f0c into from
Robb Kidd

@tenderlove Is this something I could work on a backport for 3.2?

Robb Kidd

Went ahead and threw the back-port pull request out there for people to beat up on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
7 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
View
@@ -1208,12 +1208,19 @@ def primary_key(table)
end
# Renames a table.
+ # Also renames a table's primary key sequence if the sequence name matches the
+ # Active Record default.
#
# Example:
# rename_table('octopuses', 'octopi')
def rename_table(name, new_name)
clear_cache!
execute "ALTER TABLE #{quote_table_name(name)} RENAME TO #{quote_table_name(new_name)}"
+ pk, seq = pk_and_sequence_for(new_name)
+ if seq == "#{name}_#{pk}_seq"
Aaron Patterson Owner

This test is really confusing to me. Does it actually work? Wouldn't seq contain new_name, not name?

Robb Kidd
robbkidd added a note

Nope. That's the whole point of this pull request. ALTER TABLE ... RENAME TO performed on a table in postgres does not rename any sequences that happen to be associated with that table.

For example, in the tests, the table test_models gets renamed to octopi. After the table rename, the table's ID sequencer is still named test_models_id_seq. Hence the need in postgres to rename the sequence as well.

Robb Kidd
robbkidd added a note

More followup, if I remove this logic from the rename_table method, but keep the new test checking that the sequence matches the new name (test-driven!), I see that the sequence for the octopi table is still named test_models_id_seq.

  1) Failure:
test_rename_table_for_postgresql_should_also_rename_default_sequence(ActiveRecord::Migration::RenameTableTest) [test/cases/migration/rename_table_test.rb:79]:
Expected: "octopi_id_seq"
  Actual: "test_models_id_seq"

State left in the database:

» psql activerecord_unittest
psql (9.1.4)
Type "help" for help.

activerecord_unittest=# \d octopi 
                                   Table "public.octopi"
 Column |          Type          |                        Modifiers                         
--------+------------------------+----------------------------------------------------------
 id     | integer                | not null default nextval('test_models_id_seq'::regclass)
 url    | character varying(255) | 
Indexes:
    "test_models_pkey" PRIMARY KEY, btree (id)

activerecord_unittest=# 
Aaron Patterson Owner

Ah, I got it. Thanks. Sorry, I've got a cold so my brain is running on half power. :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ new_seq = "#{new_name}_#{pk}_seq"
+ execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
+ end
end
# Adds a new column to the named table.
13 activerecord/test/cases/migration/rename_table_test.rb
View
@@ -67,6 +67,19 @@ def test_rename_table_with_an_index
rename_table :octopi, :test_models
end
+
+ def test_rename_table_for_postgresql_should_also_rename_default_sequence
+ skip 'not supported' unless current_adapter?(:PostgreSQLAdapter)
+
+ rename_table :test_models, :octopi
+
+ con = ActiveRecord::Base.connection
Rafael Mendonça França Owner

I would put this is a begin/ensure block.

begin
  con = ActiveRecord::Base.connection
  pk, seq = con.pk_and_sequence_for('octopi')
  assert_equal 'octopi_id_seq', seq
ensure
  rename_table :octopi, :test_models
end

Some command can fail and we will have an inconsistent database.

Certainly do not want an inconsistent database. I was following the pattern of the other tests in this file. I am happy to add an ensure to the new test method.

Possibly a DRYer solution for these tests would be a teardown method:

def teardown
  rename_table :octopi, :test_models if connection.table_exists? :octopi
  super
end

I also notice that the use of the ActiveRecord::Base.connection object is handled differently in each of the pre-existing tests. Should I not just use connection, the attr_reader defined in ActiveRecord::Migration::TestHelper?

I've got another branch with these two changes (add teardown method and just use connection) ready and tested.

Rafael Mendonça França Owner

Cool. Let's wait to merge that commits in another pull request after merge this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ pk, seq = con.pk_and_sequence_for('octopi')
+
+ assert_equal "octopi_#{pk}_seq", seq
+
+ rename_table :octopi, :test_models
+ end
end
end
end
Something went wrong with that request. Please try again.