Fixed #19954 -- Fixed MySQL _last_executed decoding

Queries can contain binary data undecodable with utf-8. In this
case, using the 'replace' errors mode when decoding seems like
an acceptable representation of the query.
Thanks Marcel Ryser for the report.
This commit is contained in:
Claude Paroz 2013-03-28 20:00:59 +01:00
parent ef348b5298
commit 86b1c31689
3 changed files with 8 additions and 7 deletions

View File

@ -37,7 +37,7 @@ from django.db.backends.mysql.client import DatabaseClient
from django.db.backends.mysql.creation import DatabaseCreation from django.db.backends.mysql.creation import DatabaseCreation
from django.db.backends.mysql.introspection import DatabaseIntrospection from django.db.backends.mysql.introspection import DatabaseIntrospection
from django.db.backends.mysql.validation import DatabaseValidation from django.db.backends.mysql.validation import DatabaseValidation
from django.utils.encoding import force_str from django.utils.encoding import force_str, force_text
from django.utils.safestring import SafeBytes, SafeText from django.utils.safestring import SafeBytes, SafeText
from django.utils import six from django.utils import six
from django.utils import timezone from django.utils import timezone
@ -270,7 +270,7 @@ class DatabaseOperations(BaseDatabaseOperations):
# With MySQLdb, cursor objects have an (undocumented) "_last_executed" # With MySQLdb, cursor objects have an (undocumented) "_last_executed"
# attribute where the exact query sent to the database is saved. # attribute where the exact query sent to the database is saved.
# See MySQLdb/cursors.py in the source distribution. # See MySQLdb/cursors.py in the source distribution.
return cursor._last_executed.decode('utf-8') return force_text(cursor._last_executed, errors='replace')
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

View File

@ -62,6 +62,7 @@ class Post(models.Model):
class Reporter(models.Model): class Reporter(models.Model):
first_name = models.CharField(max_length=30) first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
raw_data = models.BinaryField()
def __str__(self): def __str__(self):
return "%s %s" % (self.first_name, self.last_name) return "%s %s" % (self.first_name, self.last_name)

View File

@ -157,18 +157,18 @@ class DateQuotingTest(TestCase):
class LastExecutedQueryTest(TestCase): class LastExecutedQueryTest(TestCase):
def test_debug_sql(self): def test_debug_sql(self):
list(models.Tag.objects.filter(name="test")) list(models.Reporter.objects.filter(first_name="test"))
sql = connection.queries[-1]['sql'].lower() sql = connection.queries[-1]['sql'].lower()
self.assertIn("select", sql) self.assertIn("select", sql)
self.assertIn(models.Tag._meta.db_table, sql) self.assertIn(models.Reporter._meta.db_table, sql)
def test_query_encoding(self): def test_query_encoding(self):
""" """
Test that last_executed_query() returns an Unicode string Test that last_executed_query() returns an Unicode string
""" """
tags = models.Tag.objects.extra(select={'föö': 1}) persons = models.Reporter.objects.filter(raw_data=b'\x00\x46 \xFE').extra(select={'föö': 1})
sql, params = tags.query.sql_with_params() sql, params = persons.query.sql_with_params()
cursor = tags.query.get_compiler('default').execute_sql(None) cursor = persons.query.get_compiler('default').execute_sql(None)
last_sql = cursor.db.ops.last_executed_query(cursor, sql, params) last_sql = cursor.db.ops.last_executed_query(cursor, sql, params)
self.assertTrue(isinstance(last_sql, six.text_type)) self.assertTrue(isinstance(last_sql, six.text_type))