/tests/regressiontests/backends/tests.py
Python | 288 lines | 209 code | 31 blank | 48 comment | 8 complexity | 9e99731ea2eb98bcf985ebb057cd8323 MD5 | raw file
Possible License(s): BSD-3-Clause
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