From 752856530f94cd3579813d6474020459cc80730a Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 23 Mar 2010 14:08:31 +0000 Subject: [PATCH] [1.1.X] Fixed #12612 -- Corrected handling of parameter formatting in SQLite backend so that executemany raises exceptions when bad parameter counts are provided. Thanks to Niels for the report, and Gabriel Hurley for the help narrowing down the problem. Backport of r12836 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12837 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/backends/sqlite3/base.py | 18 +++++++++--------- tests/regressiontests/backends/tests.py | 13 +++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index b7e071d077..ec2e43aa52 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -7,6 +7,8 @@ Python 2.5 and later can use a pysqlite2 module or the sqlite3 module in the standard library. """ +import re + from django.db.backends import * from django.db.backends.signals import connection_created from django.db.backends.sqlite3.client import DatabaseClient @@ -186,6 +188,8 @@ class DatabaseWrapper(BaseDatabaseWrapper): if self.settings_dict['DATABASE_NAME'] != ":memory:": BaseDatabaseWrapper.close(self) +FORMAT_QMARK_REGEX = re.compile(r'(?![^%])%s') + class SQLiteCursorWrapper(Database.Cursor): """ Django uses "format" style placeholders, but pysqlite2 uses "qmark" style. @@ -193,19 +197,15 @@ class SQLiteCursorWrapper(Database.Cursor): you'll need to use "%%s". """ def execute(self, query, params=()): - query = self.convert_query(query, len(params)) + query = self.convert_query(query) return Database.Cursor.execute(self, query, params) def executemany(self, query, param_list): - try: - query = self.convert_query(query, len(param_list[0])) - return Database.Cursor.executemany(self, query, param_list) - except (IndexError,TypeError): - # No parameter list provided - return None + query = self.convert_query(query) + return Database.Cursor.executemany(self, query, param_list) - def convert_query(self, query, num_params): - return query % tuple("?" * num_params) + def convert_query(self, query): + return FORMAT_QMARK_REGEX.sub('?', query).replace('%%','%') def _sqlite_extract(lookup_type, dt): if dt is None: diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index a376cd8e87..da3f4ab17c 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -66,6 +66,19 @@ class DateQuotingTest(TestCase): classes = models.SchoolClass.objects.filter(last_updated__day=20) self.assertEqual(len(classes), 1) +class ParameterHandlingTest(TestCase): + def test_bad_parameter_count(self): + "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)" + cursor = connection.cursor() + query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % ( + connection.introspection.table_name_converter('backends_square'), + connection.ops.quote_name('root'), + connection.ops.quote_name('square') + )) + self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) + self.assertRaises(Exception, cursor.executemany, query, [(1,),]) + + def connection_created_test(sender, **kwargs): print 'connection_created signal'