diff --git a/django/db/backends/utils.py b/django/db/backends/utils.py index 5da4e04478..7cb8228226 100644 --- a/django/db/backends/utils.py +++ b/django/db/backends/utils.py @@ -2,6 +2,7 @@ import datetime import decimal import hashlib import logging +import re from time import time from django.conf import settings @@ -178,13 +179,19 @@ def rev_typecast_decimal(d): def truncate_name(name, length=None, hash_len=4): - """Shortens a string to a repeatable mangled version with the given length. """ - if length is None or len(name) <= length: + Shorten a string to a repeatable mangled version with the given length. + If a quote stripped name contains a username, e.g. USERNAME"."TABLE, + truncate the table portion only. + """ + match = re.match('([^"]+)"\."([^"]+)', name) + table_name = match.group(2) if match else name + + if length is None or len(table_name) <= length: return name - hsh = hashlib.md5(force_bytes(name)).hexdigest()[:hash_len] - return '%s%s' % (name[:length - hash_len], hsh) + hsh = hashlib.md5(force_bytes(table_name)).hexdigest()[:hash_len] + return '%s%s%s' % (match.group(1) + '"."' if match else '', table_name[:length - hash_len], hsh) def format_number(value, max_digits, decimal_places): diff --git a/tests/backends/test_utils.py b/tests/backends/test_utils.py index 5076c1ae08..bf62c8c6df 100644 --- a/tests/backends/test_utils.py +++ b/tests/backends/test_utils.py @@ -20,3 +20,7 @@ class TestLoadBackend(SimpleTestCase): self.assertEqual(truncate_name('some_long_table', 10), 'some_la38a') self.assertEqual(truncate_name('some_long_table', 10, 3), 'some_loa38') self.assertEqual(truncate_name('some_long_table'), 'some_long_table') + # "user"."table" syntax + self.assertEqual(truncate_name('username"."some_table', 10), 'username"."some_table') + self.assertEqual(truncate_name('username"."some_long_table', 10), 'username"."some_la38a') + self.assertEqual(truncate_name('username"."some_long_table', 10, 3), 'username"."some_loa38')