Skip to content

Commit 3ac1d8f

Browse files
committed
Support schemas in Postgresql enum_types
Follow up to rails#44896 and rails#45707
1 parent 0c9e406 commit 3ac1d8f

File tree

4 files changed

+85
-45
lines changed

4 files changed

+85
-45
lines changed

activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,15 +452,19 @@ def enum_types
452452
SELECT
453453
type.typname AS name,
454454
type.OID AS oid,
455+
n.nspname AS schema,
455456
string_agg(enum.enumlabel, ',' ORDER BY enum.enumsortorder) AS value
456457
FROM pg_enum AS enum
457-
JOIN pg_type AS type
458-
ON (type.oid = enum.enumtypid)
459-
GROUP BY type.OID, type.typname;
458+
JOIN pg_type AS type ON (type.oid = enum.enumtypid)
459+
JOIN pg_namespace n ON type.typnamespace = n.oid
460+
GROUP BY type.OID, n.nspname, type.typname;
460461
SQL
461462

462463
exec_query(query, "SCHEMA", allow_retry: true, uses_transaction: false).cast_values.each_with_object({}) do |row, memo|
463-
memo[row.first] = row.last
464+
name, schema = row[0], row[2]
465+
schema = nil if schema == current_schema
466+
full_name = [schema, name].compact.join(".")
467+
memo[full_name] = row.last
464468
end.to_a
465469
end
466470

activerecord/test/cases/adapters/postgresql/enum_test.rb

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def test_assigning_enum_to_nil
101101
def test_schema_dump
102102
@connection.add_column "postgresql_enums", "good_mood", :mood, default: "happy", null: false
103103

104-
output = dump_table_schema("postgresql_enums")
104+
output = dump_all_table_schema
105105

106106
assert output.include?("# Note that some types may not work with other database engines. Be careful if changing database."), output
107107

@@ -158,26 +158,17 @@ def test_works_with_activerecord_enum
158158
end
159159

160160
def test_enum_type_scoped_to_schemas
161-
old_search_path = @connection.schema_search_path
162-
@connection.create_schema("test_schema")
163-
@connection.schema_search_path = "test_schema"
164-
@connection.schema_cache.clear!
165-
@connection.create_enum("mood_in_other_schema", ["sad", "ok", "happy"])
161+
with_test_schema("test_schema") do
162+
@connection.create_enum("mood_in_other_schema", ["sad", "ok", "happy"])
166163

167-
assert_nothing_raised do
168-
@connection.create_table("postgresql_enums_in_other_schema") do |t|
169-
t.column :current_mood, :mood_in_other_schema, default: "happy", null: false
164+
assert_nothing_raised do
165+
@connection.create_table("postgresql_enums_in_other_schema") do |t|
166+
t.column :current_mood, :mood_in_other_schema, default: "happy", null: false
167+
end
170168
end
171-
end
172169

173-
assert_nothing_raised do
174-
@connection.drop_table("postgresql_enums_in_other_schema")
175-
@connection.drop_enum("mood_in_other_schema")
170+
assert @connection.table_exists?("postgresql_enums_in_other_schema")
176171
end
177-
ensure
178-
@connection.drop_schema("test_schema")
179-
@connection.schema_search_path = old_search_path
180-
@connection.schema_cache.clear!
181172
end
182173

183174
def test_enum_type_explicit_schema
@@ -197,4 +188,53 @@ def test_enum_type_explicit_schema
197188
ensure
198189
@connection.drop_schema("test_schema", if_exists: true)
199190
end
191+
192+
def test_schema_dump_scoped_to_schemas
193+
with_test_schema("test_schema") do
194+
@connection.create_enum("mood_in_test_schema", ["sad", "ok", "happy"])
195+
@connection.create_table("postgresql_enums_in_test_schema") do |t|
196+
t.column :current_mood, :mood_in_test_schema
197+
end
198+
199+
output = dump_all_table_schema
200+
201+
assert output.include?('create_enum "public.mood", ["sad", "ok", "happy"]'), output
202+
assert output.include?('create_enum "mood_in_test_schema", ["sad", "ok", "happy"]'), output
203+
assert output.include?('t.enum "current_mood", enum_type: "mood_in_test_schema"'), output
204+
end
205+
end
206+
207+
def test_schema_load_scoped_to_schemas
208+
silence_stream($stdout) do
209+
with_test_schema("test_schema", drop: false) do
210+
ActiveRecord::Schema.define do
211+
create_enum "mood_in_test_schema", ["sad", "ok", "happy"]
212+
create_enum "public.mood", ["sad", "ok", "happy"]
213+
214+
create_table "postgresql_enums_in_test_schema", force: :cascade do |t|
215+
t.enum "current_mood", enum_type: "mood_in_test_schema"
216+
end
217+
end
218+
219+
assert @connection.column_exists?(:postgresql_enums_in_test_schema, :current_mood, sql_type: "mood_in_test_schema")
220+
end
221+
222+
# This is outside `with_test_schema`, so we need to explicitly specify which schema we query
223+
assert @connection.column_exists?("test_schema.postgresql_enums_in_test_schema", :current_mood, sql_type: "test_schema.mood_in_test_schema")
224+
end
225+
ensure
226+
@connection.drop_schema("test_schema")
227+
end
228+
229+
private
230+
def with_test_schema(name, drop: true)
231+
old_search_path = @connection.schema_search_path
232+
@connection.create_schema(name)
233+
@connection.schema_search_path = name
234+
yield
235+
ensure
236+
@connection.drop_schema(name) if drop
237+
@connection.schema_search_path = old_search_path
238+
@connection.schema_cache.clear!
239+
end
200240
end

activerecord/test/cases/schema_dumper_test.rb

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
1212
end
1313

1414
def standard_dump
15-
@@standard_dump ||= perform_schema_dump
16-
end
17-
18-
def perform_schema_dump
19-
dump_all_table_schema []
15+
@@standard_dump ||= dump_all_table_schema
2016
end
2117

2218
def test_dump_schema_information_with_empty_versions
@@ -354,13 +350,13 @@ def test_schema_dump_includes_extensions
354350
connection = ActiveRecord::Base.connection
355351

356352
connection.stub(:extensions, ["hstore"]) do
357-
output = perform_schema_dump
353+
output = dump_all_table_schema
358354
assert_match "# These are extensions that must be enabled", output
359355
assert_match %r{enable_extension "hstore"}, output
360356
end
361357

362358
connection.stub(:extensions, []) do
363-
output = perform_schema_dump
359+
output = dump_all_table_schema
364360
assert_no_match "# These are extensions that must be enabled", output
365361
assert_no_match %r{enable_extension}, output
366362
end
@@ -370,13 +366,13 @@ def test_schema_dump_includes_extensions_in_alphabetic_order
370366
connection = ActiveRecord::Base.connection
371367

372368
connection.stub(:extensions, ["hstore", "uuid-ossp", "xml2"]) do
373-
output = perform_schema_dump
369+
output = dump_all_table_schema
374370
enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
375371
assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
376372
end
377373

378374
connection.stub(:extensions, ["uuid-ossp", "xml2", "hstore"]) do
379-
output = perform_schema_dump
375+
output = dump_all_table_schema
380376
enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
381377
assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
382378
end
@@ -427,7 +423,7 @@ def test_do_not_dump_foreign_keys_when_bypassed_by_config
427423
}
428424
)
429425

430-
output = perform_schema_dump
426+
output = dump_all_table_schema
431427
assert_no_match(/^\s+add_foreign_key "fk_test_has_fk"[^\n]+\n\s+add_foreign_key "lessons_students"/, output)
432428
ensure
433429
ActiveRecord::Base.establish_connection(:arunit)
@@ -461,7 +457,7 @@ def test_schema_dump_with_table_name_prefix_and_suffix
461457
migration = CreateDogMigration.new
462458
migration.migrate(:up)
463459

464-
output = perform_schema_dump
460+
output = dump_all_table_schema
465461
assert_no_match %r{create_table "foo_.+_bar"}, output
466462
assert_no_match %r{add_index "foo_.+_bar"}, output
467463
assert_no_match %r{create_table "schema_migrations"}, output
@@ -486,7 +482,7 @@ def test_schema_dump_with_table_name_prefix_and_suffix_regexp_escape
486482
migration = CreateDogMigration.new
487483
migration.migrate(:up)
488484

489-
output = perform_schema_dump
485+
output = dump_all_table_schema
490486
assert_no_match %r{create_table "foo\$.+\$bar"}, output
491487
assert_no_match %r{add_index "foo\$.+\$bar"}, output
492488
assert_no_match %r{create_table "schema_migrations"}, output
@@ -554,7 +550,7 @@ def down
554550
end
555551
migration.migrate(:up)
556552

557-
output = perform_schema_dump
553+
output = dump_all_table_schema
558554
assert output.include?('t.datetime "this_should_remain_datetime"')
559555
assert output.include?('t.datetime "this_is_an_alias_of_datetime"')
560556
assert output.include?('t.datetime "without_time_zone"')
@@ -583,7 +579,7 @@ def down
583579
end
584580
migration.migrate(:up)
585581

586-
output = perform_schema_dump
582+
output = dump_all_table_schema
587583
assert output.include?('t.datetime "this_should_remain_datetime"')
588584
assert output.include?('t.datetime "this_is_an_alias_of_datetime"')
589585
assert output.include?('t.timestamp "without_time_zone"')
@@ -611,7 +607,7 @@ def down
611607
end
612608
migration.migrate(:up)
613609

614-
output = perform_schema_dump
610+
output = dump_all_table_schema
615611
assert output.include?('t.datetime "this_should_remain_datetime"')
616612
assert output.include?('t.datetime "this_is_an_alias_of_datetime"')
617613
assert output.include?('t.datetime "this_is_also_an_alias_of_datetime"')
@@ -638,7 +634,7 @@ def down
638634
end
639635
migration.migrate(:up)
640636

641-
output = perform_schema_dump
637+
output = dump_all_table_schema
642638
# Normally we'd write `t.datetime` here. But because you've changed the `datetime_type`
643639
# to something else, `t.datetime` now means `:timestamptz`. To ensure that old columns
644640
# are still created as a `:timestamp` we need to change what is written to the schema dump.
@@ -672,15 +668,15 @@ def down
672668
end
673669
migration.migrate(:up)
674670

675-
output = perform_schema_dump
671+
output = dump_all_table_schema
676672
assert output.include?('t.datetime "default_format"')
677673
assert output.include?('t.datetime "without_time_zone"')
678674
assert output.include?('t.timestamptz "with_time_zone"')
679675

680676
datetime_type_was = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type
681677
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.datetime_type = :timestamptz
682678

683-
output = perform_schema_dump
679+
output = dump_all_table_schema
684680
assert output.include?('t.timestamp "default_format"')
685681
assert output.include?('t.timestamp "without_time_zone"')
686682
assert output.include?('t.datetime "with_time_zone"')
@@ -708,7 +704,7 @@ def down
708704
end
709705
migration.migrate(:up)
710706

711-
output = perform_schema_dump
707+
output = dump_all_table_schema
712708
assert output.include?('t.datetime "default_format"')
713709
assert output.include?('t.datetime "without_time_zone"')
714710
assert output.include?('t.datetime "also_without_time_zone"')
@@ -736,7 +732,7 @@ def down
736732
end
737733
migration.migrate(:up)
738734

739-
output = perform_schema_dump
735+
output = dump_all_table_schema
740736
assert output.include?('t.datetime "default_format"')
741737
assert output.include?('t.datetime "without_time_zone"')
742738
assert output.include?('t.datetime "also_without_time_zone"')
@@ -763,7 +759,7 @@ def down
763759
end
764760
migration.migrate(:up)
765761

766-
output = perform_schema_dump
762+
output = dump_all_table_schema
767763
assert output.include?('t.datetime "default_format"')
768764
assert output.include?('t.datetime "without_time_zone"')
769765
assert output.include?('t.datetime "also_without_time_zone"')
@@ -789,7 +785,7 @@ def down
789785
end
790786
migration.migrate(:up)
791787

792-
output = perform_schema_dump
788+
output = dump_all_table_schema
793789
# Normally we'd write `t.datetime` here. But because you've changed the `datetime_type`
794790
# to something else, `t.datetime` now means `:timestamptz`. To ensure that old columns
795791
# are still created as a `:timestamp` we need to change what is written to the schema dump.
@@ -822,7 +818,7 @@ def down
822818
end
823819
migration.migrate(:up)
824820

825-
output = perform_schema_dump
821+
output = dump_all_table_schema
826822
# Normally we'd write `t.datetime` here. But because you've changed the `datetime_type`
827823
# to something else, `t.datetime` now means `:timestamptz`. To ensure that old columns
828824
# are still created as a `:timestamp` we need to change what is written to the schema dump.

activerecord/test/support/schema_dumping_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def dump_table_schema(table, connection = ActiveRecord::Base.connection)
1111
ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables
1212
end
1313

14-
def dump_all_table_schema(ignore_tables)
14+
def dump_all_table_schema(ignore_tables = [])
1515
old_ignore_tables, ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::SchemaDumper.ignore_tables, ignore_tables
1616
stream = StringIO.new
1717
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)

0 commit comments

Comments
 (0)