Skip to content

Commit b78637e

Browse files
authored
Merge pull request rails#50020 from fatkodima/schema-dump-ruby-and-dump_schemas
Make schema dumper to account for `ActiveRecord.dump_schemas` when dumping in `:ruby` format
2 parents 6025230 + e841859 commit b78637e

File tree

6 files changed

+159
-17
lines changed

6 files changed

+159
-17
lines changed

activerecord/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Make schema dumper to account for `ActiveRecord.dump_schemas` when dumping in `:ruby` format.
2+
3+
*fatkodima*
4+
15
* Add `:touch` option to `update_column`/`update_columns` methods.
26

37
```ruby

activerecord/lib/active_record/connection_adapters/postgresql/schema_dumper.rb

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ module ConnectionAdapters
55
module PostgreSQL
66
class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
77
private
8+
attr_accessor :schema_name
9+
10+
def initialize(connection, options = {})
11+
super
12+
13+
@dump_schemas =
14+
case ActiveRecord.dump_schemas
15+
when :schema_search_path
16+
connection.current_schemas
17+
when String
18+
schema_names = ActiveRecord.dump_schemas.split(",").map(&:strip)
19+
schema_names & connection.schema_names
20+
else
21+
connection.schema_names
22+
end
23+
end
24+
825
def extensions(stream)
926
extensions = @connection.extensions
1027
if extensions.any?
@@ -17,19 +34,20 @@ def extensions(stream)
1734
end
1835

1936
def types(stream)
20-
types = @connection.enum_types
21-
if types.any?
22-
stream.puts " # Custom types defined in this database."
23-
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
24-
types.sort.each do |name, values|
25-
stream.puts " create_enum #{name.inspect}, #{values.inspect}"
37+
within_each_schema do
38+
types = @connection.enum_types
39+
if types.any?
40+
stream.puts " # Custom types defined in this database."
41+
stream.puts " # Note that some types may not work with other database engines. Be careful if changing database."
42+
types.sort.each do |name, values|
43+
stream.puts " create_enum #{relation_name(name).inspect}, #{values.inspect}"
44+
end
2645
end
27-
stream.puts
2846
end
2947
end
3048

3149
def schemas(stream)
32-
schema_names = @connection.schema_names - ["public"]
50+
schema_names = @dump_schemas - ["public"]
3351

3452
if schema_names.any?
3553
schema_names.sort.each do |name|
@@ -39,6 +57,15 @@ def schemas(stream)
3957
end
4058
end
4159

60+
def tables(stream)
61+
previous_schema_had_tables = false
62+
within_each_schema do
63+
stream.puts if previous_schema_had_tables
64+
super
65+
previous_schema_had_tables = @connection.tables.any?
66+
end
67+
end
68+
4269
def exclusion_constraints_in_create(table, stream)
4370
if (exclusion_constraints = @connection.exclusion_constraints(table)).any?
4471
exclusion_constraint_statements = exclusion_constraints.map do |exclusion_constraint|
@@ -110,6 +137,26 @@ def schema_expression(column)
110137
def extract_expression_for_virtual_column(column)
111138
column.default_function.inspect
112139
end
140+
141+
def within_each_schema
142+
@dump_schemas.each do |schema_name|
143+
old_search_path = @connection.schema_search_path
144+
@connection.schema_search_path = schema_name
145+
self.schema_name = schema_name
146+
yield
147+
ensure
148+
self.schema_name = nil
149+
@connection.schema_search_path = old_search_path
150+
end
151+
end
152+
153+
def relation_name(name)
154+
if @dump_schemas.size == 1
155+
name
156+
else
157+
"#{schema_name}.#{name}"
158+
end
159+
end
113160
end
114161
end
115162
end

activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,14 @@ def current_schema
235235
query_value("SELECT current_schema", "SCHEMA")
236236
end
237237

238+
# Returns an array of the names of all schemas presently in the effective search path,
239+
# in their priority order.
240+
def current_schemas # :nodoc:
241+
schemas = query_value("SELECT current_schemas(false)", "SCHEMA")
242+
decoder = PG::TextDecoder::Array.new
243+
decoder.decode(schemas)
244+
end
245+
238246
# Returns the current database encoding format.
239247
def encoding
240248
query_value("SELECT pg_encoding_to_char(encoding) FROM pg_database WHERE datname = current_database()", "SCHEMA")

activerecord/lib/active_record/schema_dumper.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def table(table, stream)
165165
# first dump primary key column
166166
pk = @connection.primary_key(table)
167167

168-
tbl.print " create_table #{remove_prefix_and_suffix(table).inspect}"
168+
tbl.print " create_table #{relation_name(remove_prefix_and_suffix(table)).inspect}"
169169

170170
case pk
171171
when String
@@ -233,7 +233,7 @@ def indexes(table, stream)
233233
if (indexes = @connection.indexes(table)).any?
234234
add_index_statements = indexes.map do |index|
235235
table_name = remove_prefix_and_suffix(index.table).inspect
236-
" add_index #{([table_name] + index_parts(index)).join(', ')}"
236+
" add_index #{([relation_name(table_name)] + index_parts(index)).join(', ')}"
237237
end
238238

239239
stream.puts add_index_statements.sort.join("\n")
@@ -318,8 +318,8 @@ def foreign_keys(table, stream)
318318
if (foreign_keys = @connection.foreign_keys(table)).any?
319319
add_foreign_key_statements = foreign_keys.map do |foreign_key|
320320
parts = [
321-
remove_prefix_and_suffix(foreign_key.from_table).inspect,
322-
remove_prefix_and_suffix(foreign_key.to_table).inspect,
321+
relation_name(remove_prefix_and_suffix(foreign_key.from_table)).inspect,
322+
relation_name(remove_prefix_and_suffix(foreign_key.to_table)).inspect,
323323
]
324324

325325
if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
@@ -361,6 +361,10 @@ def format_index_parts(options)
361361
end
362362
end
363363

364+
def relation_name(name)
365+
name
366+
end
367+
364368
def remove_prefix_and_suffix(table)
365369
# This method appears at the top when profiling active_record test cases run.
366370
# Avoid costly calculation when there are no prefix and suffix.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ def test_schema_dump_scoped_to_schemas
249249
output = dump_table_schema("postgresql_enums_in_test_schema")
250250

251251
assert_includes output, 'create_enum "public.mood", ["sad", "ok", "happy"]'
252-
assert_includes output, 'create_enum "mood_in_test_schema", ["sad", "ok", "happy"]'
252+
assert_includes output, 'create_enum "test_schema.mood_in_test_schema", ["sad", "ok", "happy"]'
253253
assert_includes output, 't.enum "current_mood", enum_type: "mood_in_test_schema"'
254254
assert_not_includes output, 'create_enum "other_schema.mood_in_other_schema"'
255255
end

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

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "cases/helper"
44
require "models/default"
55
require "support/schema_dumping_helper"
6+
require "active_support/core_ext/object/with"
67

78
module PGSchemaHelper
89
def with_schema_search_path(schema_search_path)
@@ -13,6 +14,10 @@ def with_schema_search_path(schema_search_path)
1314
@connection.schema_search_path = "'$user', public"
1415
@connection.schema_cache.clear!
1516
end
17+
18+
def with_dump_schemas(value, &block)
19+
ActiveRecord.with(dump_schemas: value, &block)
20+
end
1621
end
1722

1823
class SchemaTest < ActiveRecord::PostgreSQLTestCase
@@ -555,11 +560,13 @@ def test_rename_index
555560
end
556561

557562
def test_dumping_schemas
558-
output = dump_all_table_schema(/./)
563+
with_dump_schemas("test_schema,test_schema2,public") do
564+
output = dump_all_table_schema(/./)
559565

560-
assert_no_match %r{create_schema "public"}, output
561-
assert_match %r{create_schema "test_schema"}, output
562-
assert_match %r{create_schema "test_schema2"}, output
566+
assert_no_match %r{create_schema "public"}, output
567+
assert_match %r{create_schema "test_schema"}, output
568+
assert_match %r{create_schema "test_schema2"}, output
569+
end
563570
end
564571

565572
private
@@ -985,3 +992,75 @@ def test_no_partition_options_are_dumped
985992
assert_no_match("options:", output)
986993
end
987994
end
995+
996+
class DumpSchemasTest < ActiveRecord::PostgreSQLTestCase
997+
include SchemaDumpingHelper
998+
include PGSchemaHelper
999+
1000+
def setup
1001+
@connection = ActiveRecord::Base.connection
1002+
@connection.create_schema("test_schema")
1003+
@connection.create_schema("test_schema2")
1004+
@connection.create_enum("test_schema.test_enum_in_test_schema", ["foo", "bar"])
1005+
@connection.create_enum("test_enum_in_public", ["foo", "bar"])
1006+
@connection.create_table("test_schema.test_table")
1007+
@connection.create_table("test_schema.test_table2") do |t|
1008+
t.integer "test_table_id"
1009+
t.foreign_key "test_schema.test_table"
1010+
end
1011+
end
1012+
1013+
def teardown
1014+
@connection.drop_schema("test_schema")
1015+
@connection.drop_schema("test_schema2")
1016+
@connection.drop_enum("test_enum_in_public")
1017+
end
1018+
1019+
def test_schema_dump_with_dump_schemas_all
1020+
with_dump_schemas(:all) do
1021+
output = dump_all_table_schema
1022+
1023+
assert_includes output, 'create_schema "test_schema"'
1024+
assert_not_includes output, 'create_schema "public"'
1025+
assert_includes output, 'create_enum "test_schema.test_enum_in_test_schema"'
1026+
assert_includes output, 'create_enum "public.test_enum_in_public"'
1027+
assert_includes output, 'create_table "test_schema.test_table"'
1028+
assert_includes output, 'create_table "public.authors"'
1029+
assert_includes output, 'add_foreign_key "test_schema.test_table2", "test_schema.test_table"'
1030+
assert_includes output, 'add_foreign_key "public.authors", "public.author_addresses"'
1031+
end
1032+
end
1033+
1034+
def test_schema_dump_with_dump_schemas_string
1035+
with_dump_schemas("test_schema") do
1036+
output = dump_all_table_schema
1037+
1038+
assert_includes output, 'create_schema "test_schema"'
1039+
assert_not_includes output, 'create_schema "public"'
1040+
assert_includes output, 'create_enum "test_enum_in_test_schema"'
1041+
assert_not_includes output, "test_enum_in_public"
1042+
assert_includes output, 'create_table "test_table"'
1043+
assert_not_includes output, 'create table "authors"'
1044+
assert_includes output, 'add_foreign_key "test_table2", "test_table"'
1045+
assert_not_includes output, 'add_foreign_key "authors", "author_addresses"'
1046+
end
1047+
end
1048+
1049+
def test_schema_dump_with_dump_schemas_schema_search_path
1050+
with_dump_schemas(:schema_search_path) do
1051+
with_schema_search_path("'$user',test_schema2,test_schema") do
1052+
output = dump_all_table_schema
1053+
1054+
assert_includes output, 'create_schema "test_schema"'
1055+
assert_includes output, 'create_schema "test_schema2"'
1056+
assert_not_includes output, 'create_schema "public"'
1057+
assert_includes output, 'create_enum "test_schema.test_enum_in_test_schema"'
1058+
assert_not_includes output, 'create_enum "public.test_enum_in_public"'
1059+
assert_includes output, 'create_table "test_schema.test_table"'
1060+
assert_not_includes output, 'create_table "public.authors"'
1061+
assert_includes output, 'add_foreign_key "test_schema.test_table2", "test_schema.test_table"'
1062+
assert_not_includes output, 'add_foreign_key "public.authors", "public.author_addresses"'
1063+
end
1064+
end
1065+
end
1066+
end

0 commit comments

Comments
 (0)