Refs #29444 -- Added support for fetching a returned non-integer insert values on Oracle.
This is currently not actively used, since the ORM will ask the SQL compiler to only return auto fields.
This commit is contained in:
parent
34a88b21da
commit
bc91f27a86
|
@ -311,7 +311,7 @@ class BaseDatabaseOperations:
|
||||||
"""
|
"""
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def return_insert_id(self):
|
def return_insert_id(self, field):
|
||||||
"""
|
"""
|
||||||
For backends that support returning the last insert ID as part of an
|
For backends that support returning the last insert ID as part of an
|
||||||
insert query, return the SQL and params to append to the INSERT query.
|
insert query, return the SQL and params to append to the INSERT query.
|
||||||
|
|
|
@ -12,7 +12,7 @@ from django.utils.encoding import force_bytes, force_str
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
|
|
||||||
from .base import Database
|
from .base import Database
|
||||||
from .utils import BulkInsertMapper, InsertIdVar, Oracle_datetime
|
from .utils import BulkInsertMapper, InsertVar, Oracle_datetime
|
||||||
|
|
||||||
|
|
||||||
class DatabaseOperations(BaseDatabaseOperations):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
|
@ -333,8 +333,8 @@ END;
|
||||||
match_option = "'i'"
|
match_option = "'i'"
|
||||||
return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
|
return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
|
||||||
|
|
||||||
def return_insert_id(self):
|
def return_insert_id(self, field):
|
||||||
return "RETURNING %s INTO %%s", (InsertIdVar(),)
|
return 'RETURNING %s INTO %%s', (InsertVar(field),)
|
||||||
|
|
||||||
def __foreign_key_constraints(self, table_name, recursive):
|
def __foreign_key_constraints(self, table_name, recursive):
|
||||||
with self.connection.cursor() as cursor:
|
with self.connection.cursor() as cursor:
|
||||||
|
|
|
@ -3,14 +3,26 @@ import datetime
|
||||||
from .base import Database
|
from .base import Database
|
||||||
|
|
||||||
|
|
||||||
class InsertIdVar:
|
class InsertVar:
|
||||||
"""
|
"""
|
||||||
A late-binding cursor variable that can be passed to Cursor.execute
|
A late-binding cursor variable that can be passed to Cursor.execute
|
||||||
as a parameter, in order to receive the id of the row created by an
|
as a parameter, in order to receive the id of the row created by an
|
||||||
insert statement.
|
insert statement.
|
||||||
"""
|
"""
|
||||||
|
types = {
|
||||||
|
'FloatField': Database.NATIVE_FLOAT,
|
||||||
|
'CharField': str,
|
||||||
|
'DateTimeField': Database.TIMESTAMP,
|
||||||
|
'DateField': Database.DATETIME,
|
||||||
|
'DecimalField': Database.NUMBER,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, field):
|
||||||
|
internal_type = getattr(field, 'target_field', field).get_internal_type()
|
||||||
|
self.db_type = self.types.get(internal_type, int)
|
||||||
|
|
||||||
def bind_parameter(self, cursor):
|
def bind_parameter(self, cursor):
|
||||||
param = cursor.cursor.var(int)
|
param = cursor.cursor.var(self.db_type)
|
||||||
cursor._insert_id_var = param
|
cursor._insert_id_var = param
|
||||||
return param
|
return param
|
||||||
|
|
||||||
|
|
|
@ -235,7 +235,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return cursor.query.decode()
|
return cursor.query.decode()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def return_insert_id(self):
|
def return_insert_id(self, field):
|
||||||
return "RETURNING %s", ()
|
return "RETURNING %s", ()
|
||||||
|
|
||||||
def bulk_insert_sql(self, fields, placeholder_rows):
|
def bulk_insert_sql(self, fields, placeholder_rows):
|
||||||
|
|
|
@ -1304,7 +1304,7 @@ class SQLInsertCompiler(SQLCompiler):
|
||||||
if ignore_conflicts_suffix_sql:
|
if ignore_conflicts_suffix_sql:
|
||||||
result.append(ignore_conflicts_suffix_sql)
|
result.append(ignore_conflicts_suffix_sql)
|
||||||
col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
|
col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
|
||||||
r_fmt, r_params = self.connection.ops.return_insert_id()
|
r_fmt, r_params = self.connection.ops.return_insert_id(opts.pk)
|
||||||
# Skip empty r_fmt to allow subclasses to customize behavior for
|
# Skip empty r_fmt to allow subclasses to customize behavior for
|
||||||
# 3rd party backends. Refs #19096.
|
# 3rd party backends. Refs #19096.
|
||||||
if r_fmt:
|
if r_fmt:
|
||||||
|
|
|
@ -353,6 +353,9 @@ backends.
|
||||||
:class:`~django.db.models.DateTimeField` in ``datetime_cast_date_sql()``,
|
:class:`~django.db.models.DateTimeField` in ``datetime_cast_date_sql()``,
|
||||||
``datetime_extract_sql()``, etc.
|
``datetime_extract_sql()``, etc.
|
||||||
|
|
||||||
|
* ``DatabaseOperations.return_insert_id()`` now requires an additional
|
||||||
|
``field`` argument with the model field.
|
||||||
|
|
||||||
:mod:`django.contrib.admin`
|
:mod:`django.contrib.admin`
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,10 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class NonIntegerAutoField(models.Model):
|
||||||
|
creation_datetime = models.DateTimeField(primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class Square(models.Model):
|
class Square(models.Model):
|
||||||
root = models.IntegerField()
|
root = models.IntegerField()
|
||||||
square = models.PositiveIntegerField()
|
square = models.PositiveIntegerField()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import datetime
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
@ -5,7 +6,7 @@ from django.db.models.fields import BooleanField, NullBooleanField
|
||||||
from django.db.utils import DatabaseError
|
from django.db.utils import DatabaseError
|
||||||
from django.test import TransactionTestCase
|
from django.test import TransactionTestCase
|
||||||
|
|
||||||
from ..models import Square
|
from ..models import NonIntegerAutoField, Square
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(connection.vendor == 'oracle', 'Oracle tests')
|
@unittest.skipUnless(connection.vendor == 'oracle', 'Oracle tests')
|
||||||
|
@ -95,3 +96,23 @@ class TransactionalTests(TransactionTestCase):
|
||||||
self.assertIn('ORA-01017', context.exception.args[0].message)
|
self.assertIn('ORA-01017', context.exception.args[0].message)
|
||||||
finally:
|
finally:
|
||||||
connection.settings_dict['PASSWORD'] = old_password
|
connection.settings_dict['PASSWORD'] = old_password
|
||||||
|
|
||||||
|
def test_non_integer_auto_field(self):
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
# Create trigger that fill non-integer auto field.
|
||||||
|
cursor.execute("""
|
||||||
|
CREATE OR REPLACE TRIGGER "TRG_FILL_CREATION_DATETIME"
|
||||||
|
BEFORE INSERT ON "BACKENDS_NONINTEGERAUTOFIELD"
|
||||||
|
FOR EACH ROW
|
||||||
|
BEGIN
|
||||||
|
:NEW.CREATION_DATETIME := SYSTIMESTAMP;
|
||||||
|
END;
|
||||||
|
""")
|
||||||
|
try:
|
||||||
|
NonIntegerAutoField._meta.auto_field = NonIntegerAutoField.creation_datetime
|
||||||
|
obj = NonIntegerAutoField.objects.create()
|
||||||
|
self.assertIsNotNone(obj.creation_datetime)
|
||||||
|
self.assertIsInstance(obj.creation_datetime, datetime.datetime)
|
||||||
|
finally:
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.execute('DROP TRIGGER "TRG_FILL_CREATION_DATETIME"')
|
||||||
|
|
Loading…
Reference in New Issue