home *** CD-ROM | disk | FTP | other *** search
- // EODatabaseContext.h
- // Copyright (c) 1994, NeXT Software, Inc. All rights reserved.
-
- #import <EOControl/EOControl.h>
- #import <EOAccess/EODefines.h>
-
- @class EODatabase;
- @class EODatabaseChannel;
- @class EOAdaptorContext;
- @class EOAdaptorChannel;
- @class EOModel;
- @class EOEntity;
- @class EORelationship;
- @class EOAdaptorOperation;
- @class EODatabaseOperation;
-
- // EODatabaseContext is the basic EOObjectStore for the access framework. It
- // acts as one of possibly several EOCooperatingObjectStores for an
- // EOObjectStoreCoordinator. It acts as a store for any entities in its
- // EODatabase's model list. When saving EditingContext changes, the EODatabaseContext
- // searches the object graph using the EditingContexts list of inserted, deleted,
- // and updated objects and determines exactly which changes need to be made in
- // the database. It then creates an array of adaptorOperations which it hands to
- // an adaptor channel for execution.
- //
- // The EODatabaseContext knows how to interact with other EOCooperatingObjectStores
- // to save changes made to an object graph in more than one database server.
- //
- // When the EODatabaseContext is asked to fetch or write information to the database
- // is tries to use one of its EODatabaseChannels. If all of it's channels are busy,
- // it broadcasts a EODatabaseChannelNeededNotification in the hopes that some observer
- // can provide a new channel for the context or that an existing channel can be freed
- // up.
- //
- // An EODatabaseContext represents a single transaction scope on the database
- // server, and determines the updating and locking stragegy used by the
- // database layer. It's managed by an EODatabase object, which represents the
- // main connection to the server. If the server supports multiple concurrent
- // transaction sessions, the EODatabase may have several EODatabaseContexts.
- // If the adaptor allows multiple channels per context, then an EODatabaseContext
- // may in turn have several EODatabaseChannels, which handle actual access to
- // the data on the server. An EODatabaseContext by default has no
- // EODatabaseChannels; to create a new EODatabaseChannel, allocate and
- // initialize one with -initWithDatabaseContext:. See EODatabase.h and
- // EODatabase Channel.h for more information.
- //
- // Not all adaptors support multiple contexts per database object.
- //
- // An EODatabaseContext creates an EOAdaptorContext when initialized, and uses
- // this object to actually communicate with the database server.
- //
- // UPDATING AND LOCKING STRATEGIES
- //
- // EODatabaseContext supports three updating stategies. The following sections
- // describe each of the update modes in detail.
- //
- // EOUpdateWithPessimisticLocking:
- //
- // Under pessimistic locking, objects are locked when they're selected. This
- // ensures that no one else can modify the objects until your transaction ends.
- // However, this doesn't necessarily mean that YOUR update will succeed.
- // Other settings at the adaptor or physical database level can cause your
- // update to fail. When pessimistic locking is enabled, a transaction is automatically
- // started on the upon the first access to the database. By default, all selects
- // against the database are performed with "select FOR UPDATE". The transaction is
- // ended either when saveChangesInEditingContext: is invoked and the transaction is
- // committed, or when revertAllObjects is called and the transaction is rolled back.
- // By default, when the transaction is closed, all snapshot are invalidated and all
- // objects based on those snapshots are invalidated (refaulted).
- //
- // EOUpdateWithOptimisticLocking:
- //
- // Under optimistic locking, objects aren't locked on the server: When you
- // attempt to update an object in the database, the object's snapshot is used
- // to ensure that the values in the corresponding database row have not
- // changed since the object was fetched. As long as the snapshot matches the
- // database, the update is allowed to proceed. EOUpdateWithOptimisticLocking
- // is the default update strategy.
- //
- // EOUpdateWithNoLocking:
- //
- // Under the no-locking strategy, objects are never locked. No comparisons
- // are made between the snapshot and the row to ensure that the values in the
- //
-
- typedef enum {
- EOUpdateWithOptimisticLocking,
- EOUpdateWithPessimisticLocking,
- EOUpdateWithNoLocking,
- } EOUpdateStrategy;
-
- @interface EODatabaseContext:EOCooperatingObjectStore
- {
- EODatabase *_database;
- EOAdaptorContext *_adaptorContext;
- EOUpdateStrategy _updateStrategy;
- NSMutableArray *_uniqueStack;
- NSMutableArray *_deleteStack;
- NSMutableArray *_modifiedObjects;
- NSMutableArray *_registeredChannels;
- NSMapTable *_dbOperationsByGlobalID;
- EOObjectStoreCoordinator *_coordinator; // not retained
- EOEditingContext *_editingContext; // not retained
- void *_lockedObjects;
- unsigned _currentGeneration;
- unsigned _concurentFetches;
- NSMutableDictionary *_batchFaultBuffer;
- NSMutableDictionary *_batchToManyFaultBuffer;
- id _currentBatch;
- EOEntity *_lastEntity;
- EOGlobalID *_currentGlobalID;
- NSDictionary *_currentSnapshot;
-
- struct {
- unsigned int preparingForSave:1;
- unsigned int beganTransaction:1;
- unsigned int _RESERVED:30;
- } _flags;
- id _delegate; // not retained
- struct {
- unsigned int willRunLoginPanelToOpenDatabaseChannel:1;
- unsigned int newPrimaryKey:1;
- unsigned int willPerformAdaptorOperations:1;
- unsigned int shouldInvalidateObject:1;
- unsigned int willOrderAdaptorOperations:1;
- unsigned int shouldLockObject:1;
- unsigned int shouldRaiseForLockFailure:1;
- unsigned int shouldFetchObjects:1;
- unsigned int didFetchObjects:1;
- unsigned int _RESERVED:23;
- } _delegateRespondsTo;
- }
-
- - initWithDatabase:(EODatabase *)database;
- // Initializes a newly allocated EODatabaseContext with database as the
- // EODatabase object it works with. Returns self, or nil if no more
- // contexts can be associated with database.
-
- + (EODatabaseContext *)registeredDatabaseContextForModel:(EOModel *)model editingContext:(EOEditingContext *)editingContext;
- // Finds the objectStoreCoordinator for the editingContext and checks to see
- // it already contains a EODatabaseContext cooperating object store for this
- // model. If it does, it returns that context, otherwise it instantiates
- // a new context, adds it to the coordinator, and returns it.
-
- + (Class)contextClassToRegister;
- + (void)setContextClassToRegister:(Class)contextClass;
- // When an EOObjectStoreCoordinator sends an EOCooperatingObjectStoreNeeded
- // notification for an entity in the default model group, if
- // contextClassToRegister is non-nil, an instance of this class is
- // created, the model for the entity is registered, and the
- // context is registered with the requesting ObjectStoreCoordinator.
- // By default this class is EODatabaseContext, but its can be overridden
- // with subclasses.
-
- - (BOOL)hasBusyChannels;
- // Returns YES if the receiver has channels that have outstanding
- // operations (that is, have a fetch in progress), NO otherwise.
-
- - (NSArray *)registeredChannels;
- // Returns an array containing all channels that have been registered for
- // use with this context;
-
- - (void)registerChannel:(EODatabaseChannel *)channel;
- - (void)unregisterChannel:(EODatabaseChannel *)channel;
- // Registers or unregisters a channel with a EODatabaseContext.
- // The given channel is added or removed from the availableChannel pool
- // used to service fetch and fault requests.
- // Registered channels are retained by the context.
-
- - (EODatabaseChannel *)availableChannel;
- // This method checks its list of channels and if it finds one that
- // is not busy it returns it. Otherwise, it posts an
- // EODatabaseChannelNeededNotification with the hope that someone will
- // provide a new channel. After posting the notification, the context
- // checks its list of channels again. If there are still no available
- // channels and the list is completely empty, the context creates one
- // itself, however if the list is not empty and there are no available
- // channels, the method returns nil.
-
- - (EODatabase *)database;
- // Returns the EODatabase that the context works with.
-
- - (EOObjectStoreCoordinator *)coordinator;
- // Returns the context's coordinator or nil if there is none.
-
- - (EOAdaptorContext *)adaptorContext;
- // Returns the EOAdaptorContext used by the EODatabaseContext for
- // communication with the database server.
-
- - (void)setUpdateStrategy:(EOUpdateStrategy)strategy;
- - (EOUpdateStrategy)updateStrategy;
- // These methods set/return the update strategy used by the context. The
- // default is EOUpdateWithOptimisticLocking.
-
- - (id)delegate;
- - (void)setDelegate:(id)delegate;
- // Propagates the delegate to all of its channels.
- @end
-
- @interface EODatabaseContext(EOObjectStoreSupport)
- // The EODatabaseContext is an ObjectStore for relational-style databases
-
- - (id)faultForGlobalID:(EOGlobalID *)globalID editingContext:(EOEditingContext *)context;
- // Creates a to-one fault and register it in the editing context.
-
- - (NSArray *)arrayFaultWithSourceGlobalID:(EOGlobalID *)globalID relationshipName:(NSString *)name editingContext:(EOEditingContext *)context;
- // Create a to-many fault. The relationshipName must correspond to an EORelationship in
- // the EOEntity for the given globalID.
-
- - (void)initializeObject:(id)object withGlobalID:(EOGlobalID *)globalID
- editingContext:(EOEditingContext *)context;
- // Initializes the given instance by filling it with properties based on
- // row data fetched from the adaptor. The snapshot for the given globalID is
- // looked up and those attributes in the snapshot that are marked as class
- // properties in the EOEntity are assigned to the EO. For relationship
- // class properties, faults are constructed and assigned to the object.
-
- - (NSArray *)objectsForSourceGlobalID:(EOGlobalID *)globalID relationshipName:(NSString *)name editingContext:(EOEditingContext *)context;
- // This method is used to fire a to-many fault. The snapshot for the source object
- // is located and the EORelationship corresponding to relationshipName is used to
- // construct a qualifier from that snapshot. This qualifier is the used to
- // fetch the requested objects using objectsWithFetchSpecification:editingContext:
-
- - (void)refaultObject:object withGlobalID:(EOGlobalID *)globalID editingContext:(EOEditingContext *)context;
- // Turn the given Enterprise Object back into a fault.
-
- - (void)saveChangesInEditingContext:(EOEditingContext *)context;
- // Sent by an EditingContext to its ObjectStore to commit changes.
- // Normally the EODatabaseContext is not called directly by this method
- // but is instead called by an EOObjectStoreCoordinator.
-
- - (NSArray *)objectsWithFetchSpecification:(EOFetchSpecification *)fetchSpecification editingContext:(EOEditingContext *)context;
- // The base method to fetch an array of objects into the given context.
- // The database context obtains an available EODatabaseChannel and issues
- // a fetch with the given fetch specification.
- // Raises an exception if an error occurs.
-
- - (BOOL)isObjectLockedWithGlobalID:(EOGlobalID *)gid editingContext:(EOEditingContext *)context;
- // Returns YES if the row corresponding to the given globalID has been locked in
- // an open transaction held by this context.
-
- - (void)lockObjectWithGlobalID:(EOGlobalID *)gid editingContext:(EOEditingContext *)context;
- // Locks that database row corresponding to this globalID in the underlying
- // database server. If a transaction is not already open at the time of the
- // lock request, the transaction is begun and is held open until either
- // saveChangesInEditingContext: or invalidateAllObjects is called.
- // At that point all locks are released.
-
- - (void)invalidateAllObjects;
- - (void)invalidateObjectsWithGlobalIDs:(NSArray *)globalIDs;
- // This will discard the snapshots for the given globalIDs an broadcast an
- // invalidation notification which will cause an EditingContext containing
- // objects fetched from this context to refault those objects. The result
- // is that these objects will be refetched from the database the next time
- // they are accessed.
- @end
-
-
- @interface EODatabaseContext(EOCooperatingObjectStoreSupport)
- // The EODatabaseContext is a CooperatingObjectStore, meaning it
- // can work in concert with other CooperatingObjectStores (including
- // other EODatabaseContexts) to provide unified access to multiple databases
- // under a single EOObjectStoreCoordinator
-
- - (BOOL)ownsGlobalID:(EOGlobalID *)globalID;
- // Returns YES if the gloablID is the type created by the DatabaseContext
- // and if for an entity serviced by this context.
-
- - (BOOL)ownsObject:(id)object;
- // Returns yes if one of the Entities in the models for this databaseContext
- // creates objects of this class.
-
- - (BOOL)handlesFetchSpecification:(EOFetchSpecification *)fetchSpecification;
- // returns yes if the entity identified by the entityName in this fetchSpecification
- // can be found in one of the models owned by theis context's EODatabase.
-
- - (void)prepareForSaveWithCoordinator:(EOObjectStoreCoordinator *)coordinator editingContext:(EOEditingContext *)context;
- // Prepares to save changes. Obtains primary keys for any inserted objects
- // in the EditingContext that are owned by this context.
-
- - (void)recordChangesInEditingContext;
- // Contructs a list of EODatabaseOperations for all changes in the EditingContext
- // that are owned by this context. Forward any relationship changes discovered
- // but not owned by this context to the coordinator.
-
- - (void)recordUpdateForObject:object changes:(NSDictionary *)changes;
- // Applies changes supplied by another context to the DatabaseOperation
- // for that object in this context.
-
- - (void)performChanges;
- // Contructs EOAdaptorOperations for all EODatabaseOperations constructed in
- // during recordChangesInEditingContext and recordUpdateForObject:changes:.
- // Performs the EOAdaptorOperations on an available adaptor channel.
- // If the save succeeds, updates the snapshots in the EODatabasContext to reflect
- // the new state of the server.
- // Raises an exception is the adaptor is unable to perform the operations.
-
- - (void)commitChanges;
- // Instructs the adaptor to commit the transaction. If the commit is
- // successful, any primary and foreign key changes are written back to the
- // saved objects, database locks are released, and an EOObjectsChangedInStore
- // notification is posted describing the committed changes.
- // Raises an exception if the adaptor is unable to commit the transaction.
-
- - (void)rollbackChanges;
- // Instruct the adaptor to rollbackTransaction.
- // Rolls back and changed snapshots, and released all locks.
-
- - (NSDictionary *)valuesForKeys:(NSArray *)keys object:object;
- // Returns properties from the snapshot of the given object. These are used
- // primarily by another context to extract foreign key properties for objects
- // in this context.
- @end
-
-
- @interface EODatabaseContext(EOBatchFaulting)
-
- - (void)batchFetchRelationship:(EORelationship *)relationship forSourceObjects:(NSArray *)objects editingContext:(EOEditingContext *)editingContext;
- // Clear all the faults for the relationship pointed by the source objects and
- // make sure to perform only a single, efficient, fetch (two fetches if the
- // relationship is many to many).
-
- @end
-
- // The snapshotting methods on the context are for maintaining snapshots and
- // information about which snapshots are locked during a transaction.
-
- @interface EODatabaseContext (EODatabaseSnapshotting)
-
- - (void)recordSnapshot:(NSDictionary *)snapshot forGlobalID:(EOGlobalID *)gid;
- // records the database row snapshow the given GlobalID.
-
- - (NSDictionary *)snapshotForGlobalID:(EOGlobalID *)gid;
- // Returns the snapshot assocated with GlobalID, if there is one; else
- // returns nil. Searches first locally (in the transaction scope)
- // and then in the EODatabase.
-
- - (NSDictionary *)localSnapshotForGlobalID:(EOGlobalID *)gid;
- // Returns the snapshot assocated with GlobalID, if there is one; else
- // returns nil. Only searches locally (in the transaction scope)
- // and, not in the EODatabase.
-
- - (void)forgetSnapshotForGlobalID:(EOGlobalID *)gid;
- - (void)forgetSnapshotsForGlobalIDs:(NSArray *)gids;
-
- - (void)recordSnapshots:(NSDictionary *)snapshots;
- // record a bunch of snapshots from a dictionary keyed by Global ID
-
- - (void)registerLockedObjectWithGlobalID:(EOGlobalID *)globalID;
- - (BOOL)isObjectLockedWithGlobalID:(EOGlobalID *)globalID;
- - (void)forgetAllLocks;
- - (void)forgetLocksForObjectsWithGlobalIDs:(NSArray *)gids;
-
- @end
-
-
- // Notifications:
- EOACCESS_EXTERN NSString *EODatabaseChannelNeededNotification;
- // This nofification is broadcast whenever an EODatabaseContext is asked to perform an object
- // store operation and it doesn't have an available databaseChannel. Subscribers can create
- // a new channel and add it to the databaseContext at this time.
-
- @interface NSObject (EODatabaseContextDelegation)
-
- - (BOOL)databaseContext:(EODatabaseContext *)context willRunLoginPanelToOpenDatabaseChannel:(EODatabaseChannel *)channel;
- // When the databaseContext is about to use a channel, it checks to see if the
- // databaseChannel's corresponding adaptorChannel is open. If it is not, it
- // attempts to open the adaptorChannel by sending it an openChannel message.
- // If that doesn't succeed, the databaseContext will ask the adaptorChannel's
- // adaptor to run the login panel and open the channel. The database context
- // gives the delegate a chance to intervene in this by invoking this delegate
- // method. The delegate can return NO to stop the databaseContext from
- // running the login panel. In this case, the delegate is responsible for
- // opening the channel. If the delegate returns YES, the databaseContext
- // will run the login panel.
-
- - (NSDictionary *)databaseContext:(EODatabaseContext *)context newPrimaryKeyForObject:(id)object entity:(EOEntity *)entity;
- // If a newly inserted EO doesn't already have have a primary key set,
- // this delegate is called to generate a key. If the delegate is not implemented,
- // or returns nil, then the DatabaseContext will call
- // [EOAdaptorChannel primaryKeyForNewRowWithEntity:(EOEntity *)entity]
- // to attempt to generate the key.
-
- - (BOOL)databaseContext:(EODatabaseContext *)context failedToFetchObject:(id)object globalID:(EOGlobalID *)gid;
- // Called when a toOne fault cannot find its data in the database. The object is a cleared fault.
- // If this method returns YES, the DatabaseContext assume that the delegate set the information
- // into the object. If it returns NO, the DatatabaseContext will raise the error.
-
- - (NSArray *)databaseContext:(EODatabaseContext *)context willOrderAdaptorOperationsFromDatabaseOperations:(NSArray *)databaseOps;
- // If the delegate responds to this message, it must return an array of adaptor
- // operations. The delegate can fabricate its own array by asking each
- // databaseOperation for its list of adaptor operations and adding them to the
- // array which will eventually be returned by this method. Of course, the delegate
- // is free to optimize, order, or transform the list in whatever way it deems necessary.
-
- - (NSArray *)databaseContext:(EODatabaseContext *)context willPerformAdaptorOperations:(NSArray *)adaptorOps adaptorChannel:(EOAdaptorChannel *)adaptorChannel;
- // The delegate can return a new adaptorOps array which the databaseContext
- // will use in place of the old one. This is useful for applications that
- // may need a special ordering of the adaptor operations so as not to violate
- // any database constraints that may exist.
-
- - (BOOL)databaseContext:(EODatabaseContext *)context shouldInvalidateObjectWithGlobalID:(EOGlobalID *)globalId snapshot:(NSDictionary *)snapshot;
- // Delegate can cause the object to not be invalidated by returning NO.
-
- - (NSArray *)databaseContext:(EODatabaseContext *)context shouldFetchObjectsWithFetchSpecification:(EOFetchSpecification *)fetchSpecification editingContext:(EOEditingContext *)editingContext;
- // Called by objectsWithFetchSpecification:editingContext: to give the delegate the
- // opportunity to satisfy the fetch request from a local cache.
- // If delegate returns nil, the databaseContext with perform the fetch. Otherwise,
- // the returned array is returned as the fetch result.
-
- - (void)databaseContext:(EODatabaseContext *)context didFetchObjects:(NSArray *)objects fetchSpecification:(EOFetchSpecification *)fetchSpecification editingContext:(EOEditingContext *)editingContext;
- // Called by objectsWithFetchSpecification:editingContext: after fetching objects.
-
- // These 4 delegate methods are actually sent by the channel, but are placed
- // here for ease of access. The context and it's channels always have the
- // same delegate.
- - (BOOL)databaseContext:(EODatabaseContext *)context shouldSelectObjectsWithFetchSpecification:(EOFetchSpecification *)fetchSpecification databaseChannel:(EODatabaseChannel *)channel;
- // Invoked from -[EODatabaseChannel selectObjectsDescribedByQualifier:...] to tell the
- // delegate that the channel will select objects as specified by
- // qualifier. The delegate should not modify the qualifier or fetch order.
- // If the delegate returns YES the channel will go ahead and select the
- // object; if the delegate returns NO the channel will skip the select
- // and return.
-
- - (BOOL)databaseContext:(EODatabaseContext *)context shouldUsePessimisticLockWithFetchSpecification:(EOFetchSpecification *)fetchSpecification databaseChannel:(EODatabaseChannel *)channel;
- // Invoked from -[EODatabaseChannel -selectObjectsDescribedByQualifier:...] when
- // pessimistic locking is the update strategy specified on the channels
- // databaseContext. The delegate should not modify the qualifier or fetch order.
- // If the delegate returns YES the channel will lock the rows being selected;
- // if the delegate returns NO the channel will select the rows without locking.
-
- - (void)databaseContext:(EODatabaseContext *)context didSelectObjectsWithFetchSpecification:(EOFetchSpecification *)fetchSpecification databaseChannel:(EODatabaseChannel *)channel;
- // Invoked from -[EODatabaseChannel -selectObjectsDescribedByQualifier:...] to
- // tell the delegate that the channel selected the objects as specified by the
- // qualifier.
-
- - (NSDictionary *)databaseContext:(EODatabaseContext *)context
- shouldUpdateCurrentSnapshot:(NSDictionary *)currentSnapshot
- newSnapshot:(NSDictionary *)newSnapshot globalID:(EOGlobalID *)globalID
- databaseChannel:(EODatabaseChannel *)channel;
- // Invoked from -[EODatabaseChannel fetchObject] when a row is fetched from
- // the database for which the context already has a snapshot. This method is
- // called without first checking whether the snapshots are equivalent (the check
- // would be too expensive to do in the common case) so the receiver may be passed
- // equivalent snapshots. The default behavior is to
- // not update an older snapshot. The delegate can override this behavior by
- // returning a dictonary (possibly newSnapshot) that will be recorded as the
- // updated snapshot -- this will result in an EOObjectsChangedInStoreNotification
- // being posted indicating that all objects corresponding to this globalID are now
- // invalid. If the delegate returns nil, the context will perform the default behavior.
-
- - (BOOL)databaseContext:(EODatabaseContext *)databaseContext shouldLockObjectWithGlobalID:(EOGlobalID *)globalID snapshot:(NSDictionary *)snapshot;
- // Invoked from -[EODatabaseContext lockObjectWithGlobalID:editingContext]. The
- // delegate should return YES if it wants the method to proceed or NO to cause
- // the method to return. Delegates can override the locking mechanism by implementing
- // their own locking procedure and returning NO. The Override method should raise
- // on failure to lock exactly one object.
-
- - (BOOL)databaseContext:(EODatabaseContext *)databaseContext shouldRaiseExceptionForLockFailure:(NSException *)exception;
- // Invoked from -[EODatabaseContext lockObjectWithGlobalID:editingContext:]. This
- // method allows the delegate to suppress an exception that has occurred during the
- // attempt to lock the object.
-
- @end
-
- EOACCESS_EXTERN NSString *EOPrefetchingRelationshipHintKey;
- // This key can be used in the hints dictionary of an EOFetchSpecification
- // passed to the -objectsWithFetchSpecification:editingContext: method.
- // The value is an array of relationship paths that should be prefetched
- // along with the main fetch. For example, if fetching from the Movie
- // entity, you might specify paths of the form "(directors, roles.talent, plotSummary)".
- //
-
- EOACCESS_EXTERN NSString *EOCustomQueryExpressionHintKey;
- // This key can be used in the hints dictionary of an EOFetchSpecification
- // passed to the -objectsWithFetchSpecification:editingContext: method.
- // It should contain a SQL string for performing the fetch. The
- // expression must query the the exact same attributes in the same order
- // as EOF would query if generating the SELECT expression dynamically.
- // If this key is supplied, other aspects of the EOFetchSpecification such
- // as isDeep, qualifier, and sortOrderings are ignored (in that sense this
- // key is more of a directive than a hint).
-
-