Changeset 3254

Show
Ignore:
Timestamp:
05/26/07 08:06:33 (1 year ago)
Author:
mscott
Message:

Merging branch for #55:

  • Add test to expose cascade delete corner case where duplicate UNASSIGNED values end up in an index before everything gets a chance to be deleted.
  • Add test to make sure key restriction relaxation and re-enforcement work as intended.
  • Add relax_all_indices method to schevo.extent.Extent.
  • Call relax_all_indices on an extent during cascade delete if a key collision is detected while setting fields to UNASSIGNED.
  • Add docs to SchevoPublicApi regarding relax_index and enforce_index.
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Schevo/doc/SchevoPublicApi.txt

    r3189 r3254  
    7272Extent instances 
    7373================ 
     74 
     75 
     76Relaxing and enforcing key restrictions 
     77--------------------------------------- 
     78 
     79Schevo has the unique ability to temporarily relax key restrictions 
     80while still ensuring that the database remains in a consistent state. 
     81 
     82Key restrictions may only be relaxed within the execution of a 
     83transaction. 
     84 
     85Given an extent `db.Foo` that has a key specification of `_key(field1, 
     86field2)`, relax that key restriction by calling `relax_index`:: 
     87 
     88    db.Foo.relax_index('field1', 'field2') 
     89 
     90Key restrictions that are relaxed within a transaction are relaxed for 
     91the entire execution of the transaction, or until the key is enforced 
     92within that transaction, or until that transaction ends, whichever 
     93comes first. 
     94 
     95When a key restriction is relaxed by a transaction, it remains relaxed 
     96for all sub-transactions, regardless of any request by those 
     97sub-transactions to enforce the key restriction. 
     98 
     99To re-enforce the key restriction before the end of a transaction's 
     100execution, call `enforce_index`:: 
     101 
     102    db.Foo.enforce_index('field1', 'field2') 
     103 
     104See the ``tests/test_relax_index.py`` test case for code examples. 
    74105 
    75106 
  • trunk/Schevo/schevo/database2.py

    r3251 r3254  
    136136            assert log(2, 'Result was', repr(retval)) 
    137137            # Enforce any indices relaxed by the transaction. 
    138             for extent_name, index_spec in tx._relaxed
     138            for extent_name, index_spec in frozenset(tx._relaxed)
    139139                assert log(2, 'Enforcing index', extent_name, index_spec) 
    140                 self._enforce_index(extent_name, index_spec) 
     140                self._enforce_index_field_ids(extent_name, *index_spec) 
    141141            # If the transaction must be executed with strict 
    142142            # validation, perform that validation now. 
     
    495495 
    496496    def _enforce_index(self, extent_name, *index_spec): 
     497        """Call _enforce_index after converting index_spec from field 
     498        names to field IDs.""" 
     499        extent_map = self._extent_map(extent_name) 
     500        index_spec = _field_ids(extent_map, index_spec) 
     501        return self._enforce_index_field_ids(extent_name, *index_spec) 
     502   
     503    def _enforce_index_field_ids(self, extent_name, *index_spec): 
    497504        """Validate and begin enforcing constraints on the specified 
    498505        index if it was relaxed within the currently-executing 
     
    502509            # No-op if called outside a transaction. 
    503510            return 
    504         # ID-ify the index_spec
     511        # Find the index to re-enforce
    505512        extent_map = self._extent_map(extent_name) 
    506         index_spec = _field_ids(extent_map, index_spec) 
    507         # Find the index to re-enforce. 
    508513        indices = extent_map['indices'] 
    509514        if index_spec not in indices: 
  • trunk/Schevo/schevo/extent.py

    r2738 r3254  
    168168        self._relax(self.name, *index_spec) 
    169169 
     170    def relax_all_indices(self): 
     171        """Relax all indices using `relax_index`.""" 
     172        for index_spec in self.key_spec: 
     173            self.relax_index(*index_spec) 
     174 
    170175 
    171176class ExtentExtenders(NamespaceExtension): 
  • trunk/Schevo/schevo/transaction.py

    r3245 r3254  
    1111from schevo.constant import (CASCADE, DEFAULT, REMOVE, RESTRICT, 
    1212                             UNASSIGN, UNASSIGNED) 
    13 from schevo.error import (DatabaseMismatch, DeleteRestricted, SchemaError, 
    14                           TransactionExpired, TransactionFieldsNotChanged, 
    15                           TransactionNotExecuted) 
     13from schevo.error import ( 
     14    DatabaseMismatch, 
     15    DeleteRestricted, 
     16    KeyCollision, 
     17    SchemaError, 
     18    TransactionExpired, 
     19    TransactionFieldsNotChanged, 
     20    TransactionNotExecuted, 
     21    ) 
    1622from schevo import field 
    1723from schevo.field import not_fget 
     
    465471            field_dump_map.update(new_dump_map) 
    466472            field_related_entity_map.update(new_related_entity_map) 
    467             extent_name = referrer._extent.name 
     473            extent = referrer._extent 
     474            extent_name = extent.name 
    468475            oid = referrer._oid 
    469             db._update_entity( 
    470                 extent_name, oid, field_dump_map, field_related_entity_map) 
     476            try: 
     477                db._update_entity( 
     478                    extent_name, oid, field_dump_map, field_related_entity_map) 
     479            except KeyCollision: 
     480                # Since it takes more time to relax an index, only do 
     481                # it when we find a key collision. 
     482                extent.relax_all_indices() 
     483                # Try the update again. 
     484                db._update_entity( 
     485                    extent_name, oid, field_dump_map, field_related_entity_map) 
    471486            referrers.add((extent_name, oid, referrer)) 
    472487        # Delete entities in a deterministic (sorted) fashion. 
  • trunk/Schevo/tests/test_on_delete.py

    r3233 r3254  
    541541 
    542542 
     543class BaseOnDeleteKeyRelax(CreatesSchema): 
     544 
     545    body = """ 
     546 
     547    class Foo(E.Entity): 
     548 
     549        bar = f.entity('Bar', on_delete=CASCADE) 
     550        baz = f.entity('Baz', on_delete=CASCADE) 
     551 
     552        _key(baz) 
     553 
     554        _sample_unittest = [ 
     555            ((1,), ((1,), 1)), 
     556            ((1,), ((1,), 2)), 
     557            ((2,), ((2,), 1)), 
     558            ((2,), ((2,), 2)), 
     559            ] 
     560         
     561 
     562    class Bar(E.Entity): 
     563 
     564        number = f.integer() 
     565 
     566        _key(number) 
     567 
     568        _sample_unittest = [ 
     569            (1,), 
     570            (2,), 
     571            ] 
     572 
     573 
     574    class Baz(E.Entity): 
     575 
     576        bar = f.entity('Bar', on_delete=CASCADE) 
     577        number = f.integer() 
     578 
     579        _key(bar, number) 
     580 
     581        _sample_unittest = [ 
     582            ((1,), 1), 
     583            ((1,), 2), 
     584            ((2,), 1), 
     585            ((2,), 2), 
     586            ] 
     587 
     588    """ 
     589 
     590    def test_delete_bar(self): 
     591        assert len(db.Foo) == 4 
     592        assert len(db.Bar) == 2 
     593        assert len(db.Baz) == 4 
     594        bar1 = db.Bar.findone(number=1) 
     595        ex(bar1.t.delete()) 
     596        assert len(db.Foo) == 2 
     597        assert len(db.Bar) == 1 
     598        assert len(db.Baz) == 2 
     599 
     600 
     601class TestOnDeleteKeyRelax1(BaseOnDeleteKeyRelax): 
     602 
     603    format = 1 
     604 
     605 
     606class TestOnDeleteKeyRelax2(BaseOnDeleteKeyRelax): 
     607 
     608    format = 2 
     609 
     610 
    543611class TestOnDeleteEntityListRemove(CreatesSchema): 
    544612