Ticket #8901: 8901-r13323.diff

File 8901-r13323.diff, 8.4 KB (added by russellm, 5 years ago)

Cleaup of Skaffen's patch

  • django/db/backends/postgresql/operations.py

    diff -r 90c15a5724dd django/db/backends/postgresql/operations.py
    a b  
    5454        return '%s'
    5555
    5656    def last_insert_id(self, cursor, table_name, pk_name):
    57         cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
     57        # Use pg_get_serial_sequence to get the underlying sequence name
     58        # from the table name and column name (available since PostgreSQL 8)
     59        cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name))
    5860        return cursor.fetchone()[0]
    5961
    6062    def no_limit_value(self):
     
    9092            for sequence_info in sequences:
    9193                table_name = sequence_info['table']
    9294                column_name = sequence_info['column']
    93                 if column_name and len(column_name) > 0:
    94                     sequence_name = '%s_%s_seq' % (table_name, column_name)
    95                 else:
    96                     sequence_name = '%s_id_seq' % table_name
    97                 sql.append("%s setval('%s', 1, false);" % \
     95                if not (column_name and len(column_name) > 0):
     96                    # This will be the case if it's an m2m using an autogenerated
     97                    # intermediate table (see BaseDatabaseIntrospection.sequence_list)
     98                    column_name = 'id'
     99                sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
    98100                    (style.SQL_KEYWORD('SELECT'),
    99                     style.SQL_FIELD(self.quote_name(sequence_name)))
     101                    style.SQL_TABLE(table_name),
     102                    style.SQL_FIELD(column_name))
    100103                )
    101104            return sql
    102105        else:
     
    110113            # Use `coalesce` to set the sequence for each model to the max pk value if there are records,
    111114            # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true
    112115            # if there are records (as the max pk value is already in use), otherwise set it to false.
     116            # Use pg_get_serial_sequence to get the underlying sequence name from the table name
     117            # and column name (available since PostgreSQL 8)
     118
    113119            for f in model._meta.local_fields:
    114120                if isinstance(f, models.AutoField):
    115                     output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
     121                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    116122                        (style.SQL_KEYWORD('SELECT'),
    117                         style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))),
     123                        style.SQL_TABLE(model._meta.db_table),
     124                        style.SQL_FIELD(f.column),
    118125                        style.SQL_FIELD(qn(f.column)),
    119126                        style.SQL_FIELD(qn(f.column)),
    120127                        style.SQL_KEYWORD('IS NOT'),
     
    123130                    break # Only one AutoField is allowed per model, so don't bother continuing.
    124131            for f in model._meta.many_to_many:
    125132                if not f.rel.through:
    126                     output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
     133                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    127134                        (style.SQL_KEYWORD('SELECT'),
    128                         style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
     135                        style.SQL_TABLE(model._meta.db_table),
     136                        style.SQL_FIELD('id'),
    129137                        style.SQL_FIELD(qn('id')),
    130138                        style.SQL_FIELD(qn('id')),
    131139                        style.SQL_KEYWORD('IS NOT'),
  • tests/regressiontests/backends/models.py

    diff -r 90c15a5724dd tests/regressiontests/backends/models.py
    a b  
    11from django.db import models
    22from django.db import connection
    33
     4
    45class Square(models.Model):
    56    root = models.IntegerField()
    67    square = models.PositiveIntegerField()
     
    89    def __unicode__(self):
    910        return "%s ** 2 == %s" % (self.root, self.square)
    1011
     12
    1113class Person(models.Model):
    1214    first_name = models.CharField(max_length=20)
    1315    last_name = models.CharField(max_length=20)
     
    1517    def __unicode__(self):
    1618        return u'%s %s' % (self.first_name, self.last_name)
    1719
     20
    1821class SchoolClass(models.Model):
    1922    year = models.PositiveIntegerField()
    2023    day = models.CharField(max_length=9, blank=True)
    2124    last_updated = models.DateTimeField()
    2225
     26
     27class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
     28    class Meta:
     29        # We need to use a short actual table name or
     30        # we hit issue #8548 which we're not testing!
     31        verbose_name = 'model_with_long_table_name'
     32    primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True)
     33    m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True)
     34
     35
    2336qn = connection.ops.quote_name
    2437
    2538__test__ = {'API_TESTS': """
  • tests/regressiontests/backends/tests.py

    diff -r 90c15a5724dd tests/regressiontests/backends/tests.py
    a b  
    11# -*- coding: utf-8 -*-
    22# Unit and doctests for specific database backends.
    33import datetime
    4 import models
    54import unittest
     5
     6from django.conf import settings
     7from django.core import management
     8from django.core.management.color import no_style
    69from django.db import backend, connection, DEFAULT_DB_ALIAS
    710from django.db.backends.signals import connection_created
    8 from django.conf import settings
    911from django.test import TestCase
    1012
     13from regressiontests.backends import models
     14
    1115class Callproc(unittest.TestCase):
    1216
    1317    def test_dbms_session(self):
     
    8892        self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),])
    8993        self.assertRaises(Exception, cursor.executemany, query, [(1,),])
    9094
     95class LongNameTest(TestCase):
     96    """Long primary keys and model names can result in a sequence name
     97    that exceeds the database limits, which will result in truncation
     98    on certain databases (e.g., Postgres). The backend needs to use
     99    the correct sequence name in last_insert_id and other places, so
     100    check it is. Refs #8901.
     101    """
     102
     103    def test_sequence_name_length_limits_create(self):
     104        """Test creation of model with long name and long pk name doesn't error. Ref #8901"""
     105        models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
     106
     107    def test_sequence_name_length_limits_m2m(self):
     108        """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"""
     109        obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
     110        rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt')
     111        obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj)
     112
     113    def test_sequence_name_length_limits_flush(self):
     114        """Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901"""
     115        # A full flush is expensive to the full test, so we dig into the
     116        # internals to generate the likely offending SQL and run it manually
     117
     118        # Some convenience aliases
     119        VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
     120        VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
     121        tables = [
     122            VLM._meta.db_table,
     123            VLM_m2m._meta.db_table,
     124        ]
     125        sequences = [
     126            {
     127                'column': VLM._meta.pk.attname,
     128                'table': VLM._meta.db_table
     129            },
     130        ]
     131        cursor = connection.cursor()
     132        for statement in connection.ops.sql_flush(no_style(), tables, sequences):
     133            cursor.execute(statement)
     134
    91135
    92136def connection_created_test(sender, **kwargs):
    93137    print 'connection_created signal'
Back to Top