Code

Ticket #14091: 14091.patch

File 14091.patch, 6.2 KB (added by aaugustin, 3 years ago)
  • docs/faq/models.txt

     
    2222 
    2323``connection.queries`` includes all SQL statements -- INSERTs, UPDATES, 
    2424SELECTs, etc. Each time your app hits the database, the query will be recorded. 
    25 Note that the raw SQL logged in ``connection.queries`` may not include 
    26 parameter quoting.  Parameter quoting is performed by the database-specific 
    27 backend, and not all backends provide a way to retrieve the SQL after quoting. 
    2825 
     26If you are using the postgresql backend, note that the raw SQL logged in 
     27``connection.queries`` may be invalid, because it does not include parameter 
     28quoting. Other database backends, like postgresql_psycopg2, retrieve 
     29the SQL after quoting or rebuild it, ensuring it is valid. 
     30 
    2931.. versionadded:: 1.2 
    3032 
    3133If you are using :doc:`multiple databases</topics/db/multi-db>`, you can use the 
  • django/db/backends/sqlite3/base.py

     
    104104    def drop_foreignkey_sql(self): 
    105105        return "" 
    106106 
     107    def last_executed_query(self, cursor, sql, params): 
     108        def quote_params(params): 
     109            sql = 'SELECT ' + ', '.join(['QUOTE(?)'] * len(params)) 
     110            # use a new cursor instead of the existing cursor wrapper 
     111            # to avoid recursive logging 
     112            return cursor.connection.execute(sql, params).fetchone() 
     113        if isinstance(params, (list, tuple)): 
     114            params = quote_params(params) 
     115        else: 
     116            params = dict(zip(params.keys(), quote_params(params.values()))) 
     117        return super(DatabaseOperations, self).last_executed_query(cursor, sql, params) 
     118 
    107119    def pk_default_value(self): 
    108120        return 'NULL' 
    109121 
  • django/db/backends/mysql/base.py

     
    189189    def fulltext_search_sql(self, field_name): 
    190190        return 'MATCH (%s) AGAINST (%%s IN BOOLEAN MODE)' % field_name 
    191191 
     192    def last_executed_query(self, cursor, sql, params): 
     193        # With MySQLdb, cursor objects have an (undocumented) "_last_executed" 
     194        # attribute where the exact query sent to the database is saved. 
     195        # See MySQLdb/cursors.py in the source distribution. 
     196        return cursor._last_executed 
     197 
    192198    def no_limit_value(self): 
    193199        # 2**64 - 1, as recommended by the MySQL documentation 
    194200        return 18446744073709551615L 
  • django/db/backends/oracle/base.py

     
    208208        else: 
    209209            return "%s" 
    210210 
     211    def last_executed_query(self, cursor, sql, params): 
     212        # http://cx-oracle.sourceforge.net/html/cursor.html#Cursor.statement 
     213        # The DB API definition does not define this attribute. 
     214        return cursor.statement 
     215 
    211216    def last_insert_id(self, cursor, table_name, pk_name): 
    212217        sq_name = get_sequence_name(table_name) 
    213218        cursor.execute('SELECT "%s".currval FROM dual' % sq_name) 
  • django/db/backends/postgresql_psycopg2/base.py

     
    7373 
    7474class DatabaseOperations(PostgresqlDatabaseOperations): 
    7575    def last_executed_query(self, cursor, sql, params): 
    76         # With psycopg2, cursor objects have a "query" attribute that is the 
    77         # exact query sent to the database. See docs here: 
    78         # http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query 
     76        # http://initd.org/psycopg/docs/cursor.html#cursor.query 
     77        # The query attribute is a Psycopg extension to the DB API 2.0. 
    7978        return cursor.query 
    8079 
    8180    def return_insert_id(self): 
  • tests/regressiontests/backends/tests.py

     
    22# Unit and doctests for specific database backends. 
    33import datetime 
    44 
     5from django.conf import settings 
    56from django.core.management.color import no_style 
    67from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError 
    78from django.db.backends.signals import connection_created 
     
    8586        classes = models.SchoolClass.objects.filter(last_updated__day=20) 
    8687        self.assertEqual(len(classes), 1) 
    8788 
     89class LastExecutedQueryTest(TestCase): 
    8890 
     91    def setUp(self): 
     92        # connection.queries will not be filled in without this 
     93        settings.DEBUG = True 
     94 
     95    def tearDown(self): 
     96        settings.DEBUG = False 
     97 
     98    @unittest.skipUnless(connection.vendor in ('oracle', 'postgresql', 'sqlite'), 
     99                         "These backends use the standard parameter escaping rules") 
     100    def test_parameter_escaping(self): 
     101        # check that both numbers and string are properly quoted 
     102        list(models.Tag.objects.filter(name="special:\\\"':", object_id=12)) 
     103        sql = connection.queries[-1]['sql'] 
     104        self.assertTrue("= 'special:\\\"'':' " in sql) 
     105        self.assertTrue("= 12 " in sql) 
     106 
     107    @unittest.skipUnless(connection.vendor == 'mysql', 
     108                         "MySQL uses backslashes to escape parameters.") 
     109    def test_parameter_escaping(self): 
     110        list(models.Tag.objects.filter(name="special:\\\"':", object_id=12)) 
     111        sql = connection.queries[-1]['sql'] 
     112        # only this line is different from the test above 
     113        self.assertTrue("= 'special:\\\\\\\"\\':' " in sql) 
     114        self.assertTrue("= 12 " in sql) 
     115 
    89116class ParameterHandlingTest(TestCase): 
    90117    def test_bad_parameter_count(self): 
    91118        "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)"