home contents changes options help subscribe edit (external edit)

Author

JimFulton?

Status

IsProposal?

Problem

Changes in object implementations often require converting existing objects in a database. This can range from something as simple as renaming or moving a class to complex changes to object representation. When doing this conversion, accesses is needed to stored objects. Often these objects can be found by traversing an object tree, but sometimes this isn't possible or convenient. Often one wants to convert all objects of a given class, no matter where in the database the objects occur.

Proposal

I propose 2 new APIs?. A database API and a storage API:

     class IConversionSupport(zope.interface.Interface):
         """Provide access to 'old' objects

         This interface is provided to support the use case of
         updating existing objects to reflect new data structures or
         class locations.

         This interface uses the concept of "old" objects.  This is to
         emphasize the fact that there are no guarentees that data are
         current.  The emphasis is on converting old data.  There is
         an assumption that any new data written does not need to be
         converted.

         This interface is intended for infrequent use.  It is likely
         that the operations provided are inefficient.  If any attempt
         is made toward optimization, it will aimed at precenting
         excessive load on a ZEO server.

         """

         def old_global_names():
             """Return an iterable of global names used in the database.

             Globals are objects, most notably classes, that are
             referenced by the database but that are stored in Python
             modules.  Their names consist of module-name, class-name pairs
             (2-item iterables).

             Data are obtained without loading objects into memory.
             Therefore, names returned could include globals not
             importable by the calling application.

             Globals that were newly referenced after the start of the
             operation may be excluded.
             """

         def rename(changes, retry=0):
             """Rename one or more global objects

             An iterable of old-new global pairs is provided as an
             argument.  For example, if class C is moved from M1, to
             M2, the database can be updated with::

                db.rename([[(M1, C), (M2, C)]])

             or with::

                db.rename({(M1, C): (M2, C)}.iteritems()]

             Updates are made without loading objects into memory.
             This allows updates even when the named globals cannot be
             imported.

             Only records containing references to the old globals are
             updated.  Each record is updated in a separate transaction.

             This method can raise write-conflict errors.  A retry
             argument can be provided to cause the method to retry
             updates the given number of times.
             """

         def old_oids():
             """Return an interable of "old" database object ids

             Ids for objects added after the start of the operation
             may be excluded.  Ids may be included for objects that
             have been deleted.

             The resulting object ids can be passed to the connection get
             methods to try to get the objects.  Because an object may
             have been removed, code calling get should catch key errors:

                conn = db.open()
                for oid in db.old_oids():
                    try:
                        ob = conn.get(oid)
                    except KeyError:
                        continue
                    if isonstance(ob, class_I_care_about):
                        transaction.begin()
                        ... convert ob
                        transaction.commit()

             """

         def rename(changes):

     class IOldRecordIterableStorage(zope.interface.Interface):
         """Provide low-level iteration over current object records

         Records are required to be current at approximately 
         """

         def record_iternext(next=None):
             """Get the mext database record.

             Ordering is storage dependent.  If the argument is
             ommitted or passed as None, then the first record is returned.
             Each record is a tuple consisting of:

             oid -- The record object id

             serial -- The revision serial (transaction id) for the record

             data -- The raw data for the record

             next -- The next value to pass to record_iternext or
                     None, if this is the last record,

             The next argument must be None or a string.  This means
             also that the return value for next must be None or a string.
             """

`IConversionSupport?' will be provided by databases. `IRecordIterableStorage?` will be provided by storages that play, including `FileStorage` and `ClientStorage?`.

Implementation Status

`IRecordIterableStorage?` has been implemented for `FileStorage`. It needs to be implemented for `ClientStorage?`. `IConversionSupport?' needs to be implemented for databases.



subject:
  ( 11 subscribers )