Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix retrieving foreign keys referencing tables named like keywords in PostgreSQL and MySQL #47353

Merged
merged 1 commit into from Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -491,15 +491,15 @@ def foreign_keys(table_name)

fk_info.map do |row|
options = {
column: row["column"],
column: unquote_identifier(row["column"]),
name: row["name"],
primary_key: row["primary_key"]
}

options[:on_update] = extract_foreign_key_action(row["on_update"])
options[:on_delete] = extract_foreign_key_action(row["on_delete"])

ForeignKeyDefinition.new(table_name, row["to_table"], options)
ForeignKeyDefinition.new(table_name, unquote_identifier(row["to_table"]), options)
end
end

Expand Down
Expand Up @@ -54,6 +54,14 @@ def quoted_binary(value)
"x'#{value.hex}'"
end

def unquote_identifier(identifier)
if identifier && identifier.start_with?("`")
identifier[1..-2]
else
identifier
end
end

# Override +type_cast+ we pass to mysql2 Date and Time objects instead
# of Strings since mysql2 is able to handle those classes more efficiently.
def type_cast(value) # :nodoc:
Expand Down
Expand Up @@ -531,7 +531,7 @@ def foreign_keys(table_name)

fk_info.map do |row|
options = {
column: row["column"],
column: Utils.unquote_identifier(row["column"]),
name: row["name"],
primary_key: row["primary_key"]
}
Expand All @@ -541,8 +541,9 @@ def foreign_keys(table_name)
options[:deferrable] = extract_foreign_key_deferrable(row["deferrable"], row["deferred"])

options[:validate] = row["valid"]
to_table = Utils.unquote_identifier(row["to_table"])

ForeignKeyDefinition.new(table_name, row["to_table"], options)
ForeignKeyDefinition.new(table_name, to_table, options)
end
end

Expand Down
Expand Up @@ -12,7 +12,7 @@ class Name # :nodoc:
attr_reader :schema, :identifier

def initialize(schema, identifier)
@schema, @identifier = unquote(schema), unquote(identifier)
@schema, @identifier = Utils.unquote_identifier(schema), Utils.unquote_identifier(identifier)
end

def to_s
Expand Down Expand Up @@ -40,15 +40,6 @@ def hash
def parts
@parts ||= [@schema, @identifier].compact
end

private
def unquote(part)
if part && part.start_with?('"')
part[1..-2]
else
part
end
end
end

module Utils # :nodoc:
Expand All @@ -74,6 +65,14 @@ def extract_schema_qualified_name(string)
end
PostgreSQL::Name.new(schema, table)
end

def unquote_identifier(identifier)
if identifier && identifier.start_with?('"')
identifier[1..-2]
else
identifier
end
end
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions activerecord/test/cases/migration/foreign_key_test.rb
Expand Up @@ -340,6 +340,16 @@ def test_foreign_key_exists
assert_not @connection.foreign_key_exists?(:astronauts, :stars)
end

def test_foreign_key_exists_referencing_table_having_keyword_as_name
@connection.create_table :user, force: true
@connection.add_column :rockets, :user_id, :bigint
@connection.add_foreign_key :rockets, :user
assert @connection.foreign_key_exists?(:rockets, :user)
ensure
@connection.remove_foreign_key :rockets, :user
@connection.drop_table :user
end

def test_foreign_key_exists_by_column
@connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"

Expand Down