Ticket #12308: 12308.3.patch

File 12308.3.patch, 23.4 KB (added by Aymeric Augustin, 13 years ago)
  • docs/index.txt

     
    6666      :doc:`Transactions <topics/db/transactions>` |
    6767      :doc:`Aggregation <topics/db/aggregation>` |
    6868      :doc:`Custom fields <howto/custom-model-fields>` |
    69       :doc:`Multiple databases <topics/db/multi-db>`
     69      :doc:`Multiple databases <topics/db/multi-db>` |
     70      :doc:`Tablespaces <topics/db/tablespaces>`
    7071
    7172    * **Other:**
    7273      :doc:`Supported databases <ref/databases>` |
  • docs/howto/custom-model-fields.txt

     
    218218    * :attr:`~django.db.models.Field.choices`
    219219    * :attr:`~django.db.models.Field.help_text`
    220220    * :attr:`~django.db.models.Field.db_column`
    221     * :attr:`~django.db.models.Field.db_tablespace`: Currently only used with
    222       the Oracle backend and only for index creation. You can usually ignore
    223       this option.
     221    * :attr:`~django.db.models.Field.db_tablespace`: Only for index creation,
     222      if the backend supports :doc:`tablespaces </topics/db/tablespaces>`. You
     223      can usually ignore this option.
    224224    * :attr:`~django.db.models.Field.auto_created`: True if the field was
    225225      automatically created, as for the `OneToOneField` used by model
    226226      inheritance. For advanced use only.
  • docs/topics/db/tablespaces.txt

     
     1===========
     2Tablespaces
     3===========
     4
     5A common paradigm for optimizing performance in database systems is the use of
     6`tablespaces`_ to organize disk layout.
     7
     8.. _`tablespaces`: http://en.wikipedia.org/wiki/Tablespace
     9
     10.. warning::
     11    Django does not create the tablespaces for you. Please refer to your
     12    database engine's documentation for details on creating and managing
     13    tablespaces.
     14
     15
     16Declaring tablespaces for tables
     17--------------------------------
     18
     19A tablespace can be specified for the table generated by a model by supplying
     20the :attr:`~django.db.models.Options.db_tablespace` option inside the model's
     21``class Meta``. This option also affects tables automatically created for
     22:class:`~django.db.models.ManyToManyField`\ s in the model.
     23
     24You can use the :setting:`DEFAULT_TABLESPACE` setting to specify a default value
     25for :attr:`~django.db.models.Options.db_tablespace`. These is useful for setting
     26a tablespace for the built-in Django apps and other applications whose code you
     27cannot control.
     28
     29Declaring tablespaces for indexes
     30---------------------------------
     31
     32You can pass the :attr:`~django.db.models.Field.db_tablespace` option to a
     33``Field`` constructor to specify an alternate tablespace for the ``Field``'s
     34column index. If no index would be created for the column, the option is
     35ignored.
     36
     37You can use the :setting:`DEFAULT_INDEX_TABLESPACE` setting to specify
     38a default value for :attr:`~django.db.models.Field.db_tablespace`.
     39
     40If :attr:`~django.db.models.Field.db_tablespace` isn't specified and you didn't
     41set :setting:`DEFAULT_INDEX_TABLESPACE`, the index is created in the same
     42tablespace as the tables.
     43
     44An example
     45----------
     46
     47.. code-block:: python
     48
     49    class TablespaceExample(models.Model):
     50        name = models.CharField(max_length=30, db_index=True, db_tablespace="indexes")
     51        data = models.CharField(max_length=255, db_index=True)
     52        edges = models.ManyToManyField(to="self", db_tablespace="indexes")
     53
     54        class Meta:
     55            db_tablespace = "tables"
     56
     57In this example, the tables generated by the ``TablespaceExample`` model (i.e.
     58the model table and the many-to-many table) would be stored in the ``tables``
     59tablespace. The index for the name field and the indexes on the many-to-many
     60table would be stored in the ``indexes`` tablespace. The ``data`` field would
     61also generate an index, but no tablespace for it is specified, so it would be
     62stored in the model tablespace ``tables`` by default.
     63
     64Database support
     65----------------
     66
     67PostgreSQL and Oracle support tablespaces. SQLite and MySQL don't.
     68
     69When you use a backend that lacks support for tablespaces, Django ignores all
     70tablespace-related options.
     71
     72.. versionchanged:: 1.4
     73    Since Django 1.4, the PostgreSQL backend supports tablespaces.
  • docs/topics/db/index.txt

     
    1717   sql
    1818   transactions
    1919   multi-db
     20   tablespaces
    2021   optimization
  • docs/releases/1.4.txt

     
    262262  code are slightly emphasized. This change makes it easier to scan a stacktrace
    263263  for issues in user code.
    264264
     265* :doc:`Tablespace support </topics/db/tablespaces>` in PostgreSQL.
     266
    265267* Customizable names for :meth:`~django.template.Library.simple_tag`.
    266268
    267269* In the documentation, a helpful :doc:`security overview </topics/security>`
  • docs/ref/models/fields.txt

     
    178178
    179179.. attribute:: Field.db_tablespace
    180180
    181 The name of the database tablespace to use for this field's index, if this field
    182 is indexed. The default is the project's :setting:`DEFAULT_INDEX_TABLESPACE`
    183 setting, if set, or the :attr:`~Field.db_tablespace` of the model, if any. If
    184 the backend doesn't support tablespaces, this option is ignored.
     181The name of the :doc:`database tablespace </topics/db/tablespaces>` to use for
     182this field's index, if this field is indexed. The default is the project's
     183:setting:`DEFAULT_INDEX_TABLESPACE` setting, if set, or the
     184:attr:`~Options.db_tablespace` of the model, if any. If the backend doesn't
     185support tablespaces for indexes, this option is ignored.
    185186
    186187``default``
    187188-----------
  • docs/ref/models/options.txt

     
    6666
    6767.. attribute:: Options.db_tablespace
    6868
    69     The name of the database tablespace to use for the model. If the backend
    70     doesn't support tablespaces, this option is ignored.
     69    The name of the :doc:`database tablespace </topics/db/tablespaces>` to use
     70    for this model. The default is the project's :setting:`DEFAULT_TABLESPACE`
     71    setting, if set. If the backend doesn't support tablespaces, this option is
     72    ignored.
    7173
    7274``get_latest_by``
    7375-----------------
  • docs/ref/databases.txt

     
    633633In this case, the Oracle backend will use a separate ``SELECT`` query to
    634634retrieve AutoField values.
    635635
    636 Tablespace options
    637 ------------------
    638 
    639 A common paradigm for optimizing performance in Oracle-based systems is the
    640 use of `tablespaces`_ to organize disk layout. The Oracle backend supports
    641 this use case by adding ``db_tablespace`` options to the ``Meta`` and
    642 ``Field`` classes.  (When you use a backend that lacks support for tablespaces,
    643 Django ignores these options.)
    644 
    645 .. _`tablespaces`: http://en.wikipedia.org/wiki/Tablespace
    646 
    647 A tablespace can be specified for the table(s) generated by a model by
    648 supplying the ``db_tablespace`` option inside the model's ``class Meta``.
    649 Additionally, you can pass the ``db_tablespace`` option to a ``Field``
    650 constructor to specify an alternate tablespace for the ``Field``'s column
    651 index. If no index would be created for the column, the ``db_tablespace``
    652 option is ignored::
    653 
    654     class TablespaceExample(models.Model):
    655         name = models.CharField(max_length=30, db_index=True, db_tablespace="indexes")
    656         data = models.CharField(max_length=255, db_index=True)
    657         edges = models.ManyToManyField(to="self", db_tablespace="indexes")
    658 
    659         class Meta:
    660             db_tablespace = "tables"
    661 
    662 In this example, the tables generated by the ``TablespaceExample`` model
    663 (i.e., the model table and the many-to-many table) would be stored in the
    664 ``tables`` tablespace. The index for the name field and the indexes on the
    665 many-to-many table would be stored in the ``indexes`` tablespace. The ``data``
    666 field would also generate an index, but no tablespace for it is specified, so
    667 it would be stored in the model tablespace ``tables`` by default.
    668 
    669 Use the :setting:`DEFAULT_TABLESPACE` and :setting:`DEFAULT_INDEX_TABLESPACE`
    670 settings to specify default values for the db_tablespace options.
    671 These are useful for setting a tablespace for the built-in Django apps and
    672 other applications whose code you cannot control.
    673 
    674 Django does not create the tablespaces for you. Please refer to `Oracle's
    675 documentation`_ for details on creating and managing tablespaces.
    676 
    677 .. _`Oracle's documentation`: http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_7003.htm#SQLRF01403
    678 
    679636Naming issues
    680637-------------
    681638
  • docs/ref/settings.txt

     
    857857Default: ``''`` (Empty string)
    858858
    859859Default tablespace to use for indexes on fields that don't specify
    860 one, if the backend supports it.
     860one, if the backend supports it (see :doc:`/topics/db/tablespaces`).
    861861
    862862.. setting:: DEFAULT_TABLESPACE
    863863
     
    867867Default: ``''`` (Empty string)
    868868
    869869Default tablespace to use for models that don't specify one, if the
    870 backend supports it.
     870backend supports it (see :doc:`/topics/db/tablespaces`).
    871871
    872872.. setting:: DISALLOWED_USER_AGENTS
    873873
  • tests/modeltests/tablespaces/tests.py

     
     1import copy
     2
     3from django.db import connection
     4from django.db import models
     5from django.db.models.loading import cache
     6from django.core.management.color import no_style
     7from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
     8
     9from models import Article, ArticleRef, Scientist, ScientistRef
     10
     11# Automatically created models
     12Authors = Article._meta.get_field('authors').rel.through
     13Reviewers = Article._meta.get_field('reviewers').rel.through
     14
     15# We can't test the DEFAULT_TABLESPACE and DEFAULT_INDEX_TABLESPACE settings
     16# because they're evaluated when the model class is defined. As a consequence,
     17# @override_settings doesn't work.
     18
     19def sql_for_table(model):
     20    return '\n'.join(connection.creation.sql_create_model(model, no_style())[0])
     21
     22def sql_for_index(model):
     23    return '\n'.join(connection.creation.sql_indexes_for_model(model, no_style()))
     24
     25
     26class TablespacesTests(TestCase):
     27
     28    @classmethod
     29    def setUpClass(cls):
     30        # The unmanaged models need to be removed after the test in order to
     31        # prevent bad interactions with other tests (proxy_models_inheritance).
     32        cls.old_app_models = copy.deepcopy(cache.app_models)
     33        cls.old_app_store = copy.deepcopy(cache.app_store)
     34
     35        for model in Article, Authors, Reviewers, Scientist:
     36            model._meta.managed = True
     37
     38    @classmethod
     39    def tearDownClass(cls):
     40        for model in Article, Authors, Reviewers, Scientist:
     41            model._meta.managed = False
     42
     43        cache.app_models = cls.old_app_models
     44        cache.app_store = cls.old_app_store
     45        cache._get_models_cache = {}
     46
     47    def assertNumContains(self, haystack, needle, count):
     48        real_count = haystack.count(needle)
     49        self.assertEqual(real_count, count, "Found %d instances of '%s', "
     50                "expected %d" % (real_count, needle, count))
     51
     52    @skipUnlessDBFeature('supports_tablespaces')
     53    def test_tablespace_for_model(self):
     54        # 1 for the table + 1 for the index on the primary key
     55        self.assertNumContains(sql_for_table(Scientist), 'tbl_tbsp', 2)
     56
     57    @skipIfDBFeature('supports_tablespaces')
     58    def test_tablespace_ignored_for_model(self):
     59        # No tablespace-related SQL
     60        self.assertEqual(sql_for_table(Scientist),
     61                         sql_for_table(ScientistRef).replace('ref', ''))
     62
     63    @skipUnlessDBFeature('supports_tablespaces')
     64    def test_tablespace_for_indexed_field(self):
     65        # 1 for the table + 1 for the primary key + 1 for the index on name
     66        self.assertNumContains(sql_for_table(Article), 'tbl_tbsp', 3)
     67        # 1 for the index on reference
     68        self.assertNumContains(sql_for_table(Article), 'idx_tbsp', 1)
     69
     70    @skipIfDBFeature('supports_tablespaces')
     71    def test_tablespace_ignored_for_indexed_field(self):
     72        # No tablespace-related SQL
     73        self.assertEqual(sql_for_table(Article),
     74                         sql_for_table(ArticleRef).replace('ref', ''))
     75
     76    @skipUnlessDBFeature('supports_tablespaces')
     77    def test_tablespace_for_many_to_many_field(self):
     78        # The join table of the ManyToManyField always goes to the tablespace
     79        # of the model.
     80        self.assertNumContains(sql_for_table(Authors), 'tbl_tbsp', 2)
     81        self.assertNumContains(sql_for_table(Authors), 'idx_tbsp', 0)
     82        # The ManyToManyField declares no db_tablespace, indexes for the two
     83        # foreign keys in the join table go to the tablespace of the model.
     84        self.assertNumContains(sql_for_index(Authors), 'tbl_tbsp', 2)
     85        self.assertNumContains(sql_for_index(Authors), 'idx_tbsp', 0)
     86
     87        # The join table of the ManyToManyField always goes to the tablespace
     88        # of the model.
     89        self.assertNumContains(sql_for_table(Reviewers), 'tbl_tbsp', 2)
     90        self.assertNumContains(sql_for_table(Reviewers), 'idx_tbsp', 0)
     91        # The ManyToManyField declares db_tablespace, indexes for the two
     92        # foreign keys in the join table go to this tablespace.
     93        self.assertNumContains(sql_for_index(Reviewers), 'tbl_tbsp', 0)
     94        self.assertNumContains(sql_for_index(Reviewers), 'idx_tbsp', 2)
  • tests/modeltests/tablespaces/models.py

     
     1from django.db import models
     2
     3# Since the test database doesn't have tablespaces, it's impossible for Django
     4# to create the tables for models where db_tablespace is set. To avoid this
     5# problem, we mark the models as unmanaged, and temporarily revert them to
     6# managed during each test (see setUpClass and tearDownClass).
     7
     8class ScientistRef(models.Model):
     9    name = models.CharField(max_length=50)
     10
     11class ArticleRef(models.Model):
     12    title = models.CharField(max_length=50, unique=True)
     13    code = models.CharField(max_length=50, unique=True)
     14    authors = models.ManyToManyField(ScientistRef, related_name='articles_written_set')
     15    reviewers = models.ManyToManyField(ScientistRef, related_name='articles_reviewed_set')
     16
     17class Scientist(models.Model):
     18    name = models.CharField(max_length=50)
     19    class Meta:
     20        db_tablespace = 'tbl_tbsp'
     21        managed = False
     22
     23class Article(models.Model):
     24    title = models.CharField(max_length=50, unique=True)
     25    code = models.CharField(max_length=50, unique=True, db_tablespace='idx_tbsp')
     26    authors = models.ManyToManyField(Scientist, related_name='articles_written_set')
     27    reviewers = models.ManyToManyField(Scientist, related_name='articles_reviewed_set', db_tablespace='idx_tbsp')
     28    class Meta:
     29        db_tablespace = 'tbl_tbsp'
     30        managed = False
  • django/db/models/fields/related.py

     
    988988        'managed': managed,
    989989        'auto_created': klass,
    990990        'app_label': klass._meta.app_label,
     991        'db_tablespace': klass._meta.db_tablespace,
    991992        'unique_together': (from_, to),
    992993        'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to},
    993994        'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to},
     
    996997    return type(name, (models.Model,), {
    997998        'Meta': meta,
    998999        '__module__': klass.__module__,
    999         from_: models.ForeignKey(klass, related_name='%s+' % name),
    1000         to: models.ForeignKey(to_model, related_name='%s+' % name)
     1000        from_: models.ForeignKey(klass, related_name='%s+' % name, db_tablespace=field.db_tablespace),
     1001        to: models.ForeignKey(to_model, related_name='%s+' % name, db_tablespace=field.db_tablespace)
    10011002    })
    10021003
    10031004class ManyToManyField(RelatedField, Field):
  • django/db/backends/oracle/base.py

     
    7878    supports_bitwise_or = False
    7979    can_defer_constraint_checks = True
    8080    ignores_nulls_in_unique_constraints = False
     81    supports_tablespaces = True
    8182
    8283class DatabaseOperations(BaseDatabaseOperations):
    8384    compiler_module = "django.db.backends.oracle.compiler"
     
    325326        return ''
    326327
    327328    def tablespace_sql(self, tablespace, inline=False):
    328         return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""),
    329             self.quote_name(tablespace))
     329        if inline:
     330            return "USING INDEX TABLESPACE %s" % self.quote_name(tablespace)
     331        else:
     332            return "TABLESPACE %s" % self.quote_name(tablespace)
    330333
    331334    def value_to_db_datetime(self, value):
    332335        # Oracle doesn't support tz-aware datetimes
  • django/db/backends/__init__.py

     
    364364    # date_interval_sql can properly handle mixed Date/DateTime fields and timedeltas
    365365    supports_mixed_date_datetime_comparisons = True
    366366
     367    # Does the backend support tablespaces? Default to False because it isn't
     368    # in the SQL standard.
     369    supports_tablespaces = False
     370
    367371    # Features that need to be confirmed at runtime
    368372    # Cache whether the confirmation has been performed.
    369373    _confirmed = False
     
    695699
    696700    def tablespace_sql(self, tablespace, inline=False):
    697701        """
    698         Returns the SQL that will be appended to tables or rows to define
    699         a tablespace. Returns '' if the backend doesn't use tablespaces.
     702        Returns the SQL that will be used in a query to define the tablespace.
     703
     704        Returns '' if the backend doesn't support tablespaces.
     705
     706        If inline is True, the SQL is appended to a row; otherwise it's appended
     707        to the entire CREATE TABLE or CREATE INDEX statement.
    700708        """
    701709        return ''
    702710
  • django/db/backends/postgresql_psycopg2/base.py

     
    7474    can_defer_constraint_checks = True
    7575    has_select_for_update = True
    7676    has_select_for_update_nowait = True
     77    supports_tablespaces = True
    7778
    7879
    7980class DatabaseWrapper(BaseDatabaseWrapper):
  • django/db/backends/postgresql_psycopg2/operations.py

     
    9999        else:
    100100            return []
    101101
     102    def tablespace_sql(self, tablespace, inline=False):
     103        if inline:
     104            return "USING INDEX TABLESPACE %s" % self.quote_name(tablespace)
     105        else:
     106            return "TABLESPACE %s" % self.quote_name(tablespace)
     107
    102108    def sequence_reset_sql(self, style, model_list):
    103109        from django.db import models
    104110        output = []
  • django/db/backends/postgresql_psycopg2/creation.py

     
    4444            db_table = model._meta.db_table
    4545            tablespace = f.db_tablespace or model._meta.db_tablespace
    4646            if tablespace:
    47                 sql = self.connection.ops.tablespace_sql(tablespace)
    48                 if sql:
    49                     tablespace_sql = ' ' + sql
    50                 else:
    51                     tablespace_sql = ''
     47                tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
     48                if tablespace_sql:
     49                    tablespace_sql = ' ' + tablespace_sql
    5250            else:
    5351                tablespace_sql = ''
    5452
  • django/db/backends/creation.py

     
    5757            if tablespace and f.unique:
    5858                # We must specify the index tablespace inline, because we
    5959                # won't be generating a CREATE INDEX statement for this field.
    60                 field_output.append(self.connection.ops.tablespace_sql(tablespace, inline=True))
     60                tablespace_sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
     61                if tablespace_sql:
     62                    field_output.append(tablespace_sql)
    6163            if f.rel:
    6264                ref_output, pending = self.sql_for_inline_foreign_key_references(f, known_models, style)
    6365                if pending:
     
    7476            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    7577        full_statement.append(')')
    7678        if opts.db_tablespace:
    77             full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
     79            tablespace_sql = self.connection.ops.tablespace_sql(opts.db_tablespace)
     80            if tablespace_sql:
     81                full_statement.append(tablespace_sql)
    7882        full_statement.append(';')
    7983        final_output.append('\n'.join(full_statement))
    8084
     
    149153            qn = self.connection.ops.quote_name
    150154            tablespace = f.db_tablespace or model._meta.db_tablespace
    151155            if tablespace:
    152                 sql = self.connection.ops.tablespace_sql(tablespace)
    153                 if sql:
    154                     tablespace_sql = ' ' + sql
    155                 else:
    156                     tablespace_sql = ''
     156                tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
     157                if tablespace_sql:
     158                    tablespace_sql = ' ' + tablespace_sql
    157159            else:
    158160                tablespace_sql = ''
    159161            i_name = '%s_%s' % (model._meta.db_table, self._digest(f.column))
Back to Top