/tests/regressiontests/backends/tests.py
Python | 288 lines | 209 code | 31 blank | 48 comment | 8 complexity | 9e99731ea2eb98bcf985ebb057cd8323 MD5 | raw file
1# -*- coding: utf-8 -*- 2# Unit and doctests for specific database backends. 3import datetime 4 5from django.core.management.color import no_style 6from django.db import backend, connection, connections, DEFAULT_DB_ALIAS, IntegrityError 7from django.db.backends.signals import connection_created 8from django.db.backends.postgresql import version as pg_version 9from django.test import TestCase, skipUnlessDBFeature, TransactionTestCase 10from django.utils import unittest 11 12from regressiontests.backends import models 13 14class OracleChecks(unittest.TestCase): 15 16 @unittest.skipUnless(connection.vendor == 'oracle', 17 "No need to check Oracle cursor semantics") 18 def test_dbms_session(self): 19 # If the backend is Oracle, test that we can call a standard 20 # stored procedure through our cursor wrapper. 21 convert_unicode = backend.convert_unicode 22 cursor = connection.cursor() 23 cursor.callproc(convert_unicode('DBMS_SESSION.SET_IDENTIFIER'), 24 [convert_unicode('_django_testing!'),]) 25 26 @unittest.skipUnless(connection.vendor == 'oracle', 27 "No need to check Oracle cursor semantics") 28 def test_cursor_var(self): 29 # If the backend is Oracle, test that we can pass cursor variables 30 # as query parameters. 31 cursor = connection.cursor() 32 var = cursor.var(backend.Database.STRING) 33 cursor.execute("BEGIN %s := 'X'; END; ", [var]) 34 self.assertEqual(var.getvalue(), 'X') 35 36 @unittest.skipUnless(connection.vendor == 'oracle', 37 "No need to check Oracle cursor semantics") 38 def test_long_string(self): 39 # If the backend is Oracle, test that we can save a text longer 40 # than 4000 chars and read it properly 41 c = connection.cursor() 42 c.execute('CREATE TABLE ltext ("TEXT" NCLOB)') 43 long_str = ''.join([unicode(x) for x in xrange(4000)]) 44 c.execute('INSERT INTO ltext VALUES (%s)',[long_str]) 45 c.execute('SELECT text FROM ltext') 46 row = c.fetchone() 47 self.assertEqual(long_str, row[0].read()) 48 c.execute('DROP TABLE ltext') 49 50 @unittest.skipUnless(connection.vendor == 'oracle', 51 "No need to check Oracle connection semantics") 52 def test_client_encoding(self): 53 # If the backend is Oracle, test that the client encoding is set 54 # correctly. This was broken under Cygwin prior to r14781. 55 c = connection.cursor() # Ensure the connection is initialized. 56 self.assertEqual(connection.connection.encoding, "UTF-8") 57 self.assertEqual(connection.connection.nencoding, "UTF-8") 58 59class DateQuotingTest(TestCase): 60 61 def test_django_date_trunc(self): 62 """ 63 Test the custom ``django_date_trunc method``, in particular against 64 fields which clash with strings passed to it (e.g. 'year') - see 65 #12818__. 66 67 __: http://code.djangoproject.com/ticket/12818 68 69 """ 70 updated = datetime.datetime(2010, 2, 20) 71 models.SchoolClass.objects.create(year=2009, last_updated=updated) 72 years = models.SchoolClass.objects.dates('last_updated', 'year') 73 self.assertEqual(list(years), [datetime.datetime(2010, 1, 1, 0, 0)]) 74 75 def test_django_extract(self): 76 """ 77 Test the custom ``django_extract method``, in particular against fields 78 which clash with strings passed to it (e.g. 'day') - see #12818__. 79 80 __: http://code.djangoproject.com/ticket/12818 81 82 """ 83 updated = datetime.datetime(2010, 2, 20) 84 models.SchoolClass.objects.create(year=2009, last_updated=updated) 85 classes = models.SchoolClass.objects.filter(last_updated__day=20) 86 self.assertEqual(len(classes), 1) 87 88 89class ParameterHandlingTest(TestCase): 90 def test_bad_parameter_count(self): 91 "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)" 92 cursor = connection.cursor() 93 query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % ( 94 connection.introspection.table_name_converter('backends_square'), 95 connection.ops.quote_name('root'), 96 connection.ops.quote_name('square') 97 )) 98 self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) 99 self.assertRaises(Exception, cursor.executemany, query, [(1,),]) 100 101# Unfortunately, the following tests would be a good test to run on all 102# backends, but it breaks MySQL hard. Until #13711 is fixed, it can't be run 103# everywhere (although it would be an effective test of #13711). 104class LongNameTest(TestCase): 105 """Long primary keys and model names can result in a sequence name 106 that exceeds the database limits, which will result in truncation 107 on certain databases (e.g., Postgres). The backend needs to use 108 the correct sequence name in last_insert_id and other places, so 109 check it is. Refs #8901. 110 """ 111 112 @skipUnlessDBFeature('supports_long_model_names') 113 def test_sequence_name_length_limits_create(self): 114 """Test creation of model with long name and long pk name doesn't error. Ref #8901""" 115 models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() 116 117 @skipUnlessDBFeature('supports_long_model_names') 118 def test_sequence_name_length_limits_m2m(self): 119 """Test an m2m save of a model with a long name and a long m2m field name doesn't error as on Django >=1.2 this now uses object saves. Ref #8901""" 120 obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create() 121 rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt') 122 obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj) 123 124 @skipUnlessDBFeature('supports_long_model_names') 125 def test_sequence_name_length_limits_flush(self): 126 """Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901""" 127 # A full flush is expensive to the full test, so we dig into the 128 # internals to generate the likely offending SQL and run it manually 129 130 # Some convenience aliases 131 VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 132 VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through 133 tables = [ 134 VLM._meta.db_table, 135 VLM_m2m._meta.db_table, 136 ] 137 sequences = [ 138 { 139 'column': VLM._meta.pk.column, 140 'table': VLM._meta.db_table 141 }, 142 ] 143 cursor = connection.cursor() 144 for statement in connection.ops.sql_flush(no_style(), tables, sequences): 145 cursor.execute(statement) 146 147class SequenceResetTest(TestCase): 148 def test_generic_relation(self): 149 "Sequence names are correct when resetting generic relations (Ref #13941)" 150 # Create an object with a manually specified PK 151 models.Post.objects.create(id=10, name='1st post', text='hello world') 152 153 # Reset the sequences for the database 154 cursor = connection.cursor() 155 commands = connections[DEFAULT_DB_ALIAS].ops.sequence_reset_sql(no_style(), [models.Post]) 156 for sql in commands: 157 cursor.execute(sql) 158 159 # If we create a new object now, it should have a PK greater 160 # than the PK we specified manually. 161 obj = models.Post.objects.create(name='New post', text='goodbye world') 162 self.assertTrue(obj.pk > 10) 163 164class PostgresVersionTest(TestCase): 165 def assert_parses(self, version_string, version): 166 self.assertEqual(pg_version._parse_version(version_string), version) 167 168 def test_parsing(self): 169 self.assert_parses("PostgreSQL 8.3 beta4", (8, 3, None)) 170 self.assert_parses("PostgreSQL 8.3", (8, 3, None)) 171 self.assert_parses("EnterpriseDB 8.3", (8, 3, None)) 172 self.assert_parses("PostgreSQL 8.3.6", (8, 3, 6)) 173 self.assert_parses("PostgreSQL 8.4beta1", (8, 4, None)) 174 self.assert_parses("PostgreSQL 8.3.1 on i386-apple-darwin9.2.2, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5478)", (8, 3, 1)) 175 176# Unfortunately with sqlite3 the in-memory test database cannot be 177# closed, and so it cannot be re-opened during testing, and so we 178# sadly disable this test for now. 179class ConnectionCreatedSignalTest(TestCase): 180 @skipUnlessDBFeature('test_db_allows_multiple_connections') 181 def test_signal(self): 182 data = {} 183 def receiver(sender, connection, **kwargs): 184 data["connection"] = connection 185 186 connection_created.connect(receiver) 187 connection.close() 188 cursor = connection.cursor() 189 self.assertTrue(data["connection"] is connection) 190 191 connection_created.disconnect(receiver) 192 data.clear() 193 cursor = connection.cursor() 194 self.assertTrue(data == {}) 195 196 197class EscapingChecks(TestCase): 198 199 @unittest.skipUnless(connection.vendor == 'sqlite', 200 "This is a sqlite-specific issue") 201 def test_parameter_escaping(self): 202 #13648: '%s' escaping support for sqlite3 203 cursor = connection.cursor() 204 response = cursor.execute( 205 "select strftime('%%s', date('now'))").fetchall()[0][0] 206 self.assertNotEqual(response, None) 207 # response should be an non-zero integer 208 self.assertTrue(int(response)) 209 210 211class BackendTestCase(TestCase): 212 def test_cursor_executemany(self): 213 #4896: Test cursor.executemany 214 cursor = connection.cursor() 215 qn = connection.ops.quote_name 216 opts = models.Square._meta 217 f1, f2 = opts.get_field('root'), opts.get_field('square') 218 query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' 219 % (connection.introspection.table_name_converter(opts.db_table), qn(f1.column), qn(f2.column))) 220 cursor.executemany(query, [(i, i**2) for i in range(-5, 6)]) 221 self.assertEqual(models.Square.objects.count(), 11) 222 for i in range(-5, 6): 223 square = models.Square.objects.get(root=i) 224 self.assertEqual(square.square, i**2) 225 226 #4765: executemany with params=[] does nothing 227 cursor.executemany(query, []) 228 self.assertEqual(models.Square.objects.count(), 11) 229 230 def test_unicode_fetches(self): 231 #6254: fetchone, fetchmany, fetchall return strings as unicode objects 232 qn = connection.ops.quote_name 233 models.Person(first_name="John", last_name="Doe").save() 234 models.Person(first_name="Jane", last_name="Doe").save() 235 models.Person(first_name="Mary", last_name="Agnelline").save() 236 models.Person(first_name="Peter", last_name="Parker").save() 237 models.Person(first_name="Clark", last_name="Kent").save() 238 opts2 = models.Person._meta 239 f3, f4 = opts2.get_field('first_name'), opts2.get_field('last_name') 240 query2 = ('SELECT %s, %s FROM %s ORDER BY %s' 241 % (qn(f3.column), qn(f4.column), connection.introspection.table_name_converter(opts2.db_table), 242 qn(f3.column))) 243 cursor = connection.cursor() 244 cursor.execute(query2) 245 self.assertEqual(cursor.fetchone(), (u'Clark', u'Kent')) 246 self.assertEqual(list(cursor.fetchmany(2)), [(u'Jane', u'Doe'), (u'John', u'Doe')]) 247 self.assertEqual(list(cursor.fetchall()), [(u'Mary', u'Agnelline'), (u'Peter', u'Parker')]) 248 249 250# We don't make these tests conditional because that means we would need to 251# check and differentiate between: 252# * MySQL+InnoDB, MySQL+MYISAM (something we currently can't do). 253# * if sqlite3 (if/once we get #14204 fixed) has referential integrity turned 254# on or not, something that would be controlled by runtime support and user 255# preference. 256# verify if its type is django.database.db.IntegrityError. 257 258class FkConstraintsTests(TransactionTestCase): 259 260 def setUp(self): 261 # Create a Reporter. 262 self.r = models.Reporter.objects.create(first_name='John', last_name='Smith') 263 264 def test_integrity_checks_on_creation(self): 265 """ 266 Try to create a model instance that violates a FK constraint. If it 267 fails it should fail with IntegrityError. 268 """ 269 a = models.Article(headline="This is a test", pub_date=datetime.datetime(2005, 7, 27), reporter_id=30) 270 try: 271 a.save() 272 except IntegrityError: 273 pass 274 275 def test_integrity_checks_on_update(self): 276 """ 277 Try to update a model instance introducing a FK constraint violation. 278 If it fails it should fail with IntegrityError. 279 """ 280 # Create an Article. 281 models.Article.objects.create(headline="Test article", pub_date=datetime.datetime(2010, 9, 4), reporter=self.r) 282 # Retrive it from the DB 283 a = models.Article.objects.get(headline="Test article") 284 a.reporter_id = 30 285 try: 286 a.save() 287 except IntegrityError: 288 pass