diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py
index 958982b..928311e 100644
a
|
b
|
class BaseDatabaseOperations(object):
|
684 | 684 | to_unicode = lambda s: force_text(s, strings_only=True, errors='replace') |
685 | 685 | if isinstance(params, (list, tuple)): |
686 | 686 | u_params = tuple(to_unicode(val) for val in params) |
| 687 | elif params is None: |
| 688 | u_params = () |
687 | 689 | else: |
688 | 690 | u_params = dict((to_unicode(k), to_unicode(v)) for k, v in params.items()) |
689 | 691 | |
diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py
index fc2ff31..bc1699f 100644
a
|
b
|
class CursorWrapper(object):
|
115 | 115 | |
116 | 116 | def execute(self, query, args=None): |
117 | 117 | try: |
| 118 | # args is None means no string interpolation |
118 | 119 | return self.cursor.execute(query, args) |
119 | 120 | except Database.IntegrityError as e: |
120 | 121 | six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2]) |
diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py
index a9ae025..f4b099d 100644
a
|
b
|
class FormatStylePlaceholderCursor(object):
|
748 | 748 | params = [] |
749 | 749 | else: |
750 | 750 | params = self._format_params(params) |
751 | | args = [(':arg%d' % i) for i in range(len(params))] |
| 751 | args = [(':arg%d' % i) for i in range(len(params))] |
752 | 752 | # cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it |
753 | 753 | # it does want a trailing ';' but not a trailing '/'. However, these |
754 | 754 | # characters must be included in the original query in case the query |
755 | 755 | # is being passed to SQL*Plus. |
756 | 756 | if query.endswith(';') or query.endswith('/'): |
757 | 757 | query = query[:-1] |
758 | | query = convert_unicode(query % tuple(args), self.charset) |
| 758 | if params is None: |
| 759 | query = convert_unicode(query, self.charset) |
| 760 | else: |
| 761 | query = convert_unicode(query % tuple(args), self.charset) |
759 | 762 | self._guess_input_sizes([params]) |
760 | 763 | try: |
761 | 764 | return self.cursor.execute(query, self._param_generator(params)) |
diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py
index 85a0991..b791835 100644
a
|
b
|
class CursorWrapper(object):
|
51 | 51 | |
52 | 52 | def execute(self, query, args=None): |
53 | 53 | try: |
| 54 | # args is None means no string interpolation |
54 | 55 | return self.cursor.execute(query, args) |
55 | 56 | except Database.IntegrityError as e: |
56 | 57 | six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2]) |
diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py
index 7ddaaf8..02c3a2b 100644
a
|
b
|
class SQLiteCursorWrapper(Database.Cursor):
|
396 | 396 | This fixes it -- but note that if you want to use a literal "%s" in a query, |
397 | 397 | you'll need to use "%%s". |
398 | 398 | """ |
399 | | def execute(self, query, params=()): |
400 | | query = self.convert_query(query) |
| 399 | def execute(self, query, params=None): |
| 400 | if params is not None: |
| 401 | query = self.convert_query(query) |
401 | 402 | try: |
| 403 | if params is None: |
| 404 | return Database.Cursor.execute(self, query) |
402 | 405 | return Database.Cursor.execute(self, query, params) |
403 | 406 | except Database.IntegrityError as e: |
404 | 407 | six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2]) |
… |
… |
class SQLiteCursorWrapper(Database.Cursor):
|
415 | 418 | six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2]) |
416 | 419 | |
417 | 420 | def convert_query(self, query): |
418 | | return FORMAT_QMARK_REGEX.sub('?', query).replace('%%','%') |
| 421 | return FORMAT_QMARK_REGEX.sub('?', query).replace('%%', '%') |
419 | 422 | |
420 | 423 | def _sqlite_date_extract(lookup_type, dt): |
421 | 424 | if dt is None: |
diff --git a/django/db/backends/util.py b/django/db/backends/util.py
index 1ba2306..5adf940 100644
a
|
b
|
class CursorWrapper(object):
|
34 | 34 | |
35 | 35 | class CursorDebugWrapper(CursorWrapper): |
36 | 36 | |
37 | | def execute(self, sql, params=()): |
| 37 | def execute(self, sql, params=None): |
38 | 38 | self.set_dirty() |
39 | 39 | start = time() |
40 | 40 | try: |
| 41 | if params is None: |
| 42 | # params default might be backend specific |
| 43 | return self.cursor.execute(sql) |
41 | 44 | return self.cursor.execute(sql, params) |
42 | 45 | finally: |
43 | 46 | stop = time() |
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
index 0e1700b..ce185e3 100644
a
|
b
|
class ConnectionCreatedSignalTest(TestCase):
|
353 | 353 | |
354 | 354 | |
355 | 355 | class EscapingChecks(TestCase): |
| 356 | """ Each test is run once with normal cursor, and once with |
| 357 | CursorDebugWrapper activated by setting DEBUG to True |
| 358 | """ |
| 359 | def test_paramless_no_escaping(self): |
| 360 | cursor = connection.cursor() |
| 361 | cursor.execute("SELECT '%s'") |
| 362 | self.assertEquals(cursor.fetchall()[0][0], '%s') |
| 363 | |
| 364 | @override_settings(DEBUG=True) |
| 365 | def test_paramless_no_escaping_debug(self): |
| 366 | self.test_paramless_no_escaping() |
| 367 | |
| 368 | def test_parameter_escaping(self): |
| 369 | cursor = connection.cursor() |
| 370 | cursor.execute("SELECT '%%', %s", ('%d',)) |
| 371 | self.assertEquals(cursor.fetchall()[0], ('%', '%d')) |
| 372 | |
| 373 | @override_settings(DEBUG=True) |
| 374 | def test_parameter_escaping_debug(self): |
| 375 | self.test_parameter_escaping() |
356 | 376 | |
357 | 377 | @unittest.skipUnless(connection.vendor == 'sqlite', |
358 | 378 | "This is a sqlite-specific issue") |
359 | | def test_parameter_escaping(self): |
| 379 | def test_sqlite_parameter_escaping(self): |
360 | 380 | #13648: '%s' escaping support for sqlite3 |
361 | 381 | cursor = connection.cursor() |
362 | | response = cursor.execute( |
363 | | "select strftime('%%s', date('now'))").fetchall()[0][0] |
364 | | self.assertNotEqual(response, None) |
| 382 | cursor.execute("select strftime('%s', date('now'))") |
| 383 | response = cursor.fetchall()[0][0] |
365 | 384 | # response should be an non-zero integer |
366 | 385 | self.assertTrue(int(response)) |
367 | 386 | |
| 387 | @unittest.skipUnless(connection.vendor == 'sqlite', |
| 388 | "This is a sqlite-specific issue") |
| 389 | @override_settings(DEBUG=True) |
| 390 | def test_sqlite_parameter_escaping_debug(self): |
| 391 | self.test_sqlite_parameter_escaping() |
| 392 | |
368 | 393 | |
369 | 394 | class SqlliteAggregationTests(TestCase): |
370 | 395 | """ |