mirror of
https://github.com/django/django.git
synced 2025-04-01 12:06:43 +00:00
Fixed #23546 -- Added kwargs support for CursorWrapper.callproc() on Oracle.
Thanks Shai Berger, Tim Graham and Aymeric Augustin for reviews and Renbi Yu for the initial patch.
This commit is contained in:
parent
47ccefeada
commit
489421b015
@ -240,6 +240,9 @@ class BaseDatabaseFeatures:
|
|||||||
create_test_procedure_without_params_sql = None
|
create_test_procedure_without_params_sql = None
|
||||||
create_test_procedure_with_int_param_sql = None
|
create_test_procedure_with_int_param_sql = None
|
||||||
|
|
||||||
|
# Does the backend support keyword parameters for cursor.callproc()?
|
||||||
|
supports_callproc_kwargs = False
|
||||||
|
|
||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
|
||||||
|
@ -54,3 +54,4 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||||||
V_I := P_I;
|
V_I := P_I;
|
||||||
END;
|
END;
|
||||||
"""
|
"""
|
||||||
|
supports_callproc_kwargs = True
|
||||||
|
@ -6,6 +6,7 @@ import re
|
|||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db.utils import NotSupportedError
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.timezone import utc
|
from django.utils.timezone import utc
|
||||||
|
|
||||||
@ -45,13 +46,23 @@ class CursorWrapper:
|
|||||||
# The following methods cannot be implemented in __getattr__, because the
|
# The following methods cannot be implemented in __getattr__, because the
|
||||||
# code must run when the method is invoked, not just when it is accessed.
|
# code must run when the method is invoked, not just when it is accessed.
|
||||||
|
|
||||||
def callproc(self, procname, params=None):
|
def callproc(self, procname, params=None, kparams=None):
|
||||||
|
# Keyword parameters for callproc aren't supported in PEP 249, but the
|
||||||
|
# database driver may support them (e.g. cx_Oracle).
|
||||||
|
if kparams is not None and not self.db.features.supports_callproc_kwargs:
|
||||||
|
raise NotSupportedError(
|
||||||
|
'Keyword parameters for callproc are not supported on this '
|
||||||
|
'database backend.'
|
||||||
|
)
|
||||||
self.db.validate_no_broken_transaction()
|
self.db.validate_no_broken_transaction()
|
||||||
with self.db.wrap_database_errors:
|
with self.db.wrap_database_errors:
|
||||||
if params is None:
|
if params is None and kparams is None:
|
||||||
return self.cursor.callproc(procname)
|
return self.cursor.callproc(procname)
|
||||||
else:
|
elif kparams is None:
|
||||||
return self.cursor.callproc(procname, params)
|
return self.cursor.callproc(procname, params)
|
||||||
|
else:
|
||||||
|
params = params or ()
|
||||||
|
return self.cursor.callproc(procname, params, kparams)
|
||||||
|
|
||||||
def execute(self, sql, params=None):
|
def execute(self, sql, params=None):
|
||||||
self.db.validate_no_broken_transaction()
|
self.db.validate_no_broken_transaction()
|
||||||
|
@ -269,6 +269,10 @@ Models
|
|||||||
* The new ``field_name`` parameter of :meth:`.QuerySet.in_bulk` allows fetching
|
* The new ``field_name`` parameter of :meth:`.QuerySet.in_bulk` allows fetching
|
||||||
results based on any unique model field.
|
results based on any unique model field.
|
||||||
|
|
||||||
|
* :meth:`.CursorWrapper.callproc()` now takes an optional dictionary of keyword
|
||||||
|
parameters, if the backend supports this feature. Of Django's built-in
|
||||||
|
backends, only Oracle supports it.
|
||||||
|
|
||||||
Requests and Responses
|
Requests and Responses
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -350,10 +350,12 @@ is equivalent to::
|
|||||||
Calling stored procedures
|
Calling stored procedures
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. method:: CursorWrapper.callproc(procname, params=None)
|
.. method:: CursorWrapper.callproc(procname, params=None, kparams=None)
|
||||||
|
|
||||||
Calls a database stored procedure with the given name and optional sequence
|
Calls a database stored procedure with the given name. A sequence
|
||||||
of input parameters.
|
(``params``) or dictionary (``kparams``) of input parameters may be
|
||||||
|
provided. Most databases don't support ``kparams``. Of Django's built-in
|
||||||
|
backends, only Oracle supports it.
|
||||||
|
|
||||||
For example, given this stored procedure in an Oracle database:
|
For example, given this stored procedure in an Oracle database:
|
||||||
|
|
||||||
@ -372,3 +374,7 @@ Calling stored procedures
|
|||||||
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.callproc('test_procedure', [1, 'test'])
|
cursor.callproc('test_procedure', [1, 'test'])
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0
|
||||||
|
|
||||||
|
The ``kparams`` argument was added.
|
||||||
|
@ -3,8 +3,9 @@ from decimal import Decimal, Rounded
|
|||||||
|
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
from django.db.backends.utils import format_number, truncate_name
|
from django.db.backends.utils import format_number, truncate_name
|
||||||
|
from django.db.utils import NotSupportedError
|
||||||
from django.test import (
|
from django.test import (
|
||||||
SimpleTestCase, TransactionTestCase, skipUnlessDBFeature,
|
SimpleTestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -53,13 +54,13 @@ class TestUtils(SimpleTestCase):
|
|||||||
class CursorWrapperTests(TransactionTestCase):
|
class CursorWrapperTests(TransactionTestCase):
|
||||||
available_apps = []
|
available_apps = []
|
||||||
|
|
||||||
def _test_procedure(self, procedure_sql, params, param_types):
|
def _test_procedure(self, procedure_sql, params, param_types, kparams=None):
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute(procedure_sql)
|
cursor.execute(procedure_sql)
|
||||||
# Use a new cursor because in MySQL a procedure can't be used in the
|
# Use a new cursor because in MySQL a procedure can't be used in the
|
||||||
# same cursor in which it was created.
|
# same cursor in which it was created.
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.callproc('test_procedure', params)
|
cursor.callproc('test_procedure', params, kparams)
|
||||||
with connection.schema_editor() as editor:
|
with connection.schema_editor() as editor:
|
||||||
editor.remove_procedure('test_procedure', param_types)
|
editor.remove_procedure('test_procedure', param_types)
|
||||||
|
|
||||||
@ -70,3 +71,14 @@ class CursorWrapperTests(TransactionTestCase):
|
|||||||
@skipUnlessDBFeature('create_test_procedure_with_int_param_sql')
|
@skipUnlessDBFeature('create_test_procedure_with_int_param_sql')
|
||||||
def test_callproc_with_int_params(self):
|
def test_callproc_with_int_params(self):
|
||||||
self._test_procedure(connection.features.create_test_procedure_with_int_param_sql, [1], ['INTEGER'])
|
self._test_procedure(connection.features.create_test_procedure_with_int_param_sql, [1], ['INTEGER'])
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('create_test_procedure_with_int_param_sql', 'supports_callproc_kwargs')
|
||||||
|
def test_callproc_kparams(self):
|
||||||
|
self._test_procedure(connection.features.create_test_procedure_with_int_param_sql, [], ['INTEGER'], {'P_I': 1})
|
||||||
|
|
||||||
|
@skipIfDBFeature('supports_callproc_kwargs')
|
||||||
|
def test_unsupported_callproc_kparams_raises_error(self):
|
||||||
|
msg = 'Keyword parameters for callproc are not supported on this database backend.'
|
||||||
|
with self.assertRaisesMessage(NotSupportedError, msg):
|
||||||
|
with connection.cursor() as cursor:
|
||||||
|
cursor.callproc('test_procedure', [], {'P_I': 1})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user