Code

Ticket #12308: 12308.2.patch

File 12308.2.patch, 22.8 KB (added by aaugustin, 3 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

     
    245245  code are slightly emphasized. This change makes it easier to scan a stacktrace 
    246246  for issues in user code. 
    247247 
     248* :doc:`Tablespace support </topics/db/tablespaces>` in PostgreSQL. 
     249 
    248250* Customizable names for :meth:`~django.template.Library.simple_tag`. 
    249251 
    250252* 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

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

     
     1from django.db import connection 
     2from django.db import models 
     3from django.core.management.color import no_style  
     4from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature 
     5 
     6from models import Article, ArticleRef, Scientist, ScientistRef 
     7 
     8# Automatically created models 
     9Authors = Article._meta.get_field('authors').rel.through 
     10Reviewers = Article._meta.get_field('reviewers').rel.through 
     11 
     12# We can't test the DEFAULT_TABLESPACE and DEFAULT_INDEX_TABLESPACE settings 
     13# because they're evaluated when the model class is defined. As a consequence, 
     14# @override_settings doesn't work. 
     15 
     16def sql_for_table(model): 
     17    return '\n'.join(connection.creation.sql_create_model(model, no_style())[0]) 
     18 
     19def sql_for_index(model): 
     20    return '\n'.join(connection.creation.sql_indexes_for_model(model, no_style())) 
     21 
     22 
     23class TablespacesTests(TestCase): 
     24 
     25    def setUp(self): 
     26        for model in Article, Authors, Reviewers, Scientist: 
     27            model._meta.managed = True 
     28 
     29    def tearDown(self): 
     30        for model in Article, Authors, Reviewers, Scientist: 
     31            model._meta.managed = False 
     32 
     33    def assertNumContains(self, haystack, needle, count): 
     34        real_count = haystack.count(needle) 
     35        self.assertEqual(real_count, count, "Found %d instances of '%s', " 
     36                "expected %d" % (real_count, needle, count)) 
     37 
     38    @skipUnlessDBFeature('supports_tablespaces') 
     39    def test_tablespace_for_model(self): 
     40        # 1 for the table + 1 for the index on the primary key 
     41        self.assertNumContains(sql_for_table(Scientist), 'tbl_tbsp', 2) 
     42 
     43    @skipIfDBFeature('supports_tablespaces') 
     44    def test_tablespace_ignored_for_model(self): 
     45        # No tablespace-related SQL 
     46        self.assertEqual(sql_for_table(Scientist), 
     47                         sql_for_table(ScientistRef).replace('ref', '')) 
     48 
     49    @skipUnlessDBFeature('supports_tablespaces') 
     50    def test_tablespace_for_indexed_field(self): 
     51        # 1 for the table + 1 for the primary key + 1 for the index on name 
     52        self.assertNumContains(sql_for_table(Article), 'tbl_tbsp', 3) 
     53        # 1 for the index on reference 
     54        self.assertNumContains(sql_for_table(Article), 'idx_tbsp', 1) 
     55 
     56    @skipIfDBFeature('supports_tablespaces') 
     57    def test_tablespace_ignored_for_indexed_field(self): 
     58        # No tablespace-related SQL 
     59        self.assertEqual(sql_for_table(Article), 
     60                         sql_for_table(ArticleRef).replace('ref', '')) 
     61 
     62    @skipUnlessDBFeature('supports_tablespaces') 
     63    def test_tablespace_for_many_to_many_field(self): 
     64        # The join table of the ManyToManyField always goes to the tablespace 
     65        # of the model. 
     66        self.assertNumContains(sql_for_table(Authors), 'tbl_tbsp', 2) 
     67        self.assertNumContains(sql_for_table(Authors), 'idx_tbsp', 0) 
     68        # The ManyToManyField declares no db_tablespace, indexes for the two 
     69        # foreign keys in the join table go to the tablespace of the model. 
     70        self.assertNumContains(sql_for_index(Authors), 'tbl_tbsp', 2) 
     71        self.assertNumContains(sql_for_index(Authors), 'idx_tbsp', 0) 
     72 
     73        # The join table of the ManyToManyField always goes to the tablespace 
     74        # of the model. 
     75        self.assertNumContains(sql_for_table(Reviewers), 'tbl_tbsp', 2) 
     76        self.assertNumContains(sql_for_table(Reviewers), 'idx_tbsp', 0) 
     77        # The ManyToManyField declares db_tablespace, indexes for the two 
     78        # foreign keys in the join table go to this tablespace. 
     79        self.assertNumContains(sql_for_index(Reviewers), 'tbl_tbsp', 0) 
     80        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 setUp and tearDown). 
     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

     
    7171    can_defer_constraint_checks = True 
    7272    has_select_for_update = True 
    7373    has_select_for_update_nowait = True 
     74    supports_tablespaces = True 
    7475 
    7576 
    7677class 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))