From 0b95a96ee10d3e12aef01d449467bcf4641286b4 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Wed, 10 Nov 2021 16:38:43 +0100 Subject: [PATCH] Removed DatabaseIntrospection.get_key_columns(). Thanks Simon Charette for the report. --- django/db/backends/base/introspection.py | 8 ----- django/db/backends/dummy/base.py | 1 - django/db/backends/mysql/base.py | 4 +-- django/db/backends/mysql/introspection.py | 23 +++++---------- django/db/backends/oracle/introspection.py | 14 --------- .../db/backends/postgresql/introspection.py | 5 +--- django/db/backends/sqlite3/base.py | 4 +-- django/db/backends/sqlite3/introspection.py | 29 ------------------- docs/releases/4.1.txt | 3 ++ tests/backends/base/test_introspection.py | 5 ---- tests/introspection/tests.py | 9 ------ 11 files changed, 15 insertions(+), 90 deletions(-) diff --git a/django/db/backends/base/introspection.py b/django/db/backends/base/introspection.py index 8e9f5c751f..079c1835b0 100644 --- a/django/db/backends/base/introspection.py +++ b/django/db/backends/base/introspection.py @@ -154,14 +154,6 @@ class BaseDatabaseIntrospection: 'get_relations() method.' ) - def get_key_columns(self, cursor, table_name): - """ - Backends can override this to return a list of: - (column_name, referenced_table_name, referenced_column_name) - for all key columns in given table. - """ - raise NotImplementedError('subclasses of BaseDatabaseIntrospection may require a get_key_columns() method') - def get_primary_key_column(self, cursor, table_name): """ Return the name of the primary key column for the given table. diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index c6a533e842..06a25f2276 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -44,7 +44,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): get_table_description = complain get_relations = complain get_indexes = complain - get_key_columns = complain class DatabaseWrapper(BaseDatabaseWrapper): diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index d31594d584..3e348846cf 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -308,8 +308,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name) if not primary_key_column_name: continue - key_columns = self.introspection.get_key_columns(cursor, table_name) - for column_name, referenced_table_name, referenced_column_name in key_columns: + relations = self.introspection.get_relations(cursor, table_name) + for column_name, (referenced_column_name, referenced_table_name) in relations.items(): cursor.execute( """ SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 4e2fa3ce27..47ce6c3f4e 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -153,27 +153,18 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): Return a dictionary of {field_name: (field_name_other_table, other_table)} representing all foreign keys in the given table. """ - constraints = self.get_key_columns(cursor, table_name) - relations = {} - for my_fieldname, other_table, other_field in constraints: - relations[my_fieldname] = (other_field, other_table) - return relations - - def get_key_columns(self, cursor, table_name): - """ - Return a list of (column_name, referenced_table_name, referenced_column_name) - for all key columns in the given table. - """ - key_columns = [] cursor.execute(""" - SELECT column_name, referenced_table_name, referenced_column_name + SELECT column_name, referenced_column_name, referenced_table_name FROM information_schema.key_column_usage WHERE table_name = %s AND table_schema = DATABASE() AND referenced_table_name IS NOT NULL - AND referenced_column_name IS NOT NULL""", [table_name]) - key_columns.extend(cursor.fetchall()) - return key_columns + AND referenced_column_name IS NOT NULL + """, [table_name]) + return { + field_name: (other_field, other_table) + for field_name, other_field, other_table in cursor.fetchall() + } def get_storage_engine(self, cursor, table_name): """ diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index 1fb26cba49..f1f2e18e58 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -202,20 +202,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): ) for field_name, rel_table_name, rel_field_name in cursor.fetchall() } - def get_key_columns(self, cursor, table_name): - cursor.execute(""" - SELECT ccol.column_name, rcol.table_name AS referenced_table, rcol.column_name AS referenced_column - FROM user_constraints c - JOIN user_cons_columns ccol - ON ccol.constraint_name = c.constraint_name - JOIN user_cons_columns rcol - ON rcol.constraint_name = c.r_constraint_name - WHERE c.table_name = %s AND c.constraint_type = 'R'""", [table_name.upper()]) - return [ - tuple(self.identifier_converter(cell) for cell in row) - for row in cursor.fetchall() - ] - def get_primary_key_column(self, cursor, table_name): cursor.execute(""" SELECT diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index 5a50ee68b7..f31d906a2f 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -121,9 +121,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): Return a dictionary of {field_name: (field_name_other_table, other_table)} representing all foreign keys in the given table. """ - return {row[0]: (row[2], row[1]) for row in self.get_key_columns(cursor, table_name)} - - def get_key_columns(self, cursor, table_name): cursor.execute(""" SELECT a1.attname, c2.relname, a2.attname FROM pg_constraint con @@ -137,7 +134,7 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): c1.relnamespace = c2.relnamespace AND pg_catalog.pg_table_is_visible(c1.oid) """, [table_name]) - return cursor.fetchall() + return {row[0]: (row[2], row[1]) for row in cursor.fetchall()} def get_constraints(self, cursor, table_name): """ diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 666b367be7..fbad8039d8 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -360,8 +360,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name) if not primary_key_column_name: continue - key_columns = self.introspection.get_key_columns(cursor, table_name) - for column_name, referenced_table_name, referenced_column_name in key_columns: + relations = self.introspection.get_relations(cursor, table_name) + for column_name, (referenced_column_name, referenced_table_name) in relations: cursor.execute( """ SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py index f1f33579b9..87894967fc 100644 --- a/django/db/backends/sqlite3/introspection.py +++ b/django/db/backends/sqlite3/introspection.py @@ -170,35 +170,6 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): return relations - def get_key_columns(self, cursor, table_name): - """ - Return a list of (column_name, referenced_table_name, referenced_column_name) - for all key columns in given table. - """ - key_columns = [] - - # Schema for this table - cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"]) - results = cursor.fetchone()[0].strip() - results = results[results.index('(') + 1:results.rindex(')')] - - # Walk through and look for references to other tables. SQLite doesn't - # really have enforced references, but since it echoes out the SQL used - # to create the table we can look for REFERENCES statements used there. - for field_index, field_desc in enumerate(results.split(',')): - field_desc = field_desc.strip() - if field_desc.startswith("UNIQUE"): - continue - - m = re.search(r'"(.*)".*references (.*) \(["|](.*)["|]\)', field_desc, re.I) - if not m: - continue - - # This will append (column_name, referenced_table_name, referenced_column_name) to key_columns - key_columns.append(tuple(s.strip('"') for s in m.groups())) - - return key_columns - def get_primary_key_column(self, cursor, table_name): """Return the column name of the primary key for the given table.""" cursor.execute( diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index be028f0623..041230b93a 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -246,6 +246,9 @@ backends. * ``BaseDatabaseFeatures.has_case_insensitive_like`` is changed from ``True`` to ``False`` to reflect the behavior of most databases. +* ``DatabaseIntrospection.get_key_columns()`` is removed. Use + ``DatabaseIntrospection.get_relations()`` instead. + Dropped support for MariaDB 10.2 -------------------------------- diff --git a/tests/backends/base/test_introspection.py b/tests/backends/base/test_introspection.py index ac1785ec92..636d6683c0 100644 --- a/tests/backends/base/test_introspection.py +++ b/tests/backends/base/test_introspection.py @@ -31,11 +31,6 @@ class SimpleDatabaseIntrospectionTests(SimpleTestCase): with self.assertRaisesMessage(NotImplementedError, msg): self.introspection.get_relations(None, None) - def test_get_key_columns(self): - msg = self.may_require_msg % 'get_key_columns' - with self.assertRaisesMessage(NotImplementedError, msg): - self.introspection.get_key_columns(None, None) - def test_get_constraints(self): msg = self.may_require_msg % 'get_constraints' with self.assertRaisesMessage(NotImplementedError, msg): diff --git a/tests/introspection/tests.py b/tests/introspection/tests.py index bd5d7c942a..b55714f93e 100644 --- a/tests/introspection/tests.py +++ b/tests/introspection/tests.py @@ -168,15 +168,6 @@ class IntrospectionTests(TransactionTestCase): relations = connection.introspection.get_relations(cursor, 'mocked_table') self.assertEqual(relations, {'art_id': ('id', Article._meta.db_table)}) - @skipUnlessDBFeature('can_introspect_foreign_keys') - def test_get_key_columns(self): - with connection.cursor() as cursor: - key_columns = connection.introspection.get_key_columns(cursor, Article._meta.db_table) - self.assertEqual(set(key_columns), { - ('reporter_id', Reporter._meta.db_table, 'id'), - ('response_to_id', Article._meta.db_table, 'id'), - }) - def test_get_primary_key_column(self): with connection.cursor() as cursor: primary_key_column = connection.introspection.get_primary_key_column(cursor, Article._meta.db_table)