Fixed #27458 -- Fixed invalid sequence/index names when using "USER"."TABLE" db_table on Oracle.
This commit is contained in:
parent
398a859642
commit
69b7d4b116
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib.gis.db.models.fields import GeometryField
|
from django.contrib.gis.db.models.fields import GeometryField
|
||||||
from django.db.backends.oracle.schema import DatabaseSchemaEditor
|
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):
|
class OracleGISSchemaEditor(DatabaseSchemaEditor):
|
||||||
|
@ -91,4 +91,4 @@ class OracleGISSchemaEditor(DatabaseSchemaEditor):
|
||||||
def _create_spatial_index_name(self, model, field):
|
def _create_spatial_index_name(self, model, field):
|
||||||
# Oracle doesn't allow object names > 30 characters. Use this scheme
|
# Oracle doesn't allow object names > 30 characters. Use this scheme
|
||||||
# instead of self._create_index_name() for backwards compatibility.
|
# 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)
|
||||||
|
|
|
@ -2,6 +2,7 @@ import hashlib
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from django.db.backends.utils import strip_quotes
|
||||||
from django.db.transaction import TransactionManagementError, atomic
|
from django.db.transaction import TransactionManagementError, atomic
|
||||||
from django.utils import six, timezone
|
from django.utils import six, timezone
|
||||||
from django.utils.encoding import force_bytes
|
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,
|
The name is divided into 3 parts: the table name, the column names,
|
||||||
and a unique digest and suffix.
|
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_data = [table_name] + list(column_names)
|
||||||
hash_suffix_part = '%s%s' % (self._digest(*hash_data), suffix)
|
hash_suffix_part = '%s%s' % (self._digest(*hash_data), suffix)
|
||||||
max_length = self.connection.ops.max_name_length() or 200
|
max_length = self.connection.ops.max_name_length() or 200
|
||||||
|
|
|
@ -6,7 +6,7 @@ import uuid
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.backends.base.operations import BaseDatabaseOperations
|
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 import six, timezone
|
||||||
from django.utils.encoding import force_bytes, force_text
|
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):
|
def _get_sequence_name(self, table):
|
||||||
name_length = self.max_name_length() - 3
|
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):
|
def _get_trigger_name(self, table):
|
||||||
name_length = self.max_name_length() - 3
|
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):
|
def bulk_insert_sql(self, fields, placeholder_rows):
|
||||||
return " UNION ALL ".join(
|
return " UNION ALL ".join(
|
||||||
|
|
|
@ -209,3 +209,13 @@ def format_number(value, max_digits, decimal_places):
|
||||||
if decimal_places is not None:
|
if decimal_places is not None:
|
||||||
return "%.*f" % (decimal_places, value)
|
return "%.*f" % (decimal_places, value)
|
||||||
return "{:f}".format(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
|
||||||
|
|
|
@ -1565,7 +1565,8 @@ class ManyToManyField(RelatedField):
|
||||||
elif self.db_table:
|
elif self.db_table:
|
||||||
return self.db_table
|
return self.db_table
|
||||||
else:
|
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):
|
def _get_m2m_attr(self, related, attr):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -2266,3 +2266,34 @@ class SchemaTests(TransactionTestCase):
|
||||||
editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(),
|
editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(),
|
||||||
cast_function=lambda x: x.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)
|
||||||
|
|
Loading…
Reference in New Issue