Fixed #27458 -- Fixed invalid sequence/index names when using "USER"."TABLE" db_table on Oracle.

This commit is contained in:
Andrew Nester 2016-12-31 01:11:12 +03:00 committed by Tim Graham
parent 398a859642
commit 69b7d4b116
6 changed files with 52 additions and 7 deletions

View File

@ -1,6 +1,6 @@
from django.contrib.gis.db.models.fields import GeometryField
from django.db.backends.oracle.schema import DatabaseSchemaEditor
from django.db.backends.utils import truncate_name
from django.db.backends.utils import strip_quotes, truncate_name
class OracleGISSchemaEditor(DatabaseSchemaEditor):
@ -91,4 +91,4 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor):
def _create_spatial_index_name(self, model, field):
# Oracle doesn't allow object names > 30 characters. Use this scheme
# instead of self._create_index_name() for backwards compatibility.
return truncate_name('%s_%s_id' % (model._meta.db_table, field.column), 30)
return truncate_name('%s_%s_id' % (strip_quotes(model._meta.db_table), field.column), 30)

View File

@ -2,6 +2,7 @@ import hashlib
import logging
from datetime import datetime
from django.db.backends.utils import strip_quotes
from django.db.transaction import TransactionManagementError, atomic
from django.utils import six, timezone
from django.utils.encoding import force_bytes
@ -841,7 +842,7 @@ class BaseDatabaseSchemaEditor(object):
The name is divided into 3 parts: the table name, the column names,
and a unique digest and suffix.
"""
table_name = model._meta.db_table
table_name = strip_quotes(model._meta.db_table)
hash_data = [table_name] + list(column_names)
hash_suffix_part = '%s%s' % (self._digest(*hash_data), suffix)
max_length = self.connection.ops.max_name_length() or 200

View File

@ -6,7 +6,7 @@ import uuid
from django.conf import settings
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.backends.utils import truncate_name
from django.db.backends.utils import strip_quotes, truncate_name
from django.utils import six, timezone
from django.utils.encoding import force_bytes, force_text
@ -450,11 +450,13 @@ WHEN (new.%(col_name)s IS NULL)
def _get_sequence_name(self, table):
name_length = self.max_name_length() - 3
return '%s_SQ' % truncate_name(table, name_length).upper()
sequence_name = '%s_SQ' % strip_quotes(table)
return truncate_name(sequence_name, name_length).upper()
def _get_trigger_name(self, table):
name_length = self.max_name_length() - 3
return '%s_TR' % truncate_name(table, name_length).upper()
trigger_name = '%s_TR' % strip_quotes(table)
return truncate_name(trigger_name, name_length).upper()
def bulk_insert_sql(self, fields, placeholder_rows):
return " UNION ALL ".join(

View File

@ -209,3 +209,13 @@ def format_number(value, max_digits, decimal_places):
if decimal_places is not None:
return "%.*f" % (decimal_places, value)
return "{:f}".format(value)
def strip_quotes(table_name):
"""
Strip quotes off of quoted table names to make them safe for use in index
names, sequence names, etc. For example '"USER"."TABLE"' (an Oracle naming
scheme) becomes 'USER"."TABLE'.
"""
has_quotes = table_name.startswith('"') and table_name.endswith('"')
return table_name[1:-1] if has_quotes else table_name

View File

@ -1565,7 +1565,8 @@ class ManyToManyField(RelatedField):
elif self.db_table:
return self.db_table
else:
return utils.truncate_name('%s_%s' % (opts.db_table, self.name), connection.ops.max_name_length())
m2m_table_name = '%s_%s' % (utils.strip_quotes(opts.db_table), self.name)
return utils.truncate_name(m2m_table_name, connection.ops.max_name_length())
def _get_m2m_attr(self, related, attr):
"""

View File

@ -2266,3 +2266,34 @@ class SchemaTests(TransactionTestCase):
editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(),
cast_function=lambda x: x.time(),
)
@unittest.skipUnless(connection.vendor == 'oracle', 'Oracle specific db_table syntax')
def test_creation_with_db_table_double_quotes(self):
oracle_user = connection.creation._test_database_user()
class Student(Model):
name = CharField(max_length=30)
class Meta:
app_label = 'schema'
apps = new_apps
db_table = '"%s"."DJANGO_STUDENT_TABLE"' % oracle_user
class Document(Model):
name = CharField(max_length=30)
students = ManyToManyField(Student)
class Meta:
app_label = 'schema'
apps = new_apps
db_table = '"%s"."DJANGO_DOCUMENT_TABLE"' % oracle_user
self.local_models = [Student, Document]
with connection.schema_editor() as editor:
editor.create_model(Student)
editor.create_model(Document)
doc = Document.objects.create(name='Test Name')
student = Student.objects.create(name='Some man')
doc.students.add(student)