PageRenderTime 116ms CodeModel.GetById 60ms app.highlight 33ms RepoModel.GetById 18ms app.codeStats 0ms

/tests/regressiontests/backends/tests.py

https://code.google.com/p/mango-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