Browse files

Fixed #14091 - be more correct about logging queries in connection.qu…


Thanks to Aymeric Augustin for figuring out how to make this work across
multiple databases.

git-svn-id: bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
jacobian committed Apr 22, 2011
1 parent 598032b commit 5b0e4e49d4ab5e976fbfdde70c525a13220f3259
@@ -60,6 +60,7 @@ answer newbie questions, and generally made Django that much better:
atlithorn <>
Jökull Sólberg Auðunsson <>
Arthur <>
Aymeric Augustin <>
David Avsajanishvili <>
Mike Axiak <>
@@ -191,6 +191,12 @@ def force_no_ordering(self):
def fulltext_search_sql(self, 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/ in the source distribution.
return cursor._last_executed
def no_limit_value(self):
# 2**64 - 1, as recommended by the MySQL documentation
return 18446744073709551615L
@@ -210,6 +210,11 @@ def field_cast_sql(self, db_type):
return "%s"
def last_executed_query(self, cursor, sql, params):
# The DB API definition does not define this attribute.
return cursor.statement
def last_insert_id(self, cursor, table_name, pk_name):
sq_name = self._get_sequence_name(table_name)
cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
@@ -202,9 +202,8 @@ def max_name_length(self):
return 63
def last_executed_query(self, cursor, sql, params):
# With psycopg2, cursor objects have a "query" attribute that is the
# exact query sent to the database. See docs here:
# The query attribute is a Psycopg extension to the DB API 2.0.
return cursor.query
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,
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
parameter quoting. Parameter quoting is performed by the database-specific
backend, and not all backends provide a way to retrieve the SQL after quoting.
Note that the SQL recorded here may be :ref:`incorrectly quoted under SQLite
.. versionadded:: 1.2
@@ -465,7 +465,7 @@ itself to versions newer than the ones included with your particular Python
binary distribution, if needed.
"Database is locked" errors
SQLite is meant to be a lightweight database, and thus can't support a high
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
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
@@ -2,6 +2,7 @@
# Unit and doctests for specific database backends.
import datetime
from django.conf import settings
from import no_style
from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError
from django.db.backends.signals import connection_created
@@ -85,6 +86,35 @@ def test_django_extract(self):
classes = models.SchoolClass.objects.filter(last_updated__day=20)
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):
def test_bad_parameter_count(self):

0 comments on commit 5b0e4e4

Please sign in to comment.