Merge pull request #50475 from waymondo/postgres-partition-options

support dumping PostgreSQL inheritance & partitioning options to `schema.rb`
This commit is contained in:
Rafael Mendonça França 2024-08-22 14:08:25 -03:00 committed by GitHub
commit d4df3d5f81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 142 additions and 2 deletions

View File

@ -1,3 +1,7 @@
* Add support for dumping table inheritance and native partitioning table definitions for PostgeSQL adapter
*Justin Talbott*
* Deserialize database values before decryption * Deserialize database values before decryption
PostgreSQL binary values (`ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea`) PostgreSQL binary values (`ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea`)

View File

@ -152,9 +152,23 @@ module ActiveRecord
end end
def table_options(table_name) # :nodoc: def table_options(table_name) # :nodoc:
if comment = table_comment(table_name) options = {}
{ comment: comment }
comment = table_comment(table_name)
options[:comment] = comment if comment
inherited_table_names = inherited_table_names(table_name).presence
options[:options] = "INHERITS (#{inherited_table_names.join(", ")})" if inherited_table_names
if !options[:options] && supports_native_partitioning?
partition_definition = table_partition_definition(table_name)
options[:options] = "PARTITION BY #{partition_definition}" if partition_definition
end end
options
end end
# Returns a comment stored in database for given table # Returns a comment stored in database for given table
@ -172,6 +186,36 @@ module ActiveRecord
end end
end end
# Returns the partition definition of a given table
def table_partition_definition(table_name) # :nodoc:
scope = quoted_scope(table_name, type: "BASE TABLE")
query_value(<<~SQL, "SCHEMA")
SELECT pg_catalog.pg_get_partkeydef(c.oid)
FROM pg_catalog.pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = #{scope[:name]}
AND c.relkind IN (#{scope[:type]})
AND n.nspname = #{scope[:schema]}
SQL
end
# Returns the inherited table name of a given table
def inherited_table_names(table_name) # :nodoc:
scope = quoted_scope(table_name, type: "BASE TABLE")
query_values(<<~SQL, "SCHEMA")
SELECT parent.relname
FROM pg_catalog.pg_inherits i
JOIN pg_catalog.pg_class child ON i.inhrelid = child.oid
JOIN pg_catalog.pg_class parent ON i.inhparent = parent.oid
LEFT JOIN pg_namespace n ON n.oid = child.relnamespace
WHERE child.relname = #{scope[:name]}
AND child.relkind IN (#{scope[:type]})
AND n.nspname = #{scope[:schema]}
SQL
end
# Returns the current database name. # Returns the current database name.
def current_database def current_database
query_value("SELECT current_database()", "SCHEMA") query_value("SELECT current_database()", "SCHEMA")

View File

@ -284,6 +284,10 @@ module ActiveRecord
database_version >= 15_00_00 # >= 15.0 database_version >= 15_00_00 # >= 15.0
end end
def supports_native_partitioning? # :nodoc:
database_version >= 10_00_00 # >= 10.0
end
def index_algorithms def index_algorithms
{ concurrently: "CONCURRENTLY" } { concurrently: "CONCURRENTLY" }
end end

View File

@ -861,3 +861,90 @@ class SchemaIndexNullsNotDistinctTest < ActiveRecord::PostgreSQLTestCase
assert_no_match(/nulls_not_distinct/, output) assert_no_match(/nulls_not_distinct/, output)
end end
end end
class SchemaCreateTableOptionsTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
setup do
@connection = ActiveRecord::Base.connection
end
teardown do
@connection.drop_table "trains", if_exists: true
@connection.drop_table "transportation_modes", if_exists: true
@connection.drop_table "vehicles", if_exists: true
end
def test_list_partition_options_is_dumped
skip("current adapter doesn't support native partitioning") unless supports_native_partitioning?
options = "PARTITION BY LIST (kind)"
@connection.create_table "trains", id: false, options: options do |t|
t.string :name
t.string :kind
end
output = dump_table_schema "trains"
assert_match("options: \"#{options}\"", output)
end
def test_range_partition_options_is_dumped
skip("current adapter doesn't support native partitioning") unless supports_native_partitioning?
options = "PARTITION BY RANGE (created_at)"
@connection.create_table "trains", id: false, options: options do |t|
t.string :name
t.datetime :created_at, null: false
end
output = dump_table_schema "trains"
assert_match("options: \"#{options}\"", output)
end
def test_inherited_table_options_is_dumped
@connection.create_table "transportation_modes" do |t|
t.string :name
t.string :kind
end
options = "INHERITS (transportation_modes)"
@connection.create_table "trains", options: options
output = dump_table_schema "trains"
assert_match("options: \"#{options}\"", output)
end
def test_multiple_inherited_table_options_is_dumped
@connection.create_table "vehicles" do |t|
t.string :name
end
@connection.create_table "transportation_modes" do |t|
t.string :kind
end
options = "INHERITS (transportation_modes, vehicles)"
@connection.create_table "trains", options: options
output = dump_table_schema "trains"
assert_match("options: \"#{options}\"", output)
end
def test_no_partition_options_are_dumped
@connection.create_table "trains" do |t|
t.string :name
end
output = dump_table_schema "trains"
assert_no_match("options:", output)
end
end

View File

@ -60,6 +60,7 @@ module AdapterHelper
supports_nulls_not_distinct? supports_nulls_not_distinct?
supports_identity_columns? supports_identity_columns?
supports_virtual_columns? supports_virtual_columns?
supports_native_partitioning?
].each do |method_name| ].each do |method_name|
define_method method_name do define_method method_name do
ActiveRecord::Base.lease_connection.public_send(method_name) ActiveRecord::Base.lease_connection.public_send(method_name)