Skip to content

Commit

Permalink
Added introspection of table comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixxm committed Dec 23, 2022
1 parent 6c8259b commit e7625e1
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 16 deletions.
7 changes: 7 additions & 0 deletions django/db/backends/base/introspection.py
Expand Up @@ -77,6 +77,13 @@ def get_table_description(self, cursor, table_name):
"get_table_description() method."
)

def get_table_comment(self, cursor, table_name):
"""Return a comment on the table."""
raise NotImplementedError(
"subclasses of BaseDatabaseIntrospection may require a get_table_comment() "
"method."
)

def get_migratable_models(self):
from django.apps import apps
from django.db import router
Expand Down
13 changes: 13 additions & 0 deletions django/db/backends/mysql/introspection.py
Expand Up @@ -166,6 +166,19 @@ def to_int(i):
)
return fields

def get_table_comment(self, cursor, table_name):
cursor.execute(
"""
SELECT table_comment
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = %s
""",
[table_name],
)
row = cursor.fetchone()
return row[0] if row else None

def get_sequences(self, cursor, table_name, table_fields=()):
for field_info in self.get_table_description(cursor, table_name):
if "auto_increment" in field_info.extra:
Expand Down
12 changes: 12 additions & 0 deletions django/db/backends/oracle/introspection.py
Expand Up @@ -201,6 +201,18 @@ def get_table_description(self, cursor, table_name):
)
return description

def get_table_comment(self, cursor, table_name):
cursor.execute(
"""
SELECT comments
FROM user_tab_comments
WHERE table_name = UPPER(%s)
""",
[table_name],
)
row = cursor.fetchone()
return row[0] if row else None

def identifier_converter(self, name):
"""Identifier comparison is case insensitive under Oracle."""
return name.lower()
Expand Down
16 changes: 16 additions & 0 deletions django/db/backends/postgresql/introspection.py
Expand Up @@ -124,6 +124,22 @@ def get_table_description(self, cursor, table_name):
for line in cursor.description
]

def get_table_comment(self, cursor, table_name):
cursor.execute(
"""
SELECT obj_description(pg_class.oid)
FROM pg_class
JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
WHERE pg_class.relkind IN ('f', 'm', 'p', 'r', 'v')
AND pg_class.relname = %s
AND pg_namespace.nspname NOT IN ('pg_catalog', 'pg_toast')
AND pg_catalog.pg_table_is_visible(pg_class.oid)
""",
[table_name],
)
row = cursor.fetchone()
return row[0] if row else None

def get_sequences(self, cursor, table_name, table_fields=()):
cursor.execute(
"""
Expand Down
5 changes: 5 additions & 0 deletions tests/backends/base/test_introspection.py
Expand Up @@ -21,6 +21,11 @@ def test_get_table_description(self):
with self.assertRaisesMessage(NotImplementedError, msg):
self.introspection.get_table_description(None, None)

def test_get_table_comment(self):
msg = self.may_require_msg % "get_table_comment"
with self.assertRaisesMessage(NotImplementedError, msg):
self.introspection.get_table_comment(None, None)

def test_get_sequences(self):
msg = self.may_require_msg % "get_sequences"
with self.assertRaisesMessage(NotImplementedError, msg):
Expand Down
18 changes: 8 additions & 10 deletions tests/introspection/models.py
Expand Up @@ -55,16 +55,6 @@ class Meta:
managed = False


class ArticleReporterWithDBComment(models.Model):
article = models.ForeignKey(Article, models.CASCADE, db_comment="i am comment")
reporter = models.ForeignKey(Reporter, models.CASCADE)

class Meta:
managed = False
db_table_comment = "I am table comment"
required_db_features = {"supports_comments"}


class Comment(models.Model):
ref = models.UUIDField(unique=True)
article = models.ForeignKey(Article, models.CASCADE, db_index=True)
Expand Down Expand Up @@ -112,3 +102,11 @@ class Meta:
condition=models.Q(color__isnull=True),
),
]


class DbCommentModel(models.Model):
name = models.CharField(max_length=15, db_comment="'Name' column comment")

class Meta:
db_table_comment = "Custom table comment"
required_db_features = {"supports_comments"}
22 changes: 16 additions & 6 deletions tests/introspection/tests.py
Expand Up @@ -5,11 +5,11 @@
from .models import (
Article,
ArticleReporter,
ArticleReporterWithDBComment,
CheckConstraintModel,
City,
Comment,
Country,
DbCommentModel,
District,
Reporter,
UniqueConstraintConditionModel,
Expand Down Expand Up @@ -80,11 +80,6 @@ def test_unmanaged_through_model(self):
tables = connection.introspection.django_table_names()
self.assertNotIn(ArticleReporter._meta.db_table, tables)

@skipUnlessDBFeature("supports_comments", "supports_foreign_keys")
def test_unmanaged_db_comment_model(self):
tables = connection.introspection.django_table_names()
self.assertNotIn(ArticleReporterWithDBComment._meta.db_table, tables)

def test_installed_models(self):
tables = [Article._meta.db_table, Reporter._meta.db_table]
models = connection.introspection.installed_models(tables)
Expand Down Expand Up @@ -185,6 +180,21 @@ def test_smallautofield(self):
[connection.introspection.get_field_type(r[1], r) for r in desc],
)

@skipUnlessDBFeature("supports_comments")
def test_db_comments(self):
with connection.cursor() as cursor:
desc = connection.introspection.get_table_description(
cursor, DbCommentModel._meta.db_table
)
table_comment = connection.introspection.get_table_comment(
cursor, DbCommentModel._meta.db_table
)
self.assertEqual(
["'Name' column comment"],
[field.comment for field in desc if field.name == "name"],
)
self.assertEqual(table_comment, "Custom table comment")

# Regression test for #9991 - 'real' types in postgres
@skipUnlessDBFeature("has_real_datatype")
def test_postgresql_real_type(self):
Expand Down

0 comments on commit e7625e1

Please sign in to comment.