1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

[soc2010/query-refactor] Merged up to trunk r13350.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/query-refactor@13351 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2010-06-14 18:30:38 +00:00
parent 28499bbe36
commit 4f395e7527
6 changed files with 34 additions and 105 deletions

View File

@ -220,7 +220,6 @@ answer newbie questions, and generally made Django that much better:
Kieran Holland <http://www.kieranholland.com> Kieran Holland <http://www.kieranholland.com>
Sung-Jin Hong <serialx.net@gmail.com> Sung-Jin Hong <serialx.net@gmail.com>
Leo "hylje" Honkanen <sealage@gmail.com> Leo "hylje" Honkanen <sealage@gmail.com>
Matt Hoskins <skaffenuk@googlemail.com>
Tareque Hossain <http://www.codexn.com> Tareque Hossain <http://www.codexn.com>
Richard House <Richard.House@i-logue.com> Richard House <Richard.House@i-logue.com>
Robert Rock Howard <http://djangomojo.com/> Robert Rock Howard <http://djangomojo.com/>

View File

@ -1,5 +1,4 @@
from django.db.backends.creation import BaseDatabaseCreation from django.db.backends.creation import BaseDatabaseCreation
from django.db.backends.util import truncate_name
class DatabaseCreation(BaseDatabaseCreation): class DatabaseCreation(BaseDatabaseCreation):
# This dictionary maps Field objects to their associated PostgreSQL column # This dictionary maps Field objects to their associated PostgreSQL column
@ -52,7 +51,7 @@ class DatabaseCreation(BaseDatabaseCreation):
def get_index_sql(index_name, opclass=''): def get_index_sql(index_name, opclass=''):
return (style.SQL_KEYWORD('CREATE INDEX') + ' ' + return (style.SQL_KEYWORD('CREATE INDEX') + ' ' +
style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' + style.SQL_TABLE(qn(index_name)) + ' ' +
style.SQL_KEYWORD('ON') + ' ' + style.SQL_KEYWORD('ON') + ' ' +
style.SQL_TABLE(qn(db_table)) + ' ' + style.SQL_TABLE(qn(db_table)) + ' ' +
"(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) + "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) +

View File

@ -54,9 +54,7 @@ class DatabaseOperations(BaseDatabaseOperations):
return '%s' return '%s'
def last_insert_id(self, cursor, table_name, pk_name): def last_insert_id(self, cursor, table_name, pk_name):
# Use pg_get_serial_sequence to get the underlying sequence name cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
# from the table name and column name (available since PostgreSQL 8)
cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name))
return cursor.fetchone()[0] return cursor.fetchone()[0]
def no_limit_value(self): def no_limit_value(self):
@ -92,14 +90,13 @@ class DatabaseOperations(BaseDatabaseOperations):
for sequence_info in sequences: for sequence_info in sequences:
table_name = sequence_info['table'] table_name = sequence_info['table']
column_name = sequence_info['column'] column_name = sequence_info['column']
if not (column_name and len(column_name) > 0): if column_name and len(column_name) > 0:
# This will be the case if it's an m2m using an autogenerated sequence_name = '%s_%s_seq' % (table_name, column_name)
# intermediate table (see BaseDatabaseIntrospection.sequence_list) else:
column_name = 'id' sequence_name = '%s_id_seq' % table_name
sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \ sql.append("%s setval('%s', 1, false);" % \
(style.SQL_KEYWORD('SELECT'), (style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(table_name), style.SQL_FIELD(self.quote_name(sequence_name)))
style.SQL_FIELD(column_name))
) )
return sql return sql
else: else:
@ -113,15 +110,11 @@ class DatabaseOperations(BaseDatabaseOperations):
# Use `coalesce` to set the sequence for each model to the max pk value if there are records, # Use `coalesce` to set the sequence for each model to the max pk value if there are records,
# or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true
# if there are records (as the max pk value is already in use), otherwise set it to false. # if there are records (as the max pk value is already in use), otherwise set it to false.
# Use pg_get_serial_sequence to get the underlying sequence name from the table name
# and column name (available since PostgreSQL 8)
for f in model._meta.local_fields: for f in model._meta.local_fields:
if isinstance(f, models.AutoField): if isinstance(f, models.AutoField):
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'), (style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(model._meta.db_table), style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))),
style.SQL_FIELD(f.column),
style.SQL_FIELD(qn(f.column)), style.SQL_FIELD(qn(f.column)),
style.SQL_FIELD(qn(f.column)), style.SQL_FIELD(qn(f.column)),
style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('IS NOT'),
@ -130,10 +123,9 @@ class DatabaseOperations(BaseDatabaseOperations):
break # Only one AutoField is allowed per model, so don't bother continuing. break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many: for f in model._meta.many_to_many:
if not f.rel.through: if not f.rel.through:
output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
(style.SQL_KEYWORD('SELECT'), (style.SQL_KEYWORD('SELECT'),
style.SQL_TABLE(model._meta.db_table), style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
style.SQL_FIELD('id'),
style.SQL_FIELD(qn('id')), style.SQL_FIELD(qn('id')),
style.SQL_FIELD(qn('id')), style.SQL_FIELD(qn('id')),
style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('IS NOT'),

View File

@ -435,6 +435,8 @@ database-compatible values. A custom field might look something like::
class CustomModelField(models.Field): class CustomModelField(models.Field):
# ... # ...
def db_type(self):
# ...
def get_db_prep_save(self, value): def get_db_prep_save(self, value):
# ... # ...
@ -451,6 +453,9 @@ two extra methods have been introduced::
class CustomModelField(models.Field): class CustomModelField(models.Field):
# ... # ...
def db_type(self, connection):
# ...
def get_prep_value(self, value): def get_prep_value(self, value):
# ... # ...
@ -467,10 +472,10 @@ two extra methods have been introduced::
# ... # ...
These changes are required to support multiple databases -- These changes are required to support multiple databases --
``get_db_prep_*`` can no longer make any assumptions regarding the ``db_type`` and ``get_db_prep_*`` can no longer make any assumptions
database for which it is preparing. The ``connection`` argument now regarding the database for which it is preparing. The ``connection``
provides the preparation methods with the specific connection for argument now provides the preparation methods with the specific
which the value is being prepared. connection for which the value is being prepared.
The two new methods exist to differentiate general data-preparation The two new methods exist to differentiate general data-preparation
requirements from requirements that are database-specific. The requirements from requirements that are database-specific. The

View File

@ -1,7 +1,5 @@
from django.conf import settings
from django.db import models from django.db import models
from django.db import connection, DEFAULT_DB_ALIAS from django.db import connection
class Square(models.Model): class Square(models.Model):
root = models.IntegerField() root = models.IntegerField()
@ -10,7 +8,6 @@ class Square(models.Model):
def __unicode__(self): def __unicode__(self):
return "%s ** 2 == %s" % (self.root, self.square) return "%s ** 2 == %s" % (self.root, self.square)
class Person(models.Model): class Person(models.Model):
first_name = models.CharField(max_length=20) first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20)
@ -18,25 +15,11 @@ class Person(models.Model):
def __unicode__(self): def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name) return u'%s %s' % (self.first_name, self.last_name)
class SchoolClass(models.Model): class SchoolClass(models.Model):
year = models.PositiveIntegerField() year = models.PositiveIntegerField()
day = models.CharField(max_length=9, blank=True) day = models.CharField(max_length=9, blank=True)
last_updated = models.DateTimeField() last_updated = models.DateTimeField()
# Unfortunately, the following model breaks MySQL hard.
# Until #13711 is fixed, this test can't be run under MySQL.
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model):
class Meta:
# We need to use a short actual table name or
# we hit issue #8548 which we're not testing!
verbose_name = 'model_with_long_table_name'
primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField(primary_key=True)
charfield_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.CharField(max_length=100)
m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.ManyToManyField(Person,blank=True)
qn = connection.ops.quote_name qn = connection.ops.quote_name
__test__ = {'API_TESTS': """ __test__ = {'API_TESTS': """

View File

@ -1,17 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Unit and doctests for specific database backends. # Unit and doctests for specific database backends.
import datetime import datetime
import models
import unittest import unittest
from django.conf import settings
from django.core import management
from django.core.management.color import no_style
from django.db import backend, connection, DEFAULT_DB_ALIAS from django.db import backend, connection, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created from django.db.backends.signals import connection_created
from django.conf import settings
from django.test import TestCase from django.test import TestCase
from regressiontests.backends import models
class Callproc(unittest.TestCase): class Callproc(unittest.TestCase):
def test_dbms_session(self): def test_dbms_session(self):
@ -80,7 +76,6 @@ class DateQuotingTest(TestCase):
classes = models.SchoolClass.objects.filter(last_updated__day=20) classes = models.SchoolClass.objects.filter(last_updated__day=20)
self.assertEqual(len(classes), 1) self.assertEqual(len(classes), 1)
class ParameterHandlingTest(TestCase): class ParameterHandlingTest(TestCase):
def test_bad_parameter_count(self): def test_bad_parameter_count(self):
"An executemany call with too many/not enough parameters will raise an exception (Refs #12612)" "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)"
@ -93,50 +88,6 @@ class ParameterHandlingTest(TestCase):
self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),]) self.assertRaises(Exception, cursor.executemany, query, [(1,2,3),])
self.assertRaises(Exception, cursor.executemany, query, [(1,),]) self.assertRaises(Exception, cursor.executemany, query, [(1,),])
# Unfortunately, the following tests would be a good test to run on all
# backends, but it breaks MySQL hard. Until #13711 is fixed, it can't be run
# everywhere (although it would be an effective test of #13711).
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
class LongNameTest(TestCase):
"""Long primary keys and model names can result in a sequence name
that exceeds the database limits, which will result in truncation
on certain databases (e.g., Postgres). The backend needs to use
the correct sequence name in last_insert_id and other places, so
check it is. Refs #8901.
"""
def test_sequence_name_length_limits_create(self):
"""Test creation of model with long name and long pk name doesn't error. Ref #8901"""
models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
def test_sequence_name_length_limits_m2m(self):
"""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"""
obj = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ.objects.create()
rel_obj = models.Person.objects.create(first_name='Django', last_name='Reinhardt')
obj.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.add(rel_obj)
def test_sequence_name_length_limits_flush(self):
"""Test that sequence resetting as part of a flush with model with long name and long pk name doesn't error. Ref #8901"""
# A full flush is expensive to the full test, so we dig into the
# internals to generate the likely offending SQL and run it manually
# Some convenience aliases
VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
tables = [
VLM._meta.db_table,
VLM_m2m._meta.db_table,
]
sequences = [
{
'column': VLM._meta.pk.column,
'table': VLM._meta.db_table
},
]
cursor = connection.cursor()
for statement in connection.ops.sql_flush(no_style(), tables, sequences):
cursor.execute(statement)
def connection_created_test(sender, **kwargs): def connection_created_test(sender, **kwargs):
print 'connection_created signal' print 'connection_created signal'