support dumping PostgreSQL partitioning options to `schema.rb`

This commit is contained in:
justin talbott 2024-01-07 12:21:58 -05:00 committed by Rafael Mendonça França
parent c3f5eba1ad
commit fba7ef0369
No known key found for this signature in database
GPG Key ID: FC23B6D0F1EEE948
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
PostgreSQL binary values (`ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bytea`)

View File

@ -152,9 +152,23 @@ module ActiveRecord
end
def table_options(table_name) # :nodoc:
if comment = table_comment(table_name)
{ comment: comment }
options = {}
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
options
end
# Returns a comment stored in database for given table
@ -172,6 +186,36 @@ module ActiveRecord
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.
def current_database
query_value("SELECT current_database()", "SCHEMA")

View File

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

View File

@ -861,3 +861,90 @@ class SchemaIndexNullsNotDistinctTest < ActiveRecord::PostgreSQLTestCase
assert_no_match(/nulls_not_distinct/, output)
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_identity_columns?
supports_virtual_columns?
supports_native_partitioning?
].each do |method_name|
define_method method_name do
ActiveRecord::Base.lease_connection.public_send(method_name)