Fixed #14091 - be more correct about logging queries in connection.queries.
Thanks to Aymeric Augustin for figuring out how to make this work across multiple databases. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16081 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
598032b8c4
commit
5b0e4e49d4
1
AUTHORS
1
AUTHORS
|
@ -60,6 +60,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
atlithorn <atlithorn@gmail.com>
|
atlithorn <atlithorn@gmail.com>
|
||||||
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
Jökull Sólberg Auðunsson <jokullsolberg@gmail.com>
|
||||||
Arthur <avandorp@gmail.com>
|
Arthur <avandorp@gmail.com>
|
||||||
|
Aymeric Augustin <aymeric.augustin@m4x.org>
|
||||||
av0000@mail.ru
|
av0000@mail.ru
|
||||||
David Avsajanishvili <avsd05@gmail.com>
|
David Avsajanishvili <avsd05@gmail.com>
|
||||||
Mike Axiak <axiak@mit.edu>
|
Mike Axiak <axiak@mit.edu>
|
||||||
|
|
|
@ -191,6 +191,12 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
def fulltext_search_sql(self, field_name):
|
def fulltext_search_sql(self, field_name):
|
||||||
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name
|
||||||
|
|
||||||
|
def last_executed_query(self, cursor, sql, params):
|
||||||
|
# With MySQLdb, cursor objects have an (undocumented) "_last_executed"
|
||||||
|
# attribute where the exact query sent to the database is saved.
|
||||||
|
# See MySQLdb/cursors.py in the source distribution.
|
||||||
|
return cursor._last_executed
|
||||||
|
|
||||||
def no_limit_value(self):
|
def no_limit_value(self):
|
||||||
# 2**64 - 1, as recommended by the MySQL documentation
|
# 2**64 - 1, as recommended by the MySQL documentation
|
||||||
return 18446744073709551615L
|
return 18446744073709551615L
|
||||||
|
|
|
@ -210,6 +210,11 @@ WHEN (new.%(col_name)s IS NULL)
|
||||||
else:
|
else:
|
||||||
return "%s"
|
return "%s"
|
||||||
|
|
||||||
|
def last_executed_query(self, cursor, sql, params):
|
||||||
|
# http://cx-oracle.sourceforge.net/html/cursor.html#Cursor.statement
|
||||||
|
# The DB API definition does not define this attribute.
|
||||||
|
return cursor.statement
|
||||||
|
|
||||||
def last_insert_id(self, cursor, table_name, pk_name):
|
def last_insert_id(self, cursor, table_name, pk_name):
|
||||||
sq_name = self._get_sequence_name(table_name)
|
sq_name = self._get_sequence_name(table_name)
|
||||||
cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
|
cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
|
||||||
|
|
|
@ -202,9 +202,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return 63
|
return 63
|
||||||
|
|
||||||
def last_executed_query(self, cursor, sql, params):
|
def last_executed_query(self, cursor, sql, params):
|
||||||
# With psycopg2, cursor objects have a "query" attribute that is the
|
# http://initd.org/psycopg/docs/cursor.html#cursor.query
|
||||||
# exact query sent to the database. See docs here:
|
# The query attribute is a Psycopg extension to the DB API 2.0.
|
||||||
# http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query
|
|
||||||
return cursor.query
|
return cursor.query
|
||||||
|
|
||||||
def return_insert_id(self):
|
def return_insert_id(self):
|
||||||
|
|
|
@ -22,9 +22,8 @@ of dictionaries in order of query execution. Each dictionary has the following::
|
||||||
|
|
||||||
``connection.queries`` includes all SQL statements -- INSERTs, UPDATES,
|
``connection.queries`` includes all SQL statements -- INSERTs, UPDATES,
|
||||||
SELECTs, etc. Each time your app hits the database, the query will be recorded.
|
SELECTs, etc. Each time your app hits the database, the query will be recorded.
|
||||||
Note that the raw SQL logged in ``connection.queries`` may not include
|
Note that the SQL recorded here may be :ref:`incorrectly quoted under SQLite
|
||||||
parameter quoting. Parameter quoting is performed by the database-specific
|
<sqlite-connection-queries>`.
|
||||||
backend, and not all backends provide a way to retrieve the SQL after quoting.
|
|
||||||
|
|
||||||
.. versionadded:: 1.2
|
.. versionadded:: 1.2
|
||||||
|
|
||||||
|
|
|
@ -465,7 +465,7 @@ itself to versions newer than the ones included with your particular Python
|
||||||
binary distribution, if needed.
|
binary distribution, if needed.
|
||||||
|
|
||||||
"Database is locked" errors
|
"Database is locked" errors
|
||||||
-----------------------------------------------
|
---------------------------
|
||||||
|
|
||||||
SQLite is meant to be a lightweight database, and thus can't support a high
|
SQLite is meant to be a lightweight database, and thus can't support a high
|
||||||
level of concurrency. ``OperationalError: database is locked`` errors indicate
|
level of concurrency. ``OperationalError: database is locked`` errors indicate
|
||||||
|
@ -506,6 +506,16 @@ If you're getting this error, you can solve it by:
|
||||||
SQLite does not support the ``SELECT ... FOR UPDATE`` syntax. Calling it will
|
SQLite does not support the ``SELECT ... FOR UPDATE`` syntax. Calling it will
|
||||||
have no effect.
|
have no effect.
|
||||||
|
|
||||||
|
.. _sqlite-connection-queries:
|
||||||
|
|
||||||
|
Parameters not quoted in ``connection.queries``
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
``sqlite3`` does not provide a way to retrieve the SQL after quoting and
|
||||||
|
substituting the parameters. Instead, the SQL in ``connection.queries`` is
|
||||||
|
rebuilt with a simple string interpolation. It may be incorrect. Make sure
|
||||||
|
you add quotes where necessary before copying a query into a SQLite shell.
|
||||||
|
|
||||||
.. _oracle-notes:
|
.. _oracle-notes:
|
||||||
|
|
||||||
Oracle notes
|
Oracle notes
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
# Unit and doctests for specific database backends.
|
# Unit and doctests for specific database backends.
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.core.management.color import no_style
|
from django.core.management.color import no_style
|
||||||
from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError
|
from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError
|
||||||
from django.db.backends.signals import connection_created
|
from django.db.backends.signals import connection_created
|
||||||
|
@ -85,6 +86,35 @@ class DateQuotingTest(TestCase):
|
||||||
classes = models.SchoolClass.objects.filter(last_updated__day=20)
|
classes = models.SchoolClass.objects.filter(last_updated__day=20)
|
||||||
self.assertEqual(len(classes), 1)
|
self.assertEqual(len(classes), 1)
|
||||||
|
|
||||||
|
class LastExecutedQueryTest(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# connection.queries will not be filled in without this
|
||||||
|
settings.DEBUG = True
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
settings.DEBUG = False
|
||||||
|
|
||||||
|
# There are no tests for the sqlite backend because it does not
|
||||||
|
# implement paramater escaping. See #14091.
|
||||||
|
|
||||||
|
@unittest.skipUnless(connection.vendor in ('oracle', 'postgresql'),
|
||||||
|
"These backends use the standard parameter escaping rules")
|
||||||
|
def test_parameter_escaping(self):
|
||||||
|
# check that both numbers and string are properly quoted
|
||||||
|
list(models.Tag.objects.filter(name="special:\\\"':", object_id=12))
|
||||||
|
sql = connection.queries[-1]['sql']
|
||||||
|
self.assertTrue("= 'special:\\\"'':' " in sql)
|
||||||
|
self.assertTrue("= 12 " in sql)
|
||||||
|
|
||||||
|
@unittest.skipUnless(connection.vendor == 'mysql',
|
||||||
|
"MySQL uses backslashes to escape parameters.")
|
||||||
|
def test_parameter_escaping(self):
|
||||||
|
list(models.Tag.objects.filter(name="special:\\\"':", object_id=12))
|
||||||
|
sql = connection.queries[-1]['sql']
|
||||||
|
# only this line is different from the test above
|
||||||
|
self.assertTrue("= 'special:\\\\\\\"\\':' " in sql)
|
||||||
|
self.assertTrue("= 12 " in sql)
|
||||||
|
|
||||||
class ParameterHandlingTest(TestCase):
|
class ParameterHandlingTest(TestCase):
|
||||||
def test_bad_parameter_count(self):
|
def test_bad_parameter_count(self):
|
||||||
|
|
Loading…
Reference in New Issue