mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Simplify and speed up Postgres query for primary_keys
primary_keys(table) needs to query various metadata tables in Postgres to determine the primary key for the table. Previously, it did so using a complex common table expression against pg_constraint and pg_attribute. This patch simplifies the query by joining pg_index against pg_attribute instead of going through pg_constraint. This avoids an expensive unnest, window function query, and common table expression. EXPLAINing these queries in Postgres against a database with a single table with a composite primary key shows a 66% reduction in the plan and execute latencies. This is significant during application startup time, especially against very large schemas, where these queries would be even slower and more numerous. Closes #27949
This commit is contained in:
parent
2b4d145f31
commit
d6529af295
2 changed files with 17 additions and 10 deletions
|
@ -426,16 +426,14 @@ module ActiveRecord
|
|||
|
||||
def primary_keys(table_name) # :nodoc:
|
||||
select_values(<<-SQL.strip_heredoc, "SCHEMA")
|
||||
WITH pk_constraint AS (
|
||||
SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint
|
||||
WHERE contype = 'p'
|
||||
AND conrelid = #{quote(quote_table_name(table_name))}::regclass
|
||||
), cons AS (
|
||||
SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint
|
||||
)
|
||||
SELECT attr.attname FROM pg_attribute attr
|
||||
INNER JOIN cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.connum
|
||||
ORDER BY cons.rownum
|
||||
SELECT a.attname
|
||||
FROM pg_index i
|
||||
CROSS JOIN unnest(i.indkey) as k
|
||||
JOIN pg_attribute a
|
||||
ON a.attrelid = i.indrelid
|
||||
AND a.attnum = k
|
||||
WHERE i.indrelid = #{quote(quote_table_name(table_name))}::regclass
|
||||
AND i.indisprimary
|
||||
SQL
|
||||
end
|
||||
|
||||
|
|
|
@ -298,6 +298,10 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
|
|||
t.string :region
|
||||
t.integer :code
|
||||
end
|
||||
@connection.create_table(:barcodes_reverse, primary_key: ["code", "region"], force: true) do |t|
|
||||
t.string :region
|
||||
t.integer :code
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
|
@ -308,6 +312,11 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
|
|||
assert_equal ["region", "code"], @connection.primary_keys("barcodes")
|
||||
end
|
||||
|
||||
def test_composite_primary_key_out_of_order
|
||||
skip if current_adapter?(:SQLite3Adapter)
|
||||
assert_equal ["region", "code"], @connection.primary_keys("barcodes")
|
||||
end
|
||||
|
||||
def test_primary_key_issues_warning
|
||||
model = Class.new(ActiveRecord::Base) do
|
||||
def self.table_name
|
||||
|
|
Loading…
Reference in a new issue