activerecord-jdbc-adapter icon indicating copy to clipboard operation
activerecord-jdbc-adapter copied to clipboard

Renaming a table does not rename indexes in Rails 4

Open nbekirov opened this issue 6 years ago • 5 comments

I'm trying to upgrade the JRuby version of my Rails 4 application from 9.1.2.0 to 9.2.5.0 but faced an issue with migrations.

When a rename_table is used it does not issue an ALTER INDEX properly.

Here is a reproduction script I created: https://gist.github.com/nbekirov/88e4a9ebcc066193141f11f865bd1490

When executed under jruby 9.1.7.0 (2.3.1) it yields:

-- table_exists?(:global_suppliers)
D, [2019-01-29T17:23:21.919000 #21875] DEBUG -- :    (33.0ms)  SELECT version()
   -> 0.3560s
-- create_table(:suppliers, {:force=>true})
D, [2019-01-29T17:23:22.173000 #21875] DEBUG -- :    (87.0ms)  CREATE TABLE "suppliers" ("id" serial primary key, "name" character varying)
   -> 0.0974s
   -> 0 rows
-- add_index(:suppliers, :name)
D, [2019-01-29T17:23:22.193000 #21875] DEBUG -- :    (7.0ms)  CREATE  INDEX  "index_suppliers_on_name" ON "suppliers"  ("name")
   -> 0.0205s
   -> 0 rows
Run options: --seed 39800

# Running:

==  RenameSuppliersToMegaSuppliers: migrating =================================
-- rename_table(:suppliers, :global_suppliers)
D, [2019-01-29T17:23:22.259000 #21875] DEBUG -- :    (4.0ms)  ALTER TABLE "suppliers" RENAME TO "global_suppliers"
D, [2019-01-29T17:23:22.297000 #21875] DEBUG -- :   PK and Serial Sequence (35.0ms)            SELECT attr.attname, seq.relname
          FROM pg_class      seq,
               pg_attribute  attr,
               pg_depend     dep,
               pg_constraint cons
          WHERE seq.oid           = dep.objid
            AND seq.relkind       = 'S'
            AND attr.attrelid     = dep.refobjid
            AND attr.attnum       = dep.refobjsubid
            AND attr.attrelid     = cons.conrelid
            AND attr.attnum       = cons.conkey[1]
            AND cons.contype      = 'p'
            AND dep.refobjid      = '"global_suppliers"'::regclass

D, [2019-01-29T17:23:22.302000 #21875] DEBUG -- :    (5.0ms)  ALTER TABLE "suppliers_id_seq" RENAME TO "global_suppliers_id_seq"
D, [2019-01-29T17:23:22.314000 #21875] DEBUG -- :    (9.0ms)  ALTER INDEX "suppliers_pkey" RENAME TO "global_suppliers_pkey"
   -> 0.0625s
==  RenameSuppliersToMegaSuppliers: migrated (0.0644s) ========================

F

Finished in 0.191794s, 5.2139 runs/s, 5.2139 assertions/s.

  1) Failure:
BugTest#test_migration_up [table_rename.rb:51]:
--- expected
+++ actual
@@ -1 +1 @@
-["index_global_suppliers_on_name"]
+["index_suppliers_on_name"]

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

Compare that to the result of the same script executed under jruby 9.1.6.0 (2.3.1):

-- table_exists?(:global_suppliers)
D, [2019-01-29T17:25:59.209000 #21958] DEBUG -- :    (22.0ms)  SELECT version()
   -> 0.2370s
-- drop_table(:global_suppliers)
D, [2019-01-29T17:25:59.336000 #21958] DEBUG -- :    (38.0ms)  DROP TABLE "global_suppliers"
   -> 0.0386s
   -> 0 rows
-- create_table(:suppliers, {:force=>true})
D, [2019-01-29T17:25:59.396000 #21958] DEBUG -- :    (46.0ms)  CREATE TABLE "suppliers" ("id" serial primary key, "name" character varying)
   -> 0.0591s
   -> 0 rows
-- add_index(:suppliers, :name)
D, [2019-01-29T17:25:59.433000 #21958] DEBUG -- :    (19.0ms)  CREATE  INDEX  "index_suppliers_on_name" ON "suppliers"  ("name")
   -> 0.0373s
   -> 0 rows
Run options: --seed 54298

# Running:

==  RenameSuppliersToMegaSuppliers: migrating =================================
-- rename_table(:suppliers, :global_suppliers)
D, [2019-01-29T17:25:59.483000 #21958] DEBUG -- :    (3.0ms)  ALTER TABLE "suppliers" RENAME TO "global_suppliers"
D, [2019-01-29T17:25:59.526000 #21958] DEBUG -- :   PK and Serial Sequence (41.0ms)            SELECT attr.attname, seq.relname
          FROM pg_class      seq,
               pg_attribute  attr,
               pg_depend     dep,
               pg_constraint cons
          WHERE seq.oid           = dep.objid
            AND seq.relkind       = 'S'
            AND attr.attrelid     = dep.refobjid
            AND attr.attnum       = dep.refobjsubid
            AND attr.attrelid     = cons.conrelid
            AND attr.attnum       = cons.conkey[1]
            AND cons.contype      = 'p'
            AND dep.refobjid      = '"global_suppliers"'::regclass

D, [2019-01-29T17:25:59.534000 #21958] DEBUG -- :    (6.0ms)  ALTER TABLE "suppliers_id_seq" RENAME TO "global_suppliers_id_seq"
D, [2019-01-29T17:25:59.538000 #21958] DEBUG -- :    (2.0ms)  ALTER INDEX "suppliers_pkey" RENAME TO "global_suppliers_pkey"
D, [2019-01-29T17:25:59.585000 #21958] DEBUG -- :    (2.0ms)  ALTER INDEX "index_suppliers_on_name" RENAME TO "index_global_suppliers_on_name"
   -> 0.1061s
==  RenameSuppliersToMegaSuppliers: migrated (0.1065s) ========================

.

Finished in 0.134313s, 7.4453 runs/s, 7.4453 assertions/s.

1 runs, 1 assertions, 0 failures, 0 errors, 0 skips

What I found while trying to debug the situation:

  • ArJdbc::PostgreSQL#rename_table is trying to call ActiveRecord::ConnectionAdapters::SchemaStatements#rename_table_indexes (source)
  • only if it respond_to?(:rename_table_indexes) (same source, change commit)
  • but #rename_table_indexes is a protected method (source)
  • and jruby 9.1.7.0 respond_to? private/protected bahaviour changed (news article, bug, commit)
  • which prevents the invocation of #rename_table_indexes

Related StackOverflow issue: https://stackoverflow.com/questions/48925738/rails-4-renaming-column-with-index

nbekirov avatar Jan 29 '19 15:01 nbekirov

please could you also fill in your AR-JDBC gem and Rails 4.x version - might come handy for reproducing

kares avatar Jan 29 '19 18:01 kares

@kares, my bad - assumed that listing those only in the reproduction script is enough.

My original application setup is:

  • rails: 4.2.7.1 (want to upgrade to 4.2.11)
  • activerecord-jdbcpostgresql-adapter: 1.3.23 (want to upgrade to 1.3.25)
  • jruby: 9.1.2.0 (want to upgrade first to 9.1.17.0 then to 9.2.5.0)

In the provided reproduction script I've used:

  • activerecord: 4.2.11 (definned in gemfile block)
  • activerecord-jdbcpostgresql-adapter: 1.3.25 (definned in gemfile block)
  • jruby: 9.1.6.0/9.1.7.0 (instruction in comment)

nbekirov avatar Jan 29 '19 19:01 nbekirov

thanks, sorry I haven't looked at the script first - assumed it to be the same as you initially posted on IRC. ... still one thing that isn't clear - how is 9.2.5.0 doing in terms of reproduction for you, is it working fine ? (since this seems like a JRuby bug yet 9.1 likely won't get another release)

kares avatar Jan 30 '19 09:01 kares

With my 9.2.5.0 it is failing.

$ ruby -v
jruby 9.2.5.0 (2.5.0) 2018-12-06 6d5a228 Java HotSpot(TM) 64-Bit Server VM 9.0.1+11 on 9.0.1+11 +jit [darwin-x86_64]

My idea is that the change of how jruby behaves when evaluting respond_to? is what prevents the proper index renaming. It's not 9.1.7.0 and later versions being buggy and needing a fix, but precisely the oposite - previous versions where doing the wrong thing. Check out this sample ruby script:

class Dog
  def can_bark?
    respond_to?(:do_bark)
  end

  def bark
    do_bark
  end

  protected

  def do_bark
    'woof!  '
  end
end


dog = Dog.new
call jruby-9.1.6.0 jruby-9.2.5.0 ruby-2.4.5 and other 2.x
dog.can_bark? true false false
dog.bark woof! woof! woof!
dog.do_bark protected method `do_bark' called for #Dog:.. -same- -same-

Extended description and examples can be found in this article: https://tenderlovemaking.com/2012/09/07/protected-methods-and-ruby-2-0.html

nbekirov avatar Feb 03 '19 11:02 nbekirov

Just in case anyone needs a fast and dirty workaround, here's a monkey-patch I came up with:

module ArJdbc
  module PostgreSQL
    def respond_to_missing?(method_name, _include_private = false)
      # Allow proper index management in jruby-9.1.7.0+
      # See: https://github.com/jruby/activerecord-jdbc-adapter/issues/985
      [
          :rename_table_indexes,
          :rename_column_indexes,
          :validate_index_length!
      ].include?(method_name.to_sym) || super
    end
  end
end

nbekirov avatar Feb 07 '19 07:02 nbekirov